一、运算符 和 表达式 概述
1、运算符是指进行特定操作的符号,也叫操作符。通过运算符可以对一个或多个值进行运算,并获取运算结果。
2、表达式是指由数字、算符、数字分组符号(括号)、自由变量和约束变量等以能求得数值的有意义排列方法所得的组合。
说明:用运算符连起来的式子叫做表达式,如a+b,13+a;单独一个变量(if语句中的),也可叫表达式。
注意:
1、表达式有意义时,会先自动计算表达式结果,再赋值或打印。
2、表达式中的数据类型不同时,表达式的运算结果将会是数据范围大的那种。(byte/short/char 这三种类型在做+运算的时候,会首先被自动提升为int类型,再计算)
3、凡是用了比较运算符的表达式,其结果都是boolean型,这种表达式也叫条件表达式。
二、运算符分类
1、算术运算符 + - * / % ++ --
基本四则运算运算和数学一样(/除外)。
1.1 /运算符
表达式 整数类型/整数类型 结果为整数类型(小数部分精度自动损失)。
表达式中有浮点数(浮点数范围大,整数类型会自动隐式转换为浮点数),表达式结果返回浮点数类型。
1.2 %运算符 取余运算
语法: 被模数/模数;
取余运算。表达式结果的正负号与被模数相同,与模数无关。
1.3、+运算符
(1)对于数值来说,就是加法运算
(2)对于char类型来说,计算之前,char会被提升为int在做加法运算。
(3)对于字符串String来说,加号代表字符串连接操作(String类两个方向都重载了+运算符),结果返回字符串。
1.4、自增运算符 ++ (注意区分变量和表达式。 变量立刻加1,表达式看前++还是后++)
使变量在自身基础上加1。两种方式 后++(a++)和 前++(++a),不管哪种方式,只要使用自增运算符,原变量a立即+1。
差异: 表达式a++ 和 ++a 的值不同 //变量a 和 a++ 是两回事。 a是变量, a++ 或 ++a 是表达式
表达式a++结果是原变量的值,表达式++a结果是 原变量的新值即自增以后的值。
注意:自增运算符不会改变变量本身的数据类型(比如,byte,short即使自增,也不会升级成int)
1.5、同理自减运算符 --
使变量在自身基础上-1。两种方式 后--(a--)和 前--(--a),不管哪种方式,只要使用自增运算符,原变量a立即-1。
差异:表达式a-- 和 --a 的值不同 //变量a 和 a--是两回事。 a是变量, a-- 或 --a 是表达式
表达式a--是原变量的值,表达式--a是 原变量的新值即自减以后的值。
2、赋值运算符 = -= += *= /= %=
将等式右边的值赋值给左边的变量。赋值运算符不改变左值数据类型。
注意:= 两边数据类型不同的时候,可能出现错误的赋值直接编译不通过(大范围赋值给小范围)。其他情况会将右值自动隐式转换成和左值相同的数据类型。
exp:
short s1 = 10;
s1= s1+2; //编译失败,右边表达式自动升级为int,int赋值给byte 编译不通过
s1+=2; //s1=12,赋值运算符不改变左值数据类型,因此s1仍然为byte
进阶:关于变量赋值
如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
如果变量时引用数据类型(对象或数组),此时赋值的是变量所保存的地址值。//此方法会导致变量指针指向堆内同一代码块
3、比较运算符(关系运算符)
比较运算符的结果都是boolean型,也就是要么是true,要么是false。所以凡是用了比较运算符的表达式,其结果都是boolean型,这种表达式也叫条件表达式。
注意:比较运算符比较的时候,如果运算符两边基本数据类型不一致,会自动变量提升以后,再比较值。
如果比较运算符比较的时候,如果运算符两边是引用数据类型,则是比较两边的地址是否一致
4、逻辑运算符 (注意区分位运算符)
逻辑运算符操作的都是boolean类型的变量(逻辑运算符操作的变量多数来自于上面比较运算符比较的结果)。
异或:a和b 相异 就true
结论:开发中,推荐使用短路版,因为目的是判断整个表达式逻辑,而不是计算表达式。短路版效率更高。
逻辑与& 和 短路与&& 表达式1 &(&)表达式2;
& 和 && 的运算结果相同。
差异:
逻辑与: 会将表达式1和表达式2都进行计算得到结果后,再判断整个逻辑表达式。
短路与:一旦表达式1判定为false,则整个逻辑表达式已经为false,就不会再计算表达式2(表达式2被短路)。
exp:
boolean b2 = false;
int num2 = 3;
if(b2 && (num2++>0)) {} //此时b2为false,整个表达式已经为false,短路与不会再判定num2++, 所以num2依然为3
逻辑或| 与 短路或 || 表达式1 |(||)表达式2;
| 和 || 的运算结果相同
差异:
逻辑或:会将表达式1和表达式2都进行计算得到结果后,再判断整个逻辑表达式。
短路或:一旦表达式1判定为true,则整个表达式已经为true,就不会再计算表达式2(表达式2被短路)
5、三元运算符 (可简化某些简单的if...else 语句,但是如果有嵌套,为了保持代码可读性,还是用if...else吧)
说明:条件表达式结果为boolean类型,根据条件表达式的布尔计算结果,决定执行表达式1 或 表达式2
三元运算符可以嵌套使用 (嵌套后可读性很差,不建议嵌套)
注意:由于可以声明一个变量去接收 表达式1 或 表达式2 都可以计算出某个结果。因此表达式1 和 表达式2 的结果需要尽量是声明变量的数据类型,至少要能够隐式转换成变量的类型(实际上一定会发生隐式转换)。
exp: double num = (m>n)?2 : 1.0 // 表达式1为int,表达式2为double。 都可以赋值给变量double num,只是2会被隐式转换。
exp: Object o1 = true?new Integer(1) : new Double(2.0) ;
System.out.println(o1); //输出1.0
Integer 自动拆包为1,隐式转换为 double型1.0。再自动装包为Double(1.0),此时Object o1= new Double(1.0) 。println(o1)指 o1.toString。 由于多态性,自然是用Double类的 toString,因此输出10
6、位运算符(自己开发极少使用,但看其他API的原码会有)
位运算是直接对整数型数据的二进制(注意计算机底层数据都是以补码储存)进行运算。
注意:位运算符的 &、| 、^ 标识和 逻辑运算符 相关标识长得一模一样。区别方法,就是看运算符两边的表达式的类型,如果是布尔类型就是逻辑运算符。如果是数值类型,就是位运算符。
算法: (一定要想象)
1、左移<<(补最低位),先写出当前数的二进制。然后根据要求进行位运算,溢出部分丢弃,空出部分补0。
2、右移>>(补最高位),先写出当前数的二进制。然后根据要求进行位运算,溢出部分丢弃,空出部分负数补1正数补0。(因为负数最高位是1,正数最高位是0)
3、无符号右移>>>(补最高位),不管操作数是正数负数,一律补0
4、其他位运算符很简单,直接把操作数二进制的补码写出来,按照规则直接操作就是了。
重要结论(可自行用算法验证):
1、<<:在一定范围内,每向左移1位,相当于*2
2、 >>:在一定范围内,每向右移1位,相当于/2
面试题:最高效的方式计算2*8? 2<<3 或 8<<1 //计算机本身是二进制,二进制操作自然是最高效方式
三、运算符的优先级
注意:表格里面一元运算符、三元运算符、赋值运算符 是从右向左运算,其余都是从左向右运算。
技巧:() 优先级那么高,可以利用()提高任何优先级。