九、故障排除
在本章中,我们将介绍以下内容:
-
您最有可能遇到的错误
-
记录调试语句
-
使用调试器
大多数(如果不是全部的话)程序都会有错误——我这里说的是不平凡的程序,不是“Hello World”那种。作为一名开发人员,处理错误将是你职业的一大部分。在这一章中,我们将讨论你最有可能遇到的错误,以及如何使用 IntelliJ 来处理这些错误。
错误类型
通常,您会遇到以下几种错误:
-
句法误差
-
运行时错误
-
逻辑错误
句法误差
语法错误就是你所认为的语法错误。发生这种情况是因为你在代码中写了一些 Java 编译器的规则集不允许的东西。编译器不理解它。错误可能很简单,比如忘记关闭括号或缺少一对花括号。它也可能很复杂,比如在使用泛型时,将错误类型的参数传递给函数或参数化类。
每当您在主编辑器上看到红色的曲线时,您可以轻松地用 IntelliJ 捕捉语法错误,如图 9-1 所示。
图 9-1
主编辑器显示一个语法错误指示器
当你看到这样一条弯弯曲曲的红线时,这意味着你有一个语法错误。IntelliJ 将红色曲线放在非常靠近违规代码的地方。如果您将鼠标悬停在红色曲线上,大多数情况下,IDE 可以非常准确地告诉您代码有什么问题,这样您就可以轻松地找到并修复这些代码。
除了语法错误之外,当您试图编译使用不同的 JDK(旧版本或完全不同的 JDK)构建的代码时,您还可能会遇到编译错误。
运行时错误
运行时错误发生在你的代码遇到了意想不到的情况时。顾名思义,这个错误只在程序运行时发生。这不是你在编译时会看到的。
Java 有两种类型的异常,选中的和未选中的。IntelliJ 在检查异常方面给了你很多帮助。图 9-2 显示了当你试图调用一个抛出检查异常的方法时,在主编辑器中会发生什么。
图 9-2
显示未处理异常信息的主编辑器
IntelliJ 试图通过标记未处理的异常来提供帮助,如图 9-2 所示。如果您将鼠标悬停在弯曲的红线上足够长的时间,IntelliJ 会显示代码被标记的原因,甚至会给出如何修复它的建议。
关于检查的和未检查的异常:检查的异常是 Java 编译器在编译期间检查(或检查)的东西。如果一个方法声明它抛出一个检查过的异常(例如,FileNotFoundException、ClassNotFoundException、IOException、SQLException 等)。),则该方法必须通过将调用包含在 try-catch 中或通过重新引发异常来处理异常。
另一方面,未检查的异常在编译时不会被检查,这意味着您不需要为 try-catches 费心;你可以像任何坏事都不会发生在你的应用上一样编码。在像 C++和 Kotlin 这样的语言中,所有的异常都是未检查的,所以您可以省去 try-catch 块——如果您愿意,您仍然可以处理异常,但这是可选的。
标记和提示未处理异常的解决方案已经很好了,但是 IntelliJ 还提供了快速修复。快速修复是 IntelliJ 对它在设计时检测到的错误的修复操作;这很容易执行——你只需要在突出显示的代码问题上按下 Alt + ENTER (对于 Linux 和 Windows 用户)或 cmd + ENTER (如果你在 macOS 上),然后选择一个适当的操作过程。图 9-3 显示了这一点。
图 9-3
快速修复我们未处理的异常
清单 9-1 显示了发送者类(带有自动修正的代码)。
class Sender extends Thread {
public void run() {
try {
inputStream = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
}
Listing 9-1Class Sender
IntelliJ 尽职尽责地将适当的 try-catch 结构添加到代码中——它甚至正确地格式化了代码。不过,它没有添加 finally 子句,您必须自己编写。
逻辑错误
逻辑错误是最难发现的。顾名思义,这是你逻辑上的错误。当你的代码没有做你认为它应该做的事情时,那就是逻辑错误。有许多方法可以应对它,但最受欢迎的是使用 System.out.println() 或 Logger 类(来自 java.util.logging)。
当你检查你的代码时,你会发现某些领域你对正在发生的事情非常确定,但是也有一些领域你不太确定;在这些领域,println 或 Logger 最有用。就像留下面包屑让你跟着。
调试器
IntelliJ 是一个全功能的 IDE。它带有一个强大的调试器。虽然您可以使用 println 和 Logger 语句在您的代码中使用 sleuth,但是当您在使用 code-detective 时,使用调试器会得到更好的结果——它也更容易使用,因为它不会使您的代码混乱。
让我们考虑下面的代码(如清单 9-2 所示)来演示 IDEA 调试器。
package net.workingdev.ideabook;
import java.util.ArrayList;
import java.util.List;
public class DebugSample {
public static void main(String[] args) {
List sampleNames = createNames();
printNames(sampleNames);
addName(sampleNames, "Jane Doe");
printNames(sampleNames);
}
private static List createNames() {
ArrayList listNames = new ArrayList<String>();
listNames.add("John Doe");
listNames.add("Jane Doe");
return listNames;
}
private static void printNames(List<String> names) {
for (String name : names) {
System.out.println(name);
}
}
private static void addName(List<String> l, String name) {
l.add(name);
}
private static void removeNames(List l, String name) {
int position = l.indexOf(name);
if (position == -1) {
// the name is not in the list
}
else {
l.remove(position);
printNames(l);
}
}
}
Listing 9-2DebugSample Class
代码有五个方法(包括 main)。方法 createNames() 创建一个名字列表并返回它。方法 printNames() 接受一个姓名列表,并打印列表中的每一项。 addName() 方法接受一个列表和一个字符串;然后,它将字符串(第二个参数)添加到列表中。最后, removeName() 方法接受一个列表和一个字符串;它在列表中搜索第二个参数的出现;如果找到了,就删除该字符串,并再次打印列表。
您可以通过多种方式启动调试器:
图 9-5
从主方法的上下文菜单中运行调试
图 9-4
从底层调试
-
你可以点击装订线区域的运行图标(如图 9-4 所示),然后选择“调试”
-
可以使用类的 main 方法的上下文菜单,然后选择“调试”(如图 9-5 )。您可以通过左键单击 main 方法或使用 main 方法上的快速修复键来完成此操作——在 Windows/Linux 上,快速修复键是 Alt + ENTER ,在 macOS 上是 cmd + ENTER 。
-
或者,您可以从 IntelliJ 的主菜单栏启动调试器;转到运行➤调试。
现在,我们的代码愉快地浏览着它的逻辑;它创建一个名称列表,打印名称,添加名称,然后再次打印名称——您可以在 IntelliJ 的输出窗口中看到这一点(图 9-6 )。
图 9-6
示例代码的调试会话
可以说,当您想要查看(执行)情况时,调试器变得非常有用,我们可以通过几种方式来做到这一点。我们可以在代码中的指定位置暂停、恢复、重启或停止调试器。
当你的应用程序看起来没有反应时——这意味着它卡在了某个地方——你可以使用调试器来分析代码卡在了哪里。目前,我们的示例代码小而简单;这就是为什么它从头到尾都很轻松。让我们修改一下我们的 main 方法,让它模拟一个“卡住”的代码。清单 9-3 显示了修改后的 main 方法。
public static void main (String[] args) throws IOException {
List sampleNames = createNames();
printNames(sampleNames);
addName(sampleNames, "Jane Doe");
System.in.read();
printNames(sampleNames);
}
Listing 9-3Modified Main Method
对System.in.read()
的调用将停止程序执行;它在继续下一行之前等待用户输入。这对我们的目的来说足够好了。它模拟一个卡住的程序。现在,在调试模式下运行代码。
请注意,在输出窗口中,我们的流程没有完成(如图 9-7 所示)。卡住了。
图 9-7
DebugSample 在执行时停滞
现在点击调试器上的暂停按钮,如图 9-8 所示。
图 9-8
调试器的暂停按钮
一旦你暂停调试器,编辑器将向你显示它当前正在运行的类和方法——或者停留在那里(如图 9-9 所示)。在我们的例子中,它卡在了 System.in.read() 上;这就是为什么编辑器向我们展示了 FileInputStream 类的 readBytes() 方法——如果你切换到 Debug 窗口的调试器选项卡,你甚至可以看到堆栈跟踪(如图 9-10 所示)。
图 9-10
查看“调试器”选项卡上的堆栈跟踪
图 9-9
显示当前正在运行的调试器
现在,调试会话正在等待用户输入。按任意键,以便 System.in.read() 可以从标准输入中读取一些内容——这应该满足语句。
要继续调试会话,您可以按下恢复按钮(就在暂停按钮的正上方)。您需要的其他调试器按钮如下:
-
重启按钮–这将重启调试会话。你可以在扳手按钮的正上方找到这个按钮。
-
停止按钮–这将停止调试会话(在暂停按钮的正下方)。
分步操作
当调试会话暂停时,您可以使用调试器的各种步骤操作来“逐句通过您的代码”——步骤按钮如图 9-11 所示。
图 9-11
分步操作
-
步入(F7)——如果你想遍历每一行代码,使用这个。请记住,这将引导您遍历当前运行的类中当前运行的方法的每一行。如果该方法创建另一个对象并调用另一个方法,您将进入该另一个方法。
-
跳过(F8)——这允许你运行一行代码,然后继续下一行。
-
Force Step Into–这允许您调试 API 或库中定义的方法。如果 API 或库的源代码不可用,IntelliJ IDEA 会为您进行反编译和调试。
-
单步执行–这让您可以跳过逐行执行代码,返回到调用方法。被调用的方法会执行,但不会单步执行每一行代码。
-
Drop Frame–这允许您通过删除方法调用在代码执行过程中向后移动。
断点
断点是在代码中引入停止点的好方法。还记得在前面的调试示例会话中,我引入了 System.in.read() 来暂停代码吗?我们并不真的必须这样做;我们可以使用断点在特定的点暂停程序的执行。
要在一行代码上设置断点,请单击装订线区域(如图 9-12 所示)。
图 9-12
断点设置在第 12 行
我在第 12 行设置了断点。当您看到行号旁边的空白处有一个红点时,您就知道断点设置好了。
当您启动调试会话时,调试器将自动在断点处停止执行(如图 9-13 ),您无需再按暂停。
图 9-13
程序在断点处暂停
从这里开始,您可以使用我们之前讨论过的各种步进工具。
关键要点
-
您可能遇到的三种错误是编译类型或语法错误、运行时错误和逻辑错误。
-
语法错误是最容易修复的。IntelliJ 本身为您竭尽全力,因此您可以快速发现语法错误。有各种方法可以修复语法错误,但是大多数时候,快速修复应该可以做到。
-
通过设置断点和使用各种单步执行操作,可以一行一行地遍历代码。
十、源代码管理
在本章中,我们将介绍以下内容:
-
Git 简介
-
Git 中的基本操作
-
如何设置本地 Git repo
-
在 GitHub 上发布您的项目
当您与其他开发人员一起工作时,您需要一种方式来与其他人共享您的代码,并共享他们的代码作为回报。即使你是一个人工作,你也需要一种方法来跟踪你对代码库所做的改变。这些是您需要使用源代码控制的主要原因。
IntelliJ 可以与各种源代码控制系统一起工作,例如 Git、CVS、SVN、Mercurial 和 TFS。Git 是一个广泛使用的版本控制系统,这也是我们将在本章讨论的内容。
饭桶
Git 是一个免费的开源版本控制系统,最初由 Linus Torvalds 在 2005 年创建——Linus 负责 Linux 操作系统。Git 是一个分布式系统,不像 CVS 和 SVN 是集中式的。有了 Git,每个开发人员在本地都有他们代码 repo(repository 的缩写)的完整历史——repo 就是你所说的在版本控制下的文件夹。
Git 的分布式特性使得 repo 的初始克隆(复制)操作很慢——因为所有内容都被复制了——但后续操作(如提交、合并、比较和记录)要快得多。
我们不会深入 Git,但它通常是这样工作的:
-
您将创建一个项目文件夹,然后放置您的初始项目工件,例如,一个自述文件、几个源文件等。
-
初始化 Git 的文件夹。
-
将文件夹中的文件添加到源代码管理中;此时,您已经在源代码管理中进行了第一次更改——添加了一些文件。
-
提交您的更改。
-
在某个时候,您将编辑项目中的一些文件,甚至可能添加一些新文件。
-
将新文件添加到 Git,然后再次提交更改。
使用 Git 可以做更多的操作,比如分支、合并、责备、隐藏、拉取、克隆等等。,但我们现在保持简单。
在 IntelliJ 中使用 Git 之前,您需要先安装它。为了确定你是否已经有了 Git,获取一个终端窗口(或者 Windows 中的 cmd)并运行 Git 命令,如图 10-1 所示。
如果您不带任何参数或选项运行 Git 命令,它将打印一些使用信息,如图 10-1 所示。
图 10-1
饭桶
要找出您拥有的 Git 版本,您可以在运行 Git 命令时传递 version 参数,如图 10-2 所示。
图 10-2
Git 版本
如果您在运行 Git 后没有看到使用信息,而是看到类似“命令未找到”或“错误的命令或文件名”的消息,这意味着 Git 还没有安装。您需要先安装它,然后才能与 IntelliJ 一起使用。
可以从 https://git-scm.com
获得 Git 您会找到适合您平台的版本。git-SCM 网站上也有关于如何安装 git 的非常好的文档和说明,所以我们在这里不做介绍。
图 10-3 显示了撰写本文时 git-scm.com的网站。下载安装程序的链接位于页面的右下方。
图 10-3
git-scm.com 网站
将 Git 创建为本地存储库
安装 Git 后,您可以开始使用它来管理您的项目。
第一步是检查是否在 IntelliJ 中安装并启用了 Git 插件。如果您在 IntelliJ 的安装过程中接受了所有的默认设置,那么 Git、GitHub 和一些其他插件将会默认安装。无论如何,检查插件是否安装很容易——如果没有,只需在首选项(或设置)对话框中安装它。你可以通过 cmd +、 (macOS)或者CTRL+Alt+S(Linux/Windows)进入偏好设置对话框。
在首选项 ➤ 插件对话框中可以看到版本控制插件(如图 10-4 )。
图 10-4
Git 插件
打开要进行版本控制的项目;然后进入主菜单栏点击 VCS ,如图 10-5 所示。
图 10-5
启用版本控制
点击启用版本控制集成。IntelliJ 将要求您选择您想要使用的版本控制系统。图 10-6 显示弹出的对话框,您可以在其中选择版本控制系统。选择 Git。
图 10-6
启用版本控制集成
当您启用 Git 时,您会注意到 IDE 中的一些变化。首先,IntelliJ 会询问您是否想要将项目配置文件添加到 Git。对于您是否应该将项目配置文件添加到源代码控制中,并没有硬性规定,但是一般来说,我会建议您添加它们。JetBrains 对我们为什么要这么做有一个很好的解释。他们在 IntelliJ IDEA ( www.jetbrains.com/help/idea/2017.1/about-projects.html
)的在线帮助页面中进行了阐述。在谈到基于目录的格式时,JetBrains 说
-
那个。idea 目录包含一组配置文件(。xml)。每个文件仅包含属于某个功能区域的配置数据的一部分,这反映在文件名中,例如 compiler.xml、encodings.xml、modules.xml
-
几乎所有的文件都包含项目本身的核心信息,如组件模块的名称和位置、编译器设置等。因此,这些文件可以(也应该)在版本控制下保存。
如果您不使用 Gradle 或 Maven 之类的工具来管理依赖项,那么将项目配置文件置于源代码控制之下是一件好事。这样,每个人都可以获得项目及其依赖项的正确配置。但这也意味着每个人都必须按照您在配置文件中定义的方式来设置他们的环境。
另一方面,如果您将使用 Maven 或 Gradle 来管理依赖项,那么不要将项目配置文件添加到源代码控制中。配置文件中包含的所有信息都应该存储在 Maven/Gradle 文件中。然后,让每个人根据他们的环境配置他们的 IDE。
所以,现在,点击“总是添加”——你可以通过添加整个来覆盖这个设置。Git 忽略文件中的 idea 文件夹;我们稍后会谈到这一点。
图 10-7 显示 IntelliJ 提示我们将项目配置文件包含到源代码控制中。
图 10-7
添加项目配置文件
你可能注意到的 IDE 的另一个变化是在左下方的工具条中增加了一个新的部分(如图 10-8 所示)。
图 10-8
新的 Git 部分,添加到工具栏
工具栏上添加了一个 Git 按钮。
此时,我们已经将项目置于版本控制之下。我们已经创建了一个本地存储库(简称 repo ),用于存储我们的项目。
本地回购是一个受版本控制的文件夹,但它还没有任何上游回购——上游回购只是远程回购的另一个术语(就像 GitHub 或 Bitbucket 上托管的那些)。我们稍后会谈到这一点。
您还会注意到我们的 src 文件夹下的文件名(在项目工具窗口中)变成了红色。红色的文件名意味着它们还没有被添加到 Git 中。它们还没有升级。 iml 文件是绿色的,因为我之前添加了项目配置文件到 Git(当我点击提示添加配置文件到 Git 时,如图 10-7 )。
图 10-9 用红色显示主程序文件名。
图 10-9
主程序以红色亮起
有一些视觉上的暗示表明项目现在处于源代码控制之下,但是要真正确定这一点,我们可以看看项目的目录结构。您不会在项目结构中看到任何可见的变化,因为 Git 文件夹是隐藏的。要查看 Git 文件,你需要进入终端——在 macOS 中使用选项+ F12 快捷键,或者在 IntelliJ 中使用 Alt + F12 (对于 PC)弹出终端窗口。或者,只需单击 IDE 左下角工具栏上的“终端”按钮。
当终端打开时,您将自动位于项目的根文件夹中。发出一个 cd 命令去**。git** (点 git),然后发出命令列出目录的内容(如图 10-10 )。
图 10-10
的内容。git 文件夹
如你所见,里面有一堆文件和文件夹。
添加和提交更改
到目前为止,我添加到 Git 的唯一文件是我的项目配置文件。我还没有添加任何程序文件。我们可以通过几种方式做到这一点:
图 10-11
新建 Git 菜单项
-
我们可以进入主菜单栏,选择Git——这是一个新的菜单项。这发生在我们启用 Git 的时候(见图 10-11 )。
-
我们可以右击MainProgram.java(在项目工具窗口中),然后选择 Git ➤ 提交文件。
-
使用键盘快捷键“提交文件”,这是最快的方式。你应该试着“记住”这一点(双关语)。
在接下来的屏幕中,您将看到提交工具窗口(如图 10-12 所示)。
图 10-12
提交工具窗口
文件MainProgram.java仍然没有版本;要将其包含在变更列表中,点击文件名右边的勾选框(如图 10-13 )。
图 10-13
将文件提交给 Git
在提交工具窗口中,您将有机会包含或排除您希望包含在更改列表中的文件。勾选复选框,将该文件包括在提交中;勾掉,就不算了。就这么简单。
我添加了一行注释——“添加了主程序”——作为提交消息,然后单击提交按钮。现在,我的所有文件都已版本化,所有更改都已提交到本地存储库中。每当您在项目中添加和/或更改某些内容时,您都需要这样做。
分支
新创建的 Git repo 将有一个名为“master”的分支。要查看您的回购分支,请进入主菜单栏,然后选择 Git ➤ Branches 。分支对话框如图 10-14 所示。
图 10-14
Git 分支
您还可以使用 Git 分支对话框来创建项目的新分支。要为你的项目创建一个新的分支,只需点击对话框中的“+ New Branch”链接。在随后的屏幕中键入新分支的名称(如图 10-15 所示),然后单击“创建”
图 10-15
创建新分支
默认情况下,“签出分支”设置是打开的;大部分时间你都想戴着它。这意味着在创建分支后,您可以签出该分支。无论您做什么编辑或更改,它都将在新签出的分支上。
更改列表中的更改
当你在一个有很多文件的项目上工作时,知道哪些文件在最后一次提交后被改变了是很有用的;提交工具窗口(如图 10-16 所示)显示自上次提交以来所有被更改的文件。您可以通过点按工具栏上的标签来隐藏或取消隐藏它。或者,你也可以在主菜单栏中显示或隐藏它,查看 ➤ 工具窗口 ➤ 提交 —最快的方法是只使用 cmd + 0 (macOS)或 Alt + 0 (Linux/Windows)。
图 10-16
提交工具窗口
如果您想查看自上次提交以来文件发生了什么变化,只需在提交窗口中选择该文件(如图 10-17 所示)。
图 10-17
在提交窗口中选择一个文件。
然后,显示 Diff 对话框;你可以通过右键点击文件然后选择“显示差异”或者使用键盘快捷键来完成,键盘快捷键是 cmd + D (macOS)或者 CTRL + D (Linux/Windows)。差异对话框如图 10-18 所示。
图 10-18
差异对话框
左边的面板显示了您最后一次提交时的文件,右边的面板显示了现在的文件(包含您的更改)。文件之间的更改或“差异”在右侧面板上以浅绿色显示;该突出显示延伸到左侧面板,以指出差异的位置。
忽略文件
您可能希望将大多数项目文件置于版本控制之下,但是有些文件您可能不希望置于源代码控制之下。您可以在每次提交时过滤掉您不想放在 Git 中的文件——您只需在提交窗口中取消选中它们——但是这将会很麻烦、乏味,并且容易出错(您可能会错过一些东西)。幸运的是,解决方法很简单。
您可以创建一个名为的文件。gitignore 在项目文件夹中的任何地方——但是它通常在项目的根文件夹中。忽略文件是一个简单的文本,其中每行包含一个要忽略的文件或目录的模式。项目中与忽略文件中的模式匹配的任何文件都将被忽略,它们不会是更改列表的一部分。
如图 10-19 右键单击项目,然后选择新建 ➤ 文件,可以在项目根目录下添加一个忽略文件。
图 10-19
将文件添加到项目中
键入文件的名称-。gitignore(如图 10-20 所示)——在新文件对话框中。
图 10-20
新文件
按回车完成动作。
IntelliJ 添加了**。gitignore** 文件到项目的根目录。由于我们的项目处于版本控制之下,IDE 可能会询问您是否要将新创建的文件添加到 Git 中(如图 10-21 所示)。在这种情况下,是的,我想补充。gitignore 到版本控制。所以,我会选择“添加”
图 10-21
将文件添加到 Git
或者,如果您喜欢在命令行上工作,您可以使用 IntelliJ 的终端,然后创建**。gitignore 项目根文件夹中的**文件。那也可以。
现在可以开始向忽略文件添加条目了。清单 10-1 中列出了许多 ignore 文件中最常见的一些模式。
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# IntelliJ
out/
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# macOS
.DS_Store
Listing 10-1Git Ignore File
在生成的示例文件中(列表 10-1 ),只有在。意见文件夹被忽略;但是如果你想完全忽略整个。idea 文件夹中,只需将下面一行添加到。gitignore 文件:
.idea/
将文件添加到忽略列表的另一种方法是使用 IntelliJ 中的插件。进入首选项(macOS 中的 cmd +、)或设置(Windows/Linux 中的 CTRL + Alt + S ),然后进入插件。转到“市场”标签,然后搜索“忽略”如图 10-22 所示,我安装了**。忽略**插件(由 JetBrains 开发)和“添加到 gitignore”(由 euphoricity 开发)——你不必安装这两个插件。大多数开发者只是从 JetBrains 安装插件。
图 10-22
Gitignore 外挂程式
一旦你安装了插件,你可以通过右击项目名称(在项目工具窗口中)来添加一个 Git ignore 文件到项目中——如图 10-23 所示——然后转到新建 ➤ 。忽略文件 ➤ 。gitignore 。
图 10-23
新的。gitignore file 忽略档案
在接下来的屏幕中,您可以从各种配置文件中进行选择。因为我正在做一个 Java 项目,所以我选择了“Java”,如图 10-24 所示。
图 10-24
忽略文件生成器
点击生成完成动作。该插件生成一个忽略文件,并预加载您想在 Java 项目中忽略的最常见的文件和文件夹模式。
图 10-25 显示了生成的忽略文件。
图 10-25
已生成。gitignore 文件
GitHub 集成
GitHub 是 IntelliJ 的一等公民。如果你有一个 GitHub 账户,在 IntelliJ 中集成它是很简单的。
进入首选项(macOS 中的 cmd +、)或设置(Linux/Windows 中的 CTRL + Alt + S ),然后进入版本控制。检查 GitHub 是否存在。GitHub 在 IntelliJ 中是作为一个插件提供的,但它可能是在安装过程中安装的。所以,在大多数情况下,你应该有它——如果没有,就从插件市场安装 GitHub。
如果你有 GitHub 插件(就像我在图 10-26 中看到的),你将有机会在这个对话框中添加你的 GitHub 账户。
图 10-26
首选项 GitHub
单击“添加帐户”链接。它将启动您的默认浏览器,并将您带到 JetBrains 的页面,在那里有一个警告等待您——“只有在 JetBrains IDE 打开该页面时才继续”,如图 10-27 所示。
图 10-27
在 GitHub 中授权
点击“在 GitHub 中授权”按钮继续操作。点击链接会将你带到 GitHub 页面(如图 10-28 所示)。您现在可以授权 JetBrains 访问您的 GitHub 帐户。
图 10-28
授权 JetBrains IDE 集成
点击“授权 JetBrains”完成操作。在短暂地重定向到 JetBrains 站点后,我们会在 GitHub 中看到我们已经被授权的确认,如图 10-29 所示。
图 10-29
确认页面
现在我们回到 IDE。你应该可以在偏好设置窗口的 GitHub 部分看到你的 GitHub 账户,如图 10-30 所示。
图 10-30
首选项➤版本控制➤ GitHub
一旦我们在 IntelliJ 中设置了 GitHub,我们现在就可以开始在 GitHub 中共享任何项目。
在 GitHub 中分享当前打开的项目,你要做的就是进入主菜单栏,然后 Git ➤ GitHub ➤ 在 GitHub 上分享项目(如图 10-31 )。
图 10-31
在 GitHub 上共享项目
在随后的对话框中(图 10-32 ),输入存储库名称——这将默认为 IntelliJ 中的项目名称,但是您可以在这里更改它。我通常不去管它,这样我本地环境中的项目名称就和它在 GitHub 上的名称一样了。您可以添加描述,但这是可选的。
图 10-32
在 GitHub 对话框上共享项目
点击“共享”完成操作。
一旦 IntelliJ 将本地回购完全推送到 GitHub,您将得到一个提示(如图 10-33 所示),其中包含一个指向 GitHub 中项目页面的可点击链接。
图 10-33
在 GitHub 上成功共享项目
点击链接进入该项目的 GitHub 页面。
图 10-34 显示了 GitHub 上的项目。
图 10-34
GitHub 上的共享项目
提交并推送到远程存储库
当我们的项目仅存在于本地回购中时,我们所要做的就是“提交”变更。这已经足够了,因为我们当时没有远程回购,但是因为我们的项目现在托管在 GitHub 中,所以您也需要将本地回购中的变化反映到上游回购中。为此,你现在需要选择“提交并推送”,而不是点击“提交”,如图 10-35 所示。
图 10-35
提交和推送
单击“提交并推送”会提交所有更改并将这些更改推送到上游回购。
创作主旨
GitHub 还有另一个功能,允许其成员共享代码片段,比如 pastebin。com 。这些代码片段被称为 gists。GitHub 在其网站上提供了创建新 gists 的功能。使用 IntelliJ,您可以通过在当前打开的项目中共享源代码来创建 gists。
要开始将您的代码作为要点共享,请在当前打开的项目中选择一个源文件—在项目工具窗口中选择该文件,然后进入主菜单栏,git➤github➤create gist(如图 10-36 所示)。
图 10-36
创建要点
在随后的对话框中(图 10-37 ,您为要点提供文件名;它将默认为 IntelliJ 中的文件名,但是您可以在这里更改它。如果愿意,您可以提供描述。
图 10-37
创建要点
单击确定完成操作。
图 10-38 显示了我们在 GitHub 上发布的要点。
图 10-38
我们新创造的主旨
关键要点
-
源代码控制是现代软件开发的一部分。您需要它来跟踪变更并与团队共享代码。
-
Git 是最流行的源代码控制软件之一。它被广泛使用并定期更新。在 IntelliJ 中使用 Git 很容易。
-
GitHub 是一个流行的远程回购托管网站。在 IntelliJ 中使用 GitHub 也很容易。
-
在 GitHub 中共享一个项目(或者只是片段)在 IntelliJ 中是轻而易举的事情。
十一、测试
在本章中,我们将介绍以下内容:
-
测试
-
单元测试
-
测试基础
-
如何在 IntelliJ 中设置
测试是开发过程中同样重要的一部分。如果你想让你的应用程序被任何重要的人使用(也许是付费用户),那么这个应用程序不能有粗糙的边缘。它不能有任何重大或明显的错误。它需要经过验证和测试。
测试类型
测试有很多种;你将使用哪一个取决于你正在构建的应用程序的类型。如果你正在开发一款游戏,除了通常的功能、集成、开发者和用户验收测试之外,你很可能会包括声音测试、浸泡测试、符合性或一致性测试。假设您正在构建一个企业电子商务系统。在这种情况下,除了漏洞测试之外,你很可能会在性能或压力测试上多花一点时间——测试种类很多。下面简单介绍一些常见的。
功能测试。功能测试是测试应用程序的标准方式。它被称为功能,因为我们正在测试应用程序的特性(也称为功能),因为它们是在需求规范中指定的——需求规范是你或业务分析师在应用程序的规划阶段编写的。需求规格将被写在一个文档中(通常称为功能需求规格)。你可能在功能规范中发现的一个例子是“用户必须在进入应用程序之前登录到服务器”,“用户必须提供有效的电子邮件进行注册”,等等。测试人员,通常称为 QA 或 QC(分别是质量保证和质量控制的缩写),是执行这些测试的人。他们将创建测试资产,制定测试策略,执行它们,并最终报告执行的结果。失败的测试通常被分配给开发人员(您)来修复和重新提交。我在这里描述的是一个开发团队的典型实践,这个团队有一个单独的或者专门的测试团队;如果你是一个人的团队,QA 很可能也是你。测试是一种完全不同的技能,我强烈建议你寻求其他人的帮助,最好是那些有测试经验的人,来帮助你。
性能测试。仅仅从它的名字,你就可以猜到这种测试是做什么的。它将应用推向极限,并观察它在压力下的表现。这里你想看到的是应用程序在高于正常水平的条件下是如何反应的。
浸泡测试或耐久测试。这是一种性能测试;通常,你会让应用程序长时间运行在各种操作模式下,例如,让应用程序在暂停时或在标题屏幕上暂停很长时间。你在这里试图找到的是应用程序如何响应这些条件,以及它如何利用系统资源,如内存、CPU、网络带宽等。;您最有可能使用应用程序分析器来执行这些测量。
音量测试。这是另一种形式的性能测试;如果您的应用程序使用数据库,您可能希望了解当数据加载到数据库时它将如何响应。你要检查的是系统在各种数据负载下的反应。
尖峰测试(或可扩展性测试 ) 。这也是另一种性能测试。如果应用程序依赖于中央服务器,该测试通常会增加连接到中央服务器的用户(设备端点)数量。你需要观察用户数量的激增是如何影响用户体验的:应用程序是否仍能响应,是否对每秒帧数、延迟等有影响。
兼容性测试。在这里,您可以检查应用程序在不同设备和软硬件配置上的表现。
符合性或一致性测试。例如,如果你正在为 Android 开发一个游戏应用程序,这是你对照 Google Play 应用程序或游戏指南检查游戏的地方;请务必阅读位于 https://support.google.com/googleplay/android-developer/answer/10959797?hl=en
的 Google Play 开发者政策中心。确保你也熟悉 PEGI(泛欧游戏信息)和 ESRB(娱乐软件评级委员会)。如果游戏应用程序内容包含不符合特定评级的不良内容,则需要对其进行识别和报告。违规可能是拒绝的原因,这可能导致昂贵的返工和重新提交。如果您正在从用户那里收集数据,您可能需要审核应用程序,以检查它是否符合适用的数据隐私法规。
本地化测试。这一点至关重要,尤其是如果这款应用面向全球市场的话。应用程序标题、内容和文本需要用支持的语言进行翻译和测试。
恢复测试。这将边缘案例测试带到了另一个高度。在这里,应用程序被迫失败,您将观察应用程序在失败时的行为以及失败后如何恢复。它会让你洞察你是否写了足够多的 try-catch-finally 块。应用程序应该优雅地失败,而不是突然失败。只要有可能,运行时错误应该由 try-catch 块来保护;并且当异常发生时,尽量写日志,保存游戏状态。
渗透或安全测试。这也称为漏洞测试。这种测试试图发现应用程序的弱点。它模拟了潜在攻击者为规避应用程序的所有安全功能而进行的活动;例如,如果应用程序使用数据库来存储数据,尤其是用户数据,笔测试人员(进行渗透测试的专业人员)可能会在 Wireshark 运行时使用该应用程序——Wireshark 是一种检查数据包的工具;这是一个网络协议分析器。如果您以明文形式存储密码,它会在这些测试中显示出来。
声音测试。如果你的应用程序使用声音,检查是否有任何错误加载文件;还有,听听声音文件有没有爆裂声等等。
开发者测试。这是你(程序员)在向应用程序添加一层又一层代码时要做的测试。这包括编写测试代码(也用 Java)来测试你的实际程序。这就是所谓的单元测试。这就是本章的全部内容。
单元测试
开发人员以这样或那样的方式测试他们的代码;有些人通过简单地运行应用程序,给它输入一些内容,甚至试图通过在输入框中键入特殊字符来破坏它。他们(开发人员)经常这样做,通常是在他们完成一个方法的编码之后,这是一个自然的停止点,然后他们开始一些测试活动。我们这样做是因为我们想知道应用程序的行为是否与预期一致。更多的时候,我们所做的测试是一种非结构化的测试——如果你愿意这样称呼的话,那就是临时测试。
那么单元测试和特别测试有什么不同呢?我们为什么要为此烦恼呢?
单元测试和特别测试都是由开发人员完成的。他们的相似之处到此为止。单元测试是一种已建立的测试方法,其中对源代码的单个单元(通常是方法)进行测试,以确定测试结果是否与一组预期行为一致。单元测试是自动化的;它不是像特别测试那样手动完成的。自动化测试脚本(通常)由构建应用程序的同一个开发人员编写。
单元测试很简单;这是一个方法可能会做或产生的特定的东西。一个应用程序通常有许多单元测试,因为每个测试都被定义为一组狭窄的行为。所以,你需要大量的测试来覆盖全部的功能。Java 开发人员通常使用 JUnit 来编写单元测试。
JUnit ( https://junit.org/
)是 Kent Beck 和 Erich Gamma 编写的回归测试框架。你可能记得他们分别是创建极限编程的人和来自四人帮(g of,设计模式)的人,以及其他人。
为什么您应该进行单元测试
您需要以某种方式测试您的代码;为什么不遵循一个成熟的测试框架呢?
自动化测试是防止回归和测试代码库中那些边缘案例的最简单的方法——边缘案例通常在特别测试中被忽略。
使用单元测试的另一个令人信服的原因是它给你信心无情地重构你的代码。当您引入代码库的变更时,单元测试会在您破坏某些东西时提醒您。如果您可以信任单元测试,重新认证您的代码就像重新运行测试一样简单。
何时编写测试
这是一个激烈辩论的话题。TDD 的拥护者坚持认为,在编写实际代码之前,应该先编写测试。另一方面,一些开发人员会在编写了大量代码后编写测试。我认为,很多开发人员都介于两者之间(包括你自己)——写一点代码,然后写一点测试。
何时运行测试
您应该尽可能频繁地运行您的测试。一旦您对代码进行了重大的修改,就运行测试。
如果您的团队有一个策略,您应该在一天结束时签入所有代码,那么在签入代码之前运行所有的测试是一个好主意。这是为了确保您的更改不会破坏现有代码。
IntelliJ 中的 JUnit
IntelliJ 对 JUnit 有极好的支持;它在安装过程中安装捆绑的 JUnit 插件。你可以看看 IntelliJ 的首选项 (或者设置) ➤ 插件来验证一下(如图 11-1 )。
图 11-1
JUnit 外挂程式
在 IntelliJ 中,编写 JUnit 测试是一项简单的任务。测试只是一个使用 @Test 注释的类,如清单 11-1 所示的代码。
Note
您可以在 IntelliJ 中使用 JUnit 3、4 或 5。如你所见,对于这一章,我使用了 JUnit5。
import static org.junit.junpiter.api.Assertions;
public class SampleTest {
@Test
void shouldShowSimpleAssertion() {
Assertions.assertEquals(1,1);
}
}
Listing 11-1SampleTest.java
注释@Test 来自 JUnit5 的org.junit.jupiter.api
。Test
。当您用@Test 修饰一个方法时,它就成为了一个测试方法。
我们的测试方法(在清单 11-1 中)还没有测试任何其他的类或方法;我们只是在做一个简单的断言。
断言类来自 Junit5 的 jupiter 库。这个类中的一个方法是 assertEquals() 方法。第一个参数是期望值,第二个参数是实际值。如果第二个参数(实际值)等于第一个参数(预期值),断言返回 true,因此测试通过。
IntelliJ 让我们很容易分辨出哪个参数是期望值,哪个是实际值。IntelliJ 的参数提示(见图 11-2 )将我们从记忆方法参数的位置意义中解放出来。我们不必记住哪个参数是期望值,哪个是实际值。
图 11-2
assertEquals()方法的操作中的参数提示
虽然您可以关闭参数提示,但我建议不要这样做,因为这是一个有用的特性。然而,如果你认为你有一个有效的理由这样做,以下是如何做到这一点:
-
将插入符号放在一个参数提示上——在图 11-3 中,我将插入符号放在“预期的”提示中。
-
使用选项+ ENTER (macOS)或 Alt + ENTER (Linux/Windows)快捷键。
-
将显示一个下拉选项,如图 11-3 所示。
-
选择不显示当前方法的提示。
这将关闭 assertEquals()的参数提示。
图 11-3
关闭参数提示
IntelliJ 将在第一次遇到@Test 注释时对其进行标记。使用常用的代码检查工具即可;使用 option + ENTER (macOS)或 Alt + ENTER (Linux/Windows)将 jUnit5 添加到类路径中(如图 11-4 )。
图 11-4
将 JUnit5 添加到类路径
虽然我们刚刚将 JUnit5 添加到我们的类路径中,但是我们的库中还没有它。在接下来的对话框中(图 11-5 ), IntelliJ 将提供从 Maven 资源库下载它。我想将其下载到我的项目的库文件夹中;因此,我勾选了下载到旁边的框,如图 11-5 所示。
图 11-5
从 Maven 资源库下载库
单击确定完成操作。下载完成后,您会在项目的 lib 文件夹中找到 JUnit5 库(如图 11-6 所示)。
图 11-6
lib 文件夹中的 JUnit5 库
我们需要为断言类添加一个导入语句。您可以使用代码检查快捷键( option + ENTER 或 Alt + ENTER )来快速解决这个问题;选择导入类,如图 11-7 所示。
图 11-7
导入断言类
现在一切正常,我们可以进行测试了。由于我们只有一个测试方法,我将只点击测试方法旁边的绿色箭头(如图 11-8 所示)。
图 11-8
运行 shouldShowSimpleAssertion()
如你所料,我们的测试通过了。
当一个测试成功时,你会在通过测试的测试用例方法和测试类旁边看到绿色的复选标记,如图 11-9 所示。
图 11-9
测试成功
当测试失败时会发生什么?让我想想。编辑 assertEquals()方法,使其看起来像下面的代码:
Assertions.assertEquals(1,2);
当预期值(第一个参数)与实际值(第二个参数)不同时,测试应该会失败。
重新运行测试,不出所料,它失败了。图 11-10 向我们展示了失败测试的结果。
图 11-10
测试失败
失败的测试方法和包含它的测试类旁边的 X 标记表示断言失败。
当您选择失败的测试时(图 11-10 中运行窗口的左面板),测试失败的详细信息显示在右面板。在这里,我们可以看到期望值是 1,实际值是 2 —这就是它失败的原因。assertEquals()方法仅在预期值与实际值相同时返回 true。
测试实际的类
现在我们已经有了足够的关于设置 JUnit 测试的理论和实践知识,让我们在一个实际的类中使用它——您可能想要为此设置一个新的项目。
向新项目添加一个新的 Java 类,并编辑它以匹配清单 11-2 。
public class MainProgram {
String sayYourName() {
return getClass().getName();
}
int addTwoNumbers(int a, int b) {
return a + b;
}
}
Listing 11-2MainProgram
为现有的类创建一个测试类是非常容易的。将插入符号放在类名上,然后按 cmd + N (macOS)或Alt+Insert(Linux/Windows),再选择测试(如图 11-11 )。
图 11-11
生成➤测试
IntelliJ 提示我们“没有找到测试根”(图 11-12 )。理想情况下,您应该在项目中建立一个测试根文件夹,其中包含所有的测试类。由于我们没有这样做,IntelliJ 询问您是否希望源根目录也作为您的测试文件夹。现在,我将单击“确定”。
图 11-12
未找到测试根
点击确定后,弹出创建测试对话框。在这里我们可以输入 IntelliJ 将为我们生成的测试类的一些细节(图 11-13 )。有点问题。在当前模块中找不到 JUnit5——必须为您使用它的每个项目导入 JUnit 5 库。
图 11-13
未找到创建测试➤ JUnit5
单击“Fix”按钮将尝试下载 JUnit5 库并为我们的项目创建一个本地副本。
接下来会弹出“从 Maven 资源库下载库”(图 11-14)——你之前已经看过了,所以你已经知道该怎么做了。
图 11-14
从 Maven 资源库下载库
单击确定完成操作。下载完成后,我们将再次看到“创建测试”对话框(图 11-15 )。
图 11-15
创建测试
类名默认为要测试的类名加上单词“Test”对话框的下部面板包含 MainProgram(要测试的类)的成员方法。您可以检查您想要生成测试的方法—我想要为 sayYourName()和 addTwoNumbers()方法创建测试,所以我检查了它们。
另外,我想为一些设置生成一个存根方法,所以我在框前勾选了setUp/@框。
点击确定按钮,完成生成动作。图 11-16 向我们展示了生成的 MainProgramTest 类和项目的 lib 文件夹,其中现在包含了下载的 JUnit5 库。
图 11-16
项目工具窗口中的 Lib 文件夹
Note
在早期版本的 IntelliJ 中,单词“Test”必须是类名的一部分。这是一个要求。对于当前版本的 JUnit 来说,这不再是真的了——但是按照惯例,我们仍然将“Test”作为类的一部分。
清单 11-3 显示了生成的 MainProgramTest 类。
import static org.junit.jupiter.api.Assertions.*;
class MainProgramTest {
@org.junit.jupiter.api.BeforeEach
void setUp() {
}
@org.junit.jupiter.api.Test
void sayYourName() {
}
@org.junit.jupiter.api.Test
void addTwoNumbers() {
}
}
Listing 11-3MainProgramTest
在生成的 MainProgramTest 上可能有几个错误。它们只是缺少导入语句和类路径错误——如图 11-17 所示。您可以使用代码检查工具( option + ENTER 或 Alt + ENTER )快速解决它们。
图 11-17
将 JUnit5 添加到类路径
一旦您解决了缺少的类路径条目和导入语句,我们现在就可以开始编码实际的测试了。
编辑 MainProgramTest 类,并执行以下操作:
-
向该类添加一个 MainProgram 成员变量(mp)。
-
在 setUp()方法中实例化 mp 成员变量。
您的代码应该类似于清单 11-4 中的代码。
class MainProgramTest {
MainProgram mp = null;
@org.junit.jupiter.api.BeforeEach
void setUp() {
mp = new MainProgram();
}
@org.junit.jupiter.api.Test
void sayYourName() {
}
@org.junit.jupiter.api.Test
void addTwoNumbers() {
}
}
Listing 11-4Add
您可以使用 setUp()方法来实例化测试类的成员变量。还可以使用 setUp()方法连接数据库、打开文件、启动与服务器的连接等。
接下来,让我们为 sayYourName()和 addTwoNumbers()方法添加实际的测试。清单 11-5 显示了 MainProgramTest 类的完整代码。
import org.junit.jupiter.api.Assertions;
class MainProgramTest {
MainProgram mp = null;
@org.junit.jupiter.api.BeforeEach
void setUp() {
mp = new MainProgram();
}
@org.junit.jupiter.api.Test
void sayYourName() {
Assertions.assertEquals("MainProgram", mp.sayYourName());
}
@org.junit.jupiter.api.Test
void addTwoNumbers() {
Assertions.assertEquals(5, mp.addTwoNumbers(3,2));
}
}
Listing 11-5Complete Code for MainProgramTest
清单 11-5 中标记为粗体的语句是您需要编写的语句;这个类的其余部分是为我们生成的。
现在是运行测试的时候了。
完成的主程序测试如图 11-18 所示。
图 11-18
运行 MainProgramTest
您可以运行一个单独的测试—单击第 13 行旁边的绿色箭头将运行 sayYourName()测试;单击第 18 行上的箭头运行 addTwoNumbers()测试。
我想运行所有的测试,所以我将单击第 3 行(MainProgramTest 类)旁边的绿色箭头。
图 11-19 显示了我们测试运行的结果。
图 11-19
主程序测试的测试运行
全是绿色标记。我们所有的测试都通过了。
更多示例
我为这个新例子创建了一个新项目;如图 11-20 所示,相当原始。
图 11-20
新项目
我将为这个项目设置一个合适的测试根文件夹。来自 JetBrains ( www.jetbrains.com/help/idea/testing.html
)的指导是我们需要在创建任何测试类之前建立一个测试根文件夹。在我们之前的例子中,我们不需要创建一个专用的测试根文件夹——而且在大多数情况下,我们做得很好。但是如果你正在做一个重要的项目,最好遵循正确的指导——这就是为什么我们要建立一个测试根文件夹。
IntelliJ 中的文件夹处理方式不同。src 文件夹有一种特殊的处理方式,而测试根文件夹则完全不同。就像我前面说的,最好遵循工具制造者自己的正确指导。
创建项目时,默认情况下已经创建了 src 文件夹,但没有创建 test 文件夹。你必须自己创建它——为此,在项目工具窗口中右击项目名称,如图 11-21 所示。
图 11-21
新目录
选择新的 ➤ 目录,然后提供新的目录名。我们想要创建一个测试根目录,所以我们称之为“测试”——如图 11-22 所示。
图 11-22
新目录名
键入新目录的名称后按 ENTER 键,在项目工具窗口中创建文件夹。
接下来,右键单击新创建的测试目录,选择标记目录为 ➤ 测试源根,如图 11-23 所示。
图 11-23
将目录标记为测试源根目录
现在我们准备编码和测试。让我们从 https://codingbat.com/prob/p159227
中挑选一个简单的热身运动;问题内容如下:
"给定 2 个 int 值,如果一个为负,一个为正,则返回 true。除非参数’ negative '为真,否则只有当两者都为负时才返回 true。
因此,在 src 文件夹中,我们添加了一个源文件。你可以通过右击 src 文件夹,并选择新建 ➤ Java 类来实现。把它命名为“波斯内克”编辑它以匹配清单 11-6 。
public class PosNeg {
boolean posNeg(int a, int b, boolean negative) {
boolean retval = false;
return retval;
}
}
Listing 11-6PosNeg Class
这对我们来说应该是一个很好的起点。
让我们创建测试。
前面,我向您展示了如何使用 generate 键盘快捷键来生成测试类。您仍然可以使用它,但我想我会向您展示另一种方法,这一次只使用代码检查快捷方式,我们以前最喜欢的——命令+ ENTER (macOS)或 Alt + ENTER (对于 PC 用户)。
将插入符号放在类名(PosNeg)上——如图 11-24 所示——然后使用代码检查快捷方式。
图 11-24
通过快速修复快捷方式创建测试(command + ENTER)
选择“创建测试”在接下来的屏幕中(图 11-25 ),输入名称 test class 并选择要生成的存根方法——哦,顺便说一下,您需要修复“在模块中找不到 JUnit5 库”的错误。你已经知道怎么做了。
图 11-25
创建测试对话框
单击“确定”完成操作;当它完成时,你应该在测试文件夹中有一个类(如图 11-26 所示)。
图 11-26
生成的 PosNegTest 类
编辑 PosNegTest 类以匹配清单 11-7 。
import static org.junit.jupiter.api.Assertions.*;
class PosNegTest {
PosNeg pn = null;
@BeforeEach
void setUp() {
pn = new PosNeg();
}
@Test
void posNegOneNegativeOneFalse() {
assertEquals(true, pn.posNeg(1,-1,false));
}
@Test
void posNegNegativeOneOneFalse() {
assertEquals(true, pn.posNeg(-1,1,false));
}
@Test
void posNegNegativeFourNegativeFiveTrue() {
assertEquals(true, pn.posNeg(-4,-5,true));
}
}
Listing 11-7PosNegTest Class
我为 posNeg()方法编写了三个测试。详情见表 11-1 。
表 11-1
posNeg()方法的测试用例
|判例案件
|
因素
|
预期结果
|
| — | — | — |
| one | posNeg(1, -1, false) | 真实的 |
| Two | posNeg(-1, 1, false) | 真实的 |
| three | 正负(-4, -5, 真) | 真实的 |
通常情况下,你会为每一个你将要测试的方法准备更多的测试用例。这是因为您希望看到您的代码在各种条件下的行为。理想情况下,您应该涵盖边界条件。在我们简单的测试案例中,当两个输入都为正,两个输入都为负时,我有一个测试;当一个输入为负,另一个输入为正时,我有另一个测试。这不是详尽的报道,但这是一个良好的开端。
让我们进行测试。
图 11-27 显示三个测试都失败了。不奇怪。我们还没有对 posNeg()方法做任何事情。这是一个好的开始。这实际上是你应该开始的地方——一个失败的测试。现在,让我们第一次尝试解决问题。
图 11-27
三次测试失败
编辑 PosNeg 类以匹配清单 11-8 。
public class PosNeg {
boolean posNeg(int a, int b, boolean negative) {
boolean retval = false;
if (negative) {
if ((a * b) < 0) {
// one is positive and the other negative
retval = false;
}
else {
// either they're both positive or negative
if (a < 0) {
// both numbers are negative
retval = true;
}
else {
retval = false;
}
}
}
return retval;
}
}
Listing 11-8PosNeg Class
一点点编码,一点点测试。让我们看看我们的代码是否能让所有的测试用例都通过。重新运行测试类。
图 11-28 显示了我们工作的进度。一次考试通过了,但两次没有通过。
图 11-28
一次测试通过;两次测试失败
如果您点击任何失败的测试(运行窗口的左面板,如图 11-28 所示),详细信息将显示在右面板中。在这里,您可以看到测试方法**posnegnegneveroneoneFalse()**的预期结果应该是 true,但是我们的测试运行是 false。
因此,虽然我们当前的解决方案已经部分正确,但它在三个测试中有两个失败了。所以,还是多写一点代码,多做一点测试吧。
切换回 PosNeg 类,继续编码,直到通过所有测试。编辑它以匹配清单 11-9 。以粗体突出显示的语句是新添加的内容。
public class PosNeg {
boolean posNeg(int a, int b, boolean negative) {
boolean retval = false;
if (negative) {
if ((a * b) < 0) {
// one is positive and the other negative
retval = false;
}
else {
// either they're both positive or negative
if (a < 0) {
// both numbers are negative
retval = true;
}
else {
retval = false;
}
}
}
else {
retval = ((a * b) < 0);
}
return retval;
}
}
Listing 11-9Complete Code for PosNeg
让我们重新进行测试。
图 11-29 显示了我们测试运行的结果。现在,所有的测试都通过了。
图 11-29
所有测试都通过了
我希望这能让你开始单元测试。我们只是触及了表面。我让您来探索 JUnit 断言类的其他断言方法。
关键要点
-
测试是开发的重要部分。不要吝啬。
-
在 IntelliJ 中设置单元测试非常容易。你没有理由不用它。
-
单元测试建立对代码库的信心。如果你信任测试,你可以毫不留情地重构。当你对代码充满冒险精神时,它就是你的生命线。您可以不受惩罚地试验您的代码;继续运行您的测试,只要您没有破坏任何东西,就继续破解。
-
从一次失败的测试开始——这没什么错。编写一点代码,然后进行一点测试,直到你覆盖了所有的测试用例。
十二、应用
在本章中,我们将介绍以下内容:
-
JavaFX 概述
-
如何设置 JavaFX 并将其包含在项目中
-
如何使用 JavaFX 构建基本的 UI 应用程序
-
如何设置和使用场景构建器
用 Java 构建用户友好的应用程序有三种方法。你可以去
-
Web 应用路线,在这种情况下,你需要了解一些 JakartaEE 应用——附录 A(本书的在线资源)简要讨论了一些常见的 JakartaEE 应用以及如何使用 IntelliJ 构建它们。您可以通过位于
www.apress.com/9781484274453
的下载源代码链接访问本附录。 -
手机应用路线,这种情况下你就需要和安卓打交道了。
-
JavaFX route 是一个技术栈,它将让你构建丰富的桌面应用,这也是我们本章的主题。
简史
JavaFX 并不是 Java 生态系统中 UI 技术的起点。Java 对桌面应用技术的竞标开始得更早。早在 1995 年(Java 的首次亮相),开发人员已经使用 AWT——抽象窗口工具包的缩写——来构建小程序(这在当时是一件大事)和构建桌面应用程序。AWT 的一个限制是它使用了底层操作系统的 UI 控件(这很笨重),而且这也意味着你的应用程序在每个操作系统上看起来(和工作起来)都不一样。
1997 年,Sun Microsystems 推出了 JFC (Java 基础类),更亲切地称为 Swing API。如果您正在构建 Java 桌面应用程序,那么 Swing 就是您要使用的。Swing 大受欢迎,因为与 AWT 不同,它不使用底层操作系统的 UI 控件,这意味着该应用程序在不同平台上的外观和行为都是一致的。
2008 年的某个时候,JavaFX 作为 Swing 的继任者被推出;但是 JavaFX 的历史并不是始于 2008 年,而是更早。
表 12-1 显示了 JavaFX 的时间表。
表 12-1
JavaFX 的历史
|发布日期
|
版本
|
评论
|
| — | — | — |
| 2000 年初 | | SeeBeyond 的克里斯·奥利弗创造了 F3(形式服从功能)。F3 是一种用于开发 GUI 应用程序的脚本语言。 |
| Two thousand and five | | 太阳微系统公司收购 SeeBeyondF3 改名为 JavaFX。 |
| Two thousand and seven | | 在 JavaOne 大会上,Sun Microsystems 发布了 JavaFX。 |
| Two thousand and eight | One | JavaFX 发布了。 |
| Two thousand and nine | One point one | 包括移动支持。 |
| Two thousand and nine | One point two | 2009 年在 Q2 上映。 |
| Two thousand and ten | One point three | 2010 年在 Q2 上映。 |
| Two thousand and ten | 1.3.1 | 将于 2010 年第三季度发布。 |
| Two thousand and eleven | Two | JavaFX 脚本已被删除。JavaFX 现在支持 Java API。对 JavaFX mobile 的支持已取消。幕后发生了许多变化。添加了对惰性绑定、绑定表达式、绑定序列表达式和部分绑定重新计算的支持。另外,JavaFX 现在是开源的。 |
| Two thousand and twelve | Two point one | 现在支持 macOS 桌面。 |
| Two thousand and twelve | Two point two | 将于 2012 年第三季度发布。现在,支持 Linux 了。 |
| Two thousand and fourteen | Eight | 到目前为止,您需要单独下载 JavaFX 才能使用它。从此版本开始,JavaFX 包含在 JDK 中。所以,一旦你安装了 JDK,你就自动拥有了 JavaFX。此外,JavaFX 的版本也有重大变化。从此版本开始,JavaFX 版本将遵循 JDK 的版本号。幕后的变化包括支持 3D 图形、传感器支持、MathML、打印和富文本支持等。 |
| Two thousand and seventeen | Nine | JEP 253 (JDK 增强提案),现在我们可以用 CSS 样式化 JavaFX 了。 |
| Two thousand and eighteen | Eleven | Oracle 从 JDK 中删除了 JavaFX。从这个版本开始,您必须从 JDK 单独下载 JavaFX。 |
| Two thousand and nineteen | Twelve | 2019 年在 Q1 上映。 |
| Two thousand and nineteen | 12.0.1 | 2019 年在 Q2 上映。 |
| Two thousand and nineteen | Thirteen | 2019 年第四季度的某个时候发布。 |
| Two thousand and twenty | Fourteen | 2020 年在 Q1 上映。 |
| Two thousand and twenty | Fifteen | 2020 年第四季度发布。 |
| Two thousand and twenty-one | Sixteen | 2021 年在 Q1 上映。 |
设置
学习新技术的最好方法是开始使用该技术开发应用程序。因此,让我们构建一个使用 JavaFX 的新项目。
从 glouhqhttps://gluonhq.com/products/javafx/
为您的操作系统下载 JavaFX SDK。将其解压缩到您喜欢的位置,例如/Users/username/javafx-sdk-16。
创建一个 JavaFX 项目。在新建项目对话框上(图 12-1 ,选择 JavaFX 。
图 12-1
创建新的 JavaFX 项目
为项目命名,就像您过去创建的其他项目一样。单击“下一步”按钮继续操作。
您将被要求从 Maven 资源库下载 javafx-fxml 库(图 12-2 )。
图 12-2
从 Maven 资源库下载 JavaFX 库
下载该库,使其成为项目的一部分;单击“确定”按钮完成操作。
IntelliJ 创建项目。如图 12-3 所示,它创建了三个文件。
图 12-3
新 JavaFX 项目
接下来从主菜单栏进入文件 ➤ 项目结构 ➤ 项目,如图 12-4 所示。
图 12-4
项目结构|项目 SDK
将项目 SDK 设置为 11 或更高;在我的例子中,我将它设置为 OpenJDK 16。
当您仍在项目结构对话框中时,转到库。正如你在图 12-5 中看到的,我们项目的 lib 文件夹中还没有任何东西。在我们的应用程序中使用 JavaFX 之前,我们需要将 JavaFX 库放在这里。
图 12-5
项目结构|库
点击加号添加一个新的库(如图 12-6 )。
图 12-6
项目结构|库|新项目库
从下拉菜单中选择 Java 。
一个文件浏览器(或者说是 Finder,对于 macOS 来说)会弹出来。现在,导航到解压 JavaFX SDK 的位置,指向 lib 文件夹,如图 12-7 所示。
图 12-7
选择 JavaFX 的 lib 文件夹
点击打开按钮完成动作。
IntelliJ 将询问您在哪里添加(哪个模块)JavaFX 库。我的项目中只有一个模块,所以我将选择它(图 12-8 )。
图 12-8
选择模块
单击“确定”按钮完成操作。
您将返回到项目结构对话框。你应该在项目的库部分看到 JavaFX 库,如图 12-9 所示。
图 12-9
项目结构|库
如果您试图在此时运行项目,您将得到一个运行时错误——“Java FX 运行时组件丢失”,如图 12-10 所示。
图 12-10
运行时错误
为了解决这个问题,我们需要向 VM 添加一些选项;为此,我们需要修改运行时配置。
你可以从主菜单栏修改运行时配置,然后进入运行 ➤ 编辑配置。或者,您可以点击编辑器栏上的运行按钮(如图 12-11 ),然后选择修改配置。
图 12-11
运行|修改运行配置
点击“修改”链接,如图 12-12 所示。
图 12-12
编辑运行配置|修改选项
选择添加 虚拟机选项,如图 12-13 所示。
图 12-13
编辑运行配置|修改选项|添加虚拟机选项
在虚拟机选项文本字段(图 12-14 )上,键入以下内容:
图 12-14
运行/调试配置|虚拟机选项
--module-path /path/to/javafx-sdk-16/lib --add-modules javafx.controls,javafx.fxml
不要忘记将 /path/to/javafx-sdk-16 替换为解压 javafx 的实际目录路径。
在我的例子中,我在/Users/ted/Prog tools/JavaFX-SDK-16 中安装了 Java FX,所以这就是我在我的 VM 选项中使用的(如图 12-15 所示)。
图 12-15
编辑运行配置|虚拟机选项
单击“确定”按钮完成操作。
图 12-16 显示了我们运行时的第一个 JavaFX 应用。
图 12-16
JavaFX 应用正在运行
舞台、场景和节点
JavaFX 使用舞台、场景和节点等术语。
将舞台对象想象成一个真实的舞台——人们在那里表演戏剧,就像在剧院舞台上一样。舞台是所有场景(我们仍在剧院类比)将被执行的地方;每个场景将包含不同的设置,旨在执行特定的行为。每个场景都有一套不同的道具——一个可能有假的树,另一个可能有假的波浪,等等。
这样思考 JavaFX 应用程序是有帮助的(使用剧院舞台类比),因为它实际上有点像这样。一个场景只属于一个阶段;每个场景都会有一套不同的道具。
我现在将停止使用剧院类比,这意味着从这一点开始,当我说 stage 时,我指的是 JavaFX stage,当我说 scene 时,我指的是 JavaFX scene。
阶段对象是顶级容器。它包含了整个应用程序。一个场景只属于一个阶段。我们可以说舞台是场景的容器。
场景通常包含子组件;在 JavaFX 术语中,这些组件称为节点。我们可以说场景是节点的容器。节点是放置在场景中的元素。这些节点是 GUI 元素,例如,文本框、复选框、按钮等。
每个节点要么是一个分支节点(意味着它可以有孩子),要么是一个叶节点(意味着它不能有孩子)。场景上的节点集合形成一个树形结构,称为场景图(图 12-17 )。
图 12-17
场景图
在图中,树上的第一个节点被称为根节点。它没有父母。您将在 JavaFX 中使用的每个元素都是一个节点的实例(节点是一个类)。
JavaFX API 定义了几个可以作为根、分支或叶节点的类。分支节点的例子可以是 HBox、StackPane、BorderPane、Group、Region、GridPane 等。—这些类通常是布局类。叶节点的一些例子是文本框、矩形、滑块、单选按钮、密码字段、分隔符、工具提示等。
当用实际的类名替换时,这个数字可能类似于实际应用程序中的某个东西。
在一个实际的应用程序中,场景图可能如图 12-18 所示。
图 12-18
应用程序的场景图
你好世界
让我们回到我们的“Hello World”示例项目。
在主编辑器的选项卡中选择 sample.fxml 文件(图 12-19 )。
图 12-19
sample.fxml
FXML 文件是一种基于 XML 的用户界面标记语言(由 Oracle 公司创建),用于定义 JavaFX 的用户界面。虽然可以通过编程定义场景中的所有节点来构建 JavaFX 应用程序,但这是一项繁琐的工作。产生的代码也很难维护。FXML 是为 JavaFX 构建 ui 的首选替代方法。
创建 JavaFX 项目时,会自动生成三个文件—Main.java、Controller.java 和 sample.fxml 。
编辑 sample.fxml 以匹配清单 12-1 。
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.TextField?>
<HBox>
<Label text="User name"></Label>
<TextField></TextField>
<Button text="Login"></Button>
</HBox>
Listing 12-1sample.fxml
即使没有进一步的解释,您也可以理解前面的代码是怎么回事。
前三行是导入语句。它们的功能与我们在 Java 源文件中的功能非常相似。语法有点奇怪,因为每个语句都用一对尖括号和问号括起来——但这就是 FXML 文件。
我们知道 HBox 是一个分支或父容器,所以 Label、TextField 和 Button 节点都包含在 HBox 中,这本能地告诉我们这三个叶节点是 HBox 的子节点。
重新运行该项目会得到如图 12-20 所示的结果。
图 12-20
你好世界
JavaFX 应用程序的生命周期
所有 JavaFX 应用程序都应该扩展类Java FX . application . application类。实例化后,JavaFX 应用程序对象将经历以下生命周期:
-
构造指定应用程序类的实例。如果你定义了一个自己的构造函数,它里面的任何代码都会被调用。
-
运行时调用应用程序的
init()
方法。如果您的应用程序进行任何 HTTP、数据库或 I/O 调用,这可能是放置初始化代码的好地方。 -
运行时调用
start()
方法。此方法通常是在创建 JavaFX 项目时为您生成的。在这里,FXML 被展平为 Java 对象以形成场景图,场景被设置为 stage,并且调用了Stage.show()
方法。 -
运行时会等待应用程序完成,这种情况会在以下任一情况发生时发生:
-
应用程序调用 Platform.exit()。
-
最后一个窗口被关闭,并且
Platform
上的implicitExit
属性为真——这通常发生在您单击窗口上的关闭按钮时。
-
-
运行时调用
stop()
方法。如果您打开了任何 I/O 资源,如网络、数据库或文件资源,这是确保它们关闭的绝佳时机。
Main.java
现在我们对 JavaFX 应用程序的组成有了一些了解,让我们重新看看Main.java源文件。清单 12-2 显示了对【Main.java】的的注释解释。
| -什么 | 我们扩展了`javafx.application.Application`类。所有 JavaFX 应用程序都继承自该类。 | | ➏ | 在运行时,VM 仍然使用静态的`main()`作为入口点。从`main(),`我们调用`launch()`方法,传递任何命令行参数给它(如果我们有的话)。 | | ➋ | 运行时调用`start()`方法并传递一个 stage 对象作为参数。 | | ➌ | 创建一个父节点对象(这是根变量)。它通过将 *sample.fxml* 的内容展平到一个场景图中来填充。 | | -你好 | 场景对象附加到舞台上。 | | ➎ | 最后,运行时调用 stage 对象的`show()`方法。此时,应用程序变得可见。 |import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application { ➊
@Override
public void start(Stage primaryStage) throws Exception{ ➋
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml")); ➌
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275)); ➍
primaryStage.show(); ➎
}
public static void main(String[] args) { ➏
launch(args);
}
}
Listing 12-2Main.java
场景构建器
虽然使用 FXML 而不是通过 API 来定义 UI 元素很容易,我应该说是“更容易”,但这仍然很乏味,尤其是当 UI 变得越来越复杂时。我们可以用来组成 UI 元素的可视化编辑器仍然是理想的工具。这就是为什么我们有场景生成器工具。
Scene Builder 是一个可视化合成工具,我们可以用它来构建 FXML 文件。Oracle 最初创建了它,但出于某种原因,他们停止了对 Scene Builder 的支持。幸运的是,Scene Builder 是开源的,因此,另一个组织(Gluon)取得了所有权。
如果要使用场景构建器,您需要单独安装它;这是一个直观地编写 FXML 文件的便利工具。像任何可视化合成工具一样,您可以简单地拖放元素来构造 ui,编辑其属性,并查看元素在运行时的可视化预览。顺便说一下,Scene Builder 是使用 JavaFX 本身构建的;这就是它能在所有平台上运行的原因。
要使用场景生成器,我们需要从 https://gluonhq.com/pDownload Scene Builderroducts/scene-builder/
下载(图 12-21 )。
图 12-21
场景构建器下载页面
为您的平台选择安装程序(图 12-22 ,然后点击下载按钮完成操作。
图 12-22
场景构建器下载链接
运行安装程序,并按照提示完成。启动场景构建器应用程序;图 12-23 显示了带有未命名项目的场景构建器应用程序。
图 12-23
场景生成器|无标题项目
构建 FXML 文件
让我们使用场景构建器构建一个简单的 UI。
Scene Builder 提供了一些很棒的 UI 入门模板。如果你点击场景构建器的主菜单栏,然后转到文件➤新建模板,你将从一个预建的用户界面模板中选择(如图 12-24 所示)。这是一个很好的开始方式。您可以简单地编辑或删除您不需要的 UI 组件,并从那里继续。
图 12-24
场景构建器模板
但我们不会走那条路。我们将从头开始。
在图 12-23 中,场景构建器的中间面板就是我们的画布。我们只需在这个区域拖放 UI 组件。
我们首先需要一个容器。所以,打开手风琴(左面板,如图 12-25 )到容器,然后点击 VBox。
图 12-25
将 VBox 拖到画布上
在 VBox 上按住鼠标的同时,将其拖到画布上(这里显示“将库项目拖到这里”)。您应该在可视化编辑器中看到 VBox 小部件(图 12-26 )。
图 12-26
VBox 已添加
接下来,向 VBox 添加一个标签。打开手风琴控件到控件,然后选择标签(如图 12-27 )。
图 12-27
添加标签
使用与将 VBox 添加到画布相同的技术,将标签拖放到 VBox 中。
然后,添加一个文本字段和一个按钮。你应该有类似图 12-28 的东西。
图 12-28
标签、文本字段和按钮
如果您想要更改 UI 元素的特定属性,您可以在编辑器中选择它们,然后转到 Properties 部分。您可以更改属性,如对齐、字体、颜色或文本属性——在我的例子中,您可以在图 12-29 中看到。
图 12-29
如何编辑属性
如果您现在保存这个文件并使用纯文本编辑器打开它,您将看到类似于清单 12-3 中的内容。
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label text="Label" />
<TextField />
<Button mnemonicParsing="false" text="Button" />
</children>
</VBox>
Listing 12-3sample.fxml
您可以将它复制并粘贴到我们之前的“Hello World”应用程序的 sample.fxml 文件中,这样就可以工作了。考虑到 Scene Builder 和主要 ide(包括 IntelliJ)之间的集成质量,一些开发人员更喜欢这种工作流。
为场景构建器配置 IntelliJ
要在 IntelliJ 中打开 FXML 文件(可视化),我们需要告诉它在哪里可以找到 Scene Builder 应用程序。
打开首选项对话框(或者设置,对于 PC 用户来说),然后进入语言和框架,再进入 JavaFX 。参见图 12-30 。
图 12-30
首选项|语言和框架| JavaFX
您需要指向场景构建器应用程序的安装位置。在 macOS 中,这个位置一般是**/Applications/scene builder . app**;在 Windows 10 中,默认位置是C:\ Users \ user \ AppData \ Local \ scene builder . exe。
图 12-31 显示了场景构建器应用程序,指向它在/Applications 文件夹中的位置。
图 12-31
找到场景构建器应用程序的位置
单击“打开”按钮确认选择。
你应该返回到场景构建器路径被填充的首选项对话框,如图 12-32 所示。
图 12-32
场景构建器的路径
单击“确定”按钮完成操作。
在场景构建器中打开文件
当你打开一个**。fxml** 文件在编辑器中,编辑区下面有两个标签页(如图 12-33 )。
图 12-33
FXML 编辑器选项卡
文本区域允许您编辑代码中的标记。
场景构建器选项卡打开。场景构建器中的 fxml(图 12-34 )。
图 12-34
IntelliJ 中的场景生成器
至此,我们结束这一章;但是让我带着一句警告离开。您需要设定您对场景构建器与 IntelliJ 配合使用的预期。在撰写本文时,IntelliJ 对 Scene Builder 的支持有点不稳定——大多数时候,它可以工作,但也有不工作的时候;两者我都经历过。
大多数情况下,Scene Builder 与 IntelliJ 集成的问题是 GUI 编辑器不会在 IntelliJ 中显示,但文本标记可以正常工作。我一直在使用 JetBrains 的问题跟踪( https://youtrack.jetbrains.com
)来跟踪这个问题,这个问题仍然没有结束(在撰写本文时)。希望在 Scene Builder 和 IntelliJ 的未来版本和更新中解决这个问题。
关键要点
-
如果您需要构建跨平台的桌面应用程序,JavaFX 是一个不错的技术选择。
-
您可以通过 API 构建 JavaFX 应用程序,但这很繁琐。请改用 FXML 文件来定义 UI。
-
您可以手工构建 FXML,但这也很繁琐;请使用可视化工具,如场景构建器。
-
如果你喜欢 JavaFX,你可以在
https://openjfx.io/
和https://gluonhq.com/
获得更多关于这项技术的信息。