第一章 第二节 运算符和表达式
运算符的概念:
对数据进行运算的操作符
在Java中,针对运算符的操作数的多少,可以分为单目运算符(比如b++涉及到一个变量的)、二目运算符(比如num1+num2这种涉及到两个变量的)、三目运算符。
表达式的概念:
由数据/变量/常量加上运算符组成的代码
num1+num2
‘a’+10
cc=65535
表达式的特点:表达式可以对数据进行指定的运算,表达式还会有一个结果。
1.算术运算符
用于数学上的算术运算
1.1 加号+
Java中的+有三种用法:
1) 对数值型进行加法运算。(数值型:byte、short、int、long、float、double、char)
2) 正号。
3) 拼接字符串。
int i = 1;
char c = 'a';
// 加法
i = i + c;
// 正号
i = +c;
// 拼接字符串
String s = "Ja" + "va";
1.2 减号-
Java中的-有两种用法:
1) 对数值型进行减法运算。
2) 负号。
1.3 乘号*
对数值型进行乘法运算。
1.4 除号/
对数值型进行除法运算。
两个整数相除,结果也是整数,会舍弃小数部分,这叫地板除。如果除数或者被除数中有一个是小数,那结果就是小数。
如果除数或者被除数中有char类型,那么char类型会自动转化成整数的。
1.5 求余%
对数值型进行余数的运算。
注意:
1) 小数也可以求余。
2) 负数也可以求余,余数符号由第一表示式的符号决定。
1.6自增自减
对数值型(除了boolean)进行自增1或自减1的运算。
自增、自减只能作用于变量。
注意:前置和后置
1) 前置时,先对变量进行自增、自减运算,再计算整个表达式的值。
2) 后置时,先对计算整个表达式的值,再进行自增、自减运算。
举例说明:
int i=10;
int j=i++;
System.out.print(i);//输出i=11,因为i执行了i++;
System.out.print(j);//会输出10;因为是后置++,先计算表达式的值,
//也就是j=i=10,再进行++;
int i=10;
int j=++i;
System.out.print(i);//输出i=11,因为i执行了i++;
System.out.print(j);//会输出11;因为是前置++,先进行++,
//也就是i+1=11;再计算表达式的值:j=11;
关于多个±号连续出现
System.out.println( + ++i);//会被解析成正号和自增运算符,注意空格;
System.out.println( +-+-++i);//会被解析成正号负号正号负号自增运算符;
System.out.println( +-+- --i);//注意空格,---i是不对的!
System.out.println(i++ +-+-+-+- ++j);//注意中间部分+-+-+-+-
除了第一个+代表加号以外,其他的都代表正号负号
经典例题1:(面试题容易出)
i=10;
j=20;
System.out.println(i++ +-+-+-+ ++j);
//i++由于++是后置,所以i++现在是10,i的值被更新为11。关于中间部分+-+-+-+:第一个是加号,
然后其他的看成正负号,即是-+-部分,负负得正所以这个部分相当于两个加号,可以删去,
同样后面的+(正)号也可以删去。即:10 + -21 =-11 (++j,++是前置所以j=21;)
解析:
变量初始化:i 初始化为 10。j 初始化为 20。
表达式分析:i++ +-+-+-+ ++j 是一个复杂的表达式。我们分步计算它的值。
后置自增运算符 i++:i++ 使用当前的 i 的值(10),然后 i 自增 1。因此,此时 i 变为 11。i++ 的值为 10。
前置自增运算符 ++j:++j 首先将 j 的值自增 1,从 20 增加到 21,然后使用这个新的值。 ++j 的值为 21。
加减运算符的处理:+-+-+-+ 可以被视为连续的加法和减法运算。我们从左到右处理这些运算符。
+- 意味着 + 和 -,因此 +- 等价于 -,所以 +-+-+-+ 最终等价于 -(即将接下来的值取反)。
最终计算:将 i++ 的值(10)与 ++j 的值(21)相结合:10+(−21)=10−21=−11
总结:因此,System.out.println(i++ +-+-+-+ ++j); 的计算过程可以总结如下:
i++ 的结果是 10(此时 i 更新为 11)。
++j 的结果是 21(此时 j 更新为 21)。
表达式变为 10 - 21,最终输出 -11。
经典例题2:(面试题容易出)
i=10;
j=20;
System.out.println(i++ + ++i);//输出22;
解析:
初始值:i 初始化为 10。j 初始化为 20(在此段代码中未使用)。
表达式分析:i++ 是后置自增运算符。这意味着在这个表达式中,i 的当前值(10)将被使用,然后 i 将自增 1。
因此,i++ 的值为 10,之后 i 的值变为 11。++i 是前置自增运算符。在这个表达式中,
i 的值先增加 1(即从 11 增加到 12),然后这个新的值将被使用。因此,++i 的值为 12。
计算结果:将 i++ 的结果(10)和 ++i 的结果(12)相加:10+12=22
最终输出:System.out.println(i++ + ++i); 输出的结果是 22。此时 i 的值已经变成 12(因为 ++i 操作)。
2.比较运算符/关系运算符
对值进行比较判断,如果表达式成立,结果为true,如果表达式不成立,结果为false。
比较运算符共有六个,其中
> , >=, <,<=四个运算符只能对数值型(除了boolean型)进行比较。
==、!=即可以对基本数据类型进行比较,也可以对引用类型进行比较。
针对基本数据类型时,比较的是值是否相等。
针对引用类型时,比较两个引用是否指向同一对象,即是否指向同一块内存空间。
3. 逻辑运算符
逻辑运算符针对布尔类型进行逻辑运算,结果也是布尔型的。
3.1逻辑与
&&、&进行逻辑与运算,当两个表达式的值都是true时,运算结果为true,否则为false。
对于表达式 A && B 、A & B运算结果如下:
A | B | 结果 |
---|---|---|
true | true | true |
true | false | false |
false | true | false |
false | false | false |
3.2 逻辑或
||、|进行逻辑或运算,当两个表达式的值都是false时,运算结果为false,否则为true。
对于表达式 A || B 、A | B运算结果如下:
A | B | 结果 |
---|---|---|
true | true | true |
true | false | true |
false | true | true |
false | false | false |
3.3 逻辑非
!进行逻辑非运算,当表达式为true时,结果为false,否则为true。
对于表达式!A运算结果如下:
A | 结果 |
---|---|
true | false |
false | true |
3.4短路运算
针对&&、||两种逻辑运算,如果第一个表达式已经能够确定逻辑运算结果,则第二个表达式不再计算,这叫短路运算。
短路运算经常与自增/自减运算符同时考察
例如:
1) A && B,当A=false时,无论B值如果,计算结何都是false,B表达式将不再执行。
2) A || B,当A=true时,无论B值如何,计算结果都是true,B表达式也不再执行。
经典例题3:
int i=10;
System.out.println(false && (i++>0));
System.out.println(i);
//&&的短路原则,有false不会继续往后执行了,所以i=10
对比例题4:
int i=10;
System.out.println(false & (i++>0));
System.out.println(i);
//&只有一个,不是两个,所以不存在短路原则---有false也会继续往后执行,所以i=11;
4.位运算符
在二进制的基础上对数值型进行运算。
(学习一下二进制是如何转换成十进制的;关于二进制的运算,全部是0的时候那么二进制的值就是0;在java中的整数里面,是要分整数和负数的,所以我们拿出二进制的第一位做符号位,符号位为0代表是非负数,符号位为1代表是负的)
比如图中红色部分;以及本题的代码按位与、按位或、按位异或、按位取反的输出结果:
左移位:
且有如下结论:
右移位:每右移一位,相当于除以2;
总结:
4.1按位与
&对数值进行按位与运算,当两个数的对应位都是1时,结果对应位为1,否则为0。
4.2按位或
|对数值进行按位或运算,当两个数的对应位都是0时,结果对应位为0,否则为1。
4.3按位异或
^对数值进行按位异或运算,当两个数的对应位不同时,结果对应位为1,否则为0。
4.4按位取反
~对数值进行按位取反运算,当操作数的对应位为1时,结果的对应位为0,否则为1。
4.5左移位
<<对数值进行向左移位的操作,第一操作数的所有位向左移动指定位数,超出位范围的数据丢弃,右侧补0。
左移位可以快速完成对数据乘 2n的运算。
4.6右移位
>>对数值进行向右移位的操作,第一操作数的所有位向右移动指定位数,超出位范围的数据丢弃,左侧补符号位。
右移位可以快速完成对数据除以 2n的运算。
4.7无符号右移位
>>>对数值进行向无符号右移位的操作,第一操作数的所有位向右移动指定位数,超出位范围的数据丢弃,左侧补0。
5.赋值运算符
5.1简单赋值=
= 赋值,将右边表达式的值,存放到左边的变量中
=可以为变量/常量赋值为相应类型的值。
当赋值运算符与二目运算符进行组合,就会变成复合赋值运算符,比如:+=
int i; // 声明int类型变量i
i = 10; // 将10赋值给变量i
int a, b; // 声明两个int类型变量a, b
a = b = 10; // 将a, b变量都赋值为10
int m, n = m = 10; // 声明两个int类型变量m, n并赋值为10.
赋值表达式的值为变量最终所赋的值。
赋值运算符要求赋值时前后的数据类型必须匹配或可以自动转换。
但是:
1) byte,short,char可以赋值为表示范围的整数。
byte b = 100; // byte可以赋值为-128~127
short s = 1000; // short可以赋值为-32768~32767
char c = 10000; // char可以赋值为0~65535
2) 表达式中的byte,short,char会先转换为int再进行计算。
char c1 = 'A';
char c2 = 'B';
c1 = c1 + c2; // c1 + c2为int型,报错
3) 纯常量运算会在编译阶段进行。
int i = 10 + 20; // 编译为 int i = 30;
char c = 'A' + 'B'; // 编译为 char c = 131;
5.2复合赋值
当赋值运算符=与二目运算符进行组合,就会变成复合赋值运算符,比如:+=;A+=B相当于A=A+B;
常见的二目运算符与=还可以结合成复合赋值运算符。
例如:+=,-=,*=,/=,%=,&=,|=,<<=,>>=,>>>=。
A op= B;相当于 A = A op B。(op 是一个运算符。运算符可以是多种类型的。)
int a = 10;
int b = 20;
a += b; // 相当于a = a + b; 最终a的值为30.
注意:复合赋值运算符会进行隐式的强制类型转换。
int i = 10;
double d = 20.5;
i += d; // 隐式强制类型转换,不报错
i = i + d; // d为double类型,加法运算结果也是double类型,赋值给int类型变量i,此处报错。
比如:
解析:s2=s2+10;这个是错误的写法;因为s2是short类型,s2+10在运行过程中,short类型转成了int类型,所以s2+10的结果是int类型,不能赋值给s2本身是short类型的
s2=s1+s2也是错误的,同理如上,相当于s2=(int)s1+(int)s2;右面是int类型,左边是short类型,不能这样赋值。
6.三目运算符/三元运算符/条件运算符
A ? B : C
1) 条件A必须为布尔类型
2) 条件成立时,表达式的值为B的值。
3) 条件不成立时,表达式的值为C的值。
4) B与C的类型没有限定。
例题:
7.运算符的优先级
先乘除,后加减,有括号先算括号
如果有一表达式中,包含了很多种运算符,也有运算符的优先级,也有运算符的运算顺序。
优先级 | 运算符 | 结合性 |
---|---|---|
1 | ()、[]、{} | 自左向右 |
2 | !、+(正号)、-(负号)、~、++、– | 自右向左 |
3 | *、/、% | 自左向右 |
4 | +、- | 自左向右 |
5 | <<、>>、>>> | 自左向右 |
6 | <、<=、>、>=、instanceof | 自左向右 |
7 | ==、!= | 自左向右 |
8 | & | 自左向右 |
9 | ^ | 自左向右 |
10 | | | 自左向右 |
11 | && | 自左向右 |
12 | || | 自左向右 |
13 | ? : | 自右向左 |
14 | =、+=、-=、*=、/=、&=、|=、^=、~=、<<=、>>=、>>>= | 自右向左 |