原文:Learn Java The Hard Way 译者:飞龙 协议:CC BY-NC-SA 4.0
练习 21:嵌套 if 语句
你在上一个练习中已经看到了这一点,但你可以在if
语句的主体中放入任何你喜欢的东西,包括其他if
语句。这被称为“嵌套”,在另一个if
语句内部的if
语句称为“嵌套 if”。
这是使用它做一些有用的事情的一个例子。
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class GenderTitles
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8
9 String title;
10
11 System.out.print( "First name: " );
12 String first = keyboard.next();
13 System.out.print( "Last name: " );
14 String last = keyboard.next();
15 System.out.print( "Gender (M/F): " );
16 String gender = keyboard.next();
17 System.out.print( "Age: " );
18 int age = keyboard.nextInt();
19
20 if ( age < 20 )
21 {
22 title = first;
23 }
24 else
25 {
26 if ( gender.equals("F") )
27 {
28 System.out.print( "Are you married, " first "? (Y/N): " );
29 String married = keyboard.next();
30 if ( married.equals("Y") )
31 {
32 title = "Mrs.";
33 }
34 else
35 {
36 title = "Ms.";
37 }
38 }
39 else
40 {
41 title = "Mr.";
42 }
43 }
44
45 System.out.println( "n" title " " last );
46
47 }
48 }
你应该看到什么
代码语言:javascript复制First name: Graham
Last name: Mitchell Gender (M/F): M Age: 39
Mr. Mitchell
你可能已经发现我喜欢稍微混合一下,让你保持警惕。你注意到我这次做了什么不同吗?
通常我会在程序的顶部声明所有变量,并在稍后给它们赋值(或“初始化”)。但实际上,你不必在准备使用变量之前声明它。所以这一次,我声明了所有变量(除了title)在我第一次为它们赋值的同一行。
那么为什么我不在第 22 行声明title呢?因为那样它以后就不在“范围”内了。范围指的是程序中变量可见的位置。一般规则是,一旦声明变量,从那时起在代码中的后续部分直到声明的块结束,变量就在范围内。然后变量就超出范围,不能再使用了。
让我们看一个例子:在第 29 行,我定义(声明和初始化)了一个名为 married 的字符串变量。它是在女性性别if
语句的主体内声明的。这个变量存在于第 29 行到第 38 行,在该if
语句的主体块的右花括号处。married 变量在程序的其他任何地方都不在范围内;在第 1 到第 28 行或第 39 到第 48 行引用它会导致编译错误。
这就是为什么我必须在程序的开始处声明title。如果我在第 22 行声明它,那么当年龄小于 20 的代码块的右花括号出现时,变量将会超出范围。因为我需要title一直可见,直到第 45 行,所以我需要确保我在代码块内声明它,该代码块在第 47 行结束。
不过,我本来可以等到第 19 行再声明它。
无论如何,关于这个练习没有太多有趣的事情要说,除了它演示了嵌套。
if
语句和其他else
语句。不过,我在学习演练中有一个小惊喜。
学习演练
- 将第 39 行的
else
更改为合适的if
语句,例如:
if ( gender.equals("M") )
注意,程序不再编译。你能想出原因吗?
这是因为变量title在第 9 行声明,但没有立即赋值。然后在第 45 行,title的值被打印在屏幕上。此时变量必须有一个值,否则我们将尝试显示一个未定义的变量的值:它没有值。编译器希望防止这种情况发生。
当第 39 行是else
时,编译器可以保证无论通过嵌套的if
语句的哪条路径,title总是会得到一个值。一旦我们将其更改为常规的if
语句,现在有一种方法,人类可以输入一些内容,使其通过所有嵌套的if
语句,而不给title一个值。你能想到一个吗?
(当提示输入性别时,他们可以输入年龄 20 或更大,以及不同于"M"
或"F"
的字母。
然后,没有一个性别的if
语句会为真。)
我们可以通过将第 39 行的else
语句更改为合适的if
语句来解决这个问题(可能是个好主意),或者通过初始化
在我们声明title的时候(可能是个好主意):
代码语言:javascript复制String title = "error";
……或者类似的东西。现在title有一个值,无论如何,编译器都很高兴。
练习 22:使用大开关做决定
if
语句并不是在 Java 中比较变量值的唯一方法。还有一种叫做switch
的东西。我并不经常使用它们,但无论如何你都应该熟悉它们,以防你读到别人使用它的代码。
1 import java.util.Scanner;
2
3 public class ThirtyDays
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8
9 int month, days;
10 String monthName;
11
12 System.out.print( "Which month? (112) " );
13 month = keyboard.nextInt();
14
15 switch(month)
16 {
17 case 1: monthName = "January";
18 break;
19 case 2: monthName = "February";
20 break;
21 case 3: monthName = "March";
22 break;
23 case 4: monthName = "April";
24 break;
25 case 5: monthName = "May";
26 break;
27 case 6: monthName = "June";
28 break;
29 case 7: monthName = "July";
30 break;
31 case 8: monthName = "August";
32 break;
33 case 9: monthName = "September";
34 break;
35 case 10: monthName = "October";
36 break;
37 case 11: monthName = "November";
38 break;
39 case 12: monthName = "December";
40 break;
41 default: monthName = "error";
42 }
43
44 /* Thirty days hath September
45 April, June and November
46 All the rest have thirtyone
47 Except the second month alone....
48 */
49
50 switch(month)
51 {
52 case 9:
53 case 4:
54 case 6:
55 case 11: days = 30;
56 break;
57 case 2: days = 28;
58 break;
59 default: days = 31;
60 }
61
62 System.out.println( days " days hath " monthName );
63
64 }
65 }
你应该看到什么
代码语言:javascript复制Which month? (112) 4
30 days hath April
switch
语句以关键字switch
开始,然后是一些括号。括号内是一个单一的变量(或者简化为单一值的表达式)。然后是一个开放的大括号。
在switch
语句的主体内部有几个以关键字case
开头的case
语句,然后是括号中的变量可能相等的值。然后是一个冒号(:
)。在 Java 中很少看到冒号。
在case
之后,是值和冒号,然后是一些代码。它可以是任意行的代码,除了你不允许在switch
语句内部声明任何变量。然后在所有代码之后是关键字break
。break
标志着case
的结束。
当switch
语句运行时,计算机会找出括号内变量的当前值。然后它逐个查看case
列表,寻找匹配项。当它找到匹配项时,它会从case
所在的左侧移动到右侧,并开始运行代码,直到被break
停止。
如果没有case
匹配,且有一个default
情况(可选),那么default
中的代码将被运行。
情况将被运行。
第二个例子从第 50 行开始,演示了一旦switch
语句找到与之匹配的情况,它确实会运行右侧的代码,直到遇到break
语句。它甚至会从一个case
穿过到另一个。
我们可以利用这种穿透行为,有时做一些聪明的事情,比如计算一个月中的天数的代码。由于 9 月、4 月、6 月和 11 月都有 30 天,我们可以将它们的所有情况放在一起,让它们穿过任何一个运行相同的事情。
无论如何,我不会在这本书中再使用switch
语句,因为我几乎从来没有找到过它的好用处,但它确实存在,至少我可以说你看到了它。
学习演练
- 在第一个
switch
中删除一些break
语句,并添加一些println()
语句来确认它会将 monthName 设置为一个值,然后又一个值,直到最后被break
停止。
练习 23:更多字符串比较
嗯,你已经学会了不能用==
比较字符串;你必须使用.equals()
方法。但我认为你终于准备好看看我们如何比较字符串的字母顺序了。
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class DictionaryOrder
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8
9 String name;
10
11 System.out.print( "Give me the name of a madeup programming language:
" );
12 name = keyboard.nextLine();
13
14 if ( name.compareTo("c ") < 0 )
15 System.out.println( name " comes BEFORE c " );
16 if ( name.compareTo("c ") == 0 )
17 System.out.println( "c isn't a madeup language!" );
18 if ( name.compareTo("c ") > 0 )
19 System.out.println( name " comes AFTER c " );
20
21 if ( name.compareTo("go") < 0 )
22 System.out.println( name " comes BEFORE go" );
23 if ( name.compareTo("go") == 0 )
24 System.out.println( "go isn't a madeup language!" );
25 if ( name.compareTo("go") > 0 )
26 System.out.println( name " comes AFTER go" );
27
28 if ( name.compareTo("java") < 0 )
29 System.out.println( name " comes BEFORE java" );
30 if ( name.compareTo("java") == 0 )
31 System.out.println( "java isn't a madeup language!" );
32 if ( name.compareTo("java") > 0 )
33 System.out.println( name " comes AFTER java" );
34
35 if ( name.compareTo("lisp") < 0 )
36 System.out.println( name " comes BEFORE lisp" );
37 if ( name.compareTo("lisp") == 0 )
38 System.out.println( "lisp isn't a madeup language!" );
39 if ( name.compareTo("lisp") > 0 )
40 System.out.println( name " comes AFTER lisp" );
41
42 if ( name.compareTo("python") < 0 )
43 System.out.println( name " comes BEFORE python" );
44 if ( name.compareTo("python") == 0 )
45 System.out.println( "python isn't a madeup language!" );
46 if ( name.compareTo("python") > 0 )
47 System.out.println( name " comes AFTER python" );
48
49 if ( name.compareTo("ruby") < 0 )
50 System.out.println( name " comes BEFORE ruby" );
51 if ( name.compareTo("ruby") == 0 )
52 System.out.println( "ruby isn't a madeup language!" );
53 if ( name.compareTo("ruby") > 0 )
54 System.out.println( name " comes AFTER ruby" );
55
56 if ( name.compareTo("visualbasic") < 0 )
57 System.out.println( name " comes BEFORE visualbasic" );
58 if ( name.compareTo("visualbasic") == 0 )
59 System.out.println( "visualbasic isn't a madeup language!" );
60 if ( name.compareTo("visualbasic") > 0 )
61 System.out.println( name " comes AFTER visualbasic" );
62 }
63 }
你应该看到什么
代码语言:javascript复制Give me the name of a madeup programming language: juniper juniper comes AFTER c
juniper comes AFTER go juniper comes AFTER java juniper comes BEFORE lisp juniper comes BEFORE python juniper comes BEFORE ruby
juniper comes BEFORE visualbasic
(当然,我忍不住在第 12 行插入了一些东西。而不是使用 Scanner 对象的
.next()
方法读取一个字符串,我使用 Scanner 对象的.nextLine()
方法读取一个字符串。不同之处在于.next()
会在你输入空格时停止读取,所以如果你输入"visual
basic"
,它只会读取"visual"
,并留下其余的部分。当你使用.nextLine()
时,它会读取你输入的所有内容,包括空格和制表符,直到你按下回车键,然后将所有内容放入一个长字符串中并将其存储到变量中。
你可以使用 String 对象的.compareTo()
方法将字符串相互比较。这个
.compareTo()
方法的工作方式并不是你可能期望的,但它的工作方式是有巧妙之处的。
比较涉及两个字符串。第一个字符串是.compareTo()
左侧的字符串。第二个字符串是括号内的字符串。比较简化为一个整数!如果我们称第一个为 self,第二个为 other,它会是这样的:
int n = self.compareTo(other);
所以 self 将自己与 other 进行比较。如果 self 与 other 相同(长度相同,每个字符都相同),那么 n 将被设置为0
。如果 self 在字母表中出现在 other 之前,那么 n 将被设置为负数(小于 0 的数)。如果 self 在字母表中出现在 other 之后,那么 n 将被设置为正数(大于 0 的数)。
天才的部分在于:因为.compareTo()
给我们的是一个整数,而不仅仅是一个布尔值 true 或 false,我们只需要这一个方法来进行所有的比较:小于、大于、小于或等于,等等。
因为如果self等于other,我们会得到零,如果self小于other,我们会得到一个小于零的数字,所以我们可以写:
代码语言:javascript复制if ( self.compareTo(other) <= 0 )
如果结果小于零,if
语句将为真,如果结果等于零,if
语句将为真。
语句将为真。这有点像写
代码语言:javascript复制if ( self <= other )
…除了我刚刚写的那个实际上不会编译,而.compareTo()
的技巧会。如果你问我,这很酷。
if ( self.compareTo(other) < 0 ) // true when self < other if ( self.compareTo(other) <= 0 ) // true when self <= other if ( self.compareTo(other) > 0 ) // true when self > other if ( self.compareTo(other) >= 0 ) // true when self >= other if ( self.compareTo(other) == 0 ) // true when self == other
代码语言:javascript复制if ( self.compareTo(other) != 0 ) // true when self != other
这就是这个想法。对于初学者来说可能会有些困惑,使用起来稍微有些困难,但一旦你习惯了,就一点也不坏。
这里的另一个困难(这不仅仅是一个.compareTo()
的问题,在代码的任何地方都会发生,除非你写代码来解决它)是大小写的问题。"Bob"
和"bob"
不是相同的值。更糟糕的是,由于字母的 Unicode 值,"Bob"
在字母表中出现在"bob"
之前。如果你想避免这个问题,有很多方法,但我喜欢这两种方法中的一种:
if ( self.toLowerCase().compareTo( other.toLowerCase() ) < 0 ) // or if ( self.compareToIgnoreCase(other) < 0 )
或者你可以让人类输入任何他们想要的东西,并立即将其转换为小写,然后只与你代码中的小写进行比较。
学习演练
- 使用你选择的方法,使这个程序即使在人类输入了“错误”的大写字母的单词时也能正确工作。
- 计算机只能在内部处理数字。字母不是数字,但有一个巨大的表,将每种语言中的每个字符映射到 1,112,063 个数字中的一个,唯一标识该字符。字母“B”的 UTF-8 Unicode 值是 66;字母“b”的值是 98。
练习 24:随机选择数字
我们将在一些练习中花一些时间来学习编程书中并不总是看到的东西:如何让计算机在某个范围内选择一个“随机”数。这是因为你可以写很多的软件而不需要计算机随机选择一个数字。然而,有随机数将让我们制作一些简单的互动游戏,这很容易就能弥补这个略微奇怪的概念的痛苦。
代码语言:javascript复制 1 public class RandomNumbers
2 {
3 public static void main( String[] args )
4 {
5 int a, b, c;
6 double x, y, z;
7
8 x = Math.random();
9 y = Math.random();
10 z = Math.random();
11
12 System.out.println( "x is " x );
13 System.out.println( "y is " y );
14 System.out.println( "z is " z );
15
16 x = Math.random() * 100;
17 y = Math.random() * 100;
18 z = Math.random() * 100;
19
20 System.out.println( "nx is " x );
21 System.out.println( "y is " y );
22 System.out.println( "z is " z );
23
24 a = (int)x;
25 b = (int)y;
26 c = (int)z;
27
28 System.out.println( "na is " a );
29 System.out.println( "b is " b );
30 System.out.println( "c is " c );
31
32 x = 0.9999999999999999;
33 a = (int)(x * 100);
34
35 System.out.println( "nx is " x );
36 System.out.println( "a is " a );
37
38 x = Math.random();
39 a = 0 (int)(x*10);
40 b = 1 (int)(x*10);
41 c = 5 (int)(x*10);
42
43 System.out.println( "na is " a );
44 System.out.println( "b is " b );
45 System.out.println( "c is " c );
46 }
47 }
你应该看到什么
代码语言:javascript复制x is 0.5371428668784091
y is 0.4716636154720313
z is 0.9791002546275134
x is 57.33269918363617
y is 44.731436970719386
z is 75.79286027183542
a is 57
b is 44
c is 75
x is 0.9999999999999999
a is 99
a is 6
b is 7
c is 11
注意:你的输出不会和我的一样。记住,这些数字是随机的。
Java 有一个内置的函数叫做Math.random()
。每次调用这个函数,它都会产生一个新的随机double
,范围在[0,1)之间(也就是说,它可能正好是0
,但永远不会正好是1
,而且很可能是介于两者之间的某个值)。所以如果我写:
double x = Math.random();
…然后 x 可能有一个值为0
,或0.123544
,或0.3
,或0.999999999
,但永远不会是1.0
,也永远不会大于 1。所以在第 8 到 10 行,函数Math.random()
被调用了三次,并且结果被存储到三个不同的变量中。这三个值被打印出来,这样你就可以看到它们是什么。
不幸的是,我经常不想要一个来自[0,1)的 double。想象一下一个猜数字的游戏,你说“我在想一个小数在零和一之间的数字:试着猜猜看!”这不好玩。而且我们无法控制Math.random()
给我们的值的范围,所以我们必须自己将其压缩到一个范围内。
在第 16 到 18 行,我们选择一个新的随机数,但在存储到变量中之前将其乘以 100。(这会使小数点向右移动两位。)因此,我们可以知道在第 20 行打印出的原始随机数是0.5733269918363617
,因为乘以 100 后变成了57.33269918363617
。
请注意,乘以 100 仍然有可能得到恰好为0
的情况。如果原始随机数是 0,那么乘以它不会改变。我们存储到变量中的数字可能是12.3544
,或30.0
,或99.9999999
,但永远不会是100.0
,也永远不会大于 100。
在第 24 到 26 行,我们执行了所谓的“类型转换”或者“转换”。变量 x 是一个双精度浮点数:它可以保存带有小数的数字。变量 a 是一个整数:它只能保存整数。通常情况下,你不允许将double
的值存储到int
中。转换告诉编译器“我知道 x 是一个 double,我试图将它的值存储到一个不能保存小数的int
中。但我不在乎。你为什么不假装 x 的值是一个整数呢?如果你不得不舍弃小数点后的所有内容,也没关系。”
因此,在第 24 行,计算机复制了x的值,但小数点后的所有内容被截断并丢弃(“截断”),新的整数值存储到变量a中。(x的值不变。)这个值不是四舍五入的;它是被截断的。
理论上,这给了我们什么?如果 x 最初是0
或12.3544
或30.0
,或99.9999999
,那么 a 将是0
或12
或30
或99
,但永远不会是100
或任何大于 100 的数字。因此,a、b 和 c 的值始终为 0 到 99 的整数值。
在第 32 和 33 行,我尝试表明从双精度浮点数到整数的转换不四舍五入;小数点后的数字被截断。
最后,在第 38 到 41 行,选择一个随机数。在所有三种情况下,它都被乘以 10,然后转换为整数。这意味着转换后我们总是得到一个从 0 到 9 的数字。
但是在第 39 行,从 0 到 9 的随机数加上0
后存储到 a 中。(加0
不改变数字。)
不改变数字。)因此a将始终是 0 到 9 的值。
在第 40 行,从 0 到 9 的随机数加上1
后存储到 b 中。这使得它比原来大 1。如果原来是0
,现在是1
。如果原来是6
,现在是7
。如果原来是9
(最大值),现在是10
。因此,b 的值始终为 1 到 10。
在第 41 行,从 0 到 9 的随机数加上5
后存储到 c 中。因此,c 将始终具有 5 到 14 的值。(这仍然是十个值。)
好了,今天就到这里吧。
学习演练
- 移除第 24 行的转换。尝试编译程序。你得到什么错误消息?(然后把它放回去。)
- 运行程序多次,并确认在第 28 到 30 行打印出的a、b和c始终具有 0 到 99 的值。
- 用手指数一数,确认如果我有一个从 0 到 9 的数字,那么我可能有十个可能的数字。将随机数乘以十并截断会得到十种可能的结果(09)。将随机数乘以五并截断会得到五种可能的结果(04)。
- 运行程序多次,并确认在第 43 行打印出的a始终具有 0 到 9 的值,b始终具有 1 到 10 的值,c始终具有 5 到 14 的值。
练习 25:更复杂的随机数
上一个练习中有一些复杂的思考,所以这个练习不会教授新的东西,而是会花更多时间来学习相同的概念。
代码语言:javascript复制 1 public class RandomNumbers2
2 {
3 public static void main( String[] args )
4 {
5 int a, b, c, d, e, low, high;
6
7 a = 1 (int)(Math.random()*10);
8 b = 1 (int)(Math.random()*10);
9 c = 1 (int)(Math.random()*10);
10 d = 1 (int)(Math.random()*10);
11 e = 1 (int)(Math.random()*10);
12
13 System.out.println( a "t" b "t" c "t" d "t" e );
14
15 a = 1 (int)(Math.random()*100);
16 b = 1 (int)(Math.random()*100);
17 c = 1 (int)(Math.random()*100);
18 d = 1 (int)(Math.random()*100);
19 e = 1 (int)(Math.random()*100);
20
21 System.out.println( a "t" b "t" c "t" d "t" e );
22
23 a = 70 (int)(Math.random()*31); // 31 is 10070 1
24 b = 70 (int)(Math.random()*31);
25 c = 70 (int)(Math.random()*31);
26 d = 70 (int)(Math.random()*31);
27 e = 70 (int)(Math.random()*31);
28
29 System.out.println( a "t" b "t" c "t" d "t" e );
30
31 low = 70;
32 high = 100;
33
34 a = low (int)(Math.random()*(highlow 1));
35 b = low (int)(Math.random()*(highlow 1));
36 c = low (int)(Math.random()*(highlow 1));
37 d = low (int)(Math.random()*(highlow 1));
38 e = low (int)(Math.random()*(highlow 1));
39
40 System.out.println( a "t" b "t" c "t" d "t" e );
41 }
42 }
你应该看到什么
代码语言:javascript复制7
9
3
3
3
10
53
78
17
75
76
96
99
85
86
99
91
96
99
83
(再次强调,你看不到这一点。这些数字将是随机的。)
在第 7 到 11 行,我们选择了五个随机数。每个数字都乘以 10 并转换为整数以截断它(因此每个随机数是 10 个数字之一:0 到 9)。然后对每个数字加 1,所以变量 a 到 e 每个都得到 1 到 10 的随机数。
在第 15 到 19 行,我们再次选择了五个随机数。每个数字都乘以一
百分之一并转换为整数以截断它(因此每个随机数是 100 个数字之一:0 到 99)。然后对每个数字加 1,所以变量 a 到 e 每个都得到 1 到 100 的随机数。
在第 23 到 27 行,我们选择了另外五个随机数。每个数字都乘以 31 并转换为整数以截断它(因此每个随机数是 31 个数字之一:0 到 30)。然后每个数字都加上 70。0
加上70
得到 70。1
加上70
得到 71。23
加上70
得到 93。30
(最大值)加上70
得到 100。因此,变量 a 到 e 每个都得到 70 到 100 的随机数。
因此,一般公式是这样的:
代码语言:javascript复制int a = low (int)(Math.random() * range);
low是我们想要的最小可能数字。range是范围内应该有多少个随机数。如果你知道你的最小可能数字和最大可能数字,但不知道有多少数字,那么公式是:
代码语言:javascript复制int range = high low 1;
如果我想要的最小随机数是1
,最大随机数是5
,那么范围是五。5 减 1 是 4,然后加 1 来解决减法给出的两个数字之间的距离,而不是沿途停止点的计数。 8
你甚至可以这样写公式:
代码语言:javascript复制int a = low (int)(Math.random()*(highlow 1));
这将使计算机从low到high中选择一个随机数。这正是我们在第 34 到 38 行所做的。
学习演练
- 更改第 31 行和第 32 行的low和high的值为其他值。编译并运行程序多次,以确认您总是在该范围内获得随机数。
- 编程中常见的逻辑错误是,如果您意外地计算距离而不是停止,就会发生“栅栏问题”。这个名字来自以下脑筋急转弯:如果我需要用一堆略长于一米的木板建造五米长的栅栏,我需要多少栅栏柱?你需要五块木板,但需要六根栅栏柱。
练习 26:使用 while 循环重复自己
这是我最喜欢的练习之一,因为你将学会如何使代码块重复。如果你能做到这一点,你就能写出各种有趣的东西。
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class EnterPIN
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8 int pin, entry;
9
10 pin = 12345;
11
12 System.out.println("WELCOME TO THE BANK OF JAVA.");
13 System.out.print("ENTER YOUR PIN: ");
14 entry = keyboard.nextInt();
15
16 while ( entry != pin )
17 {
18 System.out.println("nINCORRECT PIN. TRY AGAIN.");
19 System.out.print("ENTER YOUR PIN: ");
20 entry = keyboard.nextInt();
21 }
22
23 System.out.println("nPIN ACCEPTED. YOU NOW HAVE ACCESS TO YOUR
ACCOUNT.");
24 }
25 }
你应该看到什么
代码语言:javascript复制WELCOME TO THE BANK OF JAVA. ENTER YOUR PIN: 123
INCORRECT PIN. TRY AGAIN. ENTER YOUR PIN: 1234
INCORRECT PIN. TRY AGAIN. ENTER YOUR PIN: 12345
PIN ACCEPTED. YOU NOW HAVE ACCESS TO YOUR ACCOUNT.
在第 16 行,您首次看到while
循环。while
循环类似于if
语句。它们都有括号中的条件,用于检查其真假。如果条件为假,则while
循环和if
语句都将跳过主体中的所有代码。当条件为真时,while
循环和if
语句都将执行其主体中的所有代码一次。
唯一的区别是,if
语句为真时将执行大括号中的所有代码一次。while
循环为真时将执行大括号中的所有代码一次,然后返回并再次检查条件。如果条件仍然为真,则再次执行主体中的所有代码。然后再次检查条件,如果条件仍然为真,则再次运行主体。
实际上,你可以说while
循环会执行其主体中的所有代码,只要在检查时条件为真。
最终,当检查条件时,条件将为假。然后while
循环将跳过其主体中的所有代码,程序的其余部分将继续。一旦while
循环的条件为假,它就不会再次被检查。
循环是如此伟大,因为我们终于可以做一些事情不止一次,而不必多次输入代码!事实上,程序员有时会说“保持你的代码 DRY:不要重复自己。”一旦你学会了编程并完成了本书中的所有练习,如果你发现自己在程序中多次输入(或复制粘贴)完全相同的代码,你会开始怀疑。
练习 27:一个猜数字游戏
现在你知道如何使用while
循环重复某些内容,我们将编写一个实际上另一个人可能会喜欢运行的程序?你对此和我一样兴奋吗?
1 import java.util.Scanner;
2
3 public class HighLow
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8 int secret, guess;
9
10 secret = 1 (int)(Math.random()*100);
11
12 System.out.println( "I'm thinking of a number between 1100. Try to
guess it." );
13 System.out.print( "> " );
14 guess = keyboard.nextInt();
15
16 while ( secret != guess )
17 {
18 if ( guess < secret )
19 {
20 System.out.println( "Sorry, your guess is too low. Try again."
);
21 }
22 if ( guess > secret )
23 {
24 System.out.println( "Sorry, your guess is too high. Try
again." );
25 }
26 System.out.print( "> " );
27 guess = keyboard.nextInt();
28 }
29
30 System.out.println( "You guessed it! What are the odds?!?" );
31 }
32 }
你应该看到什么
代码语言:javascript复制I'm thinking of a number between 1100. Try to guess it.
> 50
Sorry, your guess is too high. Try again.
> 25
Sorry, your guess is too high. Try again.
> 13
Sorry, your guess is too low. Try again.
> 20
Sorry, your guess is too high. Try again.
> 16
Sorry, your guess is too high. Try again.
> 18
Sorry, your guess is too high. Try again.
> 14
Sorry, your guess is too low. Try again.
> 15
You guessed it! What are the odds?!?
所以在第 10 行,计算机从 1 到 100 中选择一个随机数,并将其存储到变量secret中。我们让人类猜测。
第 16 行有一个while
循环。它说“只要变量 secret 的值与变量 guess 的值不同…运行以下代码块。”第 17 行到第 28 行是循环的主体。每当条件为真时,这十二行代码都会被执行。
在循环体内,我们有几个if
语句。我们已经知道人类的猜测与秘密数字不同,否则我们就不会一开始就进入while
循环!但我们不知道猜测是错误是因为它太低还是因为它太高,所以这些if
语句找出来并显示适当的错误消息。
然后在显示错误消息后,第 27 行我们允许他们再次猜测。人类(希望)输入一个数字,然后存储到变量guess中,覆盖该变量中的先前猜测。
然后程序循环回到第 16 行并再次检查条件。如果条件仍然为真(他们的猜测仍然不等于秘密数字),则整个循环体将再次执行。如果条件现在为假(他们猜中了),则整个循环体将被跳过,程序将跳到第 29 行。
如果循环结束,我们知道条件为假。所以我们不需要在这里加一个新的if
语句;安全地打印“你猜对了”。
练习 28:无限循环
有时让学生感到惊讶的是,制作一个重复“永远”的循环是多么容易。这些被称为“无限循环”,有时我们故意制造它们,但通常它们是逻辑错误的结果。这里有一个例子:
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class KeepGuessing
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8 int secret, guess;
9
10 secret = 1 (int)(Math.random()*10);
11
12 System.out.println( "I have chosen a number between 1 and 10. Try to
guess it." );
13 System.out.print( "Your guess: " );
14 guess = keyboard.nextInt();
15
16 while ( secret != guess )
17 {
18 System.out.println( "That is incorrect. Guess again." );
19 System.out.print( "Your guess: " );
20 }
21
22 System.out.println( "That's right! You're a good guesser." );
23 }
24 }
你应该看到什么
代码语言:javascript复制I have chosen a number between 1 and 10. Try to guess it.
Your guess: 4
That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: That is incorrect. Guess again.
Your guess: ^C
- 例如,在大学时,我在网络协议课上的一个作业是写一个网络服务器。网络服务器监听网络以获取页面请求。然后它找到请求的页面并将其发送到请求的网络浏览器。然后它等待另一个请求。我在那个作业中故意使用了一个无限循环,因为网络服务器软件旨在在机器启动时自动启动,全天候运行,并且只在机器关闭时关闭。
程序实际上没有自行停止;在程序一遍又一遍地重复时,我不得不按下 CTRL-C 来停止它。
这段代码中有一个无限循环。第 16 行检查变量secret的值是否与变量guess的值不同。如果是,它执行循环体,如果不是,它跳过循环体到第 21 行。
问题是一旦secret和guess不同,程序就永远无法到达另一行代码来改变任一变量,所以循环将永远重复第 16 行到第 20 行。
所以当你写一个 while 循环的条件时,试着记住:“我需要确保这个条件最终会变成假”。
学习演练
- 修复代码,使其不再产生无限循环。
练习 29:使用循环进行错误检查
到目前为止,在这本书中,我们大多数时间都在忽略错误检查。我们假设人类会遵循指示,如果他们的缺乏方向性导致我们的程序出错,我们只是责怪用户,不予理会。
当你只是在学习时,这是完全可以的。错误检查很难,这就是为什么大多数大型程序都有错误,并且需要一整群人非常努力地工作,以确保软件尽可能少地出现错误。
但是你终于到了能够编写一点错误检查的程度。
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class SafeSquareRoot
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8 double x, y;
9
10 System.out.print("Give me a number, and I shall find the square root
of it. ");
11 System.out.print("(No negatives, please.) ");
12 x = keyboard.nextDouble();
13
14 while ( x < 0 )
15 {
16 System.out.print("Sorry, I won't take the square root of a
negative.nNew number: ");
17 x = keyboard.nextDouble();
18 }
19
20 y = Math.sqrt(x);
21
22 System.out.println("The square root of " x " is " y);
23 }
24 }
你应该看到什么
代码语言:javascript复制Give me a number, and I shall find the square root of it. (No negatives, please.)
8
Sorry, I won't take the square root of a negative. New number: 7
Sorry, I won't take the square root of a negative. New number: 200
Sorry, I won't take the square root of a negative. New number: 5
The square root of 5.0 is 2.23606797749979
从第 14 行开始是我所说的“输入保护循环”的一个例子。在第 20 行,我们将对变量 x 中的任何值取平方根,并且在这样做之前,我们希望确保它包含一个正数。(Java 没有内置支持虚数。)我们可以使用内置的绝对值函数Math.abs()
,但我想演示错误检查,好吗?
在第 12 行,我们让人类输入一个数字。我们已经很客气地要求他们只输入一个正数,但他们可以输入任何他们喜欢的东西。(他们甚至可以输入“Mister Mxyzptlk”,但我们的错误检查技能还不够先进,无法幸存下来。)
所以在第 14 行,我们检查他们是否遵守了指示。如果x中的值为负数(小于零),我们会打印出一个错误消息,让他们再试一次。然后,在他们输入新数字之后,我们回到第 14 行,检查条件是否仍然为真。他们是否仍然没有遵循指示?如果是,再次显示错误消息并给他们另一个机会。
计算机不会不耐烦或无聊,所以人类被困在这个循环中,直到他们遵守。他们可以输入负数两十亿次,每次计算机都会礼貌地抱怨并让他们重新输入。
最终,人类会变得聪明,输入一个非负数。然后while
循环的条件将为假(终于),循环的主体将被跳过(终于),执行将在第 20 行继续,我们可以安全地计算一个我们知道是正数的数的平方根。
真正的程序到处都有这样的东西。你必须这样做,因为人类不可靠,经常做出意想不到的事情。当你的孩子在程序运行时拉起笔记本电脑并开始乱按键时会发生什么?我们希望程序不会崩溃。
哦,你有没有注意到?我在这个程序中改变了一些东西。到目前为止,我在这本书中每次在屏幕上打印东西时,我都在括号和引号之间放了一个空格,就像这样:
代码语言:javascript复制System.out.println( "This is a test." );
我这样做是因为我想要清楚地表明引号内的东西(技术上称为“字符串文字”)是一回事,而括号是另一回事。但是 Java 实际上并不在乎这些空格。(记住我充满了谎言。)如果你去掉空格,你的程序仍然会编译并且工作得和原来一样:
代码语言:javascript复制System.out.println("This is a test.");
你可能已经注意到,在第 22 行,我甚至省略了字符串文字和加号之间的空格。这样的间距不会影响 Java 程序的语法,尽管许多公司和其他软件编写团体都有“样式指南”,告诉你如果你想让团体的其他成员满意,应该以什么样的方式格式化你的代码。
有些人对此非常激动。他们认为你应该始终使用空格来缩进你的代码,或者始终将代码块的开放大括号放在上一行的末尾:
代码语言:javascript复制if ( age < 16 ) {
allowed = false;
}
…就像那样。我认为对于你自己的代码,你应该尝试其他风格,并做让你快乐的事情。当你和其他人一起工作时,你应该以让他们满意的方式格式化代码。
甚至有一些工具可以自动更改代码的格式以适应特定的风格!(搜索“源代码美化器”或“Java 代码美化器”来看一些例子。)
学习方法
- 不要使用输入保护循环,使用
if
语句和Math.abs()
来处理负数的平方根。当数字为负时,取正数的平方根,并在答案旁边打印一个小的"i"
。
练习 30:Do-While 循环
在这个练习中,我要做一些我通常不做的事情。我要向你展示在 Java 中制作循环的另一种方法。因为你只看了四个练习的while
循环,向你展示一种不同类型的循环可能会让你感到困惑。通常我喜欢等到学生做了很长时间的事情后再向他们展示做同样事情的新方法。
所以如果你认为你会感到困惑,随时可以跳过这个练习。这几乎不会伤害你,你可以在更有信心的时候再回来。
无论如何,在 Java 中有几种制作循环的方法。除了while
循环之外,还有 do-while 循环。它们几乎相同,因为它们都在括号中检查条件。如果条件为真,则执行循环体。如果条件为假,则跳过循环体(或停止循环)。
那么有什么区别呢?输入代码,然后我们再谈论它。
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class CoinFlip
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8
9 String coin, again;
10 int flip, streak = 0;
11
12 do
13 {
14 flip = 1 (int)(Math.random()*2);
15
16 if ( flip == 1 )
17 coin = "HEADS";
18 else
19 coin = "TAILS";
20
21 System.out.println( "You flip a coin and it is... " coin );
22
23 if ( flip == 1 )
24 {
25 streak ;
26 System.out.println( "tThat's " streak " in a row...." );
27 System.out.print( "tWould you like to flip again (y/n)? " );
28 again = keyboard.next();
29 }
30 else
31 {
32 streak = 0;
33 again = "n";
34 }
35 } while ( again.equals("y") );
36
37 System.out.println( "Final score: " streak );
38 }
39 }
你应该看到什么
代码语言:javascript复制You flip a coin and it is... HEADS That's 1 in a row....
Would you like to flip again (y/n)? y You flip a coin and it is. HEADS
That's 2 in a row....
Would you like to flip again (y/n)? y You flip a coin and it is. HEADS
That's 3 in a row....
Would you like to flip again (y/n)? n Final score: 3
while
循环和 do-while 循环之间只有两个区别。
-
while
循环的条件在循环体之前,但是 do-while 循环在循环体之前有关键字do
,条件在循环体结束后,紧跟着右花括号。 (并且在循环条件的右括号后有一个分号,而while
循环没有。) -
while
循环在进入循环体之前检查它们的条件,但是 do-while 循环无论如何都会运行一次循环体,并且只在第一次通过后检查条件。
在计算机科学领域,while
循环被称为“前测试”循环(因为它首先检查条件),而 do-while 被称为“后测试”循环(因为它在之后检查条件)。
如果while
循环的条件在第一次检查时为真,那么使用while
循环的代码和使用 do-while 循环的等效代码将表现完全相同。任何你可以用while
循环做的事情,你也可以用 do-while 循环(和稍微不同的代码)做,反之亦然。
那么为什么 Java 的开发者要费心制作 do-while 循环呢?因为有时你在条件中检查的是一些在至少执行一次循环体后才知道的东西。
在这种情况下,我们通过选择 1-2 之间的随机数来抛硬币,并使用if
语句。然后我们问他们是否想再抛一次或停止。如果他们说想再抛一次,我们的循环条件会重复。
如果我们用while
循环来做这个,条件会是这样的:
while ( again.equals("y") )
{
这是可以的,也可以工作,但是变量again直到第 28 行才得到一个值。所以我们的代码不会编译,因为again(用 Java 编译器的话)“可能尚未初始化”。所以我们必须在循环之前给它一个没有意义的值,只是为了取悦编译器。
这很烦人,所以 do-while 循环允许我们保持条件不变,但等到最后再检查它。这很方便。
学习演练
- 更改代码,使用
while
循环代替 do-while 循环。确保它能编译并且运行结果相同。 - 将它改回 do-while 循环。(当你忘记如何编写 do-while 循环时,你可能会回头看这段代码,我们不希望你唯一的例子被改成
while
循环。)
练习 31:逐个添加值
这个练习将演示一件你必须经常做的事情:处理一次只得到一个值。
如果我让你让人类输入三个数字并将它们相加,并且我保证他们只需要输入确切的三个数字(不多,不少),你可能会写出这样的东西:
代码语言:javascript复制int a, b, c, total;
a = keyboard.nextInt();
b = keyboard.nextInt();
c = keyboard.nextInt();
total = a b c;
如果我告诉你人类要输入五个数字,你的代码可能是这样的:
代码语言:javascript复制double num1, num2, num3, num4, num5, total;
num1 = keyboard.nextDouble();
num2 = keyboard.nextDouble();
num3 = keyboard.nextDouble();
num4 = keyboard.nextDouble();
num5 = keyboard.nextDouble();
total = num1 num2 num3 num4 num5;
但是,如果我告诉你他们想要输入一百个数字呢?或者一万个?或者可能是三个,可能是五个,我不确定?那么你需要另一种方法。你需要一个循环(这就是我们重复事情的方式),并且你需要一个变量,它将随着值的逐个添加而逐渐增加。一个从“空白”开始并逐个添加值的变量称为“累加器”变量,尽管这是一个相当古老的词,所以如果你的编程朋友年龄不到四十岁,他们可能从未听说过。
无论如何,基本想法看起来是这样的:
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class RunningTotal
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8
9 int current, total = 0;
10
11 System.out.print("Type in a bunch of values and I'll add them up. ");
12 System.out.println("I'll stop when you type a zero.");
13
14 do
15 {
16 System.out.print("Value: ");
17 current = keyboard.nextInt();
18 int newtotal = current total;
19 total = newtotal;
20 System.out.println("The total so far is: " total);
21 } while ( current != 0 );
22
23 System.out.println("The final total is: " total);
24 }
25 }
你应该看到什么
代码语言:javascript复制Type in a bunch of values and I'll add them up. I'll stop when you type a zero. Value: 3
The total so far is: 3 Value: 4
The total so far is: 7 Value: 5
The total so far is: 12 Value: 6
The total so far is: 18 Value: 0
The total so far is: 18 The final total is: 18
我们需要两个变量:一个用于保存他们刚刚输入的值(current),一个用于保存运行总数(嗯…total)。在第 9 行,我们确保首先将零放入total中。很快你就会明白为什么。
在第 17 行,人类可以输入一个数字。这是在 do-while 循环的主体内,无论如何都会运行至少一次,所以这段代码总是会发生。假设他们一开始输入3
。
在第 18 行,魔法的第一部分发生了。我们声明了一个名为newtotal的新变量,并将其值设置为人类刚刚输入的数字加上变量total 中已经存在的值。一开始total中有一个零,所以这行代码将零添加到current中,并将该数字存储到newtotal中。
然后在第 19 行,魔法的第二部分发生了:我们用 newtotal 的当前值替换 total 中的值(零)。所以现在 total 不再是零;它具有与 current 相同的值。所以 total 是0
,现在是3
。
然后我们打印小计,并在第 21 行检查current是否为零。如果不是,则循环重复到第 14 行。
人类可以输入第二个数字。假设是4
。变量 newtotal 被初始化为
current(4
)加上 total(3
),所以 newtotal 是7
。然后在第 19 行,我们将 total 的值更改为7
。
条件再次被检查,过程继续。最终,人类输入了一个0
,那个0
被添加到总数中(这不会伤害它),条件变为假,所以 do-while 循环停止循环。
在练习结束之前,我应该提到两件事:
- 因为变量newtotal在第 18 行被声明(并定义),所以该变量的范围仅限于 do-while 循环的主体。这意味着在第 21 行,newtotal不再在范围内,因此在 do-while 循环的条件中引用newtotal的任何尝试都会导致错误。该变量在每次循环中不断创建和销毁。这有点低效。
- 我们甚至可以编写代码而不使用newtotal变量。由于 Java 在将右侧的最终值存储到左侧命名的变量之前,我们可以将第 18 行和第 19 行合并为一行:
total = current total;
这个工作完全正常。(事实上,你以前见过它。)
学习演练
- 重写代码,使用
while
循环而不是 do-while 循环。确保它能够编译并确保它仍然有效。然后改回来。 - 更改 do-while 循环的条件,使得当newtotal恰好为 20 时循环停止。
哦?它不编译,因为newtotal超出了范围?更改newtotal声明的位置,使其正常工作。
练习 32:为骰子游戏添加值
Pig 是一个简单的骰子游戏,适用于两个或更多玩家。(如果您想了解更多信息,可以阅读维基百科关于 Pig 的条目。)基本思想是成为第一个“存入”100 分的人。当您掷出 1 时,您的回合结束,这一回合您不会获得任何分数。任何其他掷骰都会增加您这一回合的分数,但只有在您决定“保留”时才能保留这些分数。如果在保留之前掷出 1,那么您这一回合的所有分数都将丢失。
您已经掌握了整个 Pig 游戏的代码,但与您之前看到的较小程序相比,这是一次性的很多,所以我将把它分成两节课。今天我们只会为计算机玩家编写人工智能(A.I.)代码。这个计算机玩家将使用“在 20 时保留”策略,这意味着计算机会继续掷骰,直到他们这一回合的分数达到 20 或更多,然后无论如何都会保留。这实际上并不是一个糟糕的策略,而且编码起来也很容易。
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class PigDiceComputer
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8
9 int roll, total;
10
11 total = 0;
12
13 do
14 {
15 roll = 1 (int)(Math.random()*6);
16 System.out.println( "Computer rolled a " roll "." );
17 if ( roll == 1 )
18 {
19 System.out.println( "tThat ends its turn." );
20 total = 0;
21 }
22 else
23 {
24 total = roll;
25 System.out.println( "tComputer has " total " points so far this
round." );
26 if ( total < 20 )
27 {
28 System.out.println( "tComputer chooses to roll again." );
29 }
30 }
31 } while ( roll != 1 && total < 20 );
32
33 System.out.println( "Computer ends the round with " total " points." );
34
35 }
36 }
预期输出
代码语言:javascript复制Computer rolled a 2.
Computer has 2 points so far this round. Computer chooses to roll again.
Computer rolled a 3.
Computer has 5 points so far this round. Computer chooses to roll again.
Computer rolled a 1.
That ends its turn.
Computer ends the round with 0 points.
基本上整个程序都在一个大的 do-while 循环体中,告诉计算机何时停止:要么掷出 1,要么总数达到 20 或更多。只要掷骰不是 1 并且总数小于 20,条件就会成立,循环将从开始重新开始(在第 13 行)。我们选择 do-while 循环是因为我们希望计算机无论如何都至少掷一次骰子。
掷骰发生在第 15 行:从 1 到 6 的随机数字是掷骰的良好替代品。
在第 17 行,我们检查是否掷出了 1。如果是,所有分数都将丢失。如果不是(else
),我们将此次掷骰的分数加到总分上。请注意我们使用了“加等于”,这是我们以前见过的。
第 26 行的if
语句只是为了得到一个漂亮的消息,即计算机将再次掷骰。不错,对吧?所以下一课我们将回来玩完整的游戏!
学习演练
- 找到一个骰子(技术上应该是“骰子”,因为“骰子”是复数形式,而您只需要一个)或找到一个模拟掷骰子的应用程序或网站。拿出一张纸和一支笔。在纸张中间画一条线并制作两列。在左列上标注“掷骰”,在右列上标注“总数”。在总数列中放入
0
,并一开始将另一列留空。
然后掷骰子,并将您掷出的数字写在掷骰列的顶部。由于骰子掷出的行号是代码中的第 15 行,所以在掷骰值旁边用括号括起数字(15)
。
然后逐行执行代码,就像计算机一样。将掷骰的当前值与 1 进行比较。如果它们相等,则划掉总数列中的当前值,并在那里放入0 (20)
,因为总数将在代码的第 20 行变为零。
继续进行,直到程序结束。以下是程序“预期输出”部分所示程序示例运行的表格样式示例。
掷骰 | 总数 |
---|---|
0 (11) | |
2 (15) | |
2 (24) | |
3 (15) | |
5 (24) | |
1 (15) | |
0 (20) |
练习 33:名为“Pig”的骰子游戏
在上一课中,我们为骰子游戏Pig编写了计算机 A.I.(记住,如果你想要更多关于这个游戏的信息,你可以阅读维基百科关于 Pig 的条目。)在这一课中,我们将有整个游戏的代码,有一个人类玩家和一个计算机玩家轮流进行。
你上次写的整个程序大致对应于这个程序中的第 43 到 67 行。唯一的主要区别是,我们将有一个turnTotal变量来保存一个回合的点数,而total2变量则保存计算机从一轮到另一轮的总点数。
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class PigDice
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8
9 int roll, total1, total2, turnTotal;
10 String choice = "";
11
12 total1 = 0;
13 total2 = 0;
14
15 do
16 {
17 turnTotal = 0;
18 System.out.println( "You have " total1 " points." );
19
20 do
21 {
22 roll = 1 (int)(Math.random()*6);
23 System.out.println( "tYou rolled a " roll "." );
24 if ( roll == 1 )
25 {
26 System.out.println( "tThat ends your turn." );
27 turnTotal = 0;
28 }
29 else
30 {
31 turnTotal = roll;
32 System.out.println( "tYou have " turnTotal " points so far this
round." );
33 System.out.print( "tWould you like to "roll" again or "hold"? " );
34 choice = keyboard.next();
35 }
36 } while ( roll != 1 && choice.equals("roll") );
37
38 total1 = turnTotal;
39 System.out.println( "tYou end the round with " total1 " points." );
40
41 if ( total1 < 100 )
42 {
43 turnTotal = 0;
44 System.out.println( "Computer has " total2 " points." );
45
46 do
47 {
48 roll = 1 (int)(Math.random()*6);
49 System.out.println( "tComputer rolled a " roll "." );
50 if ( roll == 1 )
51 {
52 System.out.println( "tThat ends its turn." );
53 turnTotal = 0;
54 }
55 else
56 {
57 turnTotal = roll;
58 System.out.println( "tComputer has " turnTotal " points so far this
round." );
59 if ( turnTotal < 20 )
60 {
61 System.out.println( "tComputer chooses to roll again." );
62 }
63 }
64 } while ( roll != 1 && turnTotal < 20 );
65
66 total2 = turnTotal;
67 System.out.println( "tComputer ends the round with " total2 " points." );
68 }
69
70 } while ( total1 < 100 && total2 < 100 );
71
72 if ( total1 > total2 )
73 {
74 System.out.println( "Humanity wins!" );
75 }
76 else
77 {
78 System.out.println( "The computer wins." );
79 }
80
81 }
82 }
你应该看到什么
代码语言:javascript复制You have 0 points.
You rolled a 2.
You have 2 points so far this round.
Would you like to "roll" again or "hold"? roll You rolled a 1.
That ends your turn.
You end the round with 0 points.
Computer has 0 points.
Computer rolled a 6.
Computer has 6 points so far this round. Computer chooses to roll again.
Computer rolled a 6.
Computer has 12 points so far this round. Computer chooses to roll again.
Computer rolled a 1. That ends its turn.
Computer ends the round with 0 points.
You have 0 points.
You rolled a 3.
You have 3 points so far this round.
Would you like to "roll" again or "hold"? roll You rolled a 5.
You have 8 points so far this round.
Would you like to "roll" again or "hold"? roll You rolled a 2.
You have 10 points so far this round.
Would you like to "roll" again or "hold"? roll You rolled a 2.
You have 12 points so far this round.
Would you like to "roll" again or "hold"? hold You end the round with 12 points.
Computer has 0 points.
Computer rolled a 5.
Computer has 5 points so far this round. Computer chooses to roll again.
Computer rolled a 4.
Computer has 9 points so far this round. Computer chooses to roll again.
Computer rolled a 4.
Computer has 13 points so far this round. Computer chooses to roll again.
Computer rolled a 3.
Computer has 16 points so far this round. Computer chooses to roll again.
Computer rolled a 5.
Computer has 21 points so far this round.
Computer ends the round with 21 points.
You have 12 points.
You rolled a 2.
You have 2 points so far this round.
Would you like to "roll" again or "hold"? roll
...etc
Computer has 20 points so far this round.
Computer ends the round with 102 points.
The computer wins.
我们从两个变量开始程序:total1保存人类的总点数,total2保存计算机的总点数。两者都从 0 开始。
然后在第 15 行开始一个非常庞大的 do-while 循环,基本上包含了整个游戏,直到第 70 行才结束。向下滚动,你会看到这个循环会重复,只要total1和total2都小于 100。当任一玩家达到 100 或更多时,条件不再成立,do-while 循环不会再重复。
然后在那个 do-while 循环结束之后(从第 72 行开始),有一个if
语句和一个else
来确定赢家。
让我们往上滚动一下,看看人类的回合,从第 17 行开始。turnTotal是人类到目前为止在这一轮中赚得的点数。由于这是一轮的开始,我们应该从 0 开始。
第 20 行是一个包含人类回合的 do-while 循环的开始。它在第 36 行结束,所有在第 20 行和第 36 行之间的代码都会重复,只要人类没有掷出 1,只要人类继续选择再次掷骰子。
人类的每次掷骰子都和计算机一样开始:选择一个从 1 到 6 的随机数。我们在第 22 行打印出来。
现在可能发生两件事:要么掷骰子是 1——人类失去本轮获得的所有分数——要么掷骰子是 2-6,然后将掷骰子的点数加到他们的turnTotal上。我们显示适当的消息,在第 33 和 34 行,我们给人类选择再次掷骰的机会,或者通过保持来安全地玩。然后在第 36 行,do-while 循环的条件将检查并在适当的情况下重复回到第 20 行。
一旦玩家的回合结束,我们将turnTotal(可能为 0)加到玩家的总分上,并显示他们当前的分数。
在第 41 行,计算机的回合开始了。然而,如果人类已经达到了 100 分,计算机就不会轮到了:在这种情况下游戏结束。因此,为了防止计算机玩游戏,我们必须将整个计算机的回合包装在一个大的if
语句中,以便在人类的总分(total1)大于或等于 100 时跳过。这个if
语句从第 41 行开始,到第 68 行结束。
所以在第 43 行,计算机的回合真正开始了。这基本上与上一个练习相同,所以我不会再解释一遍。请注意,计算机正在根据其回合总数决定是否继续掷骰子。
第 70 行结束了包含整个游戏的 do-while 循环,第 72 到 79 行确定并显示赢家。
希望你能够很好地跟上游戏的流程。这相当复杂。
我还要指出,对于这样一个程序来说,每次你放一个左大括号,将其后的所有内容缩进一级是非常重要的。如果你可以从第 47 行的左大括号直观地扫描你的眼睛到第 64 行的右大括号,看看 do-while 循环中有什么,没有什么,这将为你节省很多烦恼。
练习 34:调用一个函数
上一个练习相当复杂。所以我们今天的练习会放松一下。我们将学习如何在 Java 中编写“函数”,以及如何通过“调用”来执行它。10
代码语言:javascript复制 1 public class ThereAndBackAgain
2 {
3 public static void main( String[] args )
4 {
5 System.out.println( "Here." );
6 erebor();
7 System.out.println( "Back first time." );
8 erebor();
9 System.out.println( "Back second time." );
10 }
11
12 public static void erebor()
13 {
14 System.out.println( "There." );
15 }
16 }
你应该看到什么
代码语言:javascript复制Here.
There.
Back first time.
There.
Back second time.
因此,第 5 到 9 行相当无聊,除了在第 6 和第 8 行我们提到了一些叫做“erebor”的东西,你在 Java 中以前没有见过。你知道为什么你没有见过它吗?因为它根本不存在!
跳到第 12 到 15 行,你会注意到我在我们的程序中添加了一些不在main()
主体内的东西。通常情况下,public static void main
的右大括号几乎在代码的末尾,而它之后的唯一内容就是public class
Whatevs
的右大括号。但这次不是!
第 12 到 15 行定义了一个名为erebor()
的函数。(erebor()
这个词在 Java 中没有特别的含义。我们可以将其命名为bilbo()
或smaug()
或者任何我们喜欢的名字。)
这个函数在第 13 行有一个左大括号,就像main()
总是有一个左大括号一样。在第 15 行是函数主体的结束,有一个右大括号。所以函数定义从第 12 行开始,到第 15 行结束。
这个函数是做什么的?它在屏幕上打印字符串"There."
。
10 这是我充满谎言的事情之一。从技术上讲,Java 甚至没有函数。它只有“方法”,这是一个方法,而不是函数。
但这只是一个方法,因为这就是 Java 所拥有的。在任何其他编程语言中,我们写的内容都会被称为函数而不是方法。这是因为方法是面向对象的东西,而这个程序根本不是面向对象的。
因此,尽管从技术上讲是不正确的,我将这种事情称为函数,并且只使用方法这个词。
当我做出的东西实际上像一个方法时。
我的故意错误的词汇只会在你与一个迂腐的 Java 程序员交谈时引起问题,因为他们可能会取笑你。如果发生这种情况,给他们看这个脚注,并问问他们教初学者编程多少年了。我保证这样做比从一开始就向你展示真正的方法,或者现在就这样做并尝试区分“像函数一样运行的方法”和“行为像真正方法的方法”要好。
那么现在让我们回到main()
,看看main()
体内的函数调用。
第 5 行我们在屏幕上打印字符串"Here."。然后第 6 行你会看到一个“函数调用”。这行代码告诉计算机跳到函数erebor()
,运行该函数主体中的所有代码,然后在完成后返回到第 6 行。
所以你看到当我们调用erebor()
函数时,字符串"There."会在字符串"Here."之后打印在屏幕上。当计算机运行第 6 行时,程序的执行在main()
中暂停,跳过main()
中的所有其余代码,跳到第 12 行,运行erebor()
函数主体中的所有代码(只有 1 行),然后一旦执行到第 15 行的右花括号,它返回到第 6 行的末尾,恢复main()
的执行。接着运行第 7 行。
第 7 行显示屏上显示另一条消息,然后第 8 行有另一个函数调用。函数 erebor 被第二次调用。它在第 8 行暂停了main()
,跳下去运行 erebor 的主体(再次打印字符串"There."),然后返回到第 8 行,main()
的执行继续。
最后第 9 行在屏幕上打印最后一个字符串。然后执行继续到
main()
在第 10 行。当main()
结束时,程序也结束。
这很重要,所以我会再说一遍:当main()
结束时,程序也结束。即使在class
内有很多不同的函数,程序执行也是从main()
的第一行开始的。一旦main()
的最后一行被执行,程序就停止运行,即使有一些从未被调用的函数。(我们将在下一个练习中看到一个例子。)
学习演练
- 删除第 6 行第一个函数调用末尾的括号,使其看起来像这样:
erebor;
编译后会发生什么?(然后把括号放回去。)
- 删除第二个函数调用(第 8 行)。你可以直接删除整行,或者在行前加上斜杠,这样编译器会认为它是注释,就像这样:
// erebor();
编译它,但在运行之前,你认为输出会有什么不同?运行它,看看你是否正确。
练习 35:调用函数绘制旗帜
现在你已经了解了如何定义函数和调用函数的绝对基础知识,让我们通过定义十一个函数来进行一些练习!
这个程序中没有零(0
)。所有看起来像O
的东西都是大写字母 O。还要注意,第 45 行和第 50 行使用的是print()
而不是println()
。
1 import static java.lang.System.*;
2
3 public class OverlyComplexFlag
4 {
5 public static void main( String[] args )
6 {
7 printTopHalf();
8
9 print48Colons();
10 print48Ohs();
11 print48Colons();
12 print48Ohs();
13 print48Colons();
14 print48Ohs();
15 }
16
17 public static void print48Colons()
18 {
19 out.println( "|::::::::::::::::::::::::::::::::::::::::::::::::|" );
20 }
21
22 public static void print48Ohs()
23 {
24 out.println( "|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO|" );
25 }
26
27 public static void print29Colons()
28 {
29 out.println( "|:::::::::::::::::::::::::::::|" );
30 }
31
32 public static void printPledge()
33 {
34 out.println( "I pledge allegiance to the flag." );
35 }
36
37 public static void print29Ohs()
38 {
39 out.println( "|OOOOOOOOOOOOOOOOOOOOOOOOOOOOO|" );
40 }
41
42 public static void print6Stars()
43 {
44 out.print( "| * * * * * * " );
45 }
46
47 public static void print5Stars()
48 {
49 out.print( "| * * * * * " );
50 }
51
52 public static void printSixStarLine()
53 {
54 print6Stars();
55 print29Ohs();
56 }
57
58 public static void printFiveStarLine()
59 {
60 print5Stars();
61 print29Colons();
62 }
63
64 public static void printTopHalf()
65 {
66 out.println( " ________________________________________________" ); //
1 space then 48 underscores
67 printSixStarLine();
68 printFiveStarLine();
69 printSixStarLine();
70 printFiveStarLine();
71 printSixStarLine();
72 printFiveStarLine();
73 printSixStarLine();
74 }
75
76 }
你应该看到什么
代码语言:javascript复制| * * * * * * |OOOOOOOOOOOOOOOOOOOOOOOOOOOOO|
| * * * * * |:::::::::::::::::::::::::::::|
| * * * * * * |OOOOOOOOOOOOOOOOOOOOOOOOOOOOO|
| * * * * * |:::::::::::::::::::::::::::::|
| * * * * * * |OOOOOOOOOOOOOOOOOOOOOOOOOOOOO|
| * * * * * |:::::::::::::::::::::::::::::|
| * * * * * * |OOOOOOOOOOOOOOOOOOOOOOOOOOOOO|
|::::::::::::::::::::::::::::::::::::::::::::::::|
|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO|
|::::::::::::::::::::::::::::::::::::::::::::::::|
|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO|
|::::::::::::::::::::::::::::::::::::::::::::::::|
|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO|
这个练习很荒谬。没有任何值得尊重的程序员会以这种方式在屏幕上绘制旗帜。如果编写这个程序感觉有点傻,那没关系。但是函数很重要,我更喜欢从你实际能理解的愚蠢例子开始,而不是从太难跟随的现实例子开始。
那么我们如何追踪执行这样的程序呢?我们从开始开始:main()
的第一行。在第 7 行,main 做的第一件事是调用函数printTopHalf()
。所以 main 被暂停,执行跳到printTopHalf()
函数的第一行,即第 66 行。
printTopHalf
的第一件事是在屏幕上打印一堆下划线,这将是我们旗帜的顶部。之后,执行移动到第 67 行,这是另一个函数调用!所以main()
仍然在暂停之前,等待printTopHalf()
完成,现在printTopHalf()
本身也在暂停,等待printSixStarLine()
完成并返回控制权到这里。
printSixStarLine()
从第 54 行开始,它调用print6Stars()
函数。那个函数(幸运的是)只在屏幕上显示一些东西,所以当print6Stars()
的右大括号出现在第 45 行时,它将控制权返回到printSixStarLine()
的第二行(第 55 行),然后又有一个函数调用。这通过print29Ohs()
函数的主体运行,并返回到第 56 行。然后printSixStarLine()
结束,将控制权返回到第 67 行的末尾。
在这一点上,我认为解释所有的函数调用会比跟随执行路径更加混乱,所以在这里我将按顺序打印所有执行的行号。调用一个函数会增加缩进级别,从该函数返回会减少缩进级别。
代码语言:javascript复制5 begin main
6
7 call printTopHalf
64 begin printTopHalf
65
66
67 call printSixStarLine
52 begin printSixStarLine
53
54 call print6Stars
42 begin print6Stars
43
44
45 end print6Stars
54 resume printSixStarLine
55 call print29Ohs
37 begin print29Ohs
38
39
40 end print29Ohs
55 resume printSixStarLine
56 end printSixStarLine
67 resume printTopHalf
68 call printFiveStarLine
58 begin printFiveStarLine
59
60 call print5Stars
47 begin print5Stars
48
49
50 end print5Stars
60 resume printFiveStarLine
61 call print29Colons
27 begin print29Colons
28
29
30 end print29Colons
61 resume printFiveStarLine
62 end printFiveStarLine
68 resume printTopHalf
69 call printSixStarLine
52 begin printSixStarLine
53
54 call print6Stars
42 begin print6Stars
43
44
45 end print6Stars
54 resume printSixStarLine
55 call print29Ohs
37 begin print29Ohs
38
39
40 end print29Ohs
55 resume printSixStarLine
56 end printSixStarLine
69 resume printTopHalf
70 call printFiveStarLine
58 begin printFiveStarLine
59
60 call print5Stars
47 begin print5Stars
48
49
50 end print5Stars
60 resume printFiveStarLine
61 call print29Colons
27 begin print29Colons
28
29
30 end print29Colons
61 resume printFiveStarLine
62 end printFiveStarLine
70 resume printTopHalf
71 call printSixStarLine
52 begin printSixStarLine
53
54 call print6Stars
42 begin print6Stars
43
44
45 end print6Stars
54 resume printSixStarLine
55 call print29Ohs
37 begin print29Ohs
38
39
40 end print29Ohs
55 resume printSixStarLine
56 end printSixStarLine
71 resume printTopHalf
72 call printFiveStarLine
58 begin printFiveStarLine
59
60 call print5Stars
47 begin print5Stars
48
49
50 end print5Stars
60 resume printFiveStarLine
61 call print29Colons
27 begin print29Colons
28
29
30 end print29Colons
61 resume printFiveStarLine
62 end printFiveStarLine
72 resume printTopHalf
73 call printSixStarLine
52 begin printSixStarLine
53
54 call print6Stars
42 begin print6Stars
43
44
45 end print6Stars
54 resume printSixStarLine
55 call print29Ohs
37 begin print29Ohs
38
39
40 end print29Ohs
55 resume printSixStarLine
56 end printSixStarLine
73 resume printTopHalf
74 end printTopHalf
7 resume main
8
9 call print48Colons
17 begin print48Colons
18
19
20 end print48Colons
9 resume main
10 call print48Ohs
22 begin print48Ohs
23
24
25 end print48Ohs
10 resume main
11 call print48Colons
17 begin print48Colons
18
19
20 end print48Colons
11 resume main
12 call print48Ohs
22 begin print48Ohs
23
24
25 end print48Ohs
12 resume main
13 call print48Colons
17 begin print48Colons
18
19
20 end print48Colons
13 resume main
14 call print48Ohs
22 begin print48Ohs
23
24
25 end print48Ohs
14 resume main
天哪!如果你能成功地追踪到这一点,那么你就已经在成为一个称职的程序员的路上了。
学习演练
- 你真的没有完全追踪整个程序,是吗?好吧,回去做吧。这本书不叫“学习 Java 的一半”,对吧?打印出代码,拿起一支铅笔,在一个函数调用其他地方时画一条线,当函数返回时画一条线。完成后,它应该看起来有点像一盘石墨意面!
- 在 32 到 35 行,你会找到一个名为
printPledge()
的函数的定义。但是这个函数的输出从来没有出现过。为什么?在main()
的末尾添加一个函数调用来运行这个函数,以便它出现在旗帜下面。 (尽管这个程序很邪恶,但我对那面旗帜感到非常自豪。如果你用尺子测量一切的尺寸,你会发现我的旗帜与真正的美国国旗的尺寸几乎一样。我实际上花了很长时间测量和调整一切。)
练习 36:使用函数显示骰子
上一个练习在一个函数实际上使事情变得更糟的程序中使用了函数。所以今天我们准备看一个情况,使用函数实际上使程序变得更好。
Yacht 是一个古老的骰子游戏,后来被修改为商业游戏 Yahtzee。它涉及一次掷五个骰子,并为各种组合赚取积分。最罕见的组合是“游艇”,当五个骰子都显示相同的数字时。
这个程序不做任何其他的评分,它只是掷五个骰子,直到它们都相同。(计算机速度很快,所以即使这需要很多次尝试,也不会花费很长时间。)
代码语言:javascript复制 1 public class YachtDice
2 {
3 public static void main( String[] args )
4 {
5 int roll1, roll2, roll3, roll4, roll5;
6 boolean allTheSame;
7
8 do
9 {
10 roll1 = 1 (int)(Math.random()*6);
11 roll2 = 1 (int)(Math.random()*6);
12 roll3 = 1 (int)(Math.random()*6);
13 roll4 = 1 (int)(Math.random()*6);
14 roll5 = 1 (int)(Math.random()*6);
15 System.out.println("nYou rolled: " roll1 " " roll2 " "
roll3 " " roll4 " " roll5);
16 showDice(roll1);
17 showDice(roll2);
18 showDice(roll3);
19 showDice(roll4);
20 showDice(roll5);
21 allTheSame = ( roll1 == roll2 && roll2 == roll3 && roll3 == roll4
&& roll4 == roll5 );
22
23 } while ( ! allTheSame );
24 System.out.println("The Yacht!!");
25 }
26
27 public static void showDice( int roll )
28 {
29 System.out.println(" ");
30 if ( roll == 1 )
31 {
32 System.out.println("| |");
33 System.out.println("| o |");
34 System.out.println("| |");
35 }
36 else if ( roll == 2 )
37 {
38 System.out.println("|o |");
39 System.out.println("| |");
40 System.out.println("| o|");
41 }
42 else if ( roll == 3 )
43 {
44 System.out.println("|o |");
45 System.out.println("| o |");
46 System.out.println("| o|");
47 }
48 else if ( roll == 4 )
49 {
50 System.out.println("|o o|");
51 System.out.println("| |");
52 System.out.println("|o o|");
53 }
54 else if ( roll == 5 )
55 {
56 System.out.println("|o o|");
57 System.out.println("| o |");
58 System.out.println("|o o|");
59 }
60 else if ( roll == 6 )
61 {
62 System.out.println("|o o|");
63 System.out.println("|o o|");
64 System.out.println("|o o|");
65 }
66 System.out.println(" ");
67 }
68 }
你应该看到什么
代码语言:javascript复制You rolled:
|o o|
| |
|o o|
|o o|
|o o|
|o o|
|o o|
| o |
|o o|
|o o|
| o |
|o o|
|o o|
|o o|
|o o|
4
6
5
5
6
You rolled:
|o |
| |
| o|
|o |
| |
| o|
|o o|
| o |
|o o|
2
2
5
6
6
|o o|
|o o|
|o o|
|o o|
|o o|
|o o|
...etc
You rolled: 5 5 5 5 5
|o o|
| o |
|o o|
|o o|
| o |
|o o|
|o o|
| o |
|o o|
|o o|
| o |
|o o|
|o o|
| o |
|o o|
The Yacht!!
除了第 21 行的花哨的布尔表达式之外,这个练习中有一个名为showDice
的单个函数。
在 10 到 14 行,我们选择五个随机数(每个数从 1 到 6)并将结果存储到五个整数变量roll1到roll5中。
我们想要使用一些if
语句在屏幕上显示骰子的值,但我们不想写五次相同的if
语句(因为变量是不同的)。解决方案是创建一个带参数的函数。
在第 27 行,你看到了showDice
函数定义的开始。在名称(或“标识符”)showDice
后面,有一组括号,在它们之间声明了一个变量!这个变量叫做“参数”。showDice
函数有一个参数。这个参数是一个整数。它的名字叫 roll。
这意味着每当你为showDice
写一个函数调用时,你不能只写函数的名称和括号,比如showDice()
。它不会编译。你必须在括号中包含一个整数值(这称为“参数”),要么是一个变量,要么是一个简化为整数值的表达式。
这里有一些例子。
代码语言:javascript复制showDice; // NO (without parens this refers to a variable not a function call)
showDice(); // NO (function call must have one argument, not zero)
showDice(1); // YES (one argument is just right)
showDice(4); // YES
showDice(1 2); // YES
showDice(roll2); // YES
showDice(roll5); // YES
showDice( (roll3 roll4) / 2 ); // YES (strange but legal)
showDice(17); // YES (although it won't show a proper dice picture)
showDice(3, 4); // NO (function call must have one argument, not two)
showDice(2.0); // NO (argument must be an integer, not a double)
showDice("two"); // NO (argument must be an integer, not a String)
showDice(false); // NO (argument must be an integer, not a Boolean)
在所有情况下,参数的值的副本都存储在参数中。因此,如果你这样调用函数 showDice(3);
,那么函数被调用,值3
被存储到参数 roll 中。所以到第 29 行,参数变量 roll 已经被声明并初始化为值3
。
如果我们使用变量调用函数,比如 showDice(roll2);
那么在函数体执行之前,函数被调用并且当前在 roll2 中的任何值的副本将被存储到参数变量 roll 中。
因此,在第 16 行,showDice
函数被执行,roll 将被设置为 roll1 中的任何值。
然后在第 17 行,showDice
再次被调用,但这次 roll 将被设置为 roll1 中的任何值。
roll2。第 18 行调用showDice
,同时将其参数设置为 roll3 的值。等等。
这样我们基本上运行了相同的代码块五次,但用不同的变量替换
每次掷骰子。这为我们节省了很多代码。
为了对比,我还写了一个简化的两个骰子版本的练习,而不使用函数。请注意,我必须重复完全相同的if
语句序列两次:每个变量一次。
还要注意的是,虽然定义函数比只是复制和粘贴要多一点工作
代码语言:javascript复制 1 public class YachtDiceNoFunctions
2 {
3 public static void main( String[] args )
4 {
5 int roll1, roll2;
6
7 do
8 {
9 roll1 = 1 (int)(Math.random()*6);
10 roll2 = 1 (int)(Math.random()*6);
11 int total = roll1 roll2;
12 System.out.println("nYou rolled a " roll1 " and a " roll2);
13 System.out.println(" ");
14 if ( roll1 == 1 )
15 {
16 System.out.println("| |");
17 System.out.println("| o |");
18 System.out.println("| |");
19 }
20 else if ( roll1 == 2 )
21 {
22 System.out.println("|o |");
23 System.out.println("| |");
24 System.out.println("| o|");
25 }
26 else if ( roll1 == 3 )
27 {
28 System.out.println("|o |");
29 System.out.println("| o |");
30 System.out.println("| o|");
31 }
32 else if ( roll1 == 4 )
33 {
34 System.out.println("|o o|");
35 System.out.println("| |");
36 System.out.println("|o o|");
37 }
38 else if ( roll1 == 5 )
39 {
40 System.out.println("|o o|");
41 System.out.println("| o |");
42 System.out.println("|o o|");
43 }
44 else if ( roll1 == 6 )
45 {
46 System.out.println("|o o|");
47 System.out.println("|o o|");
48 System.out.println("|o o|");
49 }
50 System.out.println(" ");
51
52
53 System.out.println(" ");
54 if ( roll2 == 1 )
55 {
56 System.out.println("| |");
57 System.out.println("| o |");
58 System.out.println("| |");
59 }
60 else if ( roll2 == 2 )
61 {
62 System.out.println("|o |");
63 System.out.println("| |");
64 System.out.println("| o|");
65 }
66 else if ( roll2 == 3 )
67 {
68 System.out.println("|o |");
69 System.out.println("| o |");
70 System.out.println("| o|");
71 }
72 else if ( roll2 == 4 )
73 {
74 System.out.println("|o o|");
75 System.out.println("| |");
76 System.out.println("|o o|");
77 }
78 else if ( roll2 == 5 )
79 {
80 System.out.println("|o o|");
81 System.out.println("| o |");
82 System.out.println("|o o|");
83 }
84 else if ( roll2 == 6 )
85 {
86 System.out.println("|o o|");
87 System.out.println("|o o|");
88 System.out.println("|o o|");
89 }
90 System.out.println(" ");
91
92 System.out.println("The total is " total "n");
93 } while ( roll1 != roll2 );
94
95 System.out.println("Doubles! Nice job.");
96 }
97 }
你应该看到什么
代码语言:javascript复制You rolled a 5 and a 4
|o o|
| o |
|o o|
|o o|
| |
|o o|
The total is 9
You rolled a 6 and a 2
|o o|
|o o|
|o o|
|o |
| |
| o|
The total is 8
You rolled a 2 and a 2
|o |
| |
| o|
|o |
| |
| o|
The total is 4
Doubles! Nice job.
学习演练
- 添加第六个骰子。注意,只需添加一个函数调用就可以轻松显示roll6。