原文:Learn Java The Hard Way 译者:飞龙 协议:CC BY-NC-SA 4.0
前言
我已经教了超过 15 年的初学者如何编码。超过 2000 名学生参加了我的课程,离开时知道如何编写简单的程序。有些人只学会了一点,而其他人在短短几年内获得了令人难以置信的技能。
我相信几乎任何人都可以教一个天才孩子如何编码。“我教了我 9 岁的女儿编码,她 6 周后就做出了她的第一个安卓应用!”如果你是天才孩子,这本书不是为你写的。
我也相信没有什么能替代编写小程序。这就是你在这本书中要做的。你将输入小程序并运行它们。
“学习的最佳方式是实践。”
—— P.R. Halmos
介绍:Java
Java 不是初学者的语言。我相信大多数“初学者”Java 书籍只适用于已经懂得如何编码或者是天才的人。
我可以教你 Java,即使你以前从未编程过,甚至不是天才。但我可能会有点作弊。
我将教给您的是 Java。但它并不是所有的 Java。我必须略去一些部分,因为您还没有准备好。如果您认为自己已经准备好学习 Java 的更复杂部分,那么 1)您是错误的,2)请购买另一本书。市面上有很多书籍会比您应付得了的复杂性更快地向您展示 Java 所能提供的一切。
特别是,我有一个巨大的遗漏:我将避免讨论面向对象编程(OOP)的话题。我相当肯定,不适应的初学者无法同时学会如何编写良好的代码和学习面向对象编程。我几乎从未见过这种方法奏效。
我计划写一本后续书籍,将涵盖面向对象编程和 Java 的更复杂部分。但您应该先完成这本书。我教学生编程已经很多年了,我从来没有一位学生从大学来拜访我,说“我希望您在基础知识上花的时间少一些”。
您将学到什么
- 如何安装 Java 编译器和文本编辑器来编写程序。
- 如何创建、编译和运行您的第一个 Java 程序。
- 变量和从用户和文件中获取输入。
- 使用 if 语句做出决策
- 循环
- 数组
- 记录
在最后一章中,您将编写一个不那么简单的基于文本的冒险游戏,其中的关卡是从文本文件中加载的。您还应该能够编写一个基于文本的纸牌游戏,如红心或黑桃。
您将不会学到什么
- 图形
- 面向对象编程
- 如何制作 Android 应用程序
我喜欢图形,在 Java 中与 C 相比并不难,但我无法涵盖所有内容并很好地教授基础知识,所以必须有所取舍。
在我看来,面向对象编程在入门书籍中没有立足之地。
Android 应用程序非常复杂,如果您是初学者,应用程序远远超出了您的能力。然而,本书中的内容不会伤害您制作应用程序的机会,而且更温和的学习节奏可能会让您坚持下去,而其他书籍可能会让您沮丧而放弃。
此外,我希望在这本书之后再写两本书。我的第二本书将涵盖 Java 中的图形和面向对象编程。我的第三本书将涵盖制作一个简单的 Android 应用程序,假设您已经完成了前两本书的学习。
如何阅读本书
尽管我提供了一个包含本书中所有练习源代码的压缩文件,但您应该自己输入代码。
对于每个练习,都要自己手动输入代码。否则你怎么学呢?我的以前的学生从来没有通过仅仅阅读他人的代码就成为了优秀的程序员。
完成学习演练,如果有的话。完成挑战任务,如果有的话。到最后,您将能够编写一些代码。
练习 0:设置
这个练习没有代码,但不要跳过它。这将帮助您安装一个体面的文本编辑器并安装 Java 开发工具包(JDK)。如果您不做这两件事,您将无法完成本书中的任何其他练习。您应该尽可能准确地遵循这些说明。
警告!
这个练习需要您在终端窗口(也称为“shell”,“控制台”或“命令提示符”)中执行操作。如果您没有终端窗口的经验,那么您可能需要先学习一下。
Zed Shaw 的出色的命令行快速入门课程在 http://cli.learncodethehardway.org/book/,将教您如何在 Windows 上使用 PowerShell 或在 OS X 上使用终端或在 Linux 上使用“bash”。
Mac OS X
要完成这个练习,完成以下任务:
- 转到 http://www.barebones.com/products/textwrangler/,使用您的网络浏览器。下载 TextWrangler 文本编辑器并安装它。
- 将 TextWrangler 放在您的 Dock 中,以便您可以轻松访问它。
- 找到一个名为“终端”的程序。(如果需要,进行搜索。)
- 也将终端放在您的 Dock 中。
- 启动终端。
- 在终端程序中,键入
javac version
并按RETURN
。您应该会看到一个类似javac 1.7.0_04
的响应。如果javac
后面的数字不完全相同,只要是 1.6 或更高都可以。但如果出现错误消息,您可能需要安装 JDK。 - 完成后,您应该回到提示符。
- 学习如何从终端创建一个文件夹(创建一个目录)。创建一个目录,以便您可以将本书中的所有代码放入其中。
- 学习如何从终端切换到这个新目录。切换到它。
- 使用文本编辑器(TextWrangler)创建一个名为
test.txt
的文件,并将其保存到您刚刚创建的目录中。 - 只使用键盘切换窗口返回到终端。
- 回到终端,查看是否可以列出目录的内容以查看您新创建的文件。
OS X:您应该看到的内容
我目前无法访问 Mac,所以这是 Zed 在他的计算机终端上按照上述步骤操作的情况。您的计算机可能会有所不同,因此看看您能否找出他所做的事情和您应该做的事情之间的所有差异。
代码语言:javascript复制Last login: Fri Jul 19 00:56:54 on ttys001
~ $ javac version
javac 1.6.22
~ $ mkdir javacode
~ $ cd javacode
javacode $ ls
# ... Use TextWrangler here to edit test.txt....
javacode $ ls
test.txt
javacode $
Windows
- 转到 http://notepadplusplus.org/,使用您的网络浏览器,获取 Notepad 文本编辑器,并安装它。您不需要是管理员才能这样做。
- 确保您可以轻松访问 Notepad ,将其放在桌面和/或快速启动栏上。这两个选项都可以在安装过程中选择。
- 从开始菜单运行 PowerShell。搜索它,然后按 Enter 运行。
- 在桌面和/或快速启动栏上创建 PowerShell 的快捷方式,以方便使用。
- 转到 http://www.oracle.com/technetwork/java/javase/downloads/,使用您的网络浏览器。
- 点击左上角的“Java”按钮,下载 Java 平台(JDK)7u25. 点击后会跳转到另一个页面。
- 在此页面上,您需要接受许可协议,然后选择列表底部的“Windows x86”版本。下载文件。
- 下载完成后,运行
jdk7u25windowsi586.exe
进行安装。点击“下一步>”后,您将首次看到一个屏幕,上面写着安装到:C:Program
Files (x86)Javajdk1.7.0_25
或类似的内容。记下这个位置,您很快就会需要它。
- 安装 JDK 后,您需要找出安装位置的确切名称。在
C:
驱动器内查看Program Files
文件夹或C:Program
Files (x86)
文件夹(如果有的话)。您要找的是一个名为Java
的文件夹。里面有一个名为jdk1.7.0_25
的文件夹,里面有一个名为bin
的文件夹。文件夹名称必须包含jdk1.7
;jre7
不一样。确保有一个bin
文件夹。 - 进入此文件夹后,您可以在文件夹位置左键单击,它将变成类似
C:Program Files (x86)Javajdk1.7.0_25bin
的内容。您可以记下这个内容,或者将其高亮显示并右键单击复制到剪贴板。 - 安装 JDK 并知道其位置后,打开您的终端窗口(PowerShell)。在 PowerShell 中,键入以下内容:
[Environment]::SetEnvironmentVariable("Path",
"$env:Path;C:Program Files (x86)Javajdk1.7.0_25bin", "User")
将所有内容放在一行上。
如果您将文件夹位置复制到剪贴板,那么您可以键入$env:Path;
之前的所有内容,然后在 PowerShell 窗口中右键单击,它应该会为您粘贴文件夹名称。然后您只需完成这一行,输入", "User")
并按ENTER
。如果出现错误,您输入了错误的内容。您可以按上箭头将其取回,使用左右箭头找到并纠正错误,然后再次按ENTER
。
- 一旦
setEnvironmentVariable
命令完成而没有给出错误,通过在提示符处键入exit
关闭 PowerShell 窗口。如果你不关闭它,你刚刚做的更改就不会生效。 - 再次启动 PowerShell。
- 在提示符处键入
javac version
。你应该会看到一个类似javac 1.7.0_25
的响应。恭喜!如果你成功了,这本书的其余部分应该相对容易。 - 之后,你应该回到一个闪烁的 PowerShell 提示符。
- 学习如何从终端窗口(PowerShell)创建一个文件夹(创建一个目录)。创建一个目录,这样你就可以把这本书中的所有代码放进去。
- 学习如何从提示符中切换到这个新目录。切换到它。
- 使用你的文本编辑器(Notepad )创建一个名为
test.txt
的文件,并将其保存到目录中 你刚刚创建的。 - 只使用键盘切换窗口回到终端。
- 回到终端,看看你是否可以列出目录的内容,以查看你新创建的文件。
Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.
PS C:UsersGraham_Mitchell> javac version
javac 1.7.0_25
PS C:UsersGraham_Mitchell> mkdir javacode
Directory: C:UsersGraham_Mitchelljavacode
Mode LastWriteTime Length Name
d7/
19/2013 7:39 PM javacode
PS C:UsersGraham_Mitchell> cd javacode
PS C:UsersGraham_Mitchelljavacode> ls
PS C:UsersGraham_Mitchelljavacode>
... Here you would use Notepad to make test.txt in javacode ...
PS C:UsersGraham_Mitchelljavacode> ls
Directory: C:UsersGraham_Mitchelljavacode
Mode LastWriteTime Length Name
a7/
19/2013 7:45 PM 4 test.txt
PS C:UsersGraham_Mitchelljavacode>
你可能会看到不同的提示和其他细微的差异,但你不应该会得到任何错误,这是一般的想法。
Linux
Linux 有很多不同的版本,所以我将为最新版本的 Ubuntu 提供说明。如果你使用其他系统,你可能知道如何修改这些说明以适应你的设置。
- 使用你的 Linux 软件包管理器安装
gedit
文本编辑器(可能只是称为“文本编辑器”)。 - 确保你可以通过将其放在启动器中轻松地找到 gedit。
- 运行 gedit,这样我们就可以更改一些默认设置,使其更适合程序员:
- 打开首选项并选择编辑器选项卡。
- 将选项卡宽度更改为 4。
- 在“自动缩进”旁边打上勾
- 打开查看选项卡,打开“显示行号”。
- 找到你的终端程序。它可能被称为 GNOME 终端、Konsole 或 xterm。
- 也把你的终端放到启动器中。
- 使用你的 Linux 软件包管理器安装 Java JDK。我使用
openjdk7jdk
,但如果你更喜欢 Oracle 的,也可以。 - 如果你还没有启动终端,启动你的终端。
- 在提示符处键入
javac version
。你应该会看到一个类似javac 1.7.0_25
的响应。如果没有,确保 JDK 已安装,并且包含可执行文件javac
的bin
目录在你的 PATH 中。 - 学习如何从终端创建一个文件夹(创建一个目录)。创建一个目录,这样你就可以把这本书中的所有代码放进去。
- 学习如何从提示符中切换到这个新目录。切换到它。
- 使用你的文本编辑器(gedit)创建一个名为
test.txt
的文件,并将其保存到你刚刚创建的目录中。 - 只使用键盘切换窗口回到终端。如果你不知道如何做,查一下。
- 回到终端,看看你是否可以列出目录的内容,以查看你新创建的文件。
Linux:你应该看到的内容
代码语言:javascript复制mitchell@grahamdesktop:~$ javac version javac 1.7.0_25
mitchell@grahamdesktop:~$ mkdir javacode mitchell@grahamdesktop:~$ cd javacode/ mitchell@grahamdesktop:~/javacode$ ls mitchell@grahamdesktop:~/javacode$
... Here you would use Notepad to make test.txt in javacode ... mitchell@grahamdesktop:~/javacode$ ls
test.txt mitchell@grahamdesktop:~/javacode$
你可能会看到不同的提示和其他细微的差异,但你不应该会得到任何错误,这是一般的想法。
初学者警告
你已经完成了第一个练习。这个练习可能对你来说很难,这取决于你对计算机的熟悉程度。如果很困难,你没有完成,回去花时间阅读和学习,然后再试一次。编程需要仔细阅读和注意细节。
如果一个程序员告诉你使用 vim 或 emacs 或 Eclipse,只需说“不”。这些编辑器是给你成为更好的程序员时使用的。你现在所需要的只是一个能让你把文本放入文件中的编辑器。我们将使用 gedit、TextWrangler 或 Notepad (从现在开始称为“文本编辑器”或“一个文本编辑器”),因为它简单,并且在所有计算机上都是一样的。专业程序员使用这些文本编辑器,所以对于你来说已经足够了。
程序员最终会告诉你使用 Mac OS X 或 Linux。如果程序员喜欢字体和排版,他会告诉你买一个 Mac OS X 电脑。如果他喜欢控制并且留着大胡子,他们会告诉你安装 Linux。再次强调,使用你现在拥有的能工作的计算机。你只需要一个编辑器、一个终端和 Java。
最后,这个设置的目的是让你在做练习时可以非常可靠地做三件事:
- 使用你的文本编辑器(Linux 上的 gedit,OSX 上的 TextWrangler,或 Windows 上的 Notepad )编写练习。
- 运行你写的练习。
- 当它们坏了就修好它们。
- 重复。
其他任何事情都只会让你困惑,所以坚持计划。
常见问题
我必须使用这个糟糕的文本编辑器吗?我想用 Eclipse!
不要使用 Eclipse。虽然它是一个不错的程序,但不适合初学者。它对初学者有两个坏处:
- 它让你做一些你现在不需要担心的事情。
- 它为你做了一些你需要先学会如何做的事情。
所以按照我的指示使用一个体面的文本编辑器和一个终端窗口。一旦你学会了编码,你可以使用其他工具,但现在不行。
我可以在我的平板电脑上完成这本书吗?还是我的 Chromebook?
很不幸。你不能在这两台机器上安装 Java 开发工具包(JDK)。你必须有某种传统的计算机。
练习 1:一个重要的消息
在这个练习中,你将编写一个在屏幕上显示重要消息的 Java 工作程序。
如果你不习惯为计算机输入详细的指令,那么这可能是本书中最难的练习之一。计算机非常愚蠢,如果你没有把每一个细节都搞对,计算机就不会理解你的指令。但是如果你能完成并使这个练习工作,那么你很有可能能够处理本书中的每一个练习,只要你每天都继续努力,并且不放弃。
打开你在练习 0 中安装的文本编辑器,并将以下文本输入到一个名为FirstProg.java
的单个文件中。确保与我写的完全匹配,包括间距、标点和大写。
1 public class FirstProg
2 {
3 public static void main( String[] args )
4 {
5 System.out.println( "I am determined to learn how to code." );
6 System.out.println( "Today's date is" );
7 }
8 }
我在每一行前面都加了行号,但不要输入行号。它们只是为了我能谈论这些行。另外,取决于你是否已经保存了文件,不同的单词可能根本没有颜色。或者如果它们有颜色,它们可能与我的颜色不同。这些差异都没关系。
我将逐行走过这个程序,只是为了确保你输入的每一个东西都是正确的。
第一行以单词public
开头,后面跟着一个空格,然后是单词class
,再后面是一个空格,然后是单词FirstProg
。 “First”中的‘F’是大写的,“Prog”中的‘P’是大写的。第一行只有两个大写字母。只有两个空格。
第二行只是一个单字符:“大括号”。你可以通过按住SHIFT
键然后按下通常在字母‘P’右边的‘[’键来显示它。
在我继续程序的第三行之前,我应该告诉你程序员通常称呼这个程序中出现的每个有趣的符号。
(
和)
被称为“括号”(复数)。其中一个被称为“括号”,但有些人只是称它们为 parens(“puhRENZ”)。这个(“(
”)有时被称为“左括号”,另一个(“)
”)被称为“右括号”,因为括号通常成对出现,一个通常在另一个的左边。左括号(“(
”)也经常被称为“开括号”,右括号被称为“闭括号”,原因类似。
第 3 行有一个开括号和一个闭括号,整个文件中没有其他括号。
【】和【】被称为“括号”,但许多程序员称它们为“方括号”,以确保不会引起混淆。在 Java 中,括号和方括号不能互换使用。括号成对出现,它们被称为“左括号”或“开括号”和“右括号”或“闭括号”。
第 3 行有一个紧挨着的开括号和闭括号。
{
和}
被称为“大括号”,有些程序员称它们为“花括号”。这
也总是成对出现的左大括号和右大括号/开大括号和闭大括号。
"
被称为“引号”,通常简称为“引号”。在 Java 中,它们总是成对出现。一对中的第一个通常被称为“开引号”,第二个是“闭引号”,尽管在两个位置上它是完全相同的字符。但第一个引号用于开始某事,第二个引号结束了那件事。
'
在技术上是“撇号”,但几乎所有程序员都称它们为“单引号”。因此,引号通常被称为“双引号”。在一些编程语言中,单引号和双引号是可以互换的,但在 Java 中不行。Java 有时会使用单引号,但在这本书中它们会相当少见。
.
在技术上是“句号”,但几乎所有程序员都说“点”。它们在编程语言中经常被使用,并且通常被用作分隔符而不是“结束符”,所以我们不称它们为句号。
这个程序中有四个句点和一个句号。
;
被称为“分号”。它位于键盘上字母’L’和引号之间。Java 中使用了很多分号,尽管在这个程序中只有两个:一个在第 5 行的末尾,另一个在第 6 行的末尾。
:
被称为“冒号”。通过按住 SHIFT 键并输入分号来获得它。Java 确实使用冒号,但它们非常少见。
最后,<
是“小于号”,>
是“大于号”,但有时它们被用作大括号或方括号。当它们以这种方式使用时,它们通常被称为“尖括号”。Java 使用尖括号,但你在这本书中看不到它们被使用。
好的,回到逐行。你已经正确地输入了前两行。
你应该按一次TAB
键开始第三行。你的光标会移动几个空格(可能是 4 或 8)。然后再次输入单词public
,一个空格,单词static
,一个空格,单词void
,一个空格,单词main
后面跟一个开括号(main
和括号之间没有空格)。括号后有一个空格,单词String
(大写’S’),一个紧挨着的开括号和闭括号,一个空格,单词args
,一个空格,最后是一个闭括号。
因此,第三行以一个制表符开始,总共有六个空格,只有“String”中的‘S’是大写的。哇。
在第四行,你的文本编辑器可能已经将光标直接放在“public”中的‘p’下面。如果没有这样做,那么你将不得不自己按 TAB 键开始第 4 行。然后只需输入另一个开大括号,就这样。
第五行应该以两个制表符开始。然后输入单词System
(大写’S’),然后是一个句点(句号),然后是单词out
,另一个句点,单词println
(发音为“PrintLine”,尽管末尾没有‘i’或‘e’),一个开括号,一个空格,一个引号(开引号),句子I
am determined to learn how to code.
(句子以句号结束),然后是一个闭引号,一个空格,一个闭括号和一个分号。
因此,第 5 行有两个制表符,九个空格,两个句点(和一个句号),一个开引号和闭引号,一个开括号和闭括号,只有两个大写字母。
第 6 行几乎与第 5 行相同,只是句子说“今天的日期是”,而不是决定性的句子。
第 7 行只以一个制表符开始。如果你的文本编辑器为你放了两个制表符,你应该能够通过按一次 BACKSPACE 键来去掉多余的制表符。然后在制表符之后有一个右花括号。
最后,第 8 行没有制表符,还有一个右花括号。你可以在第 8 行按 ENTER 键,也可以不按:Java 不在乎。
注意,文件中有两个左花括号和两个右花括号。三个左括号和三个右括号。两个“开引号”和两个“闭引号”。一个左方括号和一个右方括号。这总是正确的。
还要注意,每次我们输入一个左花括号时,下面的行会有更多的制表符,而在右花括号下面的行则会有更少的制表符。
好了,现在保存这个(如果你还没有)为FirstProg.java
,并将其保存在你在练习 0 中创建的“code”文件夹中。
确保文件名与我的完全匹配: “First”中的‘F’是大写的,“Prog”中的‘P’是大写的,其他所有字母都是小写的。文件名中不应该有空格。Java 将拒绝运行任何文件名中带有空格的程序。还要确保文件名以.java
结尾,而不是.txt
。
编译你的第一个程序
现在程序已经编写好了,希望没有错误(我们很快就会看到),启动你的终端(或 PowerShell)并切换到保存代码的目录。
进行目录列表以确保 Java 文件在那里。在我的电脑上,它看起来像这样:
代码语言:javascript复制mitchell@grahamdesktop:~$ cd javacode/
mitchell@grahamdesktop:~/javacode$ ls
FirstProg.java test.txt
mitchell@grahamdesktop:~/javacode$
将来,因为你的终端可能看起来不像我的,我会这样缩写提示:
代码语言:javascript复制$ ls
FirstProg.java test.txt
$
这样会更少混乱,因为要忽略的“错误”会更少,你只需要看看你应该输入什么和你应该看到什么。
现在,我们已经在一个叫做 Java 的编程语言中输入了一系列命令。但是计算机不能直接执行我们的命令。我们必须把这个文件交给一个“编译器”,这是一个将我们的指令翻译成更接近计算机可以执行的一些东西的程序。在 Java 中,这些东西被称为“字节码”。所以我们要运行 Java 编译器程序来“编译”我们的 Java 源代码成一个字节码文件,这样我们就能够执行它了。
Java 编译器的名字是javac
(‘c’代表“编译器”),我们这样运行它:
$ javac FirstProg.java
$
如果你对细节非常关注,并且做了我告诉你的一切,这个命令将花费一秒钟的时间运行,然后提示符将会弹出,没有消息。如果你犯了某种错误,你会看到这样的错误:
代码语言:javascript复制$ javac FirstProg.java
FirstProg.java:8: error: reached end of file while parsing
}
^
1 error
$
不要太担心特定的错误消息。当它感到困惑时,编译器会试图猜测你可能犯了什么错误。不幸的是,这些猜测是为专业程序员设计的,所以它通常对初学者类型的错误猜测不准确。
这是你可能会得到的另一种错误消息的例子:
代码语言:javascript复制$ javac FirstProg.java
FirstProg.java:5: error: ';' expected
System.out.println( "I am determined to learn how to code." ):
^
1 error
$
在这种情况下,编译器实际上是正确的:错误出现在第 5 行,具体错误是期望有一个分号(';' expected
)。(该行以冒号(:
)结尾,但应该是分号(;
)。
这里还有一个:
代码语言:javascript复制$ javac FirstProg.java
FirstProg.java:1: error: class Firstprog is public, should be declared in a file named Firstprog.java
public class Firstprog
^
1 error
$
这次是一个大小写错误。 代码中写着public class Firstprog
(注意小写的‘p’),但文件名是FirstProg.java
。因为它们不完全匹配,包括大小写,编译器会感到困惑并退出。
所以如果你有任何错误消息,修复它们,然后保存你的代码,回到终端再次编译。
警告!
如果你在文本编辑器中对代码进行了更改,你必须在尝试重新编译之前保存文件。如果你不保存更改,你仍然会编译之前保存的旧版本的代码,即使你的文本编辑器中的代码是正确的。
最终,您应该做对了,它将编译而不会出现任何错误或任何消息。进行目录列表,您应该看到字节码文件出现在文件夹中:
代码语言:javascript复制$ javac FirstProg.java
$ ls
FirstProg.class FirstProg.java test.txt
$
现在我们有了一个有效的字节码文件,我们可以通过 Java 虚拟机(JVM)程序java
来运行它(或“执行”它):
你应该看到什么
代码语言:javascript复制$ java FirstProg
I am determined to learn how to code. Today's date is
$
请注意,您输入的命令是java FirstProg
,而不是java FirstProg.java
,甚至不是java
FirstProg.class
。
您激动吗?您刚刚编写了您的第一个 Java 程序并运行它!如果您走到了这一步,那么只要您每天都在上面努力工作并且不放弃,您几乎肯定有能力完成这本书。
学习演练
在大多数练习之后,我会列出一些额外的任务,您应该在键入代码并使其编译和运行后尝试。一些学习任务将非常简单,有些将更具挑战性,但您应该始终尝试。
- 更改第 6 行引号内的内容以包括今天的日期。在进行更改后保存文件,编译文件并再次运行它。
- 更改第 5 行引号内的内容,使计算机显示您的姓名。
完成学习演练后您应该看到的内容
代码语言:javascript复制$ java FirstProg
I, Graham Mitchell, am determined to learn how to code. Today's date is Friday, July 19, 2013.
$
练习 2:更多打印
好了,现在我们已经完成了第一个艰难的任务,我们将做另一个。好处是,在这个任务中,我们仍然有很多设置代码(几乎每次都是相同的),但是设置与“有用”代码的比例要好得多。
将以下文本键入单个文件中,文件名为LetterToYourself.java
。确保与我写的内容完全匹配,包括间距、标点和大写。
1 public class LetterToYourself
2 {
3 public static void main( String[] args )
4 {
5 System.out.println( " " );
6 System.out.println( "| #### |" );
7 System.out.println( "| #### |" );
8 System.out.println( "| #### |" );
9 System.out.println( "| |" );
10 System.out.println( "| |" );
11 System.out.println( "| Bill Gates |" );
12 System.out.println( "| 1 Microsoft Way |" );
13 System.out.println( "| Redmond, WA 98104 |" );
14 System.out.println( "| |" );
15 System.out.println( " " );
16 }
17 }
请注意,第一行与上一个任务相同,只是类的名称现在是LetterToYourself
而不是FirstProg
。还要注意,您要放置内容的文件名是LetterToYourself.java
而不是FirstProg.java
。这不是巧合。
在 Java 中,每个文件只能包含一个 public 类,而公共类的名称必须与文件名(包括大小写)匹配,除了文件名以.java
结尾,而公共类名不是。
那么,在 Java 中,“public class” 意味着什么?等您长大了我会告诉您。说真的,试图一开始就详细介绍这种类型的原因是大多数“初学者”编程书籍对于真正的初学者来说都很糟糕。所以不要担心。现在只需键入它。(不幸的是,这种情况会发生很多。)
您可能会注意到此程序的第二行、第三行和第四行与上一个任务完全相同。它们没有任何区别。
然后,在第二个左大括号之后,有十一个打印语句。它们都是完全相同的,除了引号之间的内容。小竖线(“|
”)称为“管道”字符,您可以使用 Shift 反斜杠(“”)键入它。假设您使用的是普通的美国键盘,反斜杠键位于退格键和回车键之间。
一切都键入并保存为LetterToYourself.java
后,您可以像之前的任务一样编译和运行它。切换到终端窗口,将目录更改为保存代码的目录,并键入以下内容进行编译:
$ javac LetterToYourself.java
如果你非常擅长烦人的细节并且幸运的话,你将不会有错误,javac
命令将在不说任何话的情况下完成。可能你会有错误。如果是这样,回去仔细比较你输入的内容和我写的内容。最终你会发现你的错误。修复它们,再次保存文件,然后尝试重新编译。
一旦它编译没有错误,你可以像以前一样运行它:
代码语言:javascript复制$ javac LetterToYourself.java
$ java LetterToYourself
| #### |
| #### |
| #### |
| |
| |
| Bill Gates |
| 1 Microsoft Way |
| Redmond, WA 98104 |
| |
两个程序完成了。不错!到目前为止你所取得的成就并不容易,任何认为它容易的人都有很多经验,并且已经忘记了第一次尝试这些东西是什么感觉。不要放弃!
每天多做一点,这样会变得更容易。
学习演练
- 这个文件叫
LetterToYourself.java
而不是LetterToBillGates.java
!回到你的文本编辑器,把名字和地址从比尔盖茨在微软的地址改成你自己的名字和地址。然后保存,编译,再次运行。
常见的学生问题
我必须使用我的真实地址吗?
当然不是。但是确保你的虚假地址占据三行。
为什么当我运行程序时我的信不对齐?!在代码中一切看起来都很完美!
你可能在你的 println()语句中使用了制表符和空格的混合。许多文本编辑器在你按下 TAB 键时只会将光标移动 4 个空格。但当你的程序运行时,引号内嵌的任何制表符将占用 8 个空格,而不是 4 个。如果你删除引号之间的所有制表符并用空格替换它们,你的代码和运行程序时的效果应该是一样的。
练习 3:打印选择
Java 有两个常用的命令用于在屏幕上显示东西。到目前为止,我们只看过
println()
,但print()
有时也会用到。这个练习将展示它们之间的区别。
1 public class PrintingChoices
2 {
3 public static void main( String[] args )
4 {
5 System.out.println( "Alpha" );
6 System.out.println( "Bravo" );
7
8 System.out.println( "Charlie" );
9 System.out.println( "Delta" );
10 System.out.println();
11
12 System.out.print( "Echo" );
13 System.out.print( "Foxtrot" );
14
15 System.out.println( "Golf" );
16 System.out.print( "Hotel" );
17 System.out.println();
18 System.out.println( "India" );
19
20 System.out.println();
21 System.out.println( "This" " " "is" " " "a" " test." );
22
23 }
24 }
将以下代码输入到一个文件中。通过阅读代码,你能猜到文件必须叫PrintingChoices.java
吗?在未来的作业中,我可能不会告诉你如何命名 Java 文件。
当你运行它时,你应该看到这个。
代码语言:javascript复制Alpha Bravo Charlie Delta
EchoFoxtrotGolf Hotel
India
This is a test.
你能找出区别吗?
print()
和println()
都会在屏幕上显示引号之间的任何东西。但println()
在完成打印后会移到新的一行,而print()
不会:它会显示然后将光标留在行的末尾,以便下一个打印语句从同一位置开始。
你还会注意到(第 10 行)我们可以有一个println()
语句,括号里什么都不写。没有引号或其他任何东西。这个语句指示计算机什么都不打印,然后将光标移动到下一行的开头。
你可能还会注意到这个程序有一些空行(第 7、11、14、19 和 22 行)。在第一个练习中,当我写道你必须“完全匹配我写的内容,包括间距、标点和大写”,我并没有完全诚实。你的代码中多余的空行
被 Java 编译器忽略。你可以把它们放进去或移除它们,程序将完全一样。
我的学生经常指责我“充满谎言”。这是真的。我已经通过艰苦的方式学到,当学生只是在学习编程这样困难的东西时,告诉他们真相会让他们太困惑。所以我经常过分简化我说的话,即使这使得它在技术上不准确。
如果你已经知道如何编程,而我的“谎言”冒犯了你,那么这本书将很难阅读。但对于那些刚开始学习的人,我向你保证,你希望我一开始简化事情。我保证最终会揭示真相。
无论如何,在第 21 行,我做了一件新的事情。到目前为止,你只在引号内打印了一个东西。但是完全可以打印多个东西,只要在打印之前将这些东西组合起来。
因此,在第 21 行,我有六个字符串用引号括起来:单词“this”,一个空格,单词“is”,一个空格,单词“a”,最后是一个空格,后面跟着“test”和一个句号。在这六个字符串之间有一个加号(“
”),因此在第 21 行总共有五个加号。当您在字符串之间放置加号时,Java 会将它们 2 加在一起,以形成一个长字符串,然后一次性显示所有内容。
如果您的代码出现错误,那么它可能在第 21 行。记住正确启动和停止所有引号并正确获取所有这些细节是棘手的。
今天的课程希望相对容易。别担心,我会在下一个课程中弥补的。
- “String”是什么?一对引号之间的一堆字符(字母,数字,符号)。我稍后会解释更多。
- 从技术上讲,将较小的单词组合成较大的单词称为“连接”,而不是“添加”。 Java 将字符串连接在一起。
练习 4:转义序列和注释
您是否考虑过如果我们想在屏幕上显示引号会发生什么?由于我们想要显示的所有内容都包含在println()
语句的引号之间,因此在引号内放置引号将是一个问题。
大多数编程语言都允许“转义序列”,其中您使用某种转义字符来表示下一个字符不应以正常方式处理。
以下(邪恶)代码演示了 Java 的许多转义序列。称之为EscapeSequences.java
。
1 public class EscapeSequences
2 {
3 public static void main( String[] args )
4 {
5 // Initial version created using FIGlet, font "Big Money", oriented southwest
6
7 System.out.print( "t _____nt / |nt JJJJJ |" );
8 System.out.println( " ______ __ __ ______" );
9 System.out.println( "t JJ | / \ / \ / |/ \" );
10 System.out.println( "t __ JJ | aaaaaa |"" \ /""/ aaaaaa |" );
11 System.out.println( "t/ | JJ | / aa | "" /""/ / aa |" );
12 System.out.println( "tJJ \__JJ |/aaaaaaa | "" ""/ /aaaaaaa |" );
13 System.out.println( "tJJ JJ/ aa aa | """/ aa aa |" );
14 System.out.println( "t JJJJJJ/ aaaaaaa/ "/ aaaaaaa/" );
15 }
16 }
当您运行它时,您应该看到的是这样的。
代码语言:javascript复制 _____
/ |
JJJJJ | ______ __ __ ______
JJ | / / / |/
__ JJ | aaaaaa |"" /""/ aaaaaa |
/ | JJ | / aa | "" /""/ / aa |
JJ __JJ |/aaaaaaa | "" ""/ /aaaaaaa |
JJ JJ/ aa aa | """/ aa aa |
JJJJJJ/ aaaaaaa/ "/ aaaaaaa/
Java 的转义字符是反斜杠(),这与您按下以使管道(
|
)显示出来的相同键,但不需要按住 Shift。 Java 中的所有转义序列都必须在一组引号内。
"
代表引号。
t
是一个制表符;这就像您在键入代码时按 Tab 键一样。现在它可能看起来更复杂,因为您以前从未见过它,但是当您阅读别人的代码时
引号内的t
不如一堆可能是空格或制表符的空格不明确。
n
是一个换行符。在打印时,它将导致输出移到下一行的开头,然后继续打印。
\
是显示反斜杠的方法。
在第 5 行,您会注意到该行以两个斜杠(或“正斜杠”,如果您坚持的话)开头。这标记了该行为“注释”,这是程序员的人为利益。计算机完全忽略注释。
实际上,用两个斜杠标记注释的行不一定要在行的开头;我们可以写这样的东西:
代码语言:javascript复制System.out.println( "A" ); // prints an 'A' on the screen
…它完全有效。从两个斜杠到该行末尾的所有内容都将被编译器忽略。
(尽管这不是一个很好的评论;但是,任何了解 Java 的程序员都已经知道该行代码的作用。通常,您应该放置解释代码存在的原因的注释,而不是代码的作用。随着您在编码方面的技能提高,您将更擅长编写良好的注释。)
无论如何,这个练习很难,所以这次没有学习任务。下一个练习将介绍一些新内容,并恢复正常难度。
练习 5:在变量中保存信息
如果程序只能在屏幕上打印东西,那就太无聊了。我们希望我们的程序是互动的。
不幸的是,互动需要几个不同的概念共同工作,一次解释所有这些可能会令人困惑。所以我会一个一个地介绍它们。
首先是:变量!如果你上过代数课,你就会熟悉数学中的变量概念。编程语言也有变量,基本概念是一样的:
“变量是指向保存值的位置的名称。” Java 中的变量与数学变量有四个主要区别:
- 变量名可以超过一个字母长。
- 变量不仅可以保存数字,还可以保存单词。
- 当变量首次创建时,你必须选择变量将保存的值的类型。
- 变量的值(但不是它的类型)可以在程序中改变。变量
score
可能一开始的值是0
,但到程序结束时,score
可能保存的值是413500
。
好了,讨论够了。让我们开始写代码吧!我不会告诉你文件的名字应该是什么。你得自己弄清楚。
代码语言:javascript复制 1 public class CreatingVariables
2 {
3 public static void main( String[] args )
4 {
5 int x, y, age;
6 double seconds, e, checking;
7 String firstName, last_name, title;
8
9 x = 10;
10 y = 400;
11 age = 39;
12
13 seconds = 4.71;
14 e = 2.71828182845904523536;
15 checking = 1.89;
16
17 firstName = "Graham";
18 last_name = "Mitchell";
19 title = "Mr.";
20
21 System.out.println( "The variable x contains " x );
22 System.out.println( "The value " y " is stored in the variable y." );
23 System.out.println( "The experiment completed in " seconds " seconds." );
24 System.out.println( "My favorite irrational number is Euler's constant: " e );
25 System.out.println( "Hopefully your balance is more than $" checking "!" );
26 System.out.println( "My full name is " title " " firstName last_name );
27 }
28 }
你应该看到什么
代码语言:javascript复制The variable x contains 10
The value 400 is stored in the variable y. The experiment completed in 4.71 seconds.
My favorite irrational number is Euler's constant: 2.718281828459045 Hopefully your balance is more than $1.89!
My full name is Mr. GrahamMitchell
在第 5 到 7 行,我们声明了九个变量。前三个分别命名为x,y和age。这三个变量都是“整数”,这是一种可以保存±两十亿之间的值的变量类型。
一个整数变量可以保存值10
。它可以保存值8192
。一个整数变量可以保存123456789
。它不能保存值3.14
,因为这有小数部分。一个整数变量也不能保存值10000000000
,因为一百亿太大了。
在第 6 行,我们声明了变量seconds,e和checking。这三个变量都是“double”,这是一种可以保存可能有小数部分的数字的变量类型。
一个双精度变量可以保存值4.71
。它可以保存值8192
。(它可能有小数部分,但不一定有。)它几乎可以保存± 1.79769 × 10308 和 4.94065 × 10324 之间的任何值。
然而,双精度有限的精度。请注意,在第 14 行,我将值2.71828182845904523536
存储到名为 e 的变量中,但当我在第 24 行打印出该值时,只有2.718281828459045
出现。双精度没有足够的有效数字来精确保存值2.71828182845904523536
。整数具有完美的精度,但只能保存整数,不能保存巨大的值。
我们在这个练习中要看的最后一种变量类型是String
。在第 7 行,我们声明了三个 String 变量:firstName,last_name 和 title。String 变量可以保存单词和短语;名称缩写为“字符串”。
在第 9 到 11 行,我们初始化 4 三个整数值。值10
被存储到 x 中。在此之前,变量 x 存在,但其值未定义。400
被存储到 y 中,39
被存储到变量 age 中。
第 13 到 15 行给三个双精度变量赋初始值,第 17 到 19 行初始化了三个字符串变量。然后第 21 到 26 行在屏幕上显示了这些变量的值。请注意,变量名没有用引号括起来。
我知道对于这样的程序使用变量是没有意义的,但很快一切都会变得清晰起来。
- 声明-告诉程序变量的名称(或“标识符”)和类型。
- 初始化-给变量赋予其第一个(或“初始”)值。
练习 6:数学运算
现在我们知道如何在 Java 中声明和初始化变量,我们可以用这些变量进行一些数学运算。
代码语言:javascript复制 1 public class MathOperations
2 {
3 public static void main( String[] args )
4 {
5 int a, b, c, d, e, f, g;
6 double x, y, z;
7 String one, two, both;
8
9 a = 10;
10 b = 27;
11 System.out.println( "a is " a ", b is " b );
12
13 c = a b;
14 System.out.println( "a b is " c );
15 d = a b;
16 System.out.println( "ab is " d );
17 e = a b*3;
18 System.out.println( "a b*3 is " e );
19 f = b / 2;
20 System.out.println( "b/2 is " f );
21 g = b % 10;
22 System.out.println( "b is " g );
23
24 x = 1.1;
25 System.out.println( "nx is " x );
26 y = x*x;
27 System.out.println( "x*x is " y );
28 z = b / 2;
29 System.out.println( "b/2 is " z );
30 System.out.println();
31
32 one = "dog";
33 two = "house";
34 both = one two;
35 System.out.println( both );
36 }
37 }
你应该看到什么
代码语言:javascript复制a is 10, b is 27
a b is 37
ab is 17
a b*3 is 91
b/2 is 13
b is 7
x is 1.1
x*x is 1.2100000000000002
b/2 is 13.0
doghouse
加号(
)将两个整数或两个双精度数相加,或一个整数和一个双精度数(顺序不限)。对于两个字符串(就像在第 34 行),它将把这两个字符串连接在一起。
- “连接”-将字符字符串端对端连接。
减号(-
)将一个数字减去另一个数字。就像加法一样,它适用于两个整数、两个双精度数,或一个整数和一个双精度数(顺序不限)。
星号(*
)用于表示乘法。您还可以在第 17 行看到 Java 知道正确的运算顺序。b 乘以 3 得到81
,然后加上 a。
斜杠(/
)用于除法。请注意,当一个整数被另一个整数除(就像在第 19 行),结果也是一个整数而不是一个双精度数。
百分号(%
)用于表示“模数”,本质上是除法后剩下的余数。在第 21 行,b 被10
除,余数(7
)被存储到变量 g 中。
常见学生问题
- 为什么
1.1
乘以1.1
等于1.2100000000000002
而不是1.21
?为什么 0.333333 0.666666 等于 0.999999 而不是 1.0?有时候在数学中我们会得到重复的小数,大多数计算机在处理它们之前会将数字转换为二进制。结果是1.1
在二进制中是一个重复的小数。
记住我在上一个练习中说的:双精度的问题在于有限的精度。在本书中,你大多数时候可以忽略这个事实,但我希望你能记住双精度变量有时会给出略微不同于你期望的值。
练习 7:从人类那里获取输入
现在我们已经练习了一段时间创建变量,我们将看看交互式程序的另一部分:让运行我们程序的人有机会输入一些内容。
继续输入这个,但请注意程序中的第一行不是public class
行。这次我们从一个“导入”语句开始。
并非每个程序都需要从键盘获取人类的交互输入,因此这不是 Java 语言核心的一部分。就像一辆 F1 赛车不包括空调一样,编程语言通常有一个小的核心,然后有很多可选的库 6,如果需要可以包含进来。
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class ForgetfulMachine
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8
9 System.out.println( "What city is the capital of France?" );
10 keyboard.next();
11
12 System.out.println( "What is 6 multiplied by 7?" );
13 keyboard.nextInt();
14
15 System.out.println( "What is your favorite number between 0.0 and 1.0?" );
16 keyboard.nextDouble();
17
18 System.out.println( "Is there anything else you would like to tell me?" );
19 keyboard.next();
20 }
21 }
当你第一次运行这个程序时,它只会打印第一行:
代码语言:javascript复制What city is the capital of France?
然后它会在屏幕上闪烁光标,等待你输入一个单词。当我运行程序时,我输入了单词“巴黎”,但即使你输入一个不同的单词,程序也会正常工作。
然后在你输入一个单词并按回车后,程序将继续打印:
代码语言:javascript复制What is 6 multiplied by 7?
……等等。假设你对每个问题都输入了合理的答案,最终看起来会像这样:
你应该看到什么
代码语言:javascript复制What city is the capital of France? Paris
What is 6 multiplied by 7?
42
What is your favorite number between 0.0 and 1.0? 2.3
Is there anything else you would like to tell me? No, there is not.
- 库或“模块”——添加额外功能到程序中的一段代码,可能包含或不包含。
让我们来谈谈代码。在第 1 行,我们有一个import
语句。我们导入的库是 scanner 库java.util.Scanner
(“java 点 util 点 Scanner”)。这个库包含的功能允许我们从键盘或其他地方(如文件或互联网)读取信息。
第 2 行到第 7 行希望是无聊的。在第 8 行,我们看到了另一件新事物:我们创建了一个名为“keyboard”的“Scanner 对象”。(它不一定要被命名为“keyboard”;你可以在这里使用一个不同的词,只要你在你的代码中到处使用它。)这个名为 keyboard 的 Scanner 对象包含我们将称之为函数或“方法”的能力。在你使用之前,你必须创建并命名一个 Scanner 对象。
在第 10 行,我们要求名为 keyboard 的 Scanner 对象为我们做一些事情。我们说“键盘,运行你的next()
函数。”Scanner 对象将暂停程序,等待人类输入。一旦人类输入内容并按回车,Scanner 对象将把它打包成一个字符串,并允许你的代码继续。
在第 13 行,我们要求 Scanner 对象执行其nextInt()
函数。这会暂停程序,等待人类输入并按回车,然后将其打包成整数值(如果可能的话)并继续。
如果人类在这里没有输入整数会怎么样?再次运行程序,然后在第二个问题的答案中输入41.9
。
该程序会因为41.9
无法被打包成整数值而爆炸:41.9
是一个double
。最终,我们将研究如何处理类似问题的错误检查,但与此同时,如果人类输入了错误的内容导致程序崩溃,我们会责怪人类没有遵循指示,而不会担心这个问题。
第 16 行让人类输入一个 Scanner 对象将尝试转换为 double 值的内容,第 19 行让人类输入一个字符串。(任何东西都可以被打包为字符串,包括数字,所以这不太可能失败。)
尝试多次运行程序,注意何时会崩溃,何时不会。
练习 8:存储人类的回答
在上一个练习中,你学会了如何暂停程序并允许人类输入一些东西。但是输入的内容发生了什么?当你为第一个问题输入“巴黎”时,答案去哪了?嗯,它在输入后立即被丢弃了,因为我们没有放置任何指令告诉 Scanner 对象在哪里存储它。所以这就是今天课程的主题。
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class RudeQuestions
4 {
5 public static void main( String[] args )
6 {
7 String name;
8 int age;
9 double weight, income;
10
11 Scanner keyboard = new Scanner(System.in);
12
13 System.out.print( "Hello. What is your name? " );
14 name = keyboard.next();
15
16 System.out.print( "Hi, " name "! How old are you? " );
17 age = keyboard.nextInt();
18
19 System.out.println( "So you're " age ", eh? That's not old at all." );
20 System.out.print( "How much do you weigh, " name "? " );
21 weight = keyboard.nextDouble();
22
23 System.out.print( weight "! Better keep that quiet. Finally, what's your income, "
name "? " );
24 income = keyboard.nextDouble();
25
26 System.out.println( "Hopefully that is " income " per hour and not per year!" );
27 System.out.println( "Well, thanks for answering my rude questions, " name "." );
28 }
29 }
(抱歉,第 23 行像那样换行了。我不想因为那一行而使字体变得微小。就像平常一样,把它都写在一行上。)
就像上一个练习一样,当你第一次运行这个程序时,它只会显示第一个问题,然后暂停,等待回答。
代码语言:javascript复制Hello. What is your name?
请注意,因为第 13 行的第一个打印语句是print()
而不是println()
,光标会留在问题所在行的末尾闪烁。如果你使用了println()
,光标会在下一行的开头闪烁。
你应该看到什么
代码语言:javascript复制Hello. What is your name? Brick Hi, Brick! How old are you? 25
So you're 25, eh? That's not old at all. How much do you weigh, Brick? 192
192.0! Better keep that quiet. Finally, what's your income, Brick? 8.75 Hopefully that is 8.75 per hour and not per year!
Well, thanks for answering my rude questions, Brick.
在程序的顶部,我们声明了四个变量:一个名为name的字符串变量,一个名为age的整数变量,以及两个名为weight和income的双精度变量。
在第 14 行,我们看到了keyboard.next()
,我们知道它来自上一个练习,它会暂停程序并让人类输入一些东西,然后将其打包成一个字符串。那么他们输入的字符串去哪了呢?在这种情况下,我们将该值存储到名为“name”的字符串变量中。字符串值被存储到了一个字符串变量中。不错。
所以,假设你在第 14 行为你的名字输入了Brick
,字符串值"Brick"
就会被存储到第 14 行的变量名中。这意味着在第 16 行,我们可以在屏幕上显示该值!如果你问我,这相当酷。
在第 17 行,我们要求 Scanner 对象让人类输入一些东西,它将尝试将其格式化为整数,然后该值将被存储到名为age的整数变量中。我们在第 19 行将该值显示在屏幕上。
第 21 行读取一个双精度值并将其存储到weight中,第 24 行读取另一个双精度值并将其存储到income中。
这是一件非常强大的事情。有了一些变量和 Scanner 对象的帮助,我们现在可以让人类输入信息,并且可以在程序中稍后使用变量来记住它!
在我结束之前,注意例如变量income在第 9 行上被声明(我们选择了它的名称和类型),但直到第 24 行之前它都是未定义的(它没有值)。在第 24 行income最终被初始化(给出了程序的第一个值)。如果你在第 24 行之前尝试打印income的值,程序将无法编译。
无论如何,尝试输入不同的答案来回答问题,并看看你是否能在每个问题后让程序崩溃。
练习 9:使用用户输入进行计算
既然我们知道如何从用户那里获取输入并将其存储到变量中,而且我们也知道如何进行一些基本的数学运算,我们现在可以编写我们的第一个有用的程序了!
代码语言:javascript复制 1 import java.util.Scanner;
2
3 public class BMICalculator
4 {
5 public static void main( String[] args )
6 {
7 Scanner keyboard = new Scanner(System.in);
8 double m, kg, bmi;
9
10 System.out.print( "Your height in m: " );
11 m = keyboard.nextDouble();
12
13 System.out.print( "Your weight in kg: " );
14 kg = keyboard.nextDouble();
15
16 bmi = kg / (m*m);
17
18 System.out.println( "Your BMI is " bmi );
19 }
20 }
你应该看到什么
代码语言:javascript复制Your height in m: 1.75 Your weight in kg: 73
Your BMI is 23.836734693877553
这个练习(希望)相当简单。我们有三个变量(都是双精度):m(米)、kg(千克)和bmi(身体质量指数)。我们读取m和kg的值,但bmi的值不是来自人类,而是计算的结果。在第 16 行,我们计算质量除以身高的平方,并将结果存储到bmi中。然后我们将其打印出来。
身体质量指数(BMI)通常被健康和营养专业人员用来估计人群的体脂肪。因此,这个结果对健康专业人员来说是有信息价值的。目前我们只能做到这些。
最终,我们将学会如何根据 BMI 的值在屏幕上显示不同的消息,但目前这就够了。
今天是一个相当简单的任务,但我在学习挑战中为你准备了一些挑战,应该会让事情变得更加困难。
学习挑战
添加一些变量并更改程序,以便人类可以使用磅和英寸输入他们的体重和身高,然后将这些值转换为千克和米,以计算 BMI。
代码语言:javascript复制Your height in inches: 69 Your weight in pounds: 160 Your BMI is 23.625289
使人类可以分别输入他们的身高,一个是英尺,一个是英寸。
代码语言:javascript复制Your height (feet only): 5 Your height (inches): 9 Your weight in pounds: 160 Your BMI is 23.625289
练习 10:变量只能保存值
好的,现在我们可以从人类那里获取输入并进行计算,我想要引起一些我的学生经常感到困惑的事情的注意。以下代码应该可以编译,但它可能不会按照你的期望工作。
我故意在代码中制造了一个逻辑错误。这不是语法问题(编译器关心的代码部分),也不是运行时错误,比如当人类在期望整数时,Scanner 对象接收到了一个双精度数。这个逻辑错误是我设计指令流程的缺陷,导致输出不是我想要实现的。
代码语言:javascript复制 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 }
你应该看到什么
代码语言:javascript复制How much is the purchase price? 7.99 Item price: 7.99
Sales tax: 0.0
Total cost: 0.0
你对输出感到惊讶吗?你是否期望7.99 的销售税显示为0.66 而不是一个大大的零?总成本应该是
发生的是,在 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
。
我们可以这样做:
代码语言:javascript复制int x = 10, temp_x;
temp_x = x 2;
x = temp_x;
这样做是可以的,但很烦人。如果我们愿意,我们可以利用一个变量可以在代码行的开头有一个值,并在结束时存储另一个值的事实。因此,我们可以写出这样的东西:
代码语言:javascript复制int x = 10;
x = 2 x;
这也可以。第二行表示“取 x 的当前值(10
),加上2
,并存储总和到变量 x。因此,当第二行代码开始执行时,x 为10
,执行完毕后,x 为 12。加法的顺序无关紧要,所以我们甚至可以这样做:
int x = 10;
x = x 2;
…这与前一个例子相同。好的,现在来看代码!
代码语言:javascript复制 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 }
你应该看到什么
代码语言:javascript复制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 }
你应该看到什么
代码语言:javascript复制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 时困扰的东西:常规的关系运算符不适用于字符串,只适用于数字。
代码语言:javascript复制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 不是初学者的语言吗?所以有一种比较字符串是否相等的方法,让我们来看看。
代码语言:javascript复制 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 }
你应该看到什么
代码语言:javascript复制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 岁以下并且要么富有要么长得很好看时,祖母才会同意你约会她的孙子。如果那位祖母是一名程序员,并且能说服申请者诚实回答,她的程序可能会像这样:
代码语言:javascript复制 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 }
你应该看到什么
代码语言:javascript复制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 语句做决定
嘿!我真的很喜欢这个练习。你在那里经历了一些相当无聊的练习,所以现在是时候学习一些有用而不是超级困难的东西了。
代码语言:javascript复制 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 }
我们将学习如何编写具有决策的代码,以便输出不总是相同的。执行的代码会根据人输入的内容而改变。
你应该看到什么
代码语言:javascript复制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 语句的更多练习,因为它们非常重要。这也会帮助你记住关系运算符。
代码语言:javascript复制 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 }
你应该看到什么
代码语言:javascript复制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
语句做很多事情。
但有时,有其他东西可能会使事情变得更加方便。比如这个例子:快!以下表达式的逻辑相反是什么?
代码语言:javascript复制if ( onGuestList || age >= 21 || ( gender.equals("F") && attractiveness >= 8 ) )
嗯?懂了吗?如果是的话
代码语言:javascript复制if ( ! ( onGuestList || age >= 21 || ( gender.equals("F") && attractiveness >= 8 ) ) )
…然后你是对的,你是我的菜。聪明并且知道什么时候让机器为你工作。如果你说
代码语言:javascript复制if ( ! onGuestList && age < 21 && ( ! gender.equals("F") || attractiveness < 8 ) )
…然后你是对的,干得好。这实际上是相当难以正确做到的。但是这个的逻辑相反又是什么呢:
代码语言:javascript复制if ( expensiveDatabaseThatTakes45SecondsToLookup( userName, password ) == true )
我们真的想要写
代码语言:javascript复制if ( expensiveDatabaseThatTakes45SecondsToLookup( userName, password ) == false )
代码语言:javascript复制 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
。(是的,抱歉。忍不住。)
你应该看到什么
代码语言:javascript复制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 语句
几个练习之前,你学会了比较字符串不像比较数字那么容易。所以让我们用一个你可以实际测试的例子来复习一下。
代码语言:javascript复制 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 }
你应该看到什么
代码语言:javascript复制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 }
你应该看到什么
代码语言:javascript复制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
构建条件链。
坦白说:尽管我确实参加了德克萨斯大学奥斯汀分校,但我认为这不是他们真正的录取标准。在决定是否申请备用学校时,不要依赖这个程序的输出。
代码语言:javascript复制 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 }
你应该看到什么
代码语言:javascript复制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
的代码,并确认它与非扩展版本的功能相同。