笨办法学 Java(一)https://developer.aliyun.com/article/1481886
练习 10:变量只能保存值
好的,现在我们可以从人类那里获取输入并进行计算,我想要引起一些我的学生经常感到困惑的事情的注意。以下代码应该可以编译,但它可能不会按照你的期望工作。
我故意在代码中制造了一个逻辑错误。这不是语法问题(编译器关心的代码部分),也不是运行时错误,比如当人类在期望整数时,Scanner 对象接收到了一个双精度数。这个逻辑错误是我设计指令流程的缺陷,导致输出不是我想要实现的。
1 import java.util.Scanner; 2 3 public class Sequencing 4 { 5 public static void main( String[] args ) 6 { 7 // BROKEN 8 9 Scanner keyboard = new Scanner(System.in); 10 double price = 0, salesTax, total; 11 12 salesTax = price * 0.0825; 13 total = price + salesTax; 14 15 System.out.print( "How much is the purchase price? " ); 16 price = keyboard.nextDouble(); 17 18 System.out.println( "Item price:\t" + price ); 19 System.out.println( "Sales tax:\t" + salesTax ); 20 System.out.println( "Total cost:\t" + total ); 21 } 22 }
你应该看到什么
How much is the purchase price? 7.99 Item price: 7.99 Sales tax: 0.0 Total cost: 0.0
你对输出感到惊讶吗?你是否期望的销售税显示为7.99的销售税显示为7.99 的销售税显示为0.66 而不是一个大大的零?总成本应该是$8.65,对吧?发生了什么?
发生的是,在 Java(以及大多数编程语言中),变量不能保存公式。变量只能保存值。
看看第 12 行。我的学生有时会认为该行将公式price * 0.0825
存储到变量 salesTax 中,然后稍后人类将值7.99
存储到变量 price 中。他们认为在第 19 行打印 salesTax 时,计算机会以某种方式“运行”该公式。
实际上并不是这样的。事实上,这个程序甚至不应该编译。变量price在第 12 行甚至没有一个合适的值。它之所以有一个值,只是因为我在第 10 行做了一些狡猾的事情。
通常我们一直在程序的顶部声明变量,然后稍后初始化它们。但是在第 10 行,我声明了 price 并将其初始化为0
。当你同时声明和初始化一个变量时,这被称为“定义”变量。salesTax 和 total 在这里没有定义
第 10 行,只是声明。
所以在第 16 行,人类输入的值并没有初始化 price;price 已经有了一个初始值(0
)。但是人类输入的值(7.99
或其他值)确实被存储到变量 price 中。
0
被替换为7.99
。
从第 10 行到第 15 行,变量 price 包含值0
。当第 16 行开始执行并且我们在等待人类输入时,price 仍然包含0
。但是当第 16 行完成时,无论人类输入了什么,都已经存储到 price 中,替换了零。然后从第 17 行到程序结束,变量 price 包含值7.99
(或其他值)。
因此,考虑到这一点,我们可以弄清楚第 12 行实际发生了什么。第 12 行并没有将一个公式存储到 salesTax 中,但它确实存储了一个值。是什么值?它获取代码中此时变量 price 的值(即0
),将其乘以0.0825
(仍然是零),然后将这个零存储到 salesTax 中。
当第 12 行开始时,salesTax 的值是未定义的(salesTax 被声明但未定义)。到第 12 行结束时,salesTax 的值为0
。没有一行代码改变了 salesTax(没有一行代码以salesTax =
开头),因此该值永远不会改变,当它在第 19 行显示时,salesTax 仍然是零。
第 13 行也是类似的。它获取price的值此时(零),并将其加到salesTax的值此时(也是零)中,并将总和(零)存储到变量total中。total的值没有改变,total也不会以某种方式“记住”它的值来自涉及一些变量的公式。
这就是全部内容。变量保存值,而不是公式。计算机程序不是一组规则,它们是计算机按顺序执行的一系列指令,你代码中的后续操作取决于之前发生的事情。
学习演练
- 删除第 10 行的“= 0”,这样price就不再在第 10 行定义,只是声明。当你尝试编译代码时会发生什么?错误消息是否合理?(现在将“= 0”放回,以便程序再次编译。)
- 将给salesTax和total赋值的两行代码移到price获得适当值之后。确认程序现在按预期工作。
- 现在这些行发生在price变量被正确赋予真实值之后,再次尝试删除第 10 行的“= 0”。程序是否仍然报错?你感到惊讶吗?
练习 11:变量修改快捷方式
变量的值可以随着程序运行的时间而改变。(除非你编写代码来改变它,但我是说它可以改变。)
事实上,这是相当常见的。我们经常做的事情是取一个变量并对其进行加法。例如,假设变量 x 包含值10
。我们想要加上2
,这样 x 现在包含12
。
我们可以这样做:
int x = 10, temp_x; temp_x = x + 2; x = temp_x;
这样做是可以的,但很烦人。如果我们愿意,我们可以利用一个变量可以在代码行的开头有一个值,并在结束时存储另一个值的事实。因此,我们可以写出这样的东西:
int x = 10; x = 2 + x;
这也可以。第二行表示“取 x 的当前值(10
),加上2
,并存储总和到变量 x。因此,当第二行代码开始执行时,x 为10
,执行完毕后,x 为 12。加法的顺序无关紧要,所以我们甚至可以这样做:
int x = 10; x = x + 2;
…这与前一个例子相同。好的,现在来看代码!
1 public class VariableChangeShortcuts 2 { 3 public static void main( String[] args ) 4 { 5 int i, j, k; 6 7 i = 5; 8 j = 5; 9 k = 5; 10 11 System.out.println( "i: " + i + "\tj: " + j + "\tk: " + k ); 12 i = i + 3; 13 j = j 3; 14 k = k * 3; 15 System.out.println( "i: " + i + "\tj: " + j + "\tk: " + k ); 16 17 i = 5; 18 j = 5; 19 k = 5; 20 21 System.out.println( "\ni: " + i + "\tj: " + j + "\tk: " + k ); 22 i += 3; 23 j = 3; 24 k *= 3; 25 System.out.println( "i: " + i + "\tj: " + j + "\tk: " + k ); 26 27 i = j = k = 5; 28 29 System.out.println( "\ni: " + i + "\tj: " + j + "\tk: " + k ); 30 i += 1; 31 j = 2; 32 k *= 3; 33 System.out.println( "i: " + i + "\tj: " + j + "\tk: " + k ); 34 35 i = j = k = 5; 36 37 System.out.println( "\ni: " + i + "\tj: " + j + "\tk: " + k ); 38 i =+ 1; 39 j = 2; 40 System.out.println( "i: " + i + "\tj: " + j + "\tk: " + k ); 41 42 i = j = k = 5; 43 44 System.out.println( "\ni: " + i + "\tj: " + j + "\tk: " + k ); 45 i++; 46 j; 47 System.out.println( "i: " + i + "\tj: " + j + "\tk: " + k ); 48 49 } 50 }
你应该看到什么
i: 5 j: 5 k: 5 i: 8 j: 2 k: 15 i: 5 j: 5 k: 5 i: 8 j: 2 k: 15 i: 5 j: 5 k: 5 i: 6 j: 3 k: 15 i: 5 j: 5 k: 5 i: 1 j: 2 k: 5 i: 5 j: 5 k: 5 i: 6 j: 4 k: 5
希望第 1-21 行很好很无聊。我们创建三个变量,给它们赋值,显示它们,改变它们的值,然后再次打印它们。然后从第 17 行开始,我们给变量相同的值,并打印它们。
在第 22 行,我们看到了一些新东西:一种称为“复合赋值运算符”的快捷方式。i += 3
的意思与i = i + 3
相同:“取 i 的当前值,加上3
,并将结果存储为 i 的新值。当我们大声说出来时,我们会说“i 加 3”。
在第 23 行,我们看到=
(“减等于”),从 k 中减去 3,第 24 行演示了*=
,表示乘法。还有/=
,它将左边的变量除以右边的值。还有“模等于”(%=
),它将左边的变量设置为其先前值除以右边的值时余数。呼。
然后在第 27 行,我做了一些奇怪的事情。我不是用三行代码来将 i、j 和 k 都设置为5
,而是用一行代码。有些人不赞成这种技巧,但我认为在这种情况下是可以的。第 27 行的意思是“将值5
放入变量 k。然后取当前 k 中的值(5
)的副本,并将其存储到 j 中。然后取当前 j 中的值的副本,并将其存储到 i 中。”因此,当这行代码执行完毕时,所有三个变量都已更改为等于5
。
第 30 至 32 行基本上与第 22 至 24 行相同,只是我们不再使用3
作为要添加、减去或相乘的数字。
第 38 行可能看起来像是一个打字错误,如果你在自己的代码中写了这个,它可能会是一个打字错误。请注意,我写的不是+=
,而是=+
。这将编译,但它的解释方式与你期望的不同。编译器
看到i = +1;
,也就是“将 i 设置为正 1。”第 39 行类似“将 j 设置为负 2。”所以要注意这一点。
在第 45 行,我们看到了另一个快捷方式:“后增量运算符”。i++
就意味着“在 i 中加 1”。这与写i = i + 1
或i += 1
是一样的。将1
加到变量中是非常常见的。(你会看到的。)这就是为什么有一个特殊的快捷方式。
在第 46 行,我们看到“后减量运算符”:j--
。它从 j 的值中减去 1。
今天的课程很特别,因为这些快捷方式是可选的。你可以一辈子编写代码而不使用它们。但大多数程序员都很懒,不想多打字,所以如果你阅读别人的代码,你会经常看到这些。
练习 12:布尔表达式
到目前为止,我们只看到了三种类型的变量:
整数
整数,不带小数部分的数字(正数或负数)
双精度
“双精度浮点”数字(正数或负数),可能有小数部分
字符串
一个字符串是字符,保存单词、短语、符号、句子,无论什么
但用 Yoda 的话来说:“还有另一个。”“布尔”变量(以数学家乔治·布尔命名)不能保存数字或单词。它只能存储两个值中的一个:true
或false
。就是这样。我们可以用它们来执行逻辑。来看代码吧!
1 import java.util.Scanner; 2 3 public class BooleanExpressions 4 { 5 public static void main( String[] args ) 6 { 7 Scanner keyboard = new Scanner(System.in); 8 9 boolean a, b, c, d, e, f; 10 double x, y; 11 12 System.out.print( "Give me two numbers. First: " ); 13 x = keyboard.nextDouble(); 14 System.out.print( "Second: " ); 15 y = keyboard.nextDouble(); 16 17 a = (x < y); 18 b = (x <= y); 19 c = (x == y); 20 d = (x != y); 21 e = (x > y); 22 f = (x >= y); 23 24 System.out.println( x + " is LESS THAN " + y + ": " + a ); 25 System.out.println( x + " is LESS THAN or EQUAL TO " + y + ": " + b ); 26 System.out.println( x + " is EQUAL TO " + y + ": " + c ); 27 System.out.println( x + " is NOT EQUAL TO " + y + ": " + d ); 28 System.out.println( x + " is GREATER THAN " + y + ": " + e ); 29 System.out.println( x + " is GREATER THAN or EQUAL TO " + y + ": " + f ); 30 System.out.println(); 31 32 System.out.println( !(x < y) + " " + (x >= y) ); 33 System.out.println( !(x <= y) + " " + (x > y) ); 34 System.out.println( !(x == y) + " " + (x != y) ); 35 System.out.println( !(x != y) + " " + (x == y) ); 36 System.out.println( !(x > y) + " " + (x <= y) ); 37 System.out.println( !(x >= y) + " " + (x < y) ); 38 39 } 40 }
你应该看到什么
Give me two numbers. First: 3 Second: 4 3.0 is LESS THAN 4.0: true 3.0 is LESS THAN or EQUAL TO 4.0: true 3.0 is EQUAL TO 4.0: false 3.0 is NOT EQUAL TO 4.0: true 3.0 is GREATER THAN 4.0: false 3.0 is GREATER THAN or EQUAL TO 4.0: false false false false false true true false false true true true true
在第 17 行,布尔变量 a 被设置为一些奇怪的东西:比较的结果。变量 x 中的当前值与变量 y 的值进行比较。如果 x 小于 y,则比较为真,并且布尔值true
存储在 a 中。如果 x 不小于 y,则比较为假,并且布尔值false
存储在 a 中。(我认为这比写起来更容易理解。)
第 18 行类似,只是比较是“小于或等于”,布尔结果存储在b中。
第 19 行是“等于”:如果 x 持有与 y 相同的值,c 将被设置为值true
。第 20 行的比较是“不等于”。第 21 行和第 22 行分别是“大于”和“大于或等于”。
在第 24 行到第 29 行,我们在屏幕上显示了所有这些布尔变量的值。
第 32 行到第 37 行介绍了“非”运算符,即感叹号(!
)。它取逻辑相反。因此,在第 32 行,我们显示“x 是否小于 y”的逻辑否定,并打印出“x 是否大于或等于 y”的真值,它们是等价的。(“小于”的相反是“大于或等于”。)第 33 行到第 37 行显示了其余关系运算符的相反情况。
练习 13:比较字符串
在这个练习中,我们将看到一些让初学者学习 Java 时困扰的东西:常规的关系运算符不适用于字符串,只适用于数字。
boolean a, b; a = ("cat" < "dog"); b = ("horse" == "horse" );
第二行甚至无法编译!你不能在 Java 中使用<
来查看一个单词是否在另一个单词之前。在第三行中,b 确实在这里设置为值true
,但如果你将值读入变量,就不会这样:
String animal; animal = keyboard.next(); // the user types in "horse" b = ( animal == "horse" );
无论人类是否输入"horse"
,b 都将始终被设置为值false
!
我不想试图解释为什么会这样。Java 的创建者对此显然有充分的理由,但对初学者来说并不友好,解释可能只会让你更加困惑。
你还记得我警告过你 Java 不是初学者的语言吗?所以有一种比较字符串是否相等的方法,让我们来看看。
1 import java.util.Scanner; 2 3 public class WeaselOrNot 4 { 5 public static void main( String[] args ) 6 { 7 Scanner keyboard = new Scanner(System.in); 8 9 String word; 10 boolean yep, nope; 11 12 System.out.println( "Type the word \"weasel\", please." ); 13 word = keyboard.next(); 14 15 yep = word.equals("weasel"); 16 nope = ! word.equals("weasel"); 17 18 System.out.println( "You typed what was requested: " + yep ); 19 System.out.println( "You ignored polite instructions: " + nope ); 20 } 21 }
你应该看到什么
Type the word "weasel", please. no You typed what was requested: false You ignored polite instructions: true
因此,字符串有一个名为.equals()
的内置方法(“点等于”),它将自己与另一个字符串进行比较,如果它们相等,则简化为值true
,如果它们不相等,则简化为值false
。你必须使用非运算符(!
)与.equals()
方法一起来判断两个字符串是否不同。
学习演练
- 尝试在第 15 行改变比较,使得
"weasel"
在点的前面,变量 word 在括号内。确保"weasel"
仍然被引号括起来,而 word 则没有。它有效吗?
练习 14:复合布尔表达式
有时我们想使用比“小于”或“等于”更复杂的逻辑。想象一下,只有在 25 岁以上并且40 岁以下并且要么富有要么长得很好看时,祖母才会同意你约会她的孙子。如果那位祖母是一名程序员,并且能说服申请者诚实回答,她的程序可能会像这样:
1 import java.util.Scanner; 2 3 public class ShallowGrandmother 4 { 5 public static void main( String[] args ) 6 { 7 Scanner keyboard = new Scanner(System.in); 8 9 int age; 10 double income, attractiveness; 11 boolean allowed; 12 13 System.out.print( "Enter your age: " ); 14 age = keyboard.nextInt(); 15 16 System.out.print( "Enter your yearly income: " ); 17 income = keyboard.nextDouble(); 18 19 System.out.print( "How attractive are you, on a scale from 0.0 to 10.0? " ); 20 attractiveness = keyboard.nextDouble(); 21 22 allowed = ( age > 25 && age < 40 && ( income > 50000 || attractiveness >= 8.5 )); 23 24 System.out.println( "You are allowed to date my grandchild: " + allowed ); 25 } 26 }
你应该看到什么
Enter your age: 39 Enter your yearly income: 49000 How attractive are you, on a scale from 0.0 to 10.0? 7.5 You are allowed to date my grandchild: false
所以我们可以看到,对于复杂的布尔表达式,您可以使用括号来分组,使用符号&&
表示“AND”,使用符号||
表示“OR”。
我知道你在想什么:使用&
(“和符号”)表示“AND”有点说得通,但为什么要两个?谁想到使用||
(“管道管道”)表示“OR”?!?
嗯,肯·汤普森的想法,可能是。Java 语法是模仿 C++的语法,而 C++的语法基本上是从 C 的语法复制过来的,而 C 的语法是从 B 的语法修改而来的,而 B 的语法是由丹尼斯·里奇和肯·汤普森发明的。
编程语言 B 使用&
表示“AND”,|
表示“OR”,但它们是“按位的”:它们只对两个整数起作用,并且它们会逐位地遍历整数,对每一对比特进行按位 AND 或 OR 运算,在每次比较中放置1
或0
在输出中。(|
可能被使用是因为它看起来像数学符号,并且是 PDP-7 计算机键盘上的一个关键,B 最初是为其开发的。)
当肯恩和丹尼斯开始开发编程语言C来取代B时,他们决定需要一个逻辑的“AND”和“OR”,而单个符号已经被使用了,所以他们使用两个和符号来表示逻辑“AND”,两个竖线或“管道”来表示逻辑“OR”。哇。
幸运的是,你不需要知道任何这些。你只需要记住要输入什么并且输入正确。
接下来的一点有点奇怪,因为我将向您展示 AND 和 OR 的“真值表”,您将不得不将“AND”视为对两个值执行的操作,而不是一个连接词。
以下是 AND 的真值表:
输入 | 输出 | |
A | B | A && B |
真 | 真 | 真 |
真 | 假 | 假 |
假 | 真 | 假 |
假 | 假 | 假 |
您可以这样读表:假设我们的肤浅的祖母已经决定,只有在巡航便宜并且酒精包含在价格中时,她才会去乘船。所以我们假设陈述 A 是“巡航很便宜”,陈述 B 是“酒精包括在内”。表中的每一行都是一个可能的巡航航线。
第 1 行是两个语句都为真的情况。祖母会对第 1 条巡航感到兴奋吗?是的!“巡航很便宜”是真的,“酒精包括在内”也是真的,所以“祖母会去”(A && B)也是真的。
巡航#2 很便宜,但酒精不包括在内(陈述 B 是假的)。所以祖母不感兴趣:(A && B)是假的,当 A 为真时,B 为假。
清楚吗?现在这是 OR 的真值表:
输入 | 输出 | |
A | B | A || B |
真 | 真 | 真 |
真 | 假 | 真 |
假 | 真 | 真假 |
假 | 假 | 假 |
假设祖母会购买某辆二手车,如果它看起来真的很酷,或者它的油耗很好。陈述 A 是“车看起来很酷”,B 是“每加仑好里程”,结果 A 或 B 决定了祖母是否想要这辆车。
汽车#1 看起来很棒,而且一箱油可以走很远。祖母感兴趣吗?当然!我们可以说值true
与值true
进行 OR 运算的结果是true
。
事实上,祖母不喜欢的唯一一辆车是两者都是假的时候。涉及 OR 的表达式只有在其两个组成部分都为假时才为假。
学习演练
- 你知道 Java 也有位运算符吗?调查一下数字是如何用二进制表示的,看看你能否弄清楚为什么以下代码将 x 设置为值
7
,将 y 设置为值1
。
int x = 3 | 5; int y = 3 & 5;
练习 15:使用 if 语句做决定
嘿!我真的很喜欢这个练习。你在那里经历了一些相当无聊的练习,所以现在是时候学习一些有用而不是超级困难的东西了。
1 import java.util.Scanner; 2 3 public class AgeMessages 4 { 5 public static void main( String[] args ) 6 { 7 Scanner keyboard = new Scanner(System.in); 8 9 int age; 10 11 System.out.print( "How old are you? " ); 12 age = keyboard.nextInt(); 13 14 if ( age < 13 ) 15 { 16 System.out.println( "You are too young to create a Facebook account." ); 17 } 18 if ( age < 16 ) 19 { 20 System.out.println( "You are too young to get a driver's license." ); 21 } 22 if ( age < 18 ) 23 { 24 System.out.println( "You are too young to get a tattoo." ); 25 } 26 if ( age < 21 ) 27 { 28 System.out.println( "You are too young to drink alcohol." ); 29 } 30 if ( age < 35 ) 31 { 32 System.out.println( "You are too young to run for President of the United States." ); 33 System.out.println( "How sad!" ); 34 } 35 } 36 }
我们将学习如何编写具有决策的代码,以便输出不总是相同的。执行的代码会根据人输入的内容而改变。
你应该看到什么
How old are you? 17 You are too young to get a tattoo. You are too young to drink alcohol. You are too young to run for President of the United States. How sad!
好的,这就是所谓的“if 语句”。if 语句以关键字if
开头,后面跟着括号中的“条件”。条件必须是一个布尔表达式,其值为true
或false
。在下面开始了由大括号包围的一块代码,大括号里面的东西缩进了一层。这段代码被称为 if 语句的“主体”。
当 if 语句的条件为真时,if 语句的主体中的所有代码都会被执行。
当 if 语句的条件为假时,主体中的所有代码都会被跳过。你可以在 if 语句的主体中有任意多行代码;它们将作为一组被执行或跳过。
注意,当我运行代码时,我输入了17
作为我的年龄。因为 17 不小于 13,所以第 14 行的条件是假的,所以第一个 if 语句的主体中的代码(第 15 到 17 行)被跳过了。
第二个 if 语句也是假的,因为 17 不小于 16,所以它的主体中的代码(第 19 到 21 行)也被跳过了。
第三个 if 语句的条件是真的:17 确实小于 18,所以第三个 if 语句的主体不会被跳过;它被执行了,屏幕上打印出了“你太年轻了,不能纹身”的短语。练习中剩下的 if 语句都是真的。
最后的 if 语句包含两行代码在它的主体中,只是为了向你展示它会是什么样子。
学习演练
- 如果你输入一个大于 35 的年龄,会打印出什么?为什么?
- 再添加一个 if 语句,将他们的年龄与 65 进行比较。如果他们的年龄大于或等于 65 岁,就说“你已经足够老了,可以退休了!”。
- 对于每个 if 语句,添加另一个说相反的 if 语句。例如,如果他们的年龄大于或等于 13 岁,就说“你已经足够大了,可以创建一个 Facebook 账户。”完成后,无论输入什么年龄,你的程序每次都应该显示六条消息。
练习 16:更多的 if 语句
这个练习几乎没有什么新东西。这只是对 if 语句的更多练习,因为它们非常重要。这也会帮助你记住关系运算符。
1 import java.util.Scanner; 2 3 public class ComparingNumbers 4 { 5 public static void main( String[] args ) 6 { 7 Scanner keyboard = new Scanner(System.in); 8 double first, second; 9 10 System.out.print( "Give me two numbers. First: " ); 11 first = keyboard.nextDouble(); 12 System.out.print( "Second: " ); 13 second = keyboard.nextDouble(); 14 15 if ( first < second ) 16 { 17 System.out.println( first + " is LESS THAN " + second ); 18 } 19 if ( first <= second ) 20 { 21 System.out.println( first + " is LESS THAN or EQUAL TO " + second ); 22 } 23 if ( first == second ) 24 { 25 System.out.println( first + " is EQUAL TO " + second ); 26 } 27 if ( first >= second ) 28 { 29 System.out.println( first + " is GREATER THAN or EQUAL TO "+second); 30 } 31 if ( first > second ) 32 { 33 System.out.println( first + " is GREATER THAN " + second ); 34 } 35 36 if ( first != second ) 37 System.out.println( first + " is NOT EQUAL TO " + second ); 38 39 } 40 }
你应该看到什么
Give me two numbers. First: 3 Second: 4 3.0 is LESS THAN 4.0 3.0 is LESS THAN or EQUAL TO 4.0 3.0 is NOT EQUAL TO 4.0
在第 37 行,你会看到我做了一些有问题的事情:最后一个 if 语句的主体没有任何大括号围绕它。这样可以吗?
实际上是。当 if
语句的主体没有花括号时,那么在条件之后的代码的第一行将被包括在主体中。因此,由于这整个练习中的所有 if
语句的主体只有一行代码,所以这个练习中的所有 if
语句的花括号都是可选的。你可以删除并且程序会正常工作。不过,包括它们永远不会错,有些程序员总是无论如何都会加上花括号。
学习演练
1. 在第 37 行之后添加另一行代码,写上 System.out.println( "Hey." );
。缩进它,使其与上面的 println()
语句对齐,就像这样:
if ( first != second ) System.out.println( first + " is NOT EQUAL TO " + second ); System.out.println( "Hey." );
运行程序,看看会发生什么。 “嘿” 部分是否属于 if 语句主体?也就是说,当 if 语句被跳过时,“嘿”也被跳过了,还是无论如何都会运行?你觉得呢?
1. 在最后一个 if 语句的主体周围添加花括号,以便“嘿”行是主体的一部分。然后删除所有其他 if 语句主体的花括号,以便程序中只有最后一个 if 语句有它们。确认一切都按预期工作。
练习 17:否则(带 else 的 if 语句)
所以,if
语句非常棒。几乎每种编程语言都有它们,你一直都在使用它们。事实上,if
语句本身就足够功能强大,你可以只使用 if
语句做很多事情。
但有时,有其他东西可能会使事情变得更加方便。比如这个例子:快!以下表达式的逻辑相反是什么?
if ( onGuestList || age >= 21 || ( gender.equals("F") && attractiveness >= 8 ) )
嗯?懂了吗?如果是的话
if ( ! ( onGuestList || age >= 21 || ( gender.equals("F") && attractiveness >= 8 ) ) )
…然后你是对的,你是我的菜。聪明并且知道什么时候让机器为你工作。如果你说
if ( ! onGuestList && age < 21 && ( ! gender.equals("F") || attractiveness < 8 ) )
…然后你是对的,干得好。这实际上是相当难以正确做到的。但是这个的逻辑相反又是什么呢:
if ( expensiveDatabaseThatTakes45SecondsToLookup( userName, password ) == true )
我们真的想要写
if ( expensiveDatabaseThatTakes45SecondsToLookup( userName, password ) == false )
1 import java.util.Scanner; 2 3 public class ClubBouncer 4 { 5 public static void main( String[] args ) 6 { 7 Scanner keyboard = new Scanner(System.in); 8 9 int age = 22; 10 boolean onGuestList = false; 11 double attractiveness = 7.5; 12 String gender = "F"; 13 14 if ( onGuestList || age >= 21 || ( gender.equals("F") && attractiveness >= 8 ) ) 15 { 16 System.out.println("You are allowed to enter the club."); 17 } 18 else 19 { 20 System.out.println("You are not allowed to enter the club."); 21 } 22 } 23 }
…因为现在我们不得不等待 90 秒来执行两个 if
语句,而不是 45 秒。所以幸运的是,编程语言给了我们一些 else
。(是的,抱歉。忍不住。)
你应该看到什么
You are allowed to enter the club.
所以 else
关键字的意思是:看看前面的 if
语句。那个条件是
if
语句为真吗?如果是,跳过。如果之前的 if
语句没有运行,那么
否则语句将被执行。“如果 blah blah blah 为真,则运行这个代码块。否则(else),运行这个不同的代码块。”
否则非常方便,因为我们不必去计算一些复杂布尔表达式的逻辑相反。我们只需要说 else
,让计算机处理它。
else
只有在 if
语句结束后立即合法。(严格来说,它只允许在 if
语句的主体代码块结束后。)
学习演练
1. 在第 17 行和第 18 行之间,添加一个 println()
语句来在屏幕上打印一些东西(不重要,但我放了 "CCCCOMBO BREAKER"
因为我很奇怪)。尝试编译程序。为什么不能编译?
练习 18:带字符串的 if 语句
几个练习之前,你学会了比较字符串不像比较数字那么容易。所以让我们用一个你可以实际测试的例子来复习一下。
1 import java.util.Scanner; 2 3 public class SecretWord 4 { 5 public static void main( String[] args ) 6 { 7 Scanner keyboard = new Scanner(System.in); 8 9 String secret = "please", guess; 10 11 System.out.print( "What's the secret word? " ); 12 guess = keyboard.next(); 13 14 if ( guess == secret ) 15 { 16 System.out.println( "Impossible. (This will never be printed.)" ); 17 } 18 19 if ( guess.equals(secret) ) 20 { 21 System.out.println( "That's correct!" ); 22 } 23 else 24 { 25 System.out.println( "Nope, the secret word is not \"" + guess + "\"." ); 26 } 27 28 } 29 }
你应该看到什么
What's the secret word? abracadabra Nope, the secret word is not "abracadabra".
注意,和往常一样,我在偷偷加入一些东西。在第 9 行,我不仅仅是声明 secret,我还给它赋了一个值。也就是说,我“定义”了它(一次性声明和初始化)。
无论如何,第 14 行的 if
语句永远不会为真。无论你输入什么,猜测 ==
秘密永远不会成立。
(我无法解释为什么,因为那样会涉及太多细节,但这与==
只比较变量的浅层值有关,两个字符串的浅层值只有在它们引用相同的内存位置时才相等。)
有效的方法是使用.equals()
方法(它比较变量的深层值而不是它们的浅层值)。如果他们输入了正确的秘密词,这将为真。
练习 19:使用 if 和 else 链进行互斥
在上一个练习中,我们看到使用else
可以更容易地包含一块备用代码,当if
语句没有发生时,你想要运行的。
但是,如果替代代码是……另一个if
语句呢?
1 import java.util.Scanner; 2 3 public class BMICategories 4 { 5 public static void main( String[] args ) 6 { 7 Scanner keyboard = new Scanner(System.in); 8 9 double bmi; 10 11 System.out.print( "Enter your BMI: " ); 12 bmi = keyboard.nextDouble(); 13 14 System.out.print( "BMI category: " ); 15 if ( bmi < 15.0 ) 16 { 17 System.out.println( "very severely underweight" ); 18 } 19 else if ( bmi <= 16.0 ) 20 { 21 System.out.println( "severely underweight" ); 22 } 23 else if ( bmi < 18.5 ) 24 { 25 System.out.println( "underweight" ); 26 } 27 else if ( bmi < 25.0 ) 28 { 29 System.out.println( "normal weight" ); 30 } 31 else if ( bmi < 30.0 ) 32 { 33 System.out.println( "overweight" ); 34 } 35 else if ( bmi < 35.0 ) 36 { 37 System.out.println( "moderately obese" ); 38 } 39 else if ( bmi < 40.0 ) 40 { 41 System.out.println( "severely obese" ); 42 } 43 else 44 { 45 System.out.println( "very severely/\"morbidly\" obese" ); 46 } 47 } 48 }
你应该看到什么
Enter your BMI: 22.5 BMI category: normal weight
(注意:尽管 BMI 是人体脂肪的一个很好的估计值,但这个公式对于肌肉量很大的运动员,或者身材极矮或极高的人来说效果不佳。如果你担心你的 BMI,请咨询医生。)
请注意,即使几个if
语句可能都为真,只有第一个为真的if
语句才会在屏幕上打印它的消息。没有其他消息被打印:只有一个。这就是使用else
与if
的威力。
在第 15 行有一个if
语句,检查你的 BMI 是否小于15.0
,如果是,则显示该体重指数的适当类别。
第 19 行以else
开头。这个 else 关注前面的if
语句——第 15 行的那个——以确定它是否应该运行它的代码块或自动跳过它。假设你输入了 BMI 为22.5
,那么前面的if
语句不成立,也没有运行。因为那个if
语句失败了,else 将自动执行它的代码块。
然而,这段代码块紧跟在else
后面,后面是一个新的if
语句!这意味着当前面的if
语句为假时,语句if ( bmi <= 16.0 )
才会被考虑。
每当我的学生对此感到困惑时,我都会给他们一个类比。(有点粗糙,但似乎有所帮助。)
想象一下你是单身(浪漫方面的意思),你和一些朋友在酒吧或商场或其他地方。在对面,你看到一个真的很有吸引力的单身,你悄声告诉其他人:“好的,我先来。”
你的团队走向这个人,但除非他们看到你的表现如何,否则没有人会开始调情。如果你似乎正在取得进展,你的朋友们会退后,让你畅所欲言。然而,如果你被拒绝,那么你的其他伙伴之一就会感到有机会尝试并发起进攻。
这基本上就是else if
的作用。一个else if
语句(一个在if
语句前面有else
的if
语句)包含一个可能为真或可能为假的条件。但是else
意味着if
语句只会检查它是否为真或假,假设前面的if
语句(只有紧接着的那个)为假。
第 23 行的else
使得第 19 行开始的if
语句推迟到第 19 行的if
语句:如果为真,第 23 行的if
语句将跳过,即使它本来是真的。第 27 行的else
使得它的if
语句推迟到前面的if
语句,依此类推。最后一行 43 的else
就像一群中最小的狗:只有在链中所有前面的if
语句都为假时才会执行。
我们将在下一个练习中再多谈一些这个问题,现在就到此为止。
学习演练
- 从第 27 行
if
语句前面删除else
。运行程序,然后输入15.5
作为 BMI。你看到了吗,这使得第 27 行的if
语句“打破了规矩”,不再关心它之前的if
语句? - 不要让人直接输入他们的 BMI,让他们输入身高和体重,然后为他们计算 BMI。
练习 20:更多的else
和if
链。
好的,让我们更仔细地看一下使用else
和if
构建条件链。
坦白说:尽管我确实参加了德克萨斯大学奥斯汀分校,但我认为这不是他们真正的录取标准。在决定是否申请备用学校时,不要依赖这个程序的输出。
1 import java.util.Scanner; 2 import static java.lang.System.*; 3 4 public class CollegeAdmission 5 { 6 public static void main( String[] args ) 7 { 8 Scanner keyboard = new Scanner(System.in); 9 int math; 10 11 out.println( "Welcome to the UT Austin College Admissions Interface!" ); 12 out.print( "Please enter your SAT math score (200800): " ); 13 math = keyboard.nextInt(); 14 15 out.print( "Admittance status: " ); 16 17 if ( math >= 790 ) 18 out.print( "CERTAIN " ); 19 else if ( math >= 710 ) 20 out.print( "SAFE " ); 21 else if ( math >= 580 ) 22 out.print( "PROBABLE " ); 23 else if ( math >= 500 ) 24 out.print( "UNCERTAIN " ); 25 else if ( math >= 390 ) 26 out.print( "UNLIKELY " ); 27 else // below 390 28 out.print( "DENIED " ); 29 30 out.println(); 31 } 32 }
你应该看到什么
Welcome to the UT Austin College Admissions Interface! Please enter your SAT math score (200800): 730 Admittance status: SAFE
现在,在我进入这个练习的新内容之前,我应该解释一下我在这个程序中采取的一个快捷方式。你有没有注意到顶部有第二个import
语句?如果没有,那么你的代码没有编译,或者你认为我在所有地方都写错了,应该是out.println
而不是System.out.println
。
好吧,我不想在这本书中过多地谈论面向对象的代码,因为那对初学者来说太复杂了,但是可以这样想。在 Java 中有一个内置对象叫做System
。在该对象内部还有另一个名为out
的对象。名为out
的对象包含一个名为print()
和一个名为println()
的方法。
所以当你写System.out.println
时,你是在要求计算机运行名为out
的对象内部的名为println
的方法(它本身是内置导入库java.lang.System
的一部分)。
因此,我可以创建一个名为out
的变量,这不会有问题:
String out;
尽管有一个名为out
的对象存在,但它在System
对象内部,所以名称不会冲突。
如果我懒惰并且没有任何愿望拥有自己命名为out
的变量,那么我可以要求计算机“将类java.lang.System
中的所有静态项目导入当前命名空间”:
import static java.lang.System.*;
所以现在我可以只输入out.println
而不是System.out.println
。哇!
在这个练习中,我还省略了界定每个if
语句主体中代码块的所有花括号。因为我只想在每个if
语句的主体中有一个语句,所以这是可以的。如果我想要有多于一行的代码,那么我就必须把花括号放回去。
无论如何,在之前的练习中,我写了如何将else
放在if
语句前面使其延迟到前一个if
语句。当前一个为真并执行其主体中的代码时,当前一个会自动跳过(链中的所有其他else if
语句也会跳过)。这会使得只有第一个为真的值会触发if
语句,其他所有的都不会运行。我们有时会说if
语句是“互斥的”:只有一个会执行。不会少于一个,也不会多于一个。
今天的练习是另一个例子。但是这次我想指出,互斥只能正常工作是因为我按正确的顺序放置了if
语句。
因为第一个为真的将会执行,而其他的不会,所以你需要确保链中的第一个if
语句是最难实现的。然后是下一个最难的,以此类推,最容易的放在最后。在学习演练中,我会让你改变if
语句的顺序,你会看到这样会搞乱事情。
此外,从技术上讲,else
语句应该有花括号,就像if
语句一样,通过将else if
之间什么都不放置来利用花括号是可选的事实。这使得代码更加紧凑。如果按计算机解释的方式排列,先前的代码将是这样的。也许这会帮助你理解else
在if
前面的“延迟”行为;也许这只会让你困惑。希望它会有所帮助。
1 import java.util.Scanner; 2 import static java.lang.System.*; 3 4 public class CollegeAdmissionExpanded 5 { 6 public static void main( String[] args ) 7 { 8 Scanner keyboard = new Scanner(System.in); 9 int math; 10 11 out.println( "Welcome to the UT Austin College Admissions Interface!" ); 12 out.print( "Please enter your SAT math score (200800): " ); 13 math = keyboard.nextInt(); 14 15 out.print( "Admittance status: " ); 16 17 if ( math >= 790 ) 18 { 19 out.print( "CERTAIN " ); 20 } 21 else 22 { 23 if ( math >= 710 ) 24 { 25 out.print( "SAFE " ); 26 } 27 else 28 { 29 if ( math >= 580 ) 30 { 31 out.print( "PROBABLE " ); 32 } 33 else 34 { 35 if ( math >= 500 ) 36 { 37 out.print( "UNCERTAIN " ); 38 } 39 else 40 { 41 if ( math >= 390 ) 42 { 43 out.print( "UNLIKELY " ); 44 } 45 else // below 390 46 { 47 out.print( "DENIED " ); 48 } 49 } 50 } 51 } 52 } 53 out.println(); 54 } 55 }
是的。所以你可以看到为什么我们通常只是使用else if
。
学习演练
- 在原始的代码文件(
CollegeAdmission.java
)中,除了最后一个之外,删除所有的else
。
最后一个。运行它并注意它如何打印所有的消息。然后把else
放回去。 - 将第 25 行和第 26 行移动到第 18 行和第 19 行之间。编译并运行它,注意程序几乎总是只说
"UNLIKELY"
,因为大多数 SAT 分数都超过 390,而且if
语句在列表中的位置很高,大部分时间都会占据主导地位。 - 如果愿意,可以输入
CollegeAdmissionExpanded.java
的代码,并确认它与非扩展版本的功能相同。