算术运算符
算术运算符包括:+
,-
,*
,/
,%
,++
,--
+
当左右两边都是数值型时,则做加法运算。
当左右两边有一方为字符串,则做拼接运算。任何一个 Java 对象都可以转换为字符串。
如果 +
作用于字符,就将字符转为整数进行计算。
程序示例:
public static void main(String[] args) {
System.out.println(100 + 86); // 186
System.out.println("100" + 86); // 10086
System.out.println(100 + 3 + "Hello"); // 103Hello
System.out.println("Hello" + 100 + 3); // Hello1003
System.out.println(3.7 + "hello"); // 3.7hello
System.out.println("abc" + true); // abctrue
System.out.println('中' + "abc" + true); // 中abctrue
System.out.println('a' + 'b'); // 195
System.out.println('a' + "b"); // ab
}
代码 System.out.println(100 + 3 + "Hello");
中,加法从右到左依次进行。
程序示例:
public static void main(String[] args) {
String s1 = "天龙八部";
String s2 = "笑傲江湖";
String s3 = s1 + s2;
System.out.println(s3); // 天龙八部笑傲江湖
char ch1 = '男';
char ch2 = '女';
// char ch3 = ch1 + ch2; // Type mismatch: cannot convert from int to char
System.out.println(ch1 + ch2); // 52906
// 程序中有小数参与计算时,不限于何种计算,可能是加减乘除或者任何一种计算,都可能发生结果不精确的现象
double num1 = 123.56;
double num2 = 100.11;
double num3 = num1 + num2;
System.out.println(num3); // 223.67000000000002
}
/
除法分为整数除法和浮点数除法。当两个操作数都是整数时,执行整数除法,否则执行浮点数除法。
两个整数相除,即便除不尽,小数部分也会直接丢弃而不是四舍五入。浮点数参与除法运算,即便数学上能除得尽,结果也是浮点数。
程序示例:
public static void main(String[] args) {
// 两个整数相除,即便除不尽,小数部分也会直接丢弃而不是四舍五入
System.out.println(10 / 4); // 2
double num1 = 10 / 4;
int num2 = 10 / 4;
System.out.println(num1); // 2.0
System.out.println(num2); // 2
// 浮点数参与除法运算,即便数学上能除得尽,结果也是浮点数
System.out.println(8.0 / 4); // 2.0
double num3 = 8.0;
int num4 = 4;
double num5 = num3 / num4;
System.out.println(num5); // 2.0
// int num6 = num3 / num4; // Type mismatch: cannot convert from double to int
}
整数被 0 除将产生一个异常,而浮点数被 0 除将会得到一个无穷大或 NaN 结果。
程序示例:
// 整数被 0 除将产生一个异常,而浮点数被 0 除将会得到一个无穷大或 NaN 结果
public class DivideDemo {
public static void main(String[] args) {
// System.out.println(10 / 0); // Exception in thread "main" java.lang.ArithmeticException: / by zero
System.out.println(10.0 / 0); // Infinity
System.out.println(-10.0 / 0); // -Infinity
System.out.println(0.0 / 0); // NaN
System.out.println(-0.0 / 0); // NaN
System.out.println(0.0 / 0.0); // NaN
System.out.println(0 / 0.0); // NaN
}
}
%
%
作用于整数时,取模的结果的正负号和第一个运算数相同。
%
作用于整数时的结果根据公式计算:a % b = a - a / b * b
程序示例:
public static void main(String[] args) {
System.out.println(10 % 3); // 1
System.out.println(-10 % 3); // -1
System.out.println(10 % (-3)); // 1 , -3 可以不要括号
System.out.println(-10 % (-3)); // -1 , -3 可以不要括号
}
程序示例:
public static void main(String[] args) {
int all_days = 59;
int weeks = 59 / 7;
int days = 59 % 7;
System.out.println(all_days + " 天等于 " + weeks + " 星期 " + days + " 天."); // 59 天等于 8 星期 3 天.
}
%
可以作用于浮点数。
如果 a % b
中的 a
是浮点数,那么 a % b = a - (int)a / b * b
程序示例:
System.out.println(-10.5 % 3); // -1.5
System.out.println(10.5 % -3); // 1.5
System.out.println(-10.4 % 3); // -1.4000000000000004 , 说明结果可能是一个近似值
++
语句 ++i;
和 i++;
独立使用时,效果和 i = i + 1;
完全一致。
程序示例:
public class ArithmeticOperatorExcercise01 {
public static void main(String[] args) {
int i = 1;
i = i++; // temp = i; i = i + 1; i = temp;
System.out.println(i); // 1
int j = 1;
j = ++j; // j = j + 1; temp = j; j = temp;
System.out.println(j); // 2
}
}
很多程序员认为前缀和后缀这种行为容易让人困惑。在 Java 中,很少在表达式中使用 ++
和 --
。
关系运算符
关系运算符也叫比较运算符。关系运算符组成的表达式叫做关系表达式。
关系运算符的结果都是 boolean
型,也就是要么是 true
,要么是 false
。
关系表达式经常用在 if
结构的条件中或循环结构的条件中。
程序示例:
public static void main(String[] args) {
int a = 9;
int b = 8;
boolean flag = a > b;
System.out.println(flag); // true
System.out.println(a < b); // false
// int c = a > b; // Type mismatch: cannot convert from boolean to int
}
逻辑运算符
逻辑运算符用于连接多个条件表达式。
逻辑运算符的运算结果也是 boolean
类型。
逻辑运算符可以分为两组:
短路与 &&,短路或 ||,取反 !
逻辑与 &,逻辑或 |,逻辑异或 ^
a & b
:& 叫逻辑与,规则:当 a 和 b 同时为 true,则结果为 true,否则为 false。
a && b
:&& 叫短路与,规则:当 a 和 b 同时为 true,则结果为 true,否则为 false。
a | b
:| 叫逻辑或,规则:当 a 和 b 只要有一个为 true,则结果为 true,否则为 false。
a || b
:|| 叫短路或,规则:当 a 和 b 只要有一个为 true,则结果为 true,否则为 false。
!a
:! 叫取反,或者非运算。当 a 为 true,则结果为 false,当 a 为 false,则结果为 true。
a ^ b
:^ 叫逻辑异或,当 a 和 b 不同时,则结果为 true,否则为 false。
区分逻辑与 &
和短路与 &&
:仅当第一个条件为 false 时,这两个运算符有区别。当第一个条件为 false 时,整个表达式一定为 false,此时短路与 &&
将不再执行第二个表达式,而逻辑与 &
将继续执行第二个表达式。实际使用中一般都用短路与,因为效率更高。
程序示例:
public class LogicOperatorDemo {
public static void main(String[] args) {
System.out.println(true ^ true); // false
System.out.println(true ^ false); // true
System.out.println(false ^ true); // true
System.out.println(false ^ false); // false
}
}
程序示例:
public static void main(String[] args) {
int a = 10;
int b = 1;
if (a < 1 && ++b < 20) { // 第一个条件为 false,第二个条件不执行
System.out.println("OK");
} else {
System.out.println("NO");
}
System.out.println(b); // 1
if (a < 1 & ++b < 20) { // 第一个条件为 false,第二个条件仍执行
System.out.println("OK");
} else {
System.out.println("NO");
}
System.out.println(b); // 2
}
区分逻辑或 |
和短路或 ||
:仅当第一个条件为 true 时,两个运算符有区别。当第一个表达式为 true 时,整个表达式一定为 true,此时短路或 ||
将不再执行第二个表达式,而逻辑或 |
将继续执行第二个表达式。实际使用中较多使用短路或,因为效率更高。
程序示例:
public static void main(String[] args) {
int a = 1;
int b = 10;
if (a < 10 || ++b < 20) { // 第一个条件为 true,第二个条件不执行
System.out.println("OK");
} else {
System.out.println("NO");
}
System.out.println(b); // 10
if (a < 10 | ++b < 20) { // 第一个条件为 true,第二个条件仍执行
System.out.println("OK");
} else {
System.out.println("NO");
}
System.out.println(b); // 11
}
程序示例:
public static void main(String[] args) {
System.out.println(60 > 20); // true
System.out.println(!(60 > 20)); // false
System.out.println((10 > 1) ^ (10 > 2)); // false
System.out.println((10 < 1) ^ (10 > 2)); // true
}
可以利用短路运算符来避免错误。例如,在下面的表达式中:
boolean b = x != 0 && 1 / x > x + y; // no division by 0
如果 x 等于 0,那么第二部分就不会计算。因此,如果 x 为 0,也就不会计算 1 / x
,就不会出现除以 0 的错误。
赋值运算符
赋值运算符分两类:
基本赋值运算符:=
复合赋值运算符:+=
,-=
,*=
,/=
,%=
自增,自减和复合赋值运算符会进行类型转换。
程序示例:
public class AssignOperator {
public static void main(String[] args) {
byte b = 2;
// b = b + 2; // Type mismatch: cannot convert from int to byte
b += 2; // 等价于 b = (byte)(b + 2)
System.out.println(b); // 4
// b = b + 1; // Type mismatch: cannot convert from int to byte
b++; // 等价于 b = (byte)(b + 1);
System.out.println(b); // 5
}
}
在 Java 中,赋值是一个表达式(expression),也就是说,它有一个值,具体来讲就是所赋的那个值。可以使用这个值完成一些操作。
程序示例:
public class AssignOperatorDemo {
public static void main(String[] args) {
int x = 1;
int y = x += 4;
System.out.println(y); // 5
}
}
这种嵌套赋值很容易混淆,应当分别清楚地写出这些赋值:
程序示例:
public class AssignOperatorDemo {
public static void main(String[] args) {
int x = 1;
x += 4;
int y = x;
System.out.println(y); // 5
}
}
条件运算符
基本语法:条件表达式 ? 表达式1 : 表达式2
如果条件表达式结果为 true,则整个表达式的值为表达式 1 的值,否则整个表达式的值为表达式 2 的值。表达式 1 和表达式 2 只有一个会执行。
程序示例:
public static void main(String[] args) {
int a = 10;
int b = 99;
System.out.println(a > b ? a++ : b++); // 99
System.out.println(a); // 10
System.out.println(b); // 100
System.out.println(a < b ? ++a : ++b); // 11
System.out.println(a); // 11
System.out.println(b); // 100
}
表达式 1 和表达式 2 必须是可以赋给接收变量的类型或者可以进行自动类型转换的。
程序示例:
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = a > b ? a : b;
System.out.println(c); // 20
// short d = a > b ? a : b; // Type mismatch: cannot convert from int to short
short e = a > b ? (short) a : (short) b;
System.out.println(e); // 20
short f = (short) (a > b ? a : b); // 后面的三元运算符表达式要括起来,否则强制类型转换只对 a 起作用
System.out.println(f); // 20
double g = a > b ? a : b; // 自动类型转换
System.out.println(g); // 20.0
}
三元运算符可以修改为 if - else
语句。
语句 int res = a > b ? a : b;
等价于:
if (a > b) {
int res = a;
} else {
int res = b;
}
程序示例:
// 求三个数中的最大值
public static void main(String[] args) {
int a = 10, b = 20, c = 30;
int tmp;
/*
方法1
if (a > b) {
tmp = a;
} else {
tmp = b;
}
if (tmp < c) {
tmp = c;
}
*/
/*
方法2,最推荐
tmp = a > b ? a : b;
tmp = tmp > c ? tmp : c;
*/
// 方法3
tmp = (a > b ? a : b) > c ? (a > b ? a : b) : c; // 30
System.out.println(tmp);
}
位运算符
位运算符按位运算,也会对符号位起作用,是对补码进行操作的。
Java 中有 7 个位运算符:
&
:按位与,两位全为 1 时,结果为 1,否则结果为 0。
|
:按位或,两位只要有一个为 1,则结果为 1,否则结果为 0。
^
:按位异或,两位不同时,即一个为 0 而另一个为 1 时,结果为 1,否则结果为 0。
~
:按位取反,0 变为 1,1 变为 0。
>>
:算术右移,低位溢出(溢出就是扔掉不要了),符号位不变,即用原来的符号位补现在的符号位,并用 0 补上溢出的其余数字位。右移一次就是除以 2,右移两次就是除两次 2。
<<
:算术左移,符号位不变,低位补上 0。左移一次就是乘以 2,左移两次就是乘以 2 再乘以 2。
>>>
:逻辑右移,也叫无符号右移,规则为:低位溢出,高位补 0,即不论正负,高位一律补 0。没有 <<<
。
程序示例:
public class BitOperator {
public static void main(String[] args) {
System.out.println(2 & 3); // 0010 & 0011 = 0010
System.out.println(~-2); // 1
// -2 原码:10000000 00000000 00000000 00000010
// -2 反码:11111111 11111111 11111111 11111101
// -2 补码:11111111 11111111 11111111 11111110
// ~-2 补码:00000000 00000000 00000000 00000001 ,最高位为0
// ~-2 反码,原码与~-2补码相等,三码合一
System.out.println(~2); // -3
// 2的原码,反码,补码: 00000000 00000000 00000000 00000010
// ~2的补码: 11111111 11111111 11111111 11111101 ,最高位为 1
// ~2的反码(~2的补码减1): 11111111 11111111 11111111 11111100
// ~2的原码: 10000000 00000000 000000000 00000011 ,等于十进制的 -3
}
}
如果 n 是一个整数变量,而且 n 的二进制表示中从右边数第 4 位为 1,则 int fourthBitFromRight = (n & 0b1000) / 0b1000
; 会返回 1,否则返回 0。利用并结合适当的 2 的幂,可以屏蔽其他位,而只留下其中的某一位。
需要建立位模式来完成位掩码时,>>
和 <<
运算符会很方便:
int fourthBitFromRight = (n & (1 << 3)) >> 3;
移位运算符的右操作数要完成模 32 的运算(除非左操作数是 long 类型,在这种情况下需要对右操作数完成模 64 运算)。
例如,1 << 35
的值等同于 1 << 3
或 8
。
在 C/C++ 中,不能保证 >>
是完成算术移位(扩展符号位)还是逻辑移位(填充 0)。实现者可以选择其中更高效的任何一种做法。这意味着 C/C++ 中的 >>
运算符对负数生成的结果可能会依赖于具体的实现。Java 则消除了这种不确定性。
运算符的优先级
上一行优先级高于下一行。
只有单目运算符和赋值运算符是从右往左运算的。
与 C 或 C++ 不同,Java 不使用逗号运算符。不过,可以在 for 语句的第 1 和第 3 部分中使用逗号分隔表达式列表,也就是说 Java 中的逗号只作为分隔符而不是逗号运算符。