运算符使用细节

double除法的细节

  1. public static void main(String[] args){
  2. double a = 10 / 4;
  3. double b = 10.0 / 4;
  4. System.out.println(a);
  5. System.out.println(b);
  6. }

image.png
整数相除只会保留整数(无论是变量还是常量都一样),然后再赋给double,因此a为2。10.0为double类型,double/int 或者 double/double 都会保留小数,因此b为2.5。

取余的本质

  1. public static void main(String[] args){
  2. int a = -10 % 3;
  3. int b = 10 % -3;
  4. System.out.println(a);
  5. System.out.println(b);
  6. }

image.pngimage.png
取余的本质是一个公式—— a % b = a - a / b * b 。

前置++和后置++

用临时变量的规则进行解释。
前置++:先自增,再进行相关操作——对于 b = a++,(1) a = a + 1; (2) temp = a; (3) b = temp;
后置++:先进行相关操作,后自增——对于 b = a++,(1) temp = a; (2) a = a + 1; (3) b = temp;

  1. public static void main(String[] args){
  2. int a = 10;
  3. int b = a++;//先把a赋给b,a再加一
  4. int c = 10;
  5. int d = ++c;//c先加一,再把c赋给d
  6. System.out.println("a="+a);
  7. System.out.println("b="+b);
  8. System.out.println("c="+c);
  9. System.out.println("d="+d);
  10. }

image.png

  1. int i = 1;
  2. i = i++;

根据规则进行判断—— (1) temp = i; (2) i = i +1; (3) i = temp; 因此最终i仍然为1。

  1. int i = 1;
  2. i = ++i;

根据规则进行判断—— (1) i = i + 1; (2) temp = i; (3) i = temp; 因此最终i为2。

三元运算符里的++和—:

  1. int a = 10;
  2. int b = 99;
  3. int result = a < b ? a++ : b--;
  4. System.out.println("result=" + result);
  5. System.out.println("a=" + a);
  6. System.out.println("b=" + b);

a<b为真,返回a++,因为是后置++,因此返回a后,a再加一,对b不进行操作。
image.pngimage.png

赋值语句的bool值问题

  1. boolean a = false;
  2. if(a = false)//这里是赋值表达式
  3. System.out.println(111);//没有输出
  4. System.out.println(a);//a最后仍为false

boolean类型在实际编译的过程都是转化为int类型进行底层运算的,false转为0,true转为非0整数。 if(a = false) ==> if((a = 0) != 0) 也就是a先赋为0,再判断是否不等于0,结果为false,不输出。
同理 if(a = true) ==> if((a = 1) != 0) a先赋为1,再判断是否不等于0

短路与和逻辑与

短路与:&& 逻辑与:&
两者的共同点:判断结果相同,全部都是true则为结果true,出现一个false则结果为false。
两者的不同点:对于&&短路与而言,如果第一个条件为false,后面的条件就不会再判断。对于&逻辑与而言,如果第一个条件为false,后面的条件仍然会判断。&&效率较高。

  1. public static void main(String[] args){
  2. int a = 5,b = 9;
  3. if(a < 1 && ++b < 50) {//短路与
  4. System.out.print("");
  5. }
  6. System.out.print("a=" + a + " b=" + b);//结果b仍为9,说明后面没有判断,++没起作用
  7. }

image.png

  1. public static void main(String[] args){
  2. int a = 5,b = 9;
  3. if(a < 1 & ++b < 50) {//逻辑与
  4. System.out.print("");
  5. }
  6. System.out.print("a=" + a + " b=" + b);//b为10,++起作用了
  7. }

image.png
同理,短路或(||)与逻辑或(|)的区别就是——短路或如果第一个条件为true,则第二个条件不会判断,最终结果为true,效率高。 而逻辑或不管第一个条件是否为true,第二个条件都要判断
开发中基本使用 && 和 || 。
逻辑异或(a ^ b)—— 当a和b不同时,结果为true,否则为false。

四种进制

进制简介

二进制:0-1,满2进1,以0b或0B开头
十进制:0-9,满10进1。
八进制:0-7,满8进1,以数字0开头
十六进制:0-9以及A(10)-F(15),满16进1,以0x或0X开头表示,A-F不区分大小写

二进制转十进制

从最低位开始(最右边),将每个位上的数提取出来,乘以2的(位数-1)次方,然后求和。对于八进制和十六进制转十进制,只需要把2换成8或16即可。

十进制转二进制

将该十进制数不断除以2,直到商为0为止,然后将每步得到的余数倒过来,就是对应的二进制。对于十进制转八进制和十六进制,只需把除以2改为除以8或16。

二进制转八进制

从低位开始(最右边),将二进制数每三位一组(因为二进制111正好为7),转成对应的八进制数即可。如果最后不够三位,在最前面补零。
举例:0b11010101 —— 101为5,010为2,011为3,按顺序摆放,结果为 0325。

二进制转十六进制

从低位开始,将二进制数每四位一组(因为二进制1111正好为15),转成对应的十六进制数即可。
举例:0b11010101 —— 0101为5,1101为13即D,排好序结果为0xD5。

八进制转二进制

将八进制数每一位,转成对应的一个三位的二进制数即可,不足三位前面补零。
举例:0237 —— 2为10(即010),3为11(即011),7为111,整体为 0b010 011 111。

十六进制转二进制

将十六进制数每一位,转换成对应的一个四位的二进制数即可,不足四位前面补零。
举例:0x23B —— 2为 0010,3为 0011,B为 1011,整体为 0b0010 0011 1011。

位运算符

原码、反码、补码

  1. 二进制的最高位是符号位——0表示整数,1表示负数。
    2. 正数的原码,反码,补码都一样(三码合一)。
    3. 负数的反码 = 它的原码符号位(也就是最高位)不变,其它位取反(1变0,0变1)。
    4. 负数的补码 = 反码 + 1。
    5. 数字0的反码,补码都是0。
    6. Java中的所有数都是有符号的。
    7.在计算机运算的时候,都是以补码的方式进行运算的。当我们看运算结果的时候,要看它的原码。

    位运算符介绍

    按位与 &:两位全是1,结果为1,否则为0。
    按位或 | :两位有一个为1,结果为1,否则为0。
    按位异或 ^ :两位一个为0,一个为1(也就是不同),结果为1,否则为0。
    按位取反 ~ :把0变成1,把1变成0。
    算数右移 >> :从最低位开始删除n个字符,如果该数为正,则高位补0,若为负数,则高位补1
    算数左移 << :从符号位右边开始删除n个字符,不分正负数,低位补0
    逻辑右移(无符号右移)>>> :从最低位开始删除n个字符,不管是正数还是负数,高位都补零。
    举例:
    2&3 的计算——
    2的原码为 00000000 00000000 00000000 00000010
    由三码合一原则得,2得补码也为 00000000 00000000 00000000 00000010
    同理得3的补码为 00000000 00000000 00000000 00000011
    2&3为 00000000 00000000 00000000 00000010 (根据补码进行计算),这是一个正数,原码与补码相同,得到2&3为2。

~ -2 的计算:
先得到 -2 的原码 10000000 00000000 00000000 00000010 (可以简写成 10000010)
再得到 -2 的反码 11111111 11111111 11111111 11111101
最后得到 -2 的补码 11111111 11111111 11111111 11111110 (补码等于反码加一)
~ -2操作后为 00000000 00000000 00000000 00000001
这就是运算后的补码,因为操作之后变成了一个正数,因此三码合一,运算后的原码与补码相同,得到 ~ -2为 1。

~2 的计算:
先得到 2的原码 00000000 00000000 00000000 00000010,根据三码合一原则,补码相同。
~2操作后为 11111111 11111111 11111111 11111101 得到运算后的补码。
再得到运算后的反码 11111111 11111111 11111111 11111100 (反码等于补码加一)
最后得到运算后的原码 10000000 00000000 00000000 00000011 (注意最高位不变)
根据原码计算,得到 ~2 为 3。

主要就是注意:计算后得到的是补码,根据正数还是负数进行转换,转换成反码,再转换成原码,反码转原码最高位不变。

20 >> 2 的计算:
20的原码为 00000000 00000000 00000000 00010100 (可简写为 00010100
20的补码与原码相同,为 00010100,向右删除2位,结果为 000101,然后在高位补上2个0(因为是正数),结果为 00000101,十进制为5(也就是20 / 2 / 2)

-20 >> 2 的计算:
-20的原码为 10000000 00000000 00000000 00010100 (简写为 10010100,第一位是符号位)
-20的反码为 11101011 补码是 11101110,向右删除2位,结果为 111011,在高位补上2个1,结果为 11111011(计算后的补码),计算后的反码为 11111010,计算后的原码为 10000101,得到结果为 -5 (注意负数不一定是除以n个2,比如 -7>>2,结果为-2)。

-20 >>> 2的计算:(注意这里负数就不能用简写了,因为补充的内容与符号位相反)
-20的原码是 10000000 00000000 00000000 00010100
-20的反码是 11111111 11111111 11111111 11101011
-20的补码是 11111111 11111111 11111111 11101100
向右删除2位,结果为 11111111 11111111 11111111 111011
在高位补上2个0,结果为 0011111111 11111111 11111111 111011 (操作后的补码)
这里需要注意一点—— >>>运算符的结果,就是操作后的补码!所以最终结果为 1073741819(也就是这个补码所对应的十进制值,不用再计算它的原码了)。