入门篇-其之九-流程控制之条件判断
本文中使用到的工具是Intellij IDEA和JDK 8,需要安装两款工具的请查看这两篇教程:点我查看安装JDK8/11/17教程、点我查看安装Intellij IDEA教程。
前面我们写过的代码,都是在main
方法中自上到下按顺序执行的,举一个代码栗子:
/**
* 计算西瓜的价格
*
* @author iCode504
* @date 2023-10-31
*/
public class MyWatermelonDemo1 {
public static void main(String[] args) {
int price = 2; // 西瓜的单价
int weight = 10; // 西瓜的重量(公斤)
int totalPrice = price * weight; // 购买价格
System.out.println("西瓜的价格是: " + totalPrice + "元");
}
}
这段代码就是先定义西瓜的单价、再定义西瓜的重量,然后计算西瓜的价格,最后对价格进行输出。像这样代码从上到下执行的结构就是顺序结构。
程序一共有三种控制结构:顺序结构、选择结构和循环结构。其中选择结构是根据条件判定的结果,选择执行不同的代码,例如:红灯停,绿灯行。判断条件就是交通信号灯的状态。
Java也有选择结构,并且有多种类型的条件判断语句:单分支的if
语句、双分支的if-else
语句、多分支的if-else if-else
语句、if
嵌套语句和switch
语句。
一、单分支if语句
单分支if
语句的语法如下:
if (条件表达式) {
执行代码...
}
其中条件表达式的计算结果必须是boolean
类型。如果条件表达式的计算结果是true
,那么就会执行if
内部的代码;如果表达式为false
,此时就会跳过if
代码块(也就是if
内部代码不执行),概念图如下:
我们可以在if
代码块中可以编写多个执行语句。
以下是if
的使用案例:
案例:之前长春下了大暴雪,气温骤降,我想在某宝上买几双棉袜子,假设每双袜子4元,请确保输入的数字大于0再计算购买价格。
分析:解题的关键在于要保证输入的数字要大于0,因此要判断输入的数字是否大于0,示例代码如下:
import java.util.Scanner;
/**
* if条件判断
*
* @author iCode504
* @date 2023-11-07
*/
public class IfDemo1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入袜子的数量");
int count = scanner.nextInt();
double totalPrice = 0.0; // 默认总价格初始值为0.0
double price = 4; // 袜子的价格4元
// 条件判断: 输入的数量是否大于0
if (count > 0) {
totalPrice = count * price;
System.out.println("购买了" + count + "双袜子, 价格是" + totalPrice + "元");
}
}
}
运行结果:
案例:输入一个数字,如果能被10整除,则输出内容为:xx能被10整除。如果能被15整除,则输出内容为:xx能被15整除。
解决本题的关键点在于被10整除和被15整除的条件怎么计算。其实前面我们学过取余运算符,如果数值number
能被10整除,那么可以写成number % 10 == 0
;如果数值number
能被15整除,那么可以写成number % 15 == 0
。这两个布尔表达式可以写入到两个if
语句中:
import java.util.Scanner;
/**
* 使用if语句判断数字能否被10和15整除
*
* @author iCode504
* @date 2023-11-07
*/
public class IfDemo2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入数字: ");
int number = scanner.nextInt();
// 整除10的条件:数字对10取余等于0
if (number % 10 == 0) {
System.out.println(number + "能被10整除");
}
// 整除15的条件:数字对15取余等于0
if (number % 15 == 0) {
System.out.println(number + "能被15整除");
}
}
}
输入不同的数字以后,会得到如下的运行结果:
案例:输入两个整数,如果输入的第一个数字比第二个数字大,那么执行两数交换,并将交换结果输出。否则不交换,正常输出两个数
本题的条件表达式是输入的两个数字的比较,无论数字大小比较结果如何,都需要将结果进行输出,我们可以将输出语句放到if
语句后面执行。
两数交换有多种方式,较为稳妥的方式是再定义一个临时变量,用这个临时变量来接收第一个变量值,然后第二个变量值赋值给第一个变量,最后将临时变量的值赋值给第二个变量。
以下是示例代码:
import java.util.Scanner;
/**
* 单分支if语句实现两数交换
*
* @author iCode504
* @date 2023-11-11
*/
public class IfDemo3 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入两个整数");
int number1 = scanner.nextInt();
int number2 = scanner.nextInt();
int temp = 0; // 定义临时变量
if (number1 > number2) { // 实现两数交换功能
temp = number1;
number1 = number2;
number2 = temp;
}
System.out.println("第一个数是: " + number1 + ", 第二个数是: " + number2);
}
}
运行结果:
当然,除了上述方式能实现两数交换,还有其他的方式。
方式一:使用加减法进行交换(推荐使用整数,浮点数不推荐,因为浮点数计算时会出现误差)
int number1 = 3;
int number2 = 2;
number1 = number1 + number2; // number1 = 3 + 2 = 5
number2 = number1 - number2; // number2 = 5 - 2 = 3
number1 = number1 - number2; // number1 = 5 - 3 = 2
方式二:使用位运算符进行交换(推荐使用整数,此处涉及到二进制异或运算,异或运算可以查看这篇文章:入门篇-其之六-Java运算符(中)第四部分-位运算符)
int number1 = 3;
int number2 = 2;
number1 = number1 ^ number2; // 3 ^ 2 = 1
number2 = number1 ^ number2; // 3 ^ 1 = 2
number1 = number1 ^ number2; // 1 ^ 2 = 3
当然,上述三种方式我个人最推荐的还是第一种定义临时变量的方式,这种方式对处理浮点类型的数进行交换很友好,如果使用了下面两种方式的话,可能会在计算过程中出现精度损失的问题。后两种方式的好处是不需要定义第三个变量,只需要进行一系列运算即可完成整数值的交换。
二、if-else双分支语句
前面讲过的单分支if
语句只有在布尔表达式为true
的时候执行其内部的内容,但是如果在布尔表达式为false
的时候不会做任何事情。为了解决上述问题,Java为我们提供了if-else
双分支语句。以下是双分支if-else
语句代码结构:
if (条件表达式) {
执行代码1...
} else {
执行代码2...
}
如果条件表达式的值是true
,那么就执行if
内部的语句,如果条件表达式为false
,此时就进入else
代码块。执行流程图如下:
案例:我们还是以上述买袜子为例,最近双十一打折,如果买了10双及以上袜子,此时每双袜子打八折优惠,否则打九折优惠(袜子的价格假设是4元/双)。
题目中的条件表达式在于要买的袜子数量是否大于等于10,如果是,价格打8折,否则打9折,使用刚刚讲到的if-else
语句即可搞定。
当然,这道题中还有一个隐藏的细节需要我们处理:输入袜子的数量需要大于0,否则判定为无效,这个可以使用单分支if
语句就可以搞定。
以下是示例代码:
import java.util.Scanner;
/**
* if-else双分支语句
*
* @author iCode504
* @date 2023-11-11
*/
public class IfDemo4 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要购买的袜子数量");
int count = scanner.nextInt();
double price = 4; // 每双袜子的价格
double totalPrice = 0.0; // 默认总价格为0.0
// 需要保证输入的袜子数量要大于0
if (count > 0) {
// 如果袜子的数量大于10,此时每双袜子的价格为8折,即每双袜子的价格乘以0.8,九折的计算方式和上述内容同理
if (count >= 10) {
totalPrice = price * 0.8 * count;
} else {
totalPrice = price * 0.9 * count;
}
}
System.out.println("购买" + count + "双袜子,双十一期间购买价格是" + totalPrice + "元");
}
}
运行结果(可能会出现浮点数计算不准确的情况,属于正常现象):
三、if-else if-else多分支语句
双分支的if-else
语句对于条件表达式为true
和false
的时候比较适用,但是如果对于一个问题而言,此时经过分析可能存在多个条件表达式时,if-else
语句并不能很好地完成任务,此时Java为我们提供了另一种分支语句:if-else if-else
语句,其语法格式如下:
if (条件表达式1) {
执行代码1...
} else if (条件表达式2) {
执行代码2...
} else if (条件表达式3) {
执行代码3...
} ...
else if (条件表达式n) {
执行代码n...
} else {
不符合上述所有条件表达式时执行else代码...
}
以上述语法格式为例,其执行顺序为:
- 如果条件表达式1的结果为
true
,那么执行代码1,如果结果是false
,此时就会跳转到第一个else if
。 - 如果条件表达式2的结果是
true
,那么执行代码2,如果结果是false
,那么就会跳转到第二个else if
。 - 如果条件表达式3的结果是
true
,那么执行代码3,如果结果是false
,那么就会跳转到下一个else if
,依次类推。 - 当上述所有的条件表达式都不满足(即结果全部是
false
)时,就会执行else
中的语句。
多分支的if-else if-else
语句中,你可以写任意个else if
,每个else if
需要写上条件表达式。
当然,最后的else
也是可选的,if
和else-if
搭配使用也是可以的。以下是执行流程图:
案例:已知长春的地铁/轻轨票价标准如下
- 0-7公里(含7公里),票价2元;
- 7-13公里(含13公里),票价3元;
- 13-19公里(含19公里),票价4元;
- 19-27公里(含27公里),票价5元;
- 27-35公里(含35公里),票价6元;
- 35公里以上每增加10公里,增加1元
假设从1号线红嘴子地铁站到8号线广通路轻轨站的距离是31.4公里,从2号线汽车公园地铁站到2号线东方广场地铁站的距离是20.5公里,从4号线长春站北轻轨站到4号线天工路轻轨站的距离是16.3公里。
输入上述里程,利用程序计算出乘坐轨道交通所需要的票价。
上述题目中出现了多个条件判断,每个条件判断执行内容都不相同,使用多分支语句if-else if-else
语句比较合适。题目中有一个隐藏条件,输入里程数不能为负数,当然这个条件也直接写入条件判断即可。
如果乘坐轨道交通的里程超过35公里以后,每增加10公里,增加1元。例如:乘坐45公里就要在35公里对应的票价6元的基础上再增加1元,当然,55公里、65公里依次类推。假设称作里程为44.9公里,此时收费标准仍为35公里的票价。
针对上述问题,我们可以在代码中进一步呈现:
import java.util.Scanner;
/**
* if-else if-else多分支语句的使用
*
* @author iCode504
* @date 2023-11-14
*/
public class ElseIfDemo1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入里程数: ");
// 里程数使用double类型比较合适,因为题目中涉及到距离的使用到了小数
double distance = scanner.nextDouble();
int price = 0;
if (distance <= 0) {
System.out.println("无效里程");
} else if (distance > 0 && distance <= 7) {
price = 2;
} else if (distance > 7 && distance <= 13) {
price = 3;
} else if (distance > 13 && distance <= 19) {
price = 4;
} else if (distance > 19 && distance <= 27) {
price = 5;
} else if (distance > 27 && distance <= 35) {
price = 6;
} else {
// 超过35公里的需要额外进行处理
price = 6; // 35公里对应的票价
// 计算多余的里程
// 这里需要进行强制类型转换的目的有两个:
// 1. 最后计算票价的price是int类型
// 2. 针对类似在35~45公里之间的里程后续的票价计算处理
int additionalDistance = (int) (distance - 35);
// 计算票价
price = price + additionalDistance / 10;
}
System.out.println("乘坐长春轨道交通里程" + distance + "公里,票价" + price + "元");
}
}
运行结果如下:
四、if的嵌套使用
正如标题所讲,if
语句可以嵌套使用。举个栗子:在main
方法中,假设已经存在了一个if-else
语句,那么在这个if
代码块或者else
代码块还可以存在条件判断语句,下面就是其中一种if
的嵌套使用方式(事实上它可以if
代码块可以进行多种组合嵌套使用):
if (条件表达式1) {
if (条件表达式2) {
执行代码1...
} else {
执行代码2...
}
} else {
if (条件表达式3) {
执行代码3...
} else {
执行代码4...
}
}
它的执行流程如下:
- 如果条件表达式1的执行结果是
true
,进入条件表达式2,如果条件表达式2执行结果是true
,此时执行代码1。 - 如果条件表达式1的执行结果是
true
,进入条件表达式2,如果条件表达式2执行结果是false
,此时执行代码2。 - 如果条件表达式1的执行结果是
false
,进入条件表达式3,如果条件表达式3执行结果是true
,此时执行代码3。 - 如果条件表达式1的执行结果是
false
,进入条件表达式3,如果条件表达式3执行结果是false
,此时执行代码4。
执行流程图如下所示:
日常写代码的过程中,尽量保证代码嵌套的层数不超过两层。
案例:输入三个数,要求输出是按照从大到小进行排列。例如,输入三个数为20、30、10,输出结果为30、20、10
- 第一层条件:比较第一个数和第二个数。
- 第二层条件:比较第二个数和第三个数。
- 第三层条件:比较第一个数和第三个数。
import java.util.Scanner;
/**
* if的嵌套--三个数字排列
*
* @author iCode504
* @date 2023-11-23
*/
public class IfDemo5 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入三个整数: ");
int number1 = scanner.nextInt();
int number2 = scanner.nextInt();
int number3 = scanner.nextInt();
if (number1 > number2) {
if (number2 > number3) {
System.out.println("三个数从大到小的排序是: " + number1 + " " + number2 + " " + number3);
} else {
if (number1 > number3) {
System.out.println("三个数从大到小的排序是: " + number1 + " " + number3 + " " + number2);
} else {
System.out.println("三个数从大到小的排序是: " + number3 + " " + number1 + " " + number2);
}
}
} else {
if (number2 < number3) {
System.out.println("三个数从大到小的排序是: " + number3 + " " + number2 + " " + number1);
} else {
if (number1 > number3) {
System.out.println("三个数从大到小的排序是: " + number2 + " " + number3 + " " + number1);
} else {
System.out.println("三个数从大到小的排序是: " + number2 + " " + number1 + " " + number3);
}
}
}
}
}
运行结果:
虽然运行结果符合我们的预期,但是我只能说:这样的代码写的非常糟糕!!!这段代码中if
的嵌套层数达到了三层,事实上if
嵌套两层以上可读性就大打折扣了。
我们可以使用Java数组、循环和数组方法对此问题做进一步处理(小白可以跳过这一部分)。
import java.util.Arrays;
import java.util.Scanner;
/**
* 三数比较--按照从大到小的顺序排列
*
* @author iCode504
* @date 2023-11-23
*/
public class IfDemo6 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入三个数字: ");
Integer[] array = new Integer[3];
for (int i = 0; i < array.length; i++) {
array[i] = scanner.nextInt();
}
// 调用Arrays.sort方法对数组排序,排序规则从大到小(lambda表达式)
Arrays.sort(array, (o1, o2) -> o2 - o1);
System.out.println("三个数从大到小的排序是: " + array[0] + " " + array[1] + " " + array[2]);
}
}
运行结果:
五、switch语句
if-else if-else
多分支语句可以用于多个条件表达式的判断,我们可以写非常多的else if
,然而过多的else if
可能会导致代码的可读性变差。
Java为我们提供了swtich
语句在一定程度上可以简化多条件分支。以下是switch
的语法结构:
switch (表达式) {
case 值1:
执行代码1...
break;
case 值2:
执行代码2...
// break
case 值3:
执行代码3...
break;
...
case 值n:
执行代码n...
break;
default:
上述条件都不适用执行代码...
}
1. switch
语句中表达式的计算结果、值1、值2、...、值n的数据类型必须要保持一致。支持的数据类型包括:byte
、short
、int
、char
、字符串类型String
(JDK 7新特性)、枚举类型(后续会讲到,JDK 7新特性)。
2. 如果表达式的计算结果和case
中某个值相等时,就会执行这个case
内的代码。
3. switch
语句中的default
是可选的,它的作用是当表达式的计算结果和所有case
的值都不相等时才会执行default
语句,如果default
语句不存在时,所有的case
对应的值和判定值都不相等时,跳出switch
语句。
4. break
的作用是跳出switch
语句(break
关键字还会在循环中遇到),在每一个case
对应的代码块后面写上break
是个好习惯。
如果case
中不加break
,此时switch
语句会出现穿透性,即当某一个case
执行完成后,它会继续执行下面其他的case
。以下是一个是否使用break
的案例:
案例:输入数字1~7,使用
switch
语句输出当前日期(假设7代表星期日)
import java.util.Scanner;
/**
* switch语句--不加break--穿透性
*
* @author iCode504
* @date 2023-11-15
*/
public class SwitchDemo1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入星期数(1~7), 7表示星期日");
int number = scanner.nextInt();
switch (number) {
case 1:
String monday = "星期一";
System.out.println("今天是" + monday);
case 2:
String tuesday = "星期二";
System.out.println("今天是" + tuesday);
case 3:
String wednesday = "星期三";
System.out.println("今天是" + wednesday);
case 4:
String thursday = "星期四";
System.out.println("今天是" + thursday);
case 5:
String friday = "星期五";
System.out.println("今天是" + friday);
case 6:
String saturday = "星期六";
System.out.println("今天是" + saturday);
case 7:
String sunday = "星期日";
System.out.println("今天是" + sunday);
default:
System.out.println("无效日期");
}
}
}
运行结果:
很明显,输入数字3的时候,由于没有break
,当执行case 3
内部代码以后,它会向下执行其他case
中的代码,直至default
内的代码执行完毕为止。并且这段代码还有可以进一步修改的空间,以下是加入break
并进行简化的代码:
import java.util.Scanner;
/**
* switch语句--添加break--穿透性
*
* @author iCode504
* @date 2023-11-15
*/
public class SwitchDemo2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入星期数(1~7), 7表示星期日");
int number = scanner.nextInt();
String weekday;
switch (number) {
case 1:
weekday = "星期一";
break;
case 2:
weekday = "星期二";
break;
case 3:
weekday = "星期三";
break;
case 4:
weekday = "星期四";
break;
case 5:
weekday = "星期五";
break;
case 6:
weekday = "星期六";
break;
case 7:
weekday = "星期日";
break;
default:
weekday = "无效星期";
}
System.out.println("今天是" + weekday);
}
}
运行结果:
从上述结果可以看出,使用break
以后,就可以阻断switch
穿透性。
switch
语句执行流程如下图所示(每个case
都带上break
语句):
在了解了switch
语句的基础上,我们再来讲解一个switch
语句和if-else
语句结合使用的案例:
案例:输入年份和月份,输出格式如下:xxxx年xx月有xx天。
常识:1、3、5、7、8、10、12恒定是31天;4、6、9、11恒定为30天。这几个月份我们可以利用switch
的穿透性替换掉多条件的else if
判断。
需要额外考虑的是:2月份的天数需要考虑年份是闰年还是平年,闰年能被400整除,例如:2000年,1600年是闰年,1900年就不是闰年。此外,如果不能被100整除,而能被4整除的也是闰年,例如:2020,2016,2004,2008年都是闰年。
结合上述分析,我们可以使用代码进一步复现:
import java.util.Scanner;
/**
* switch和if结合使用
*
* @author iCode504
* @date 2023-11-15
*/
public class SwitchDemo3 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入年份: ");
int year = scanner.nextInt();
System.out.print("请输入月份: ");
int month = scanner.nextInt();
int day = 0;
switch (month) {
// 利用switch的穿透性
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
day = 31;
break;
case 4:
case 6:
case 9:
case 11:
day = 30;
break;
case 2:
// 2月份需要额外针对年份进行判断
if (year % 400 == 0) {
day = 29;
} else if (year % 4 == 0 && year % 100 != 0) {
day = 28;
}
break;
default:
// 不在1-12月份内做出说明
System.out.println("无效的月份");
}
System.out.println(year + "年" + month + "月有" + day + "天");
}
}
运行结果:
以我个人的开发经验来看,使用switch
语句的频率要比使用if
语句要少很多,二者的执行效率基本上差不太多。
if
可以编写更加灵活的条件表达式。比如:判断某个整数在[10, 20]
区间内,此时使用if
条件表达式可以写成if (number >= 10 && number <= 20)
,如果使用switch
解决此问题会让代码变得更加复杂(因为你要写很多个case
进行比较)。
switch
更擅长特定类型的值进行比较。以上面根据某年某月求当前月份由多少天为例,事实上完全使用if
语句实现,只不过我们需要写成:
使用前面案例对应的switch
代码,和if
语句对比,个人觉得可读性变高:
因此,使用if
语句还是switch
语句还是得根据具体的代码场景而决定。
六、《阿里巴巴Java开发手册》关于条件判断语句的相关规范
1. 【强制】在一个switch
块内,每个case
要么通过continue/break/return
等来终止,要么注释说明程序将继续执行到哪一个case
为止;在一个switch
块内,都必须包含一个default
语句并且放在最后,即使它什么代码也没有。
说明:注意break
是退出switch
语句块,而return
是退出方法体。
2. 【强制】当switch
括号内的变量类型为String
并且此变量为外部参数时,必须先进行null
判断。
反例:如下的代码输出内容是什么?
public class SwitchString {
public static void main(String[] args) {
method(null);
}
public static void method(String param) {
switch (param) {
// 肯定不是进入这里
case "sth":
System.out.println("it's sth");
break;
// 也不是进入这里
case "null":
System.out.println("it's null");
break;
// 也不是进入这里
default:
System.out.println("default");
}
}
}
3. 【强制】在if/else/for/while/do
语句中必须使用大括号。
说明:即使只有一行代码,禁止不采用大括号的编码方式:if (条件表达式) statements;
,上述代码需改成如下格式:
if (条件表达式) {
statements;
}
4. 【推荐】表达异常的分支时,少用if-else
方式,这种方式可以改写成:
if (condition) {
...
return obj;
}
// 接着写 else 的业务逻辑代码;
说明:如果非使用if()...else if()...else...
方式表达逻辑,避免后续代码维护困难,请勿超过 3 层。
正例:超过 3 层的if-else
的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句示例如下(不了解卫语句是什么的小伙伴可以参考这篇文章:点我查看):
public void findBoyfriend (Man man){
if (man.isUgly()) {
System.out.println("本姑娘是外貌协会的资深会员");
return;
}
if (man.isPoor()) {
System.out.println("贫贱夫妻百事哀");
return;
}
if (man.isBadTemper()) {
System.out.println("银河有多远,你就给我滚多远");
return;
}
System.out.println("可以先交往一段时间看看");
}
5. 【推荐】避免采用取反逻辑运算符。
说明:取反逻辑不利于快速理解,并且取反逻辑写法必然存在对应的正向逻辑写法。
正例:使用if (x < 628)
来表达 x 小于 628。
反例:使用if (!(x >= 628))
来表达 x 小于 628。
七、知识点总结
流程控制之条件判断知识点总结如下图所示:
如需高清大图,请点击右侧链接下载文件:点我下载