终于,我们已经了解了基本所有的关于变量与操作符的知识了,你也终于可以对你的各种类型的变量们“肆意妄为”了。那么这节课,我们将开始接触编程逻辑的训练了。
以我们目前的所学,我们只知道计算机可以按照某种预定的顺序去执行代码。但是有的时候我们可不能让程序不顾目的地一条路走到底,这时咱们就请判断语句闪亮登场吧。
我想先请各位思考一下,大家每天是怎么选择穿什么衣服的:
- 如果某件衣服穿过但还没洗,我则不会考虑穿这件衣服;
- 如果我想穿的裤子颜色是黑色的,我则不会再选择黑色的衣服;
- 如果我想……
在这个过程中,我是通过一次次判断(检查某件衣服的情况)与执行相对应的操作(把排除的衣服放到另一边)进行筛选的。
而这就是写判断语句块的两大要素:条件和相应条件对应的该执行的语句
我们直接来看if
判断语句块的代码:
if (condition) { // condition为条件
statements; // statements为相应条件成立时需要执行的语句(组)
...
}
condition
:本质上是一个布尔表达式,其实就是一个条件,诸如radius < 0
、GPA >= 3.7 && GPA < 3.8
等statements
:相对应括号条件成立时,所需要的执行的语句
执行效果最终表现为:当condition这个地方的条件成立(值为true)时,才执行后面的语句块(大括号括起来的地方)里的statements。
有的时候我们希望condition不成立的时候,也去做一些特定的事情。那么我们就可以用到if-else
语句块了:
if (condition) {
// do something
...
}
else {
// do something else
...
}
在这里,我们先停下来看一个例子:
我们希望按照四年一闰,百年不闰,四百年闰的规则输出一个年份(year)是一个闰年还是平年(想要练习一下的同学可以先不看下面的答案哦)
注意我刚刚说的两大要素,首先我们想到的必然是去把四年一闰,百年不闰,四百年闰的规则用逻辑表达式表示出来。第一步,我们需要判断以下三点:
- 条件1:变量year能被4整除?
- 条件2:变量year能被100整除?
- 条件3:变量year能被400整除?
接下来你要做的就是确定这个几个条件之间的逻辑关系,也就是它们应该用什么逻辑操作符进行连接:
- 当条件1成立且条件2不成立时,year才是闰年,显然,这里需要用到且操作符
- 但是注意,上面的条件忘记了考虑像2000年(千禧年!)这种闰年
- 不难发现,只要条件3成立,year就一定是闰年,因此条件3应该与上面的条件使用或操作符连接
那么,我们的判断就应该是这样的:year % 4 == 0 && year % 100 != 0 || year % 400 == 0
,如果你不确定是&&
先算还是||
先算,推荐你打一个括号:(year % 4 == 0 && year % 100 != 0) || year % 400 == 0
那么当它成立时,我则打印xxxx is a leap year
;否则我应该打印xxxx is a common year
。
最终,我们的核心代码如下:
if( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 ) {
System.out.println(year + " is a leap year!");
}
else {
System.out.println(year + " is a common year!")
}
同学们常常被debug的问题困扰着,明明本地IDEA里自己输啥都对,但是放到OJ上一跑就WA(Wrong Answer),手动怎会如此.jpg
当遇到这种问题时,建议各位去仔细想一想每一种情况,去针对每一种情况为自己出一些样例。比如在这个题中,你就可以想出针对三个条件成立与不成立的样例,如:
- 2021年(123都不成立)
- 2024年(1成立,2不成立,3不成立)
- 2100年(1成立,2成立,3不成立)
- 2000年(1成立,2成立,3成立)
检查看看,它们是否都有正确的结果呢?
不知道大家是否了解2000年的千年虫危机(好像已经是大部分人出生之前的事情了)。大量计算机的智能系统,年份只使用额两位十进制数表示,因此当系统进行(或涉及到)跨世纪的日期处理运算时(如多个日期之间的计算或比较等),就会出现错误的结果,进而引发各种各样的系统功能紊乱甚至崩溃。除此之外,有一部分电脑系统缺乏对四百年闰的判断底层逻辑,导致日期直接由2月28日过渡到了3月1日。
当某个条件进入else分支时,你想要再次进行判断,就可以使用方便的else if
,而不是再在else块内写一个if块啦:
if (condition1) {
...
}
else if (condition2) {
...
}
else {
...
}
switch语句可以根据变量的值来选择下一步执行哪一句代码。
下面的代码想要根据month的值,来输出相应月份对应的季节:
switch(month){
case 3:
case 4:
case 5: System.out.println("Spring"); break;
case 6:
case 7:
case 8: System.out.println("Summer"); break;
case 9:
case 10:
case 11: System.out.println("Autumn"); break;
case 12:
case 1:
case 2: System.out.println("Winter"); break;
default: System.out.println("Invalid month!"); // run default branch if there's no same value of month
}
当程序执行到这里时,switch
会根据其后小括号中变量month
的值,选择应该从哪里执行,而break;
语句,会结束switch
块,直接去执行这之后的代码。我写到这里的时候正好是十一假期,那我们就假设month
的值为10。那么程序在经过第一行的判断后,就会找case 10
的那一行,也就是第9行。但是第九行没有语句,也没有break;
,所以程序会继续向下执行第十行的语句,打印出Autumn
字样,然后出现了break;
,于是直接结束了switch
块。更细节地,month
为3、4、5时会输出Spring
;month
为6、7、8时会输出Summer
;month
为9、10、11时会输出Autumn
;month
为12、1、2时会输出Winter
;若都不是,则输出Invalid month!
。
- 括号内的变量值只可以是char、byte、short、int或者String型值
- case后的值必须与括号中的变量的类型相同
- default语句块和break语句都是可加可不加的,重点在于你的逻辑
条件表达式(condition expression)像是一种简化版的if-else
语句块,我们先来看看它的形式:
// condition ? expression1 : expression2
max = (num1 > num2) ? num1 : num2;
其中的(num1 > num2) ? num1 : num2
就是一个条件表达式,若condition
为真,则结果为expression1
;否则为expression2
。在这个具体的例子中,当num1
大于num2
时,表达式结果为num1
,赋值给变量max
的值就是num1
的值;否则就是num2
。
上面的代码其实等效于如下的代码:
if (num1 > num2) {
max = num1;
}
else {
max = num2;
}
当某个语句块中只有一条语句时,我们可以省略其周围的大括号,比如上一节最后的代码就可以写成这样:
if (num1 > num2)
max = num1;
else
max = num2;
但是为了避免一些不必要的歧义(例子请看下节),建议大家在初学阶段都打上该打的括号。
逻辑能力不仅仅是与生俱来的,也可以是后天的努力习得的。
多多练习如何将一些抽象的判断用逻辑语言表现出来
(思考:如何判断一个字符串是不是一个合法的学号呢?)