码字不易,对你有帮助 点赞/转发/关注 支持一下作者
微信搜公众号:不会编程的程序圆
看更多干货,获取第一时间更新
如果想看更好的排版,可以阅读原文
点击阅读原文
思维导图
目录
@[toc]
正文
前言
在正式开始讲 运算符 之前,我们先来讨论一下上节课我们所学的 变量 的一个注意点。
- byte 与 char
- short 与 char
上面两组类型之间的转换不管是从小到大还是从大到小,都需要进行强制类型转换。
public class helloWorld{
public static void main(String[] args) {
byte bt = 10;// byte 类型大小为 1 个字节
char ch = ' ';// char 类型大小为 2 个字节
short sh = 10;// short 类型大小为 2 个字节
ch = bt;// 将 1 个字节的 bt 赋值给 2 个字节的 ch
//编译会报错:不兼容的类型: 从byte转换到char可能会有损失
ch = sh;
sh = ch;
//编译器还是会报错:不兼容的类型:从short/char转换到char/short可能会有损失
}
}
原因:
1、char 是字符型,而 byte 和 short 是数值型,他们之间不建议转换。
2、char 类型字符对应的 十进制 是没有负数的。
一 运算符
1. 算术运算符
+
-
*
/
%
注意:
-
int / int 结果还是 int, 需要使用 double 来计算
int a = 1; int b = 2; System.out.println(a / b); // 结果为 0
如何正确的输出我们想要的 0.5 呢?
//以下三种方式得到的结果都是 0.5 System.out.println(1.0 / 2); System.out.println(1 / 2.0); System.out.println(1.0 / 2.0);
需要注意的是,在 1.0 / 2 时,1.0 无疑是 double 类型,在运算时 2 也会被提升为 double 类型,也就是 1.0 / 2.0
-
0 不能作为除数
int a = 1; int b = 0; System.out.println(a / b) // 运行结果 Exception in thread "main" java.lang.ArithmeticException: / by zero at Test.main(Test.java:5)
-
% 表示取余,不仅仅可以对 int 求模,也能对 double 来求模
System.out.println(11.5 % 2.0); //运行结果 1.5
-
% 的操作数出现负数
System.out.println(5 % 2); System.out.println(5 % -2); System.out.println(-5 % -2); System.out.println(-5 % -2); //运行结果 1 1 -1 -1
我们在 【C语言必知必会】的【C语言入门到精通】的运算符一节中讨论过这个问题。
简单的来说,结果正负取决于 左操作数 的正负。
+=
-=
*=
/=
%=
int a = 10;
a += 1; // 等价于 a = a + 1
System.out.println(a);
//运行结果
11
++
--
int a = 10;
int b = ++a;
System.out.println(b);
int c = a++;
System.out.println(c);
//运行结果
11
11
结论:
- 如果不取自增运算的表达式的返回值, 则前置自增和后置自增没有区别。
- 如果取表达式的返回值, 则前置自增的返回值是自增之后的值, 后置自增的返回值是自增之前的值。
思考题:下面程序会输出什么?
int i = 10;
i = i++;
System.out.println(i);
点击查看答案
2. 关系运算符
==
!=
<
>
>=
<=
int a = 10;
int b = 20;
System.out.println(a == b);
System.out.println(a != b);
System.out.println(a < b);
System.out.println(a > b);
System.out.println(a <= b);
System.out.println(a >= b);
//运行结果
false
true
true
false
true
false
注意: 关系运算符的表达式返回值都是 boolean 类型。
3. 逻辑运算符
注意: 逻辑运算符的操作数(操作数往往是关系运算符的结果)和返回值都是 boolean 。
这一点是和 C 语言不同的地方:
int a = 1, b = 2;
System.out.println(a && b);
//编译错误
helloWorld.java:7: 错误: 二元运算符 '&&' 的操作数类型错误
System.out.println(a && b);
^
应当做出如下修改:
boolean flag1 = false, flag2 = false;
System.out.println(flag1 && flag2);
//运行结果:
false
逻辑运算符主要有三个:
&&
||
!
逻辑与 &&
规则: 两个操作数都为 true, 结果为 true, 否则结果为 false 。
int a = 10;
int b = 20;
int c = 30;
System.out.println(a < b && b < c);
//运行结果
true
逻辑或 ||
规则: 两个操作数都为 true, 结果为 true, 否则结果为 false 。
int a = 10;
int b = 20;
int c = 30;
System.out.println(a < b || b > c)
//运行结果
true
逻辑非 !
规则: 操作数为 true, 结果为 false; 操作数为 false, 结果为 true(这是个单目运算符, 只有一个操作数)
int a = 10;
int b = 20;
System.out.println(!a < b);
//编译错误
! 只能作用于 boolean 类型,而 a 是 int 类型
短路求值
&& 和 || 遵守短路求值的规则。
System.out.println(10 > 20 && 10 / 0 == 0);
System.out.println(10 < 20 || 10 / 0 == 0);
//打印结果:
false
true
我们都知道, 计算 10 / 0 会导致程序抛出异常。但是上面的代码却能正常运行, 说明 10 / 0 并没有真正被求值。
结论:
- 对于 && ,如果左侧表达式值为 false, 则表达式的整体的值一定是 false, 无需计算右侧表达式
- 对于 || ,如果左侧表达式值为 true, 则表达式的整体的值一定是 true, 无需计算右侧表达式
**& 和 | (不推荐使用) **
& 和 | 如果操作数为 boolean 的时候, 也表示逻辑运算. 但是和 && 以及 || 相比, 它们不支持短路求值
System.out.println(10 > 20 & 10 / 0 == 0); // 程序抛出异常
System.out.println(10 < 20 | 10 / 0 == 0); // 程序抛出异常
4、位运算符
位运算符主要有 4 个:
&
|
~
^
位操作表示 按二进制位运算。计算机中都是使用二进制来表示数据的(01构成的序列), 按位运算就是在按照二进制位的
每一位依次进行计算。(这与 C 语言基本相同,我们在 C 的教程中也详细讲过,这里不做展开。)
按位与:&
如果两个二进制位都是 1, 则结果为 1, 否则结果为 0 。
int a = 10;//01010
int b = 20;//10100
System.out.println(a & b);
//运行结果:
0
操作数有负数的情况
byte a = -1;//1111 1111
byte b = 13;//0000 1101
System.out.println(a & b);
//运行结果:
13
byte a = -1;//1111 1111
byte b = -2;//1111 1110
System.out.println(a & b);
//运行结果:
-2
这说明,负数补码的符号位是参数 & 运算的。
按位或:|
如果两个二进制位都是 0, 则结果为 0, 否则结果为 1 。
int a = 10;//01010
int b = 20;//10100
System.out.println(a | b);
//运行结果
30(11110)
注意: 当 & 和 | 的操作数为整数(int, short, long, byte) 的时候, 表示按位运算, 当操作数为 boolean 的时候, 表示逻辑运算 。
按位取反:~
如果该位为 0 则转为 1, 如果该位为 1 则转为 0 。
int a = 0xf;
System.out.printf("%x\n", ~a);
//运行结果:
fffffff0
解析:
- 0x 前缀的数字为 十六进制 数字. 十六进制可以看成是二进制的简化表示方式. 一个十六进制数字对应 4 个二进
制位. - 0xf 表示 10 进制的 15, 也就是二进制的 1111
- printf 能够格式化输出内容, %x 表示按照十六进制输出(在 C 语言中,转换说明 x 表示以十六进制打印 unsigned int 类型的值,unsigned int 是 4 个 字节,Java 中虽然没有 unsigned int 但是 %x 应该同 C 一样打印 4 个字节。所以,a 虽然是 0xf, 但是被编译器看作是:0x0000000f )。
- \n 表示换行符
按位异或:^
如果两个数字的二进制位相同, 则结果为 0, 相异则结果为 1 。
byte a = 10;//1010
byte b = 14;//1110
System.out.println(a ^ b);
//运行结果:
4(0010)
5、移位运算(了解)
如果你想搞明白,可以去看看我的 C 语言的操作符的文章 和 【C进阶】第一节数据存储。
<<
>>
>>>
左移:<<
最左侧位不要了, 最右侧补 0 。
int a = 0x10;
System.out.printf("%x\n", a << 1);
//运行结果:(注意打印的格式是 16 进制)
20(相当于乘以 2)
右移:>>
最右侧位不要了, 最左侧补符号位(正数补0, 负数补1) 。
int a = 0x10;
//0x10 -> 16 -> 0001 0000 >> 1 -> 0000 1000 -> 8
System.out.printf("%x\n", a >> 1);
// 运行结果
8(相当于除以 2)
int b = 0xffff0000;//这是一个负数
System.out.printf("%x\n", b >> 1);
// 运行结果
ffff8000
无符号右移:>>>
最右侧位不要了, 最左侧补 0 。
int a = -1;
System.out.println(a >> 1);
System.out.println(a >>> 1);
//运行结果:
-1
2147483647
思考一下:
byte a = -1;
System.out.println(a >>> 1);//这样会得到 127吗?
//运行结果:
2147483647
为什么会输出这样的值?
前面我们说过,运算过程中 byte,short 这类大小小于 4 个字节的类型会进行数值提升,转化为 int
那么这样可以吗?
byte a = -1;// 1111 1111
//a >>> 1 -> 0111 1111 1111 1111 1111 1111 1111 1111
byte b = (byte)(a >>> 1);
//知道了数值提升,我们这样将运算后的 int 转化为 byte ,这样可以得到正确结果吗?
System.out.println(b);
//运行结果:
-1
这是因为:第二步强制类型转换时,int 会发生截断,byte 只得到 int 的后 8 位:1111 1111。在输出 b 时,1111 1111 还是会被当作 -1 。这样看似 >>> 操作符没有什么卵用,但是我们应该知道,在计算机内部真实的情况。
总结:
- 左移 1 位, 相当于原数字 * 2. 左移 N 位, 相当于原数字 * 2 的N次方。
- 右移 1 位, 相当于原数字 / 2. 右移 N 位, 相当于原数字 / 2 的N次方。
- 由于计算机计算移位效率高于计算乘除, 当某个代码正好乘除 2 的N次方的时候可以用移位运算代替。
- 移动负数位或者移位位数过大都没有意义 。
6、条件运算符
表达式1 ? 表达式2 : 表达式3
含义:当 表达式1 的值为 true 时, 整个表达式的值为 表达式2 的值; 当 表达式1 的值为 false 时, 整个表达式的值为 表达式3 的值
// 求两个整数的最大值
int a = 10;
int b = 20;
int max = a > b ? a : b;
7、运算符优先级
运算符之间是有优先级的. 具体的规则我们不必记忆. 在可能存在歧义的代码中加上括号即可 。
想看详细的还是去看我 C 语言运算符相关的教学。
总结
- % 操作再 Java 中也能针对 double 来计算 。
- 需要区分清楚 前置自增 和 后置自增之间的区别 。
- 由于 Java 是强类型语言, 因此对于类型检查较严格, 因此像 && 之类的运算操作数必须是 boolean 。
- 要区分清楚 & 和 | 什么时候是表示按位运算, 什么时候表示逻辑运算 。
二 注释
Java 的注释方式和 C 一样,主要有以下几种:
//行注释
/*这是
一个
块注释*/
/*
*这是
*文档
*注释
*/
三 关键字
与 C 语言相同,定义的 变量名 不能与 关键字 冲突。
用于定义访问权限修饰符 | private | protected | public | ||
---|---|---|---|---|---|
定义类,函数,变量修饰符 | abstract | final | static | synchronized | |
定义类与类之间的关系 | extends | implements | |||
定义建立实例以及引用实例,判断实例 | new | this | super | instanceof | |
用于异常处理 | try | catch | finally | throw | throws |
用于包 | package | import | |||
其他修饰符 | native | strictfp | transient | volatile | assert |
[0 基础学 java ] 系列的代码可以在我的 Github 仓库查看,地址如下:
https://github.com/hairrrrr/Java_SE_EnjoyLearning
欢迎 star !(点一个 star 方便你下回查看)
本系列的教学也可以在 GitHub 观看(GitHub 上看教学的好处是所有文章的目录比较清晰)。
以上就是本次的内容。
如果文章有错误欢迎指正和补充,感谢!
最后,如果你还有什么问题或者想知道到的,可以在评论区告诉我呦,我可以在后面的文章加上你们的真知灼见。
关注我,看更多干货!
我是程序圆,我们下次再见。