目录
一. 运算符
1.1 运算符介绍
1.2 算术运算符
1.2.1 基本四则运算符:加减乘除模(+ - * / %)
1.2.2 增量运算符(+= -= *= /= %=)
1.2.3 自增自减运算符(++ --)
1.3 关系运算符
1.4 逻辑运算符(重点)
1.4.1 逻辑与&&
1.4.2 逻辑或||
1.4.3 逻辑非!
1.4.4 短路求值
1.5 位运算符
1.6 移位运算符(了解)
1.7 条件运算符(三目运算符)
1.8 运算符的优先级
二. 程序逻辑控制
2.1 顺序结构
2.2 分支结构
2.2.1 if语句
2.2.2 switch 语句
2.3 循环结构
2.3.1 while循环
2.3.2 for循环
2.3.3 do-while循环
2.3.4 循环控制关键字
三. 总结
一. 运算符
1.1 运算符介绍
计算机最基本的用途就是进行数学运算,如a+b,a-b等,这些+-*/之类就叫运算符,不同运算符的操作含义不同。
作为一门计算机语言,Java提供了一套丰富的运算符用来操作变量。Java中运算符主要可分为以下:算术运算符(+-*/),关系运算符(<>==),逻辑运算符,位运算符,移位运算符以及条件运算符。
1.2 算术运算符
1.2.1 基本四则运算符:加减乘除模(+ - * / %)
模运算就是数学中除法的余数,如10 / 3 = 商3余1,那么10 % 3 =1。这几个运算符不多介绍。
注意:
- 这些运算符都是二元运算符,使用时必须要有左右两个操作数
- int / int 结果仍是int,且结果向下取整(舍弃小数部分)
- 做除法和取模时,右操作数不能为0(0不能做被除数)
- %不仅能对整数取模,也可以对小数取模,但没有意义,一般都是对整数
- 二元运算符两侧操作类型不一致时,向类型大的提升(上节的类型提升)
1.2.2 增量运算符(+= -= *= /= %=)
这种运算符操作完成后,会将结果赋值给左操作数
int a = 1; int b = 3; int c = a += b; //c的值为4 // a += b 相当于 a = a + b其余几个也同理。需要注意的是:增量运算符在计算时,可以不进行强制类型转换
double a = 3.14; int b = 2; b += a; //不会报错 b = b + a; //编译报错,等号右边被提升为double类型1.2.3 自增自减运算符(++ --)
++是给变量值+1,--是给变量值-1,但前置++与后置++有一定区别,建议少用这个运算符
public class HelloWorld { public static void main(String[] args) { int a = 1; a++; // 后置++ 表示给a的值加1,此时a的值为2 System.out.println(a++); // 注意:后置++是先使用变量原来值,表达式结束时给变量+1,因此输出2 System.out.println(a); // 输出3 ++a; // 前置++ 表示给a的值加1 System.out.println(++a); // 注意:前置++是先给变量+1,然后使用变量中的值,因此输出5 System.out.println(a); // 输出5 // --操作符给操作-1,与++含义类似 } }注意:
- 若单独使用,【前置++】与【后置++】没有任何区别
- 若混合使用,【前置++】先+1,再使用变量+1之后的值,【后置++】先使用变量原来的值,表达式结束时给变量+1
- 只有变量才能使用自增/自减操作符,常量不能用,因为常量不允许修改。
1.3 关系运算符
关系运算符是用来做判断的,比较两个变量之间关系,主要有六个,== != < > <= >= ,其计算结果为 true 或 false
int a = 10; int b = 20; // 注意:在Java中 = 表示赋值,要与数学中的含义区分 // 在Java中 == 表示相等 System.out.println(a == b); // false System.out.println(a != b); // true System.out.println(a < b); // true System.out.println(a > b); // false System.out.println(a <= b); // true System.out.println(a >= b); // false注意:Java中需要多次判断时,不能连着写,如3 < a < 5,要与数学作区分
1.4 逻辑运算符(重点)
逻辑运算符主要用来表示多个表达式之间的关系,同时也是写判断逻辑的核心,主要有三个:&& || !,运算结果都是boolean类型
1.4.1 逻辑与&&
- 语法规则:表达式1 && 表达式2,左右表达式必须是boolean类型的结果。
- 特点为一假则假,即两个表达式都为真,结果才为真,只要有一个假,结果就是假。
| 表达式1 | 表达式2 | 结果 |
|---|---|---|
| 真 | 真 | 真 |
| 真 | 假 | 假 |
| 假 | 真 | 假 |
| 假 | 假 | 假 |
System.out.println(a > 100 && b > 100); // 左为假 且 右为假 则结果为假1.4.2 逻辑或||
- 语法规则:表达式1 || 表达式2,左右表达式必须是boolean类型的结果。
- 特点为一真则真。
| 表达式1 | 表达式2 | 结果 |
|---|---|---|
| 真 | 真 | 真 |
| 真 | 假 | 真 |
| 假 | 真 | 真 |
| 假 | 假 | 假 |
1.4.3 逻辑非!
- 语法规则:! 表达式
- 特点为真变假,假变真
| 表达式 | 结果 |
|---|---|
| 真 | 假 |
| 假 | 真 |
1.4.4 短路求值
&&和||都遵守短路求值的规则
System.out.println(10 > 20 && 10 / 0 == 0); // 打印 false System.out.println(10 < 20 || 10 / 0 == 0); // 打印 true正常情况下,10 / 0 会导致程序抛出异常,但上面代码是能正常执行的,说明10/0并没有被真正执行到。
注意:
- 对于 && , 如果左侧表达式值为 false, 则表达式结果一定是 false, 无需计算右侧表达式.
- 对于 ||, 如果左侧表达式值为 true, 则表达式结果一定是 true, 无需计算右侧表达式.
- Java中不仅有&&,||,还有&和|这两个位运算符(马上就会说到),& 和 | 如果表达式结果为 boolean 时, 也表示逻辑运算. 但与 && || 相比, 它们不支持短路求值。也就是说不管左边表达式是否能判断出结果,右边表达式都会执行
1.5 位运算符
Java 中数据存储的最小单位是字节,而数据操作的最小单位是比特位. 字节是最小的存储单位,1字节等于8bit位,多个字节组合在一起可以表示各种不同的数据。
位运算符主要有四个: & | ~ ^ ,除 ~ 是一元运算符外,其余都是二元运算符。
位操作表示 按二进制位运算. 计算机中都是使用二进制来表示数据的(01构成的序列), 按位运算就是在按照二进制位的每一位依次进行计算。
如何理解位运算呢?以上面int类型为例,int类型大小为固定的4字节,4字节又相当于32bit位,我们写的这些数字,在计算机中都是以2进制形式存储的,比如int a = 1; 这里的1,如果要以2进制位的形式来表示就是0000 0000 0000 0000 0000 0000 0000 0001 (int对应32位),我们下面通过例子来说明位运算符
按位与&:如果两个二进制位都是1,则结果为1,否则结果为0.
int a = 10; int b = 20; System.out.println(a & b);为了方便,我们这里写二进制位只写后八位(实际上肯定是以32为存储的,但是前面24位不影响结果)
进行按位运算,首先要把10和20转成2进制,分别为1010和10100(进制转换这里可以参考其他网上资料,简单来说二进制就是满2进1,十进制满10进1,其他没啥区别)
0000 1010 //10的二进制
0001 0100 //20的二进制
0000 0000 //按位与后,得到0的二进制
按位或|:如果两个二进制位有一个为1,结果为1,否则结果为0
int a = 10; int b = 20; System.out.println(a | b);0000 1010 //10的二进制
0001 0100 //20的二进制
0001 1110 //按位或后,得到30的二进制
注意: 当 & 和 | 的操作数为整数(int, short, long, byte) 的时候, 表示按位运算, 当操作数为 boolean 的时候, 表示逻辑运算.
按位取反 ~: 如果该位为 0 则转为 1, 如果该位为 1 则转为 0
int a = 0xf; System.out.printf("%x\n", ~a)- 0x 前缀的数字为 十六进制 数字. 十六进制可以看成是二进制的简化表示方式. 一个十六进制数字对应 4 个二进制位.
- 0xf 表示 10 进制的 15, 也就是二进制的 1111
- printf 能够格式化输出内容, %x 表示按照十六进制输出.
- \n 表示换行符
按位异或 ^: 如果两个数字的二进制位相同, 则结果为 0, 相异则结果为 1
int a = 0x1; int b = 0x2; System.out.printf("%x\n", a ^ b);注意:如果两个数相同,则异或的结果为0
1.6 移位运算符(了解)
移位运算符有三个: << >> >>> ,都是二元运算符,且都是按照二进制比特位来运算的。
左移 << :最左侧位不要了,最右侧补0
int a = 0x10; System.out.printf("%x\n", a << 1); // 运行结果(注意, 是按十六进制打印的) 200001 0000 //10的十六进制 (对应十进制为16)
0010 0000 //左移一位后,十进制为32,对应十六进制为20,以16进制打印结果为20
注意:向左移位时,丢弃的是符号位,因此正数左移可能会变成负数。
右移 >>(算术右移):最右侧位不要了, 最左侧补符号位(正数补0, 负数补1)
int a = 0x10; System.out.printf("%x\n", a >> 1); // 运行结果(注意, 是按十六进制打印的) 8 int b = 0xffff0000; System.out.printf("%x\n", b >> 1); // 运行结果(注意, 是按十六进制打印的) ffff80000001 0000 //10的十六进制 (对应十进制为16)
0000 1000 //右移1位后,十进制为8,对应16进制为8,以16进制打印还是8
下面的也同理
无符号右移 >>>(逻辑右移):最右侧位不要了, 最左侧补 0.
int a = 0xffffffff; System.out.printf("%x\n", a >>> 1); // 运行结果(注意, 是按十六进制打印的) 7fffffff注意:
- 左移 1 位, 相当于原数字 * 2. 左移 N 位, 相当于原数字 * 2 的N次方. 【针对正数而言】
- 右移 1 位, 相当于原数字 / 2. 右移 N 位, 相当于原数字 / 2 的N次方. 【针对正数而言】
- 由于计算机计算移位效率高于计算乘除, 当某个代码正好乘除 2 的N次方的时候可以用移位运算代替. 【针对正数而言】
- 移动负数位或者移位位数过大都没有意义.
有兴趣的话也可以了解下负数进行左移右移时,如何计算结果,这里不过多展开
1.7 条件运算符(三目运算符)
条件运算符只有一个:表达式1 ? 表达式2 : 表达式3
当 表达式1 的值为 true 时, 整个表达式的值为 表达式2 的值;
当 表达式1 的值为 false 时, 整个表达式的值为 表达式3 的值.
这是Java中唯一一个三目运算符,更是条件判断语句的简化写法.
// 求两个整数的最大值 int a = 10; int b = 20; int max = a > b ? a : b;注意:
- 表达式2和表达式3的结果要是同类型的,除非能发生类型隐式类型转换
- 表达式不能单独存在,其产生的结果必须要被使用。
int a = 10; int b = 20; a > b? a : b; // 报错:Error:(15, 14) java: 不是语句
1.8 运算符的优先级
一条表达式中,各个运算符可以混合起来用,但运算符优先级不同,如* / 优先级高于+ -
// 求a和b的平均值 int a = 10; int b = 20; int c = a + (b - a) >> 1; System.out.println(c);此时由于+的优先级高于>>,因此会先计算左侧内容,整体为20,再进行右移,最终结果c=10
注意:运算符之间是有优先级的. 具体的规则我们不必记忆. 在可能存在歧义的代码中加上括号即可
int c = a + ((b - a) >> 1);了解,实际上基本不会用到,直接加()表示最高优先级即可:
二. 程序逻辑控制
2.1 顺序结构
顺序结构就是按代码书写顺序,一行一行从上到下执行,调整代码顺序,执行顺序也会变化。此处不多赘述
2.2 分支结构
2.2.1 if语句
if语句有三种写法,if,if-else if-else
基本语法格式为:
if (布尔表达式) { // 语句 } //若布尔表达式结果为true,执行if中语句,否则不执行if (布尔表达式) { //语句1 } else { //语句2 } //if中布尔表达式成立,执行语句1,不成立执行语句2if (布尔表达式1) { //语句1 } else if (布尔表达式2) { //语句2 } else { //语句3 } //若布尔表达式1成立,执行语句1 //若布尔表达式2成立,执行语句2 //若上述条件都不成立,执行语句3常见的就以上三种写法
下面给出一个案例:
/* 例如: 分数在 [90, 100] 之间的,为优秀 分数在 [80, 90) 之前的,为良好 分数在 [70, 80) 之间的,为中等 分数在 [60, 70) 之间的,为及格 分数在 [ 0, 60) 之间的,为不及格 错误数据 */ public class HelloWorld { public static void main(String[] args) { //这里看不懂没关系,后面会说到,此时score的值为用户在键盘中输入的值 Scanner sc = new Scanner(System.in); int score = sc.nextInt(); if(score >= 90){ System.out.println("优秀"); }else if(score >= 80){ System.out.println("良好"); }else if(score >= 70){ System.out.println("中等"); }else if(score >= 60){ System.out.println("及格"); }else if(score >= 0){ System.out.println("不及格"); }else{ System.out.println("错误数据"); } } }关于上述语句,第二个if(score >= 80),隐藏的前提条件就是if(score < 90) ,即后一个条件是在前一个条件不满足的基础上成立的
看到这里,就可以去写一个简单练习了,比如判断一个年份是否是闰年这种,只需要简单的条件判断就可以达成:
//判断闰年的标准: //1. 如果一个年份能被4整除但不能被100整除,则是闰年 //2. 如果一个年份能被400整除,也是闰年 public class HelloWorld { public static void main(String[] args) { int year = 2000; if (year % 100 == 0) { // 判定世纪闰年 if (year % 400 == 0) { System.out.println("是闰年"); } else { System.out.println("不是闰年"); } } else { // 普通闰年 if (year % 4 == 0) { System.out.println("是闰年"); } else { System.out.println("不是闰年"); } } } }如果上面写法比较熟练了,也可以采用下面写法
public class HelloWorld { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int year = sc.nextInt(); if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { System.out.println("是闰年"); }else { System.out.println("不是闰年"); } } }这两种写法本质上是一样的,只不过后者可读性可能更好。
if语句需要注意的两个问题:
1. 分号问题
int x = 20; if (x == 10) ; { System.out.println("hehe"); } // 运行结果 hehe此处if后面多写了个分号,导致分号成为了 if 语句的语句体, 而 { } 中的代码已经成为了和一个 if 无关的代码块.
2. 悬垂else问题
int x = 10; int y = 10; if (x == 10) if (y == 10) System.out.println("aaa"); else System.out.println("bbb");if /else 语句中可以不加大括号,但是也可以写语句(此时只能写一条语句). 此时else是和最接近if相匹配,但实际开发中不建议这么写,因为可读性太差,最好加上大括号。
2.2.2 switch 语句
基本语法:
switch(表达式){ case 常量值1:{ 语句1; [break;] } case 常量值2:{ 语句2; [break;] } ... default:{ 内容都不满足时执行语句; [break;] } }执行流程:
- 先计算表达式的值
- 和case依次比较,一旦有相应的匹配就执行该项下的语句,直到遇到break时结束
- 当表达式的值没有与所列项匹配时,执行default
代码示例:
//根据day的值输出日期 public class HelloWorld { public static void main(String[] args) { int day = 1; switch (day) { case 1: System.out.println("星期一"); break; case 2: System.out.println("星期二"); break; case 3: System.out.println("星期三"); break; case 4: System.out.println("星期四"); break; case 5: System.out.println("星期五"); break; case 6: System.out.println("星期六"); break; case 7: System.out.println("星期日"); break; default: System.out.println("输入有误"); break; } } }注意事项:
- 多个case后的常量值不可以重复
- switch的括号内只能是以下类型的表达式:
基本类型:byte、char、short、int,注意不能是long类型
引用类型:String常量串、枚举类型 - break 不要遗漏, 否则会失去 "多分支选择" 的效果
int day = 1; switch(day) { case 1: System.out.println("星期一"); // break; case 2: System.out.println("星期二"); break; } //运行结果: 星期一 星期二 - switch 不能表达复杂的条件
// 例如: 如果 num 的值在 10 到 20 之间, 就打印 hehe // 这样的代码使用 if 很容易表达, 但是使用 switch 就无法表示. if (num > 10 && num < 20) { System.out.println("hehe"); } - switch 虽然支持嵌套, 但是很丑,一般不推荐~
综上,笔者建议尽量少用switch,switch能解决的,if-else也都能解决
2.3 循环结构
2.3.1 while循环
while循环适合不知循环次数,先判断再执行
语法格式:
while(循环条件) { 循环语句; }循环条件为true,则执行循环语句,否则结束循环。
2.3.2 for循环
for循环是一般情况下最常用,最清晰的循环了,适合已知循环次数
语法:
for(表达式1;布尔表达式2;表达式3){ 表达式4; }- 表达式1: 用于初始化循环变量初始值设置,在循环最开始时执行,且只执行一次
- 表达式2: 循环条件,满则循环继续,否则循环结束
- 表达式3: 循环变量更新方式
示例:打印1-10
for(int i = 1;i<=10;i++) { System.out.println(i); }若其中表达式2缺失,表示该循环为死循环
2.3.3 do-while循环
语法:
do{ 循环语句; }while(循环条件);特点是至少跑一次的循环,实际很少用
2.3.4 循环控制关键字
break关键字:break 的功能是让循环提前结束,在某些情况下,提前打破循环
continue关键字:continue 的功能是跳过这次循环, 立即进入下次循环.
三. 总结
Java前面内容基本都是介绍一些基本概念,都是一些基础中的基础,便于后续的学习。