Java 05 – Java的运算符
目录
1 运算符的基本概念
(1) 运算符: 对常量和变量进行操作的符号, 就叫作为运算符.
常见的运算符有: ① 算术运算符, ② 赋值运算符, ③ 关系运算符, ④ 逻辑运算符, ⑤ 三元(三目)运算符.
(2) 表达式: 用运算符将常量或变量连接起来的、符合Java语法的式子称为表达式. 不同运算符连接的式子体现的是不同类型的表达式.
比如: 定义两个int类型的变量a, b, 做加法:
a + b
—— 这就是一个表达式.
2 算数运算符
(1) 算数运算符的分类
运算符 | 作用 | 运算符 | 作用 |
---|---|---|---|
+ |
加 | % |
求模, 也就是求余数 |
- |
减 | ++ |
自增, 也就是+1 |
* |
乘 | -- |
自减, 也就是-1 |
/ |
除 |
(2) 求模(取余)和除法的区别
%
: 取余运算符, 得到的是两个相除数据的余数 —— 用来判断第1个数是否能被第2个数整除.
/
: 除法运算符, 得到是两个相除数据的商, 省去余数 —— 它的整数除和小数除是有区别的: 整数之间的除法, 只保留整数部分而舍弃小数部分, 比如:int x = 3510; x = x / 1000 * 1000; // 此时x的结果是3000
使用说明:
① 在控制结果的范围时, 可以使用求模(mod)运算, 结果的正负以被模数为参照. 比如:
运算 结果 运算 结果 2 % -3
2 4 % -3
1 -2 % 3
-2 -4 % 3
-1 ②
%2
—— 结果是0、1交替的数, 可以作为开关设置;③ 如果对负数取模, 模数的负号可以忽略不记, 比如:
5 % -2 = 1
.
(3) 字符和字符串参与加法操作
字符参与运算, 其实是取这个字符在ASCII表中对应的数值来操作, 比如:
'a'
—— 对应数字97, 'A'
—— 对应数字65, '0'
—— 对应数字48.
字符串参与+
运算, 内部进行的不是加法运算, 而是字符串的拼接 —— 也就是说, 字符串和其他类型的数据做拼接, 结果是字符串类型.
+
除字符串相加功能之外, 还能把非字符串转换成字符串, 比如:System.out.println("5 + 5 = " + 5 + 5); // 打印的结果是: 5 + 5 = 55
(4) 自增++与自减–的用法
++
、--
运算符: 对变量作自加1或者自减1的操作, 它们既可以跟在变量后面, 也可以放在变量前面, 比如:
a++;
--b;
使用说明:
① 单独使用的时候,
++
、--
放在变量前面或者后面, 运算效果都是一样的.② 参与操作的时候:
·
++
、--
在变量的后面, 先对变量进行相关操作, 再对变量作++
、--
运算 —— 先运算, 再自增(自减);·
++
、--
在变量的前面, 先对变量作++
、--
, 再对变量进行其他相关操作 —— 先自增(自减), 再运算.int a = 1, b = 1; int c, d; c = ++a; // 执行完后c = 2 ——— 先a = a + 1; 再c = a; d = b++; // 执行完后d = 1 ——— 先开辟临时空间temp记录b的值, 然后: d = temp; b = b + 1; // 到这里, a=2, b=2, c=2, d=1
(5) 示例代码
/**
* 算术运算符的使用
*
* @author Heal Chow
* @date 2019/03/04 23:26
*/
public class OperatorDemo1 {
public static void main(String[] args) {
// 定义两个变量
int a = 3;
int b = 5;
// 算数运算符的基本使用:
System.out.println(a + b); // 8
System.out.println(a - b); // -2
System.out.println(a * b); // 15
System.out.println(a / b); // 0
// 整数相除只能得到整数, 要想得到小数, 就必须有浮点数参与运算
System.out.println(3 / 4); // 0
System.out.println(3.0 / 4); // 0.75
// 除法与求模的区别:
System.out.println(a / b); // 0
System.out.println(a % b); // 3
// 字符参与加法操作
char c1 = '0';
char c2 = 'a';
System.out.println(a + c1); // 3 + 48 = 51
System.out.println(a + c2); // 3 + 97 = 100
// 字符串参与加法操作
System.out.println("hello" + a); // hello3
System.out.println("hello" + a + b); // hello35
System.out.println(a + b + "hello"); // 8hello(先a + b)
// 自增(++)与自减(--)的用法
int d = 10;
System.out.println("初始值d:" + d); // 初始值d:10
// 单独使用, 单独执行下述任一句:
// d++;
// ++d;
// System.out.println("运算后d:" + d); // d = 11
// 参与操作, 单独执行下述任一句:
int e = 10;
// int f = e++; // e = 11, f = 10
int f = ++e; // e = 11, f = 11
System.out.println("运算后e:" + e); // 运算后e:11
System.out.println("运算后f:" + f); // 运算后f:11
}
}
3 赋值运算符
(1) 赋值运算符的分类
基本的赋值运算符: =
, 扩展的赋值运算符: +=
, -=
, *=
, /=
, %=
.
举例说明:
①
a += 20;
—— 相当于:a = (a的数据类型)(a + 20);
—— 有强制类型转换.②
a -= 10;
—— 相当于:a = (a的数据类型)(a 1 10);
—— 有强制类型转换.short s = 4; s = s + 5; // 编译报错: 无法确定右侧的结果是否在short范围之内, 容易丢失精度 s += 5; // 编译通过: += 是赋值运算符, 暗含强制类型提升
(2) 示例代码
/**
* 赋值运算符的使用
*
* @author Heal Chow
* @date 2019/03/04 23:35
*/
public class OperatorDemo2 {
public static void main(String[] args) {
// 把10赋值给int类型的变量a
int a = 10;
// += 把左边和右边的数据进行运算, 最后赋值给左边. 左边只能是变量
a += 10;// 相当于a = a + 10
System.out.println("a:" + a); // a:20
short s = 20;
// s -= 5; // 相当于 s = s - 5;
s = (short) (s - 5);
System.out.println("s:" + s); // s:15
}
}
4 关系运算符
(1) 关系运算符的分类
关系运算符有以下6种: ==
, !=
, >
, >=
, <
, <=
.
关系运算符的结果都是boolean型: 要么是true, 要么是false.
注意: 关系运算符
==
不能误写为赋值运算符=
, 这是很多新手常犯的错误之一.
(2) 示例代码
/**
* 关系运算符的使用
*
* @author Heal Chow
* @date 2019/03/04 23:42
*/
public class OperatorDemo3 {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 10;
System.out.println(a == b); // false
System.out.println(a == c); // true
System.out.println("-----------------");
System.out.println(a != b); // true
System.out.println(a != c); // false
System.out.println("-----------------");
System.out.println(a > b); // false
System.out.println(a > c); // false
System.out.println("-----------------");
System.out.println(a >= b); // false
System.out.println(a >= c); // true
System.out.println("-----------------");
int x = 3;
int y = 4;
// System.out.println(x == y); // false
// System.out.println(x = y); // 4, 是将y赋值给x, 再将x输出
boolean bb = (x == y);
// boolean cc = (x = y); // 报错
int cc = (x = y);
System.out.println("bb:" + bb); // bb:false
System.out.println("cc:" + cc); // cc:4
}
}
5 逻辑运算符
(1) 逻辑运算符的分类
逻辑运算符包括: &(逻辑与)
, |(逻辑或)
, ^(逻辑异或)
, !(逻辑非)
, &&(双与)
, ||(双或)
.
(2) 逻辑运算符的基本用法
逻辑运算符一般用于连接boolean
类型的表达式或值, 也就是连接关系表达式.
表达式: 就是用运算符把常量或变量连接起来的符合Java语法的式子. 比如:
算术表达式:
a + b
, 比较表达式(条件表达式):a == b
.
使用说明:
&
– 逻辑与: 表达式中只要有0
或false
, 结果就是false
, 如果没有0
或false
, 那结果就是true
;
|
– 逻辑或: 表达式中只要有1
或true
, 结果就是true
, 如果没有1
或true
, 那结果就是false
;
^
– 逻辑异或: 表达式前后相同, 结果就是false
, 如果不同, 结果就是true
;
!
– 逻辑非: 取反的意思, 非true
就是false
, 非false
就是true
.
特点: 偶数个表达式或值, 不改变结果.
(3) 示例代码
/**
* 逻辑运算符的用法
*
* @author Heal Chow
* @date 2019/03/04 23:48
*/
public class OperatorDemo4 {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 30;
System.out.println((a > b) & (a > c)); // false
System.out.println((a < b) & (a > c)); // false
System.out.println((a > b) & (a < c)); // false
System.out.println((a < b) & (a < c)); // true
System.out.println("---------------");
System.out.println((a > b) | (a > c)); // false
System.out.println((a < b) | (a > c)); // true
System.out.println((a > b) | (a < c)); // true
System.out.println((a < b) | (a < c)); // true
System.out.println("---------------");
System.out.println((a > b) ^ (a > c)); // false
System.out.println((a < b) ^ (a > c)); // true
System.out.println((a > b) ^ (a < c)); // true
System.out.println((a < b) ^ (a < c)); // false
System.out.println("---------------");
System.out.println((a > b)); // false
System.out.println(!(a > b)); // !false -> true
System.out.println(!!(a > b)); // !!false -> !true -> false
}
}
(4) 逻辑运算符&&
与&
的区别
① 最终结果相同;
② &&
具有短路效果 —— 如果左边是false
, 就立即结束判断, 也就是说不执行右边的表达式;
③ &
没有短路效果 —— 无论左边是false
或true
, 右边的表达式都会执行.
(5) 逻辑运算符||
和|
的区别
① 最终结果一样;
② ||
具有短路效果, 如果左边是true
, 就立即结束判断, 也就是说不执行右边的表达式;
③ |
没有短路效果, 无论左边是false
或true
, 右边的表达式都会执行.
(6) 示例代码
/**
* &&和&、||和|的示例
*
* @author Heal Chow
* @date 2019/03/04 23:55
*/
public class OperatorDemo5 {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 30;
System.out.println((a > b) && (a > c)); // false
System.out.println((a < b) && (a > c)); // false
System.out.println((a > b) && (a < c)); // false
System.out.println((a < b) && (a < c)); // true
System.out.println("---------------");
System.out.println((a > b) || (a > c)); // false
System.out.println((a < b) || (a > c)); // true
System.out.println((a > b) || (a < c)); // true
System.out.println((a < b) || (a < c)); // true
System.out.println("---------------");
int x = 3;
int y = 4;
// System.out.println((x++ > 4) & (y++ > 5)); // false & false
// System.out.println("x:" + x); // x:4 --> 前后部都执行, x自增为4
// System.out.println("y:" + y); // y:5 --> y自增为5
System.out.println((x++ > 4) && (y++ > 5));
System.out.println("x:" + x); // x:4 --> 前部为执行, 结果为false, x自增为4
System.out.println("y:" + y); // y:4 --> 由于短路, 后部的y++没有执行
}
}
6 三元运算符
(1) 三元运算符的使用格式
(关系表达式) ? 表达式1 : 表达式2;
如果关系表达式的结果是
true
, 那就执行表达式1, 最后的结果是表达式1的结果;如果关系表达式的结果为
false
, 那就执行表达式2, 最后的结果是表达式2的结果.
(2) 示例代码
/**
* 三元运算符的使用
*
* @author Heal Chow
* @date 2019/03/05 00:03
*/
public class OperatorDemo6 {
public static void main(String[] args) {
// 获取2个整数中大的那个数
int a1 = 10;
int b1 = 20;
// a1大于b1吗? 大于的话就是a1, 小于的话就是b1
int c1 = (a1 > b1) ? a1 : b1;
System.out.println("c1:" + c1); // c1:20
System.out.println("---------------");
// 比较两个整数是否相同
int a2 = 10;
int b2 = 20;
boolean flag = (a2 == b2) ? true : false;
// boolean flag = (a2 == b2);
System.out.println(flag); // false
System.out.println("---------------");
// 获取三个整数中的最大值
int a3 = 10;
int b3 = 30;
int c3 = 20;
// 先比较某两个整数的大值, 再比较第三个
int temp = ((a3 > b3) ? a3 : b3);
int max = ((temp > c3) ? temp : c3);
System.out.println("max:" + max); // max:30
}
}
7 扩展: 位运算
7.1 原码、反码和补码
(1) 关于原码:
+3
的原码为00000011
—— 第一位是符号位;
-3
的原码为10000011
—— 第一位是符号位.
(2) 关于反码:
+3
的反码为00000011
—— 正数的反码是其本身;
-3
的反码为11111100
—— 负数的反码是在其原码的基础上, 符号位不变, 其余各个位取反.
(3) 关于补码:
+3
的补码为00000011
—— 正数的补码就是其本身;
-3
的补码为11111101
—— 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1, 也就是在反码的基础上+1.
(4) 扩展说明:
使用补码可以把减法变为加法, 而且
0
的问题也会解决(+0和-0的补码都是00000000
).Java中使用补码的格式保存
byte, short, int, long
类型的数据.
7.2 位运算符
运算符 | 功能 | 操作说明 |
---|---|---|
& |
对补码的每一位进行与运算 | 二进制位进行& 运算, 只有1 & 1 = 1 , 其他都是0;可用于获取二进制数中的有效位1. |
| |
对补码的每一位进行或运算 | 二进制位进行| 运算, 只有0 | 0 = 0 , 其他都是1 |
^ |
对补码的每一位进行异或运算 | 相同二进制位进行^ 运算, 结果是0, 即1 ^ 1 = 0 , 0 ^ 0 = 0 ;不同二进制位进行 ^ 运算, 结果是1, 即1 ^ 0 = 1 , 0 ^ 1 = 1 ;一个数亦或另一个数两次, 结果是它本身( 6^3^3 = 6 ). |
~ |
按补码的每一位进行取反操作 | 一个数取反再 +1 等于他的相反数(~5+1 = -5 ), 再取反并+1 , 等于它自己. |
>> |
带符号右移, 高位补符号位, 正数右移1位相当于除以2 | 被移位的二进制最高位是0, 右移后空缺位补0; 被移位的二进制最高位是1, 右移后空缺位补1. |
>>> |
无符号右移, 高位补0 , 正数右移1位相当于除以2 |
被移位的二进制最高位无论是0或1, 空缺位都补0. 多用在操作二进制数据的某一段二进制位. |
<< |
左移, 正数左移1位相当于乘2 |
说明: 除了~
是一元操作符外, 其他位操作符都是二元操作符号.
7.3 byte类型转int类型
Java在将byte
类型的数值转为int
的过程中, 会对符号位进行扩充: 10000000
-> 11111111 11111111 11111111 10000000
, 这样数值就能保持不变. 比如下面的例子中, 字节类型的-128
也是整型的-128
.
byte b = -128;
int i1 = b & 0xff;
int i2 = b;
System.out.println(i1); // 128
System.out.println(i2); // -128
System.out.println(Integer.toBinaryString(i1)); // 10000000
System.out.println(Integer.toBinaryString(i2)); // 11111111111111111111111110000000
但是如果byte
是无符号的整数, 就要考虑使用0xff
进行与操作 —— 这样得到的结果数值不变, 还能避免Java自动进行符号位扩充.
0xff
的补码为11111111
, 意味着和它与操作将只保留后8位, 并且结果总是非负数.
7.4 [扩展] 一道位运算的面试题
一般位运算也意味着高效的运算: 现代计算机底层对所有的计算都是转换成了对二进制的计算.
由此产生了很多技巧性的题目, 比如这道常见的面试题: 对两个整数变量的值进行互换(不需要第三方变量).
方案① —— 通过加/减法实现:
// + - 法可能会造成精度的丢失 a = a + b; b = a – b; a = a – b;
方案② —— 通过位运算实现 —— 效率更高:
// 一个数异或另一个数两次, 结果为该数本身, 不会造成精度丢失 a = a ^ b; b = a ^ b; a = a ^ b;
说明: 示例代码可在 我的GitHub上 查看并下载.
参考资料
(全文完)
(感谢阅读, 转载请注明作者和出处 瘦风的南墙 , 请勿用于任何商业用途)