java笔记5-运算符

运算符涉及到一些运算(这句是废话) ,需要关注精度、溢出、运算优先级、类型自动提升、强制转型等问题,会在下面的内容中逐一讲解;当然了解计算在内存中的本质才是学好的关键。

分类:

  按照操作数的数目进行分类  单目运算符 a++  双目运算符a+b 三目运算符(a>b)?x:y  这么分类好像没有任何意义

  按照功能进行分类:

    算数运算符

      +  -  *  /  %  ++  --

      这里整数除0会抛异常,小数除0会得到Infinity

    赋值运算符

      =  +=  -=  *=  /=  %=

      单独的算数计算相对比较简单,因此把算数运算符和赋值运算符一起讲解,需要搞清楚两种运算符之间的区别

    关系运算符(比较)

      >  >=  <  <=  !=  ==  

      关系运算符比较后会返回boolean类型true false 

    逻辑运算

      &(逻辑与)  |(逻辑或)  ^(逻辑异或)  !(逻辑非)  &&(短路与)  ||(短路或)

      逻辑运算符一般用来对多个关系运算符做判断,前后连接的是boolean结果,没有计算机基础的话可能对这几个符号比较陌生,这里解释下

      &:前后都是true才返回true     |:前后有一个true就返回true    ^:前后结果不一致返回true  !:单目运算符 将boolean的值取反

      &&:当第一个条件是false时,后面的就不需要看了,如果左边的是false的时候可以提高性能     &&:当第一个条件是true时,后面的就不需要看了       

    位bit运算

      &(按位与)  |(按位或)  ^(按位异或)  ~(按位取反)  <<(按位左位移)  >>(按位右位移)  >>>(按位右位移无符号)

      讲位运算之前,需要先科普下计算机的原码、反码、补码。原码展示的是一个数的二进制数 int类型的2 就是 00000000 00000000 00000000 00000010 至于反码和补码也都是用二进制数表示,只是和原码不同的表示方式而已,在java语言中,就是以补码的形式存在的。所以我们先要搞清楚原码和补码之间的相互转换

      原码---->反码--->补码

        正数:原码、反码、补码都是一样的(-0,-128这些特殊情况后续讲解)

        负数:

          原码--->反码   符号位不变,剩余位按位取反

          反码--->补码  末位+1

          原码--->补码  符号位不变,剩余位按位取反,末位+1

      补码---->反码--->原码

        正数:原码、反码、补码都是一样的

        负数:

          补码--->反码 符号位不变,剩余位按位取反

          反码--->原码 末位+1

          补码--->原码 符号位不变,剩余位按位取反,末位+1

System.out.println(~-4);  //3
System.out.println(3&5);  //1
System.out.println(-2<<1); //2147483644

      概念讲解完了,来分析下上面几行代码的实际步骤

      -4的按位取反:

        -4的原码是 10000000 00000000 00000000 00000100

        -4的反码是 11111111     11111111      11111111      11111011

        -4的补码是 11111111 11111111 11111111 11111100               ---这就是-4这个数在java中的实际展示

        -4的按位取反(取反以后还是补码的形式) 00000000 00000000 00000000 00000011

        -4按位取反后的原码  00000000 00000000 00000000 00000011         ---所以-4按位取反以后得到的结果是3

      3&5:       

        3的原码是 00000000 00000000 00000000 00000011

        3的补码是 00000000 00000000 00000000 00000011

        5的补码是 00000000 00000000 00000000 00000101  

        按位与1相当与true 0相当与false

        按位与的结果(补码) 00000000 00000000 00000000 00000001

        正数的补码还是其本身 所以3&5结果是1

      -10>>>1 

        -8的原码是 10000000 00000000 00000000 00001000

        -8的反码是 11111111 11111111 11111111 11110111

        -8的补码是 11111111 11111111 11111111 11111000

        无符号右移一位后(补码)  01111111 11111111 11111111 11111100    因为正数的补码是其本身,转化成原码得到十进制 2147483644

        

 

精度

  整型的计算是精确的,浮点型的是不精确的,尽量不要使用浮点型去计算,这里有一个没解决的问题为什么x是0.1y是0.0999.....

int a = 5;
int b = 5/2;   //b=2
double x = 1.0/10;      //0.1
double y = 1-9.0/10;   //0.09999999999999998

溢出

  整数由于存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会出错,却会得到一个奇怪的结果

  整数运算在除数为0时会报错,而浮点数运算在除数为0时,不会报错,但会返回几个特殊值

     

int a =2147483647;
a=a+1;     //-2147483648  
double d1 = 0.0 / 0; // NaN
double d2 = 1.0 / 0; // Infinity
double d3 = -1.0 / 0; // -Infinity

类型自动提升

  类型自动提升考虑的是不同基本类型之间的计算,提升的方法,可以参考java核心卷I:

  如果两个操作数其中有一个是double类型,另一个操作就会转换为double类型。

  否则,如果其中一个操作数是float类型,另一个将会转换为float类型。

  否则,如果其中一个操作数是long类型,另一个会转换为long类型。

  否则,两个操作数都转换为int类型。

一些容易出错的地方

byte x = 1;
x=x+1 //报错
x=(byte)(x+1);  //x是变量空间里的byte类型  1是常量区里的int类型1 相加的时候会类型自动提升到int,再次赋值给byte类型的时候需要强制类型转换
x+=1 //这里不会报错

 

  同样是byte类型 x=x+1会报错,报错的原因在代码在代码注释中已经解释过了,x+=1为什么不会报错,这里有两种解释,

    一种在笔记3中提到过,也是赋值运算符的特性,如果等号右边是常量则 会自动把int类型的1变成byte类型的1

    另一种解释:复合赋值 E1 op= E2等价于简单赋值E1 = (T)((E1)op(E2))其中T是E1的数据类型,op为操作符 我个人倾向第二种解释更好理解些,当然同时引申出来的问题就是复合赋值会有精度的损失。

关于赋值运算的运算解析 博客https://blog.csdn.net/honeygirls/article/details/81321512说的很详细,这里引用下

 

 

 

 

 

 

  

posted @ 2019-05-22 14:38  国际惯例  阅读(213)  评论(0编辑  收藏  举报