笨办法学 Java(二)(1)

简介: 笨办法学 Java(二)(1)

练习 21:嵌套 if 语句

你在上一个练习中已经看到了这一点,但你可以在if语句的主体中放入任何你喜欢的东西,包括其他if语句。这被称为“嵌套”,在另一个if语句内部的if语句称为“嵌套 if”。

这是使用它做一些有用的事情的一个例子。

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 }

你应该看到什么

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语句。不过,我在学习演练中有一个小惊喜。

学习演练

  1. 将第 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的时候(可能是个好主意):

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? (1­12) " );
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 thirty­one
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 }

你应该看到什么

Which month? (1­12) 4
30 days hath April

switch语句以关键字switch开始,然后是一些括号。括号内是一个单一的变量(或者简化为单一值的表达式)。然后是一个开放的大括号。

switch语句的主体内部有几个以关键字case开头的case语句,然后是括号中的变量可能相等的值。然后是一个冒号(:)。在 Java 中很少看到冒号。

case之后,是值和冒号,然后是一些代码。它可以是任意行的代码,除了你不允许在switch语句内部声明任何变量。然后在所有代码之后是关键字breakbreak标志着case的结束。

switch语句运行时,计算机会找出括号内变量的当前值。然后它逐个查看case列表,寻找匹配项。当它找到匹配项时,它会从case所在的左侧移动到右侧,并开始运行代码,直到被break停止。

如果没有case匹配,且有一个default情况(可选),那么default中的代码将被运行。

情况将被运行。

第二个例子从第 50 行开始,演示了一旦switch语句找到与之匹配的情况,它确实会运行右侧的代码,直到遇到break语句。它甚至会从一个case穿过到另一个。

我们可以利用这种穿透行为,有时做一些聪明的事情,比如计算一个月中的天数的代码。由于 9 月、4 月、6 月和 11 月都有 30 天,我们可以将它们的所有情况放在一起,让它们穿过任何一个运行相同的事情。

无论如何,我不会在这本书中再使用switch语句,因为我几乎从来没有找到过它的好用处,但它确实存在,至少我可以说你看到了它。

学习演练

  1. 在第一个switch中删除一些break语句,并添加一些println()语句来确认它会将 monthName 设置为一个值,然后又一个值,直到最后被break停止。

练习 23:更多字符串比较

嗯,你已经学会了不能用==比较字符串;你必须使用.equals()

方法。但我认为你终于准备好看看我们如何比较字符串的字母顺序了。

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 made­up 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 made­up 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 made­up 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 made­up 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 made­up 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 made­up 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 made­up 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 made­up language!" );
60         if ( name.compareTo("visualbasic") > 0 )
61             System.out.println( name + " comes AFTER  visualbasic" );
62     }
63 }

你应该看到什么

Give me the name of a made­up 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,我们会得到一个小于零的数字,所以我们可以写:

if ( self.compareTo(other) <= 0 )

如果结果小于零,if语句将为真,如果结果等于零,if语句将为真。

语句将为真。这有点像写

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
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. 使用你选择的方法,使这个程序即使在人类输入了“错误”的大写字母的单词时也能正确工作。
  2. 计算机只能在内部处理数字。字母不是数字,但有一个巨大的表,将每种语言中的每个字符映射到 1,112,063 个数字中的一个,唯一标识该字符。字母“B”的 UTF-8 Unicode 值是 66;字母“b”的值是 98。

练习 24:随机选择数字

我们将在一些练习中花一些时间来学习编程书中并不总是看到的东西:如何让计算机在某个范围内选择一个“随机”数。这是因为你可以写很多的软件而不需要计算机随机选择一个数字。然而,有随机数将让我们制作一些简单的互动游戏,这很容易就能弥补这个略微奇怪的概念的痛苦。

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 }

你应该看到什么

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 最初是012.354430.0,或99.9999999,那么 a 将是0123099,但永远不会是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 的值。(这仍然是十个值。)

好了,今天就到这里吧。

学习演练

  1. 移除第 24 行的转换。尝试编译程序。你得到什么错误消息?(然后把它放回去。)
  2. 运行程序多次,并确认在第 28 到 30 行打印出的abc始终具有 0 到 99 的值。
  3. 用手指数一数,确认如果我有一个从 0 到 9 的数字,那么我可能有十个可能的数字。将随机数乘以十并截断会得到十种可能的结果(0­9)。将随机数乘以五并截断会得到五种可能的结果(0­4)。
  4. 运行程序多次,并确认在第 43 行打印出的a始终具有 0 到 9 的值,b始终具有 1 到 10 的值,c始终具有 5 到 14 的值。

练习 25:更复杂的随机数

上一个练习中有一些复杂的思考,所以这个练习不会教授新的东西,而是会花更多时间来学习相同的概念。

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 100­70+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()*(high­low+1));
35         b = low + (int)(Math.random()*(high­low+1));
36         c = low + (int)(Math.random()*(high­low+1));
37         d = low + (int)(Math.random()*(high­low+1));
38         e = low + (int)(Math.random()*(high­low+1));
39 
40         System.out.println( a + "\t" + b + "\t" + c + "\t" + d + "\t" + e );
41     }
42 }


笨办法学 Java(二)https://developer.aliyun.com/article/1481895

相关文章
|
6月前
|
存储 Java Go
笨办法学 Java(四)(3)
笨办法学 Java(四)(3)
34 1
|
6月前
|
Java
Java中 a+=b和a=a+b有什么区别?
Java中 a+=b和a=a+b有什么区别?
105 0
|
6月前
|
存储 Java 程序员
笨办法学 Java(三)(3)
笨办法学 Java(三)(3)
96 0
|
6月前
|
存储 人工智能 网络协议
笨办法学 Java(二)(2)
笨办法学 Java(二)
57 0
|
6月前
|
存储 Java 程序员
笨办法学 Java(一)(1)
笨办法学 Java(一)
71 0
|
6月前
|
存储 Java 索引
笨办法学 Java(四)(2)
笨办法学 Java(四)(2)
38 0
|
6月前
|
存储 Java 数据库
笨办法学 Java(四)(1)
笨办法学 Java(四)(1)
52 0
|
6月前
|
存储 Java 程序员
笨办法学 Java(一)(2)
笨办法学 Java(一)(2)
76 0
|
6月前
|
存储 Java 程序员
笨办法学 Java(二)(3)
笨办法学 Java(二)(3)
54 0
|
6月前
|
存储 Java
笨办法学 Java(三)(1)
笨办法学 Java(三)(1)
39 0
下一篇
无影云桌面