1 算数运算符
1.1 基本四则运算符
基本四则运算符为: +、 -、 *、/ 和%,它们的规则比较简单, 但比较值得注意的是除法和取余。
除法/:
因为int / int 结果还是 int, 需要使用 double 来计算,具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
System.out.println(4/8);//执行结果为0,因为这里前后都是整型,所以结果也应该为整型
System.out.println(4/8.0);//执行结果为0.5,因为这里有浮点类型,所以执行时会将前面的整型进行数值提升,所以结果为浮点类型
}
}
同时,我们需要注意,0 不能作为除数。虽然编译时并不会报错,但运行会报错,出现异常。具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
System.out.println(11.5%2.0);
//下面再一次感知一下Java的安全性
System.out.println(8/0);//编译不会报错,但运行会报错
System.out.println("bhdbhcsbjbxjsnxj");
}
}
此代码进行运行时将会出现报错,这个报错叫做异常。如下图所示:
异常: 分为很多种异常。此时所遇到的java.lang.ArithmeticException异常叫做算数异常。冒号后面的/ by zero,是异常的原因,这里的意思是此时我们除了一个0;at TestDemo是出现异常的类;.main是出现异常的方法;(TestDemo.java:10)是异常在此.java文件中出现的行数,一点击这个就可以快速定位到当前你出现错误的地方。
我们要知道,当出现异常之后,程序将终止执行。即此异常语句之前的内容均可执行,此异常语句之后的内容不再进行执行。例如上面这个代码中,异常语句前面的那一句输出就可以执行,执行结果为1.5,而异常语句后面的那一句输出就不可以执行了,当然也就没有此语句的执行结果了。
取余%:
% 表示取余, 不仅仅可以对 int 求模, 也能对 double 来求模。具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
System.out.println(10%3);//执行结果为1,因为这里前后都是整型,所以结果也应该为整型
System.out.println(11.5%2.0);//执行结果为1.5,可以算小数的余数。因为这里前后都是浮点类型,所以结果也应该为浮点类型
}
}
1.2 复合运算符
1.2.1 增量赋值运算符
增量赋值运算符有: +=、 -= 、*=、 /= 、%=和^=(异或等)。CPU取数据时是按照4个字节来取的,当数据类型小于4个字节,如byte只有一个字节,在运算过程中,它会先提升为4个字节进行运算。我们所用到的其余增量赋值运算符也均符合运算时会将数据类型小于4个字节的隐式的转为满足四个字节的类型,就不需要进行强制类型转换了。具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
byte a = 10;
byte b = 20;
//a = a+b;CPU取数据时是按照4个字节来取的,当数据类型小于4个字节,如byte只有一个字节,在运算过程中,它会先提升为4个字节进行运算,所以此时会出现编译报错
a += b;//等价于 a = a + 1,编译通过,+= 复合运算符 它在运算时已经将byte隐式的转为int类型了,就不需要进行强制类型转换了。
//其余增量赋值运算符也均符合运算时会将数据类型小于4个字节的隐式的转为int类型,就不需要进行强制类型转换了。
}
1.2.2 自增和自减运算符
自减自增运算符有:++、- -。主要分为有表达式的自增自减和无表达式的自增自减两种情况,或者前置自增自减和后置自增自减两种情况。具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
int a = 10;
/*
a++;
System.out.println(a);//此时执行结果为11,此时前置自增和后置自增没有区别
*/
/*
++a;
System.out.println(a);//此时执行结果也为11,此时前置自增和后置自增也没有区别
*/
/*
int b = a++;
System.out.println(b);//执行结果为10,但此时a的值为11。因为此后置++,程序先将a的值赋给b,所以此时b的值为10,然后a再进行++运算,a变为11了,但不再赋给b了
*/
int b = ++a;
System.out.println(b);//执行结果为11,a的值也为11.因为此前置++,程序先对a进行++运算,a变为11了,然后再将运算后的a赋给b,所以b的值为11
}
}
总结:
- 如果不取自增运算的表达式的返回值, 则前置自增和后置自增没有区别。
- 如果取表达式的返回值, 则前置自增的返回值是自增之后的值, 后置自增的返回值是自增之前的值。
2 关系运算符
关系运算符有:==、!=、<、>、<=和>=。关系运算符的表达式返回值都是 boolean 类型,其结果就两个:true或者false。具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println(a >= b);//执行结果为false,关系运算符(>=就是关系运算符)的表达式返回值都是 boolean 类型,其结果就两个:true或者false。
}
}
3 逻辑运算符
逻辑运算符有:&&、|| 和!。逻辑运算符的操作数(操作数往往是关系运算符的结果)和返回值都是 boolean类型的。
逻辑与&&:
- 布尔表达式1 && 布尔表达式2。
- 真:布尔表达式1 和 布尔表达式2 都为真,其结果才能为真。
- 假:只要布尔表达式中有一个为假,其结果就为假。
逻辑或||:
- 布尔表达式1 || 布尔表达式2。
- 真:只要布尔表达式中有一个为真,其结果就是真。
- 假:布尔表达式1 和 布尔表达式2 都为假,其结果才能为假。
逻辑非!:
逻辑非!的操作数为 true, 结果为 false; 操作数为 false, 结果为 true(这是个单目运算符, 只有一个操作数)。
具体代码示例如下:
public static void main19(String[] args) {
int a = 10;
int b = 20;
int c = 30;
//!true !false !它是一个逻辑运算符,所以它这个后面所跟的只能是boolean类型,只能取真或者假。
//System.out.println(!a);编译报错,这是逻辑非
System.out.println(!(a<=b));//编译通过,因为这个!后面的等式一定是一个boolean类型的,满足其要求
System.out.println(a < b && b < c);//编译通过,执行结果为true
/*
* int a = 1;
* int b = 2;
* int c = 0;
* if(a++ && b++)//这样写会编译报错,因为a++和b++的结果都不是布尔表达式
* {
* System.out.println("1");
* }else{
* System.out.println("2");
* }
* 结论:这是一个错误的代码
* */
}
}
短路求值:
逻辑与&& 和逻辑 || 都遵守短路求值的规则,我们知道, 计算10 / 0 会导致程序抛出异常(因为除数不能为0,所以此时会抛出算数)。 但是下面的代码却能正常运行, 说明 10 / 0 并没有真正被求值,此时就是进行了短路求值操作。
短路求值规则:
- 短路与:对于 && , 如果左侧表达式值为 false, 则表达式的整体的值一定是 false, 无需计算右侧表达式。
- 短路或:对于 ||, 如果左侧表达式值为 true, 则表达式的整体的值一定是 true, 无需计算右侧表达式。
具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
System.out.println(10 > 20 && 10 / 0 == 0); // 短路与:根据&&前面的表达式就可得出此语句的执行结果,此时表达式10 / 0 并没有真正被求值,执行结果为 false
System.out.println(10 < 20 || 10 / 0 == 0); // 短路或:根据||前面的表达式就可得出此语句的执行结果,此时表达式10 / 0 并没有真正被求值,执行结果为 true
System.out.println(10 < 20 && 10 / 0 == 0); // 不符合短路与规则,根据&&前面的表达式不可直接得出此语句的执行结果,此时表达式10 / 0 需要真正被求值,程序抛出异常
System.out.println(10 > 20 || 10 / 0 == 0); // 不符合短路或规则,根据||前面的表达式不可直接得出此语句的执行结果,此时表达式10 / 0 需要真正被求值,程序抛出异常
}
}
&和|:
& 和 | 如果操作数为 boolean 的时候, 也表示逻辑运算.。但是和 && 以及 || 相比, 它们不支持短路求值。具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
System.out.println(10 > 20 & 10 / 0 == 0); // 程序抛出异常
System.out.println(10 < 20 | 10 / 0 == 0); // 程序抛出异常
}
}
4 位运算符
位运算符有:&(按位与)、 | (按位或)、~(按位取反)和 ^(按位异或)。
Java 中对数据的操作的最小单位不是字节, 而是二进制位。位操作表示按二进制位运算,计算机中都是使用二进制来表示数据的(01构成的序列), 按位运算就是在按照二进制位的每一位依次进行计算。
按位与 &: 如果两个二进制位都是 1, 则结果为 1, 否则结果为 0。
按位或 |: 如果两个二进制位都是 0, 则结果为 0, 否则结果为 1。即只要有1位是1,其结果就为1。
注意: 当 & 和 | 的操作数为整数(int, short, long, byte) 的时候, 表示按位运算, 当操作数为 boolean 的时候, 表示逻辑运算。
具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println(a & b);
//进行按位运算, 需要先把 10 和 20 转成二进制, 分别为 1010 和 10100,然后再进行计算
}
}
按位取反 ~: 如果该位为 0 则转为 1, 如果该位为 1 则转为 0。具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
int a = 0xf;
System.out.printf("%x\n", ~a)
}
}
注意:
- 0x 前缀的数字为十六进制 数字.,十六进制可以看成是二进制的简化表示方式.,一个十六进制数字对应 4 个二进制位。
- 0xf 表示 10 进制的 15, 也就是二进制的 1111。
- printf 能够格式化输出内容, %x 表示按照十六进制输出。
- \n 表示换行符。
按位异或 ^: 如果两个数字的二进制位相同, 则结果为 0, 相异则结果为1。即不一样的位上进行或运算,一样的位上结果为0,0^n=n(0异或任何数字等于任何数字)。具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
int a = 0x1;
int b = 0x2;
System.out.printf("%x\n", a ^ b);
}
}
5 移位运算符
移位运算符有:<<(左移)、 >>(右移)和 >>>(无符号右移)。它都是按照二进制位来运算的。
左移 <<: 最左侧位不要了, 最右侧补 0。
右移 >>: 最右侧位不要了, 最左侧补符号位(正数补0, 负数补1)。
无符号右移 >>>: 最右侧位不要了, 最左侧补 0。
具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
int a = 0x10;
System.out.printf("%x\n", a << 1); // 运行结果(注意, 是按十六进制打印的) 为20。
System.out.printf("%x\n", a >> 1); // 运行结果(注意, 是按十六进制打印的) 为8 。
int b = 0xffff0000;
System.out.printf("%x\n", b >> 1); // 运行结果(注意, 是按十六进制打印的) 为ffff8000。
int c = 0xffffffff;
System.out.printf("%x\n", c >>> 1); // 运行结果(注意, 是按十六进制打印的) 为7fffffff。
}
}
注意:
- 左移 1 位, 相当于原数字 * 2. 左移 N 位, 相当于原数字 * 2 的N次方。
- 右移 1 位, 相当于原数字 / 2. 右移 N 位, 相当于原数字 / 2 的N次方。
- 由于计算机计算移位效率高于计算乘除, 当某个代码正好乘除 2 的N次方的时候可以用移位运算代替。
- 移动负数位或者移位位数过大都没有意义。
例如有一道面试题:如何将一个数字快速的扩大8倍?(重点词:快速)
答:将这个数字进行左移操作,左移3位。
6 条件运算符
条件运算符只有一个: 表达式1 ? 表达式2 : 表达式3。它也是 Java 中唯一的一个三目运算符 , 是条件判断语句的简化写法。
当 表达式1 的值为 true 时, 整个表达式的执行结果为 表达式2 的值; 当 表达式1 的值为 false 时, 整个表达式的执行结果为 表达式3 的值。具体代码示例如下:
public class TestDemo {
public static void main(String[] args) {
// 代码1:求两个整数的最大值
int a = 10;
int b = 20;
int max = a > b ? a : b;
//代码2
boolean flg = true ? true == true ? false:true:false;
System.out.println(flg);//执行结果为false
}
}
代码2的执行结果为何是false,具体解析如下图所示:
运算符总结: 运算符之间是有优先级的,但具体的规则我们不必记忆,在可能存在歧义的代码中加上括号即可。