Java运算与控制

173 阅读10分钟

Java运算与控制

运算符

运算符是一种特殊的符号,用来表示数据的运算,赋值或者比较等

算术运算符

操作符描述例子
+加法 - 相加运算符两侧的值A + B 等于 30
-减法 - 左操作数减去右操作数A – B 等于 -10
*乘法 - 相乘操作符两侧的值A * B等于200
/除法 - 左操作数除以右操作数B / A等于2
取余 - 左操作数除以右操作数的余数B%A等于0
++自增: 操作数的值增加1B++ 或 ++B 等于 21(区别详见下文)
--自减: 操作数的值减少1B-- 或 --B 等于 19(区别详见下文)
+字符串连接“lns”+"love"+"zlr" = "lnslovezlr"

代码讲解

//  / 整除使用 
// 注意:整数被0除将会产生一个异常,浮点书被0除将会得到Infinity结果
10 / 4; // 2
10.0 / 4 // 2.5 
double d = 10 / 4; // 2.0  int -> double
double i = 10.0/0; // Infinity 无穷的意思// % 使用
// 取模的本质 公式: a % b = a - a / b * b (当 a 是整数)   a % b = a - (int)a / b * b (当 a 是浮点数,但是是近似值)
10 % 3 // 1
-10 % 3 // -1
10 % -3 // 1
-10 % -3 // -1
// 总结得出取模的正负取决于被模数

关系运算符

关系运算符的结果都为boolean类型, true / false

关系运算符组成的表达式我们称为关系表达式

运算符描述例子
==检查如果两个操作数的值是否相等,如果相等则条件为真。(A == B)为假
!=检查如果两个操作数的值是否相等,如果值不相等则条件为真。(A != B) 为真
检查左操作数的值是否大于右操作数的值,如果是那么条件为真。(A> B)为假
<检查左操作数的值是否小于右操作数的值,如果是那么条件为真。(A <B)为真
>=检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真。(A> = B)为假
<=检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真。(A <= B)为真
instanceof用于判断对象的运行类型是否为某某类型或者是某某类型的子类“XX” instanceof String 为真

逻辑运算符

用于连接多个条件(多个关系表达式)

操作符描述
&& 短路与如果某一个条件为false,则后面的条件不用判断,结果为false
& 逻辑与全部条件都要执行
II 短路或如果某一个条件为true,则后面的条件不用判断,结果为true
I 短路或全部条件都要执行
!取反取反:true -> false ; false -> true
^异或如果两个条件都为true或者false则返回false,反之返回true

举例

image-20220111141500121

习题练习

// 习题1
int x = 5,y = 5;
if(x++ == 6 & ++y == 6) x = 11; // 逻辑与 &  ;  结果 x = 6, y = 6
​
// 习题2
int x = 5,y = 5;
if(x++ == 6 && ++y == 6) x = 11; // 短路与 &&  ;  结果 x = 6, y = 5
​
// 习题3
int x = 5,y = 5;
if(x++ == 5 | ++y == 5) x = 11; // 逻辑或 |  ;  结果 x = 11, y = 6
​
// 习题4
int x = 5,y = 5;
if(x++ == 5 || ++y == 5) x = 11; // 短路或 ||  ;  结果 x = 11, y = 5
​
// 习题5
boolean x = true, y = true;
short z = 46;
if((z++ == 46) && (y = true)) z++;
if((x = false) || (++z == 49)) z++;
// 结果:z = 50

赋值运算符

将某个运算后的值赋给指定的变量

操作符描述例子
=简单的赋值运算符,将右操作数的值赋给左侧操作数C = A + B将把A + B得到的值赋给C
+ =加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数C + = A等价于C = C + A
- =减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数C - = A等价于C = C - A
* =乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数C * = A等价于C = C * A
/ =除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数C / = A,C 与 A 同类型时等价于 C = C / A
(%)=取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数C%= A等价于C = C%A
<< =左移位赋值运算符C << = 2等价于C = C << 2
>> =右移位赋值运算符C >> = 2等价于C = C >> 2
&=按位与赋值运算符C&= 2等价于C = C&2
^ =按位异或赋值操作符C ^ = 2等价于C = C ^ 2
=按位或赋值操作符C= 2等价于C = C2

注意细节

  1. 运算顺序从右往左
  2. 复合赋值运算符会进行类型转换(上文强制类型转换中有详细介绍)

条件运算符

条件运算符也被称为三元运算符。该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。

// 如果条件的表达式为 true ,运算后的结果表达式1,反之运行表达式2
// 格式:条件表达式 ?表达式1 : 表达式2 
// 可以理解为如下
if(true) 
    return 表达式1 
else 
    return 表达式2 

位运算

进制转换

其他进制转换成十进制

规则:从最低位(右边)开始,将每个位上的数提取出来,乘以进制(其他进制的进制数)的(位数-1)次方,然后求和

// 举例:八进制转换成十进制
0234 = 4*8^0 + 3*8^1 + 2*8^2 = 156

十进制转换成其他进制

规则:将该数不断除以进制(其他进制的进制数),直到商为0为止,然后将每步得到的余数倒过来,就是对应的进制数

举例: 十进制转换成二进制

// 将34转换成二进制 0B0010_0010
34 / 2 = 17  34 % 2 = 0
17 / 2 = 8   17 % 2 = 1
8 / 2 = 4    8 % 2 = 0
4 / 2 = 2    4 % 2 = 0
2 / 2 = 1    2 % 2 = 0
1 / 2 = 0    1 % 2 = 1 
// 结果反转 10_0010 高位补齐 -> 0010_0010

位运算符

操作符描述例子
如果相对应位都是1,则结果为1,否则为0(A&B),得到12,即0000 1100
|如果相对应位都是 0,则结果为 0,否则为 1(A | B)得到61,即 0011 1101
如果相对应位值相同,则结果为0,否则为1(A ^ B)得到49,即 0011 0001
按位取反运算符翻转操作数的每一位,即0变成1,1变成0。(〜A)得到-61,即1100 0011
<<按位左移运算符。左操作数按位左移右操作数指定的位数。A << 2得到240,即 1111 0000
>>按位右移运算符。左操作数按位右移右操作数指定的位数。A >> 2得到15即 1111
>>>无符号右移,按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。A>>>2得到15即0000 1111

计算机底层运行实例

引入原码,反码,补码

  • 二进制的最高位是符号位:0表示正数,1表示负数
  • 自然数的原码,反码,补码都是相同的(0的反码,补码都是0)
  • 负数的反码等于它的原码符号位不变,其他位取反
  • 负数的补码等于它的反码+1(负数的反码等于它的补码-1)
  • 计算机运行的数据以补码的方式进行运算,但是输入输出的都是原码

注意细节

无符号右(>>>):无论是正数还是负数,都是高位补0

负数右移(>>):高位补符号位1 ; 负数左移(<<):低位补0

正数右移(>>):高位补符号位0 ; 正数左移(<<):低位补0

// 运用补码原码知识
// 注意位运算操作的都是补码而非原码!!
System.out.println(2 & 6); // 2
/*
 * 讲解计算机运行过程
 * 运算用补码
 * 1. 2的原码 ==> 00000000 00000000 00000000 00000010 == 2 的补码
 * 2. 6的原码 ==> 00000000 00000000 00000000 00000110 == 6 的补码
 * 运算结果为原码
 * 3. 按位与& ==> 00000000 00000000 00000000 00000010 == 6 的原码 结果为2
 */
​
System.out.println(~-2); // 1
/*
 * 讲解计算机运行过程
 * 运算用补码
 * 1. -2的原码 ==> 10000000 00000000 00000000 00000010
 * 2. -2的补码 ==> 11111111 11111111 11111111 11111110
 * 3. ~-2的补码 ==> 00000000 00000000 00000000 00000001
 * 运算结果为原码
 * 3. 原码结果为 00000000 00000000 00000000 00000001 == 1
 */
​
System.out.println(-2 >> 3); // -1
/*
 * 讲解计算机运行过程
 * 运算用补码
 * 1. -2的原码 ==> 10000000 00000000 00000000 00000010
 * 2. -2的补码 ==> 11111111 11111111 11111111 11111110
 * 3. -2>>3的补码 ==> 11111111 11111111 11111111 11111111
 * 4. -2>>3的反码 ==> 11111111 11111111 11111111 11111110
 * 运算结果为原码
 * 5. 原码结果为 10000000 00000000 00000000 00000001 == -1
 */

运算符优先级

类别操作符关联性
后缀() [] . (点操作符)左到右
一元expr++ expr--从左到右
一元++expr --expr + - ~ !从右到左
乘性* /%左到右
加性+ -左到右
移位>> >>> <<左到右
关系> >= < <=左到右
相等== !=左到右
按位与左到右
按位异或左到右
按位或左到右
逻辑与&&左到右
逻辑或左到右
条件?:从右到左
赋值= + = - = * = / =%= >> = << =&= ^ ==从右到左
逗号左到右

控制结构

控制循环类型

  • 顺序控制:程序从上到下逐行地执行,中间没有跳转和任何判断

  • 分支控制:if - else 判断

    // 单分支
    if(条件表达式){ 
        执行语句;
    }
    // 双分支
    if(条件表达式){ 
        执行语句;
    }else{
        执行语句;
    }
    // 多分支
    if(条件表达式){ 
        执行语句;
    }else if(条件表达式){
        执行语句;
    }else{
        
    }
    
  • 嵌套分支:在一个分支结构中嵌套另一层分支结构(最好不要超过三层)

  • switch分支结构

    switch(表达式){ // 表达式有具体的值 
           case 常量 1:语句一; // 表达式代表一个具体值,常量1与该值进行比较,相同则运行语句一
                      break; // 退出switch循环,否则直接进行语句2的运行(无需比较常量2)
    ​
           case 常量2:语句二; // 表达式代表一个具体值,常量2与该值进行比较,相同则运行语句二
                      break; 
           ....
           default: 最后一个语句; // 当不等于所有值,则默认执行default语句   
    }
    

    细节注意:

    1. 表达式的数据类型必须和常量的数据类型必须相同 或者 可以自动转换的数据类型(必须是其他类型转换成int类型 比如:byte(表达式类型) - > int(常量类型))
    2. 表达式中具体值和常量的数据类型只能是(byte,short,int,char,enum,String)中的其中一个
    3. default关键字是可选择添加的
  • for循环控制 && while循环控制 && do...while循环语句

    // 基本语法
    for(循环变量初始化;循环条件;循环变量的变化){
        循环操作(可以多条语句);
    }
    运行顺序: 循环变量初始化 --> [ 循环条件 --> 循环操作 --> 循环条件的变化 ]
       
    ​
    // 等价于while x
    循环变量初始化;
    while(循环条件){
        循环操作(可以多条语句);
        循环变量的变化;
    }
    ​
    // 等价于do...while  先执行后判断
    循环变量初始化;
    do{
        循环操作(可以多条语句);
        循环变量的变化;
    }
    while(循环条件);
    

中断控制流程语句

break语句

概念:用于退出当前循环

// 不带标签的语句
for(int i = 0;i < 10;i++){
    for(int j = 0;j < 10;j++){
        break; // 默认中断当下循环
    }
}
​
// 带有标签的语句(格式: 标签名自定义 + :)
label1:
    for(int i = 0;i < 10;i++){
        label2:
        for(int j = 0;j < 10;j++){
            break label1; // 中断标签1的循环
        }
    }
​
// switch 中断语句
switch(表达式){ // 表达式有具体的值 
       case 常量 1:语句一; // 表达式代表一个具体值,常量1与该值进行比较,相同则运行语句一
                  break; // 退出switch循环,否则直接进行语句2的运行(无需比较常量2)
​
       case 常量2:语句二; // 表达式代表一个具体值,常量2与该值进行比较,相同则运行语句二
                  break; 
       ....
       default: 最后一个语句; // 当不等于所有常量,则默认执行default语句   
}
​

continue语句

概念:结束本次循环,继续进行下一次循环

// 不带标签的语句
for(int i = 0;i < 10;i++){
    for(int j = 0;j < 10;j++){
        continue; // 默认结束本次循环,而非退出循环,注意和break的区别
    }
}
​
// 带有标签的语句(格式: 标签名自定义 + :)
label1:
    for(int i = 0;i < 10;i++){
        label2:
        for(int j = 0;j < 10;j++){
            continue label1; // 结束label1的本次循环,跳转到label标签匹配的循环首部
        }
    }

return语句

概念:表示跳出当前所在的方法,如果该方法是main主方法,则相当于结束程序

注意:break 和 continue 必须使用在 loop 或者 switch中,而return可以用在方法的任何位置