提交 c2678ec5 编写于 作者: W wizardforcel

2021-05-11 21:31:46

上级 7fa79484
......@@ -35,7 +35,7 @@ JIT 使用代码执行的统计信息来优化代码,这也很有趣。例如
这些应用程序通过网络连接到证券交易所,它们根据市场变化在毫秒内进行股票买卖。Java 能够做到这一点。执行编译的 Java 代码所需的 Java 运行时环境(也包括 JVM 本身)包含允许 Java 程序访问网络、磁盘上的文件和其他资源的代码。为此,运行时包含代码可以实例化、执行的高级类,以及执行低级作业的高级类。你也要这样做。这意味着实际的 Java 代码不需要处理 IP 包、TCP 连接,甚至当它想要在某些微服务架构中使用或提供 REST 服务时,也不需要处理 HTTP。它已经在运行库中实现,应用程序程序员所要做的就是在代码中包含类,并在与程序匹配的抽象级别上使用它们提供的 API。当你用 Java 编程时,你可以专注于你想要解决的实际问题,那就是*业务*代码,而不是底层的系统代码。如果它不在标准库中,您将在某个外部库中的某个产品中找到它,并且您很可能会找到解决该问题的开源解决方案。
这也是 Java 的一个优点。有大量的开源库可用于各种不同的用途。如果您找不到适合您的问题的库,并且开始编写一些低级代码,那么您可能是做错了什么。本书中的一些主题很重要,比如类加载器或反射,不是因为你必须每天使用它们,而是因为它们被框架使用,了解它们有助于你理解这些框架是如何工作的。如果不使用反射或直接编写自己的类加载器或程序多线程就无法解决问题,那么您可能选择了错误的框架。几乎可以肯定有一个很好的例子:Apache 项目、Google 和软件行业的许多其他重要参与者将其 Java 库发布为开源。
这也是 Java 的一个优点。有大量的开源库可用于各种不同的用途。如果您找不到适合您的问题的库,并且开始编写一些低级代码,那么您可能是做错了什么。本书中的一些主题很重要,比如类加载器或反射,不是因为你必须每天使用它们,而是因为它们被框架使用,了解它们有助于你理解这些框架是如何工作的。如果不使用反射或直接编写自己的类加载器或程序多线程就无法解决问题,那么您可能选择了错误的框架。几乎可以肯定有一个很好的例子:ApacheCommons 、Google 和软件行业的许多其他重要参与者将其 Java 库发布为开源。
多线程编程也是如此。Java 从一开始就是一个多线程编程环境。JVM 和运行时支持执行代码的程序。执行在多个线程上并行运行。有一些运行时语言结构支持程序的并行执行。这些构造中的一些是非常低级的,而另一些则处于高度抽象级别。多线程代码利用多核处理器,这是更有效的。这些处理器越来越普遍。20 年前,只有高端服务器有多个处理器,只有数字 Alpha 处理器有 64 位架构,CPU 时钟高于 100MHz。10 年前,多处理器结构在服务器端很常见,大约 5 年前,多核处理器出现在一些台式机和笔记本电脑上;今天,甚至手机也有。当 Java 在 1995 年诞生时,创造它的天才们已经看到了这个未来。
......
......@@ -1016,7 +1016,7 @@ System.out.println(d); //prints: 42.0
请注意,只有在将原始类型转换为相应的包装器类型时,才能进行自动装箱。否则,编译器将生成一个错误。
`Byte``Short`wrappers 的方法`valueOf()`的输入值需要强制转换,因为这是我们在上一节讨论过的原始类型的缩小。
`Byte``Short`包装器的方法`valueOf()`的输入值需要强制转换,因为这是我们在上一节讨论过的原始类型的缩小。
# 拆箱
......@@ -1066,7 +1066,7 @@ System.out.println(d3); //prints: 42
本章最后描述了原始类型、包装类型和`String`文字之间转换的过程和方法。
在下一章中,我们将讨论 Java 异常框架、checked 和 unchecked(运行时)异常、`try-catch-finally`块、`throws``throw`语句,以及异常处理的最佳实践。
在下一章中,我们将讨论 Java 异常框架、受检和非受检(运行时)异常、`try-catch-finally`块、`throws``throw`语句,以及异常处理的最佳实践。
# 测验
......@@ -1085,7 +1085,7 @@ System.out.println(d3); //prints: 42
3. 选择所有正确的语句:
1. 如果没有访问修饰符,该类只能由同一包的其他类和接口访问
2. 私有类的私有方法可以被同一`.java`文件中声明的其他类访问
3. 私有类的 public 方法可以被不在同一`.java`文件中声明但来自同一包的其他类访问
3. 私有类的`public`方法可以被不在同一`.java`文件中声明但来自同一包的其他类访问
4. 受保护的方法只能由类的后代访问
4. 选择所有正确的语句:
......
# 异常处理
我们在第 1 章“Java12 入门”中简要介绍了异常。在本章中,我们将更系统地讨论这个问题。Java 中有两种异常:checked 异常和 unchecked 异常。两者都将被演示,并解释两者之间的区别。读者还将了解与异常处理相关的 Java 构造的语法以及处理异常的最佳实践。本章将以可用于调试生产代码的断言语句的相关主题结束。
我们在第 1 章“Java12 入门”中简要介绍了异常。在本章中,我们将更系统地讨论这个问题。Java 中有两种异常:受检异常和非受检异常。两者都将被演示,并解释两者之间的区别。读者还将了解与异常处理相关的 Java 构造的语法以及处理异常的最佳实践。本章将以可用于调试生产代码的断言语句的相关主题结束。
本章将讨论以下主题:
......@@ -140,7 +140,7 @@ void someMethod(String s){
如果没有一个子句捕捉到异常,则会进一步抛出异常,直到它被某个方法调用者中的`try...catch`语句处理,或者传播到应用程序代码之外。在这种情况下,JVM 终止应用程序并退出。
添加一个`finally`块不会改变所描述的行为。如果存在,不管是否生成了异常,它总是被执行。`finally`块通常用于释放资源:关闭数据库连接、文件等。但是,如果资源实现了`Closeable`接口,那么最好使用 try with resources 语句,该语句允许自动释放资源。下面是如何使用 Java7 实现的:
添加一个`finally`块不会改变所描述的行为。如果存在,不管是否生成了异常,它总是被执行。`finally`块通常用于释放资源:关闭数据库连接、文件等。但是,如果资源实现了`Closeable`接口,那么最好使用资源尝试语句,该语句允许自动释放资源。下面是如何使用 Java7 实现的:
```java
try (Connection conn = DriverManager.getConnection("dburl",
......@@ -158,7 +158,7 @@ try (Connection conn = DriverManager.getConnection("dburl",
本例创建数据库连接,检索数据并对其进行处理,然后关闭(调用`close()`方法)`conn``rs`对象。
Java9 增强了 try with resources 语句功能,允许创建表示`try`块外资源的对象,然后在 try with resources 语句中使用这些对象,如下所示:
Java9 增强了资源尝试语句功能,允许创建表示`try`块外资源的对象,然后在资源尝试语句中使用这些对象,如下所示:
```java
void method(Connection conn, ResultSet rs) {
......@@ -173,11 +173,11 @@ void method(Connection conn, ResultSet rs) {
}
```
前面的代码看起来更简洁,尽管在实践中,程序员更喜欢在同一上下文中创建和释放(关闭)资源。如果这也是您的偏好,请考虑将`throws`语句与 try with resources 语句结合使用。
前面的代码看起来更简洁,尽管在实践中,程序员更喜欢在同一上下文中创建和释放(关闭)资源。如果这也是您的偏好,请考虑将`throws`语句与资源尝试语句结合使用。
# throws 语句
# `throws`语句
前面使用 try with resources 语句的示例可以使用在相同上下文中创建的资源对象重新编写,如下所示:
前面使用资源尝试语句的示例可以使用在相同上下文中创建的资源对象重新编写,如下所示:
```java
Connection conn;
......@@ -200,7 +200,7 @@ try (conn; rs) {
}
```
我们必须处理`SQLException`,因为它是一个 checked 异常,`getConnection()``createStatement()``executeQuery()``next()`方法在它们的`throws`子句中声明它,下面是一个例子:
我们必须处理`SQLException`,因为它是一个受检异常,`getConnection()``createStatement()``executeQuery()``next()`方法在它们的`throws`子句中声明它,下面是一个例子:
```java
Statement createStatement() throws SQLException;
......@@ -226,7 +226,7 @@ void throwsDemo() throws SQLException {
最后,如果方法抛出几个不同的异常,可以列出基本的`Exception`异常类,而不是列出所有异常。这将使编译器感到高兴,但这并不是一个好的实践,因为它隐藏了方法用户可能期望的特定异常的细节。
请注意,编译器不会检查方法体中的代码可以引发何种异常。因此,可以在`throws`子句中列出任何异常,这可能会导致不必要的开销。如果程序员错误地在`throws`子句中包含一个 checked 异常,而该异常从未被方法实际抛出,那么该方法的用户可能会为它编写一个从未执行过的`catch`
请注意,编译器不会检查方法体中的代码可以引发何种异常。因此,可以在`throws`子句中列出任何异常,这可能会导致不必要的开销。如果程序员错误地在`throws`子句中包含一个受检异常,而该异常从未被方法实际抛出,那么该方法的用户可能会为它编写一个从未执行过的`catch`
# throw 语句
......@@ -314,7 +314,7 @@ boolean assertSomething(int x, String y, double z){
# 摘要
本章向读者介绍了 Java 异常处理框架,了解了两种异常:checked 和 unchecked(运行时),以及如何使用`try-catch-finally``throws`语句处理它们。读者还学习了如何生成(抛出)异常以及如何创建自己的(自定义)异常。本章最后介绍了异常处理的最佳实践。
本章向读者介绍了 Java 异常处理框架,了解了两种异常:受检和非受检(运行时),以及如何使用`try-catch-finally``throws`语句处理它们。读者还学习了如何生成(抛出)异常以及如何创建自己的(自定义)异常。本章最后介绍了异常处理的最佳实践。
在下一章中,我们将详细讨论字符串及其处理,以及输入/输出流和文件读写技术。
......
......@@ -28,7 +28,7 @@ System.out.println("0 0".length()); //prints: 3
```
当字符串长度(字符数)为`0`时,下面的`isEmpty()`方法返回 true
当字符串长度(字符数)为`0`时,下面的`isEmpty()`方法返回`true`
```java
System.out.println("".isEmpty()); //prints: true
......@@ -242,7 +242,7 @@ System.out.println("ab".repeat(0)); //prints:
```
如果字符串长度为`0`或只包含空格,`isBlank()`方法返回 true。例如:
如果字符串长度为`0`或只包含空格,`isBlank()`方法返回`true`。例如:
```java
System.out.println("".isBlank()); //prints: true
......@@ -357,7 +357,7 @@ System.out.println("'" + StringUtils.trimToEmpty(" ") + "'"); // ''
`java.io`包包含支持许多(但不是所有)可能数据源的类。它主要围绕文件、网络流和内部内存缓冲区的输入来构建。它不包含许多网络通信所必需的类。它们属于 Java 网络 API 的`java.net``javax.net`等包。只有在建立了网络源或目的地(例如网络套接字)之后,程序才能使用`java.io`包的`InputStream``OutputStream`类读写数据
`java.nio`包的类与`java.io`包的类具有几乎相同的功能。但是,除此之外,它们还可以在非阻塞的 T3 模式下工作,这可以在某些情况下显著提高性能。我们将在第 15 章“反应式编程”中讨论非阻塞处理。
`java.nio`包的类与`java.io`包的类具有几乎相同的功能。但是,除此之外,它们还可以在非阻塞的模式下工作,这可以在某些情况下显著提高性能。我们将在第 15 章“反应式编程”中讨论非阻塞处理。
# 流数据
......@@ -373,7 +373,7 @@ System.out.println("'" + StringUtils.trimToEmpty(" ") + "'"); // ''
在本节的其余部分,我们将回顾`java.io`包的类以及其他包中一些流行的相关类。
# 类 InputStream 及其子类
# 类`InputStream`及其子类
在 Java 类库中,`InputStream`抽象类有以下直接实现:`ByteArrayInputStream``FileInputStream``ObjectInputStream``PipedInputStream``SequenceInputStream``FilterInputStream``javax.sound.sampled.AudioInputStream`
......@@ -545,7 +545,7 @@ try (ObjectOutputStream objectOutputStream =
}
```
请注意,在运行前面的代码之前,必须先创建文件。我们将在“创建文件和目录”一节中展示如何进行。并且,为了提醒您,我们使用了 try with resources 语句,因为`InputStream``OutputStream`都实现了`Closeable`接口
请注意,在运行前面的代码之前,必须先创建文件。我们将在“创建文件和目录”一节中展示如何进行。并且,为了提醒您,我们使用了资源尝试语句,因为`InputStream``OutputStream`都实现了`Closeable`接口
# 管道输入流
......@@ -634,7 +634,7 @@ try {
}
```
如您所见,worker 的对象被传递到了`Thread`类的构造器中。`Thread`对象的`start()`方法执行传入的`Runnable``run()`方法。我们看到了我们预期的结果,`PipedInputWorker`打印了`PipedOutputWorker`写入管道流的所有字节。我们将在第 8 章“多线程和并发处理”中详细介绍线程。
如您所见,工作器的对象被传递到了`Thread`类的构造器中。`Thread`对象的`start()`方法执行传入的`Runnable``run()`方法。我们看到了我们预期的结果,`PipedInputWorker`打印了`PipedOutputWorker`写入管道流的所有字节。我们将在第 8 章“多线程和并发处理”中详细介绍线程。
# 顺序输入流
......@@ -703,15 +703,15 @@ try(FileInputStream fis =
`javax.crypto.CipherInputStream`类将`Cipher`添加到`read()`方法中。如果`Cipher`初始化为解密,`javax.crypto.CipherInputStream`将在返回之前尝试解密数据。
`java.util.zip.DeflaterInputStream`类以 deflate 压缩格式压缩数据。
`java.util.zip.DeflaterInputStream`类以 Deflate 压缩格式压缩数据。
类以 deflate 压缩格式解压缩数据。
类以 Deflate 压缩格式解压缩数据。
`java.security.DigestInputStream`类使用流经流的位来更新相关的消息摘要。`on (boolean on)`方法打开或关闭摘要功能。计算的摘要可使用`getMessageDigest()`方法检索。
`javax.swing.ProgressMonitorInputStream`类提供了对`InputStream`读取进度的监控。可以使用`getProgressMonitor()`方法访问监控对象。
# javax.sound.sampled 文件.音频输入流
# `javax.sound.sampled.AudioInputStream`
`AudioInputStream`类表示具有指定音频格式和长度的输入流。它有以下两个构造器:
......@@ -722,7 +722,7 @@ try(FileInputStream fis =
还有一个`javax.sound.sampled.AudioSystem`类,它的方法处理`AudioInputStream`对象。它们可用于读取音频文件、流或 URL,以及写入音频文件,还可用于将音频流转换为其他音频格式。
# 类 OutputStream 及其子类
# 类`OutputStream`及其子类
`OutputStream`类是`InputStream`类的一个对等类,它是一个抽象类,在 **Java 类库****JCL**)中有以下直接实现:`ByteArrayOutputStream``FilterOutputStream``ObjectOutputStream``PipedOutputStream``FileOutputStream`
......@@ -840,7 +840,7 @@ System.out.printf("Hi ")
至此,我们结束了对`OutputStream`子类的讨论,现在将注意力转向另一个类层次结构`Reader``Writer`类及其子类。
# 类 reader 和 writer 及其子类
# 类`Reader`和`Writer`及其子类
正如我们已经多次提到的,`Reader``Writer`类在功能上与`InputStream``OutputStream`类非常相似,但专门处理文本。它们将流字节解释为字符,并有自己独立的`InputStream``OutputStream`类层次结构。在没有`Reader``Writer`或它们的任何子类的情况下,可以将流字节作为字符进行处理。我们在前面描述`InputStream``OutputStream`类的章节中看到了这样的示例。但是,使用`Reader``Writer`类可以简化文本处理,代码更易于阅读。
......@@ -864,7 +864,7 @@ System.out.printf("Hi ")
如您所见,唯一需要实现的方法是两个抽象的`read()``close()`方法。然而,这个类的许多子类也重写了其他方法,有时是为了更好的性能或不同的功能。JCL 中的`Reader`子类是:`CharArrayReader``InputStreamReader``PipedReader``StringReader``BufferedReader``FilterReader``BufferedReader`类有`LineNumberReader`子类,`FilterReader`类有`PushbackReader`子类。
# Writer 及其子类
# `Writer`及其子类
抽象的`Writer`类写入字符流。它是`OutputStream`的一个模拟,具有以下方法:
......@@ -884,7 +884,7 @@ System.out.printf("Hi ")
JCL 中的`Writer`子类是:`CharArrayWriter``OutputStreamWriter``PipedWriter``StringWriter``BufferedWriter``FilterWriter``PrintWriter``OutputStreamWriter`类有一个`FileWriter`子类。
# 其他类别的 java.io 文件包裹
# `java.io`包的其他类
`java.io`包的其他类别包括:
......@@ -982,7 +982,7 @@ String line = console.readLine("Enter some%s", "thing:");
```
最后,Console 类的最后三个方法如下:
最后,`Console`类的最后三个方法如下:
* `PrintWriter writer()`:创建一个与此控制台关联的`PrintWriter`对象,用于生成字符的输出流
* `Reader reader()`:创建一个与此控制台相关联的`Reader`对象,用于将输入作为字符流读取
......@@ -1098,7 +1098,7 @@ try(FileReader fr = new FileReader(filePath);
如您所见,使用前面丰富的方法可以对文本解释进行微调。
# ObjectStreamClass 和 ObjectStreamField
# `ObjectStreamClass`和`ObjectStreamField`
`ObjectStreamClass``ObjectStreamField`类提供对 JVM 中加载的类的序列化数据的访问。`ObjectStreamClass`对象可以使用以下查找方法之一找到/创建:
......@@ -1192,7 +1192,7 @@ printInfo(osc3);
从一个不可序列化的对象中,我们可以提取关于类的信息,但不能提取关于字段的信息。
# 班级 java.util.Scanner 文件
# `java.util.Scanner`类
`java.util.Scanner`类通常用于从键盘读取输入,但可以从实现`Readable`接口的任何对象读取文本(该接口只有`int read(CharBuffer buffer)`方法)。它用一个分隔符(空白是默认分隔符)将输入值拆分为使用不同方法处理的标记。
......@@ -1417,7 +1417,7 @@ for(File f: File.listRoots()){
但是,对文件过滤器的讨论超出了本书的范围。
# Apache 公共实用程序 FileUtils 和 IOUtils
# Apache 公共实用程序`FileUtils`和`IOUtils`
JCL 最流行的伙伴是 [ApacheCommons 项目](https://commons.apache.org),它提供了许多库来补充 JCL 功能。`org.apache.commons.io`包的类包含在以下根包和子包中:
......@@ -1432,7 +1432,7 @@ JCL 最流行的伙伴是 [ApacheCommons 项目](https://commons.apache.org),
请参阅 [Apache Commons 项目文档](https://commons.apache.org)了解更多细节。
# 类 FileUtils
# 类`FileUtils`
一个流行的`org.apache.commons.io.FileUtils`类允许对您可能需要的文件执行所有可能的操作,如下所示:
......
......@@ -25,7 +25,7 @@ Java 集合支持存储和访问集合元素的各种算法:有序列表、唯
要标识集合中的元素,请使用`equals()`方法。为了提高性能,实现`Set`接口的类也经常使用`hashCode()`方法。它允许快速计算一个整数(称为**散列值****散列码**),该整数在大多数时间(但并非总是)对每个元素都是唯一的。具有相同哈希值的元素被放置在相同的*桶*中。在确定集合中是否已经存在某个值时,检查内部哈希表并查看是否已经使用了这样的值就足够了。否则,新元素是唯一的。如果是,则可以将新元素与具有相同哈希值的每个元素进行比较(使用`equals()`方法)。这样的过程比逐个比较新元素和集合中的每个元素要快
这就是为什么我们经常看到类的名称有“Hash”前缀,表示类使用了 Hash 值,所以元素必须实现`hashCode()`方法,在实现时一定要确保`equals()`方法每次为两个对象返回`true`时,`hashCode()`方法返回的这两个对象的散列值也是相等的。否则,所有刚才描述的使用哈希值的算法都将不起作用。
这就是为什么我们经常看到类的名称有`Hash`前缀,表示类使用了哈希值,所以元素必须实现`hashCode()`方法,在实现时一定要确保`equals()`方法每次为两个对象返回`true`时,`hashCode()`方法返回的这两个对象的散列值也是相等的。否则,所有刚才描述的使用哈希值的算法都将不起作用。
最后,在讨论`java.util`接口之前,先谈一下泛型。
......@@ -61,7 +61,7 @@ list.add(42); //compilation error
* `<? extends T>`表示`T``T`的子类型,其中`T`是用作集合泛型的类型
* `<? super T>`表示`T`或其任何基(父)类,其中`T`是用作集合泛型的类型
那么,让我们从实现`List``Set`接口的类的对象的创建方式开始,或者换句话说,可以初始化`List``Set`类型的变量。为了演示这两个接口的方法,我们将使用两个类:an`ArrayList`(实现`List`)和`HashSet`(实现`Set`)。
那么,让我们从实现`List``Set`接口的类的对象的创建方式开始,或者换句话说,可以初始化`List``Set`类型的变量。为了演示这两个接口的方法,我们将使用两个类:`ArrayList`(实现`List`)和`HashSet`(实现`Set`)。
# 如何初始化列表和设置
......@@ -198,7 +198,7 @@ System.out.println(list3); //prints: [s1, s2]
现在,在我们了解了如何初始化集合之后,我们可以转向接口`List``Set`中的其他方法。
# 接口 java.lang.Iterable 语言
# `java.lang.Iterable`接口
`Collection`接口扩展了`java.lang.Iterable`接口,这意味着那些直接或不直接实现`Collection`接口的类也实现了`java.lang.Iterable`接口。`Iterable`界面只有三种方式:
......@@ -374,7 +374,7 @@ System.out.println(list); //prints: [null, S2, s1, s3]
* 使用`Comparable`接口实现(称为**自然顺序**
* 使用`Comparator`接口实现
`Comparable`接口只有`compareTo()`方法。在前面的例子中,我们已经在`String`类中的`Comparable`接口实现的基础上实现了`Comparator`接口。如您所见,此实现提供了与`Comparator.nullsFirst(Comparator.naturalOrder())`相同的排序顺序,这种实现方式称为*“函数式编程”**,我们将在第 13 章*函数式编程中详细讨论。【第 13 页】**
`Comparable`接口只有`compareTo()`方法。在前面的例子中,我们已经在`String`类中的`Comparable`接口实现的基础上实现了`Comparator`接口。如您所见,此实现提供了与`Comparator.nullsFirst(Comparator.naturalOrder())`相同的排序顺序,这种实现方式称为**函数式编程**,我们将在第 13 章“函数式编程”中详细讨论。
*# 接口集
......@@ -486,7 +486,7 @@ System.out.println(list); //prints: [Person{age=45, name=Kelly}]
这些方法是静态的,这意味着它们不依赖于对象状态,因此它们也被称为**无状态方法****实用程序方法**
# 班级 java.util.Collections 文件
# `java.util.Collections`类
`Collections`类中有许多方法可以管理集合、分析、排序和比较它们。其中有 70 多个,所以我们没有机会谈论所有这些问题。相反,我们将研究主流应用程序开发人员最常使用的:
......@@ -573,7 +573,7 @@ System.out.println(persons); //prints: [Person{name=Bob, age=15},
正如我们已经提到的,`Collections`类中还有更多的实用程序,因此我们建议您至少查看一次它的文档并查看所有的功能。
# Apache 项目的类 CollectionUtils
# ApacheCommons `CollectionUtils`类
ApacheCommons 项目中的`org.apache.commons.collections4.CollectionUtils`类包含静态无状态方法,这些方法是对`java.util.Collections`类方法的补充,它们有助于搜索、处理和比较 Java 集合。
......@@ -612,7 +612,7 @@ ApacheCommons 项目中的`org.apache.commons.collections4.CollectionUtils`类
我们将简要回顾其中的每一项。
# 班级 java.util.Arrays 数组
# `java.util.Arrays`类
我们已经用过几次了。它是阵列管理的主要实用程序类。这个实用程序类过去非常流行,因为有`asList(T...a)`方法。它是创建和初始化集合的最简洁的方法:
......@@ -655,7 +655,7 @@ System.out.println(Arrays.deepEquals(arr3, arr4)); //prints: true
如您所见,`Arrays.deepEquals()`每次比较两个相等的数组时,当一个数组的每个元素等于另一个数组在同一位置的元素时,返回`true`,而`Arrays.equals()`方法返回相同的结果,但只对一维数组。
# Apache 项目的类 ArrayUtils
# ApacheCommons `ArrayUtils`类
`org.apache.commons.lang3.ArrayUtils`类是对`java.util.Arrays`类的补充,它向数组管理工具箱添加了新方法,并且在可能抛出`NullPointerException`的情况下能够处理`null`。要使用这个类,您需要向 Maven`pom.xml`配置文件添加以下依赖项:
......@@ -691,7 +691,7 @@ System.out.println(Arrays.deepEquals(arr3, arr4)); //prints: true
它们在类创建期间特别有用,因此我们将主要关注与此任务相关的方法。
# 班级 java.util.Objects
# `java.util.Objects`类
`Objects`类只有 17 个方法都是静态的。在将它们应用于`Person`类时,我们来看看其中的一些方法,假设这个类是集合的一个元素,这意味着它必须实现`equals()``hashCode()`方法:
......@@ -739,7 +739,7 @@ System.out.println(Objects.deepEquals(x1, y)); //prints: false
`Objects.hash()`方法也处理空值。需要记住的一点是,`equals()`方法中比较的属性列表必须与作为参数传入`Objects.hash()`的属性列表相匹配。否则,两个相等的`Person`对象将具有不同的哈希值,这使得基于哈希的集合无法正常工作。
另一件值得注意的事情是,还有另一个与 hash 相关的`Objects.hashCode()`方法,它只接受一个参数。但是它产生的值并不等于只有一个参数的`Objects.hash()`产生的值。例如:
另一件值得注意的事情是,还有另一个与哈希相关的`Objects.hashCode()`方法,它只接受一个参数。但是它产生的值并不等于只有一个参数的`Objects.hash()`产生的值。例如:
```java
System.out.println(Objects.hash(42) == Objects.hashCode(42));
......@@ -846,7 +846,7 @@ for(String e: list){
在撰写本文时,`Objects`类有 17 种方法。我们建议您熟悉它们,以避免在已经存在相同实用程序的情况下编写自己的实用程序
# Apache 项目的类 ObjectUtils
# ApacheCommons `ObjectUtils`类
上一节的最后一条语句适用于 ApacheCommons 库的`org.apache.commons.lang3.ObjectUtils`类,它补充了上一节中描述的`java.util.Objects`类的方法。本书的范围和分配的大小不允许对`ObjectUtils`类的所有方法进行详细的回顾,因此我们将按相关功能分组对它们进行简要的描述。要使用这个类,您需要在 Maven`pom.xml`配置文件中添加以下依赖项:
......@@ -866,10 +866,10 @@ for(String e: list){
* 几个`identityToString()`方法生成所提供对象的`String`表示,就像由`toString()`生成一样,这是`Object`基类的默认方法,并且可选地将其附加到另一个对象
* 分析`null`的对象数组的`allNotNull()``anyNotNull()`方法
* `firstNonNull()``defaultIfNull()`方法,它们分析一个对象数组并返回第一个 not-`null`对象或默认值
* `firstNonNull()``defaultIfNull()`方法,它们分析一个对象数组并返回第一个`null`对象或默认值
* `max()``min()``median()``mode()`方法,它们分析一个对象数组并返回其中一个对应于方法名称的对象
# java.time 文件包装
# `java.time`包
`java.time`包及其子包中有许多类。它们是作为处理日期和时间的其他(旧的包)的替代品引入的。新类是线程安全的(因此,更适合多线程处理),同样重要的是,它们的设计更加一致,更易于理解。此外,新的实现在日期和时间格式上遵循了**国际标准组织****ISO**),但也允许使用任何其他自定义格式。
......@@ -883,7 +883,7 @@ for(String e: list){
所有这些,以及`java.time`包的其他类,以及它的子包都有丰富的功能,涵盖了所有的实际案例。但我们不打算讨论所有这些问题;我们将只介绍基本知识和最流行的用例。
# 类 LocalDate
# `LocalDate`类
班级`LocalDate`不带时间。它表示 ISO 8601 格式的日期(YYYY-MM-DD):
......@@ -1084,7 +1084,7 @@ System.out.println(lt2.isBefore(lt4)); //prints: true
`LocalTime`类中还有很多其他有用的方法,如果您需要处理日期,我们建议您阅读这个类的 API 以及`java.time`包及其子包的其他类。
# 类 LocalDateTime
# `LocalDateTime`
`LocalDateTime`类包含日期和时间,并且具有`LocalDate``LocalTime`类所具有的所有方法,因此我们不在这里重复它们。我们只展示如何创建`LocalDateTime`类的对象:
......
......@@ -31,11 +31,11 @@ JCL 是实现该语言的包的集合。更简单地说,它是 JDK 中包含
为了使用 JCL 包,可以导入它,而无需向`pom.xml`文件添加新的依赖项。这就是标准库和外部库的区别;如果您需要在 Maven`pom.xml`配置文件中添加一个库(通常是一个`.jar`文件)作为依赖项,那么这个库就是一个外部库。否则,它就是一个标准库或 JCL
一些 JCL 包名以`java`开头。传统上,它们被称为**核心 Java 包**,而那些以`javax`开头的包则被称为“扩展”。之所以这样做,可能是因为这些扩展被认为是可选的,甚至可能独立于 JDK 发布。也有人试图推动前扩展库成为一个核心包。但这将需要将包名从“java”更改为“javax”,这将打破使用`javax`包的现有应用程序。因此,这个想法被抛弃了,所以核心和扩展之间的区别逐渐消失。
一些 JCL 包名以`java`开头。传统上,它们被称为**核心 Java 包**,而那些以`javax`开头的包则被称为“扩展”。之所以这样做,可能是因为这些扩展被认为是可选的,甚至可能独立于 JDK 发布。也有人试图推动前扩展库成为一个核心包。但这将需要将包名从`java`更改为`javax`,这将打破使用`javax`包的现有应用程序。因此,这个想法被抛弃了,所以核心和扩展之间的区别逐渐消失。
这就是为什么,如果你在 Oracle 官方网站上查看 Java API,你会发现不仅有`java``javax`包被列为标准,还有`jdk``com.sun``org.xml`以及其他一些包。这些额外的包主要由工具或其他专用应用程序使用。在我们的书中,我们将主要集中在主流 Java 编程上,只讨论`java``javax`包。
# java.lang 语言
# `java.lang`
这个软件包非常重要,使用它不需要进口。JVM 作者决定自动导入它。它包含最常用的 JCL 类:
......@@ -57,7 +57,7 @@ JCL 是实现该语言的包的集合。更简单地说,它是 JDK 中包含
* `Process``ProcessBuilder`类:允许创建其他 JVM 进程
* 许多其他有用的类和接口
# java.util 文件
# `java.util`
`java.util`包的大部分内容专门用于支持 Java 集合:
......@@ -78,9 +78,9 @@ JCL 是实现该语言的包的集合。更简单地说,它是 JDK 中包含
* `Random`:通过生成伪随机数流来补充`java.lang.Math.random()`方法
* `StringTokeneizer`:将`String`对象分解为由指定分隔符分隔的标记
* `StringJoiner`:构造一个字符序列,由指定的分隔符分隔,并可选地由指定的前缀和后缀包围
* 许多其他有用的实用程序类,包括支持国际化和 base64 编码和解码的类
* 许多其他有用的实用程序类,包括支持国际化和 Base64 编码和解码的类
# java.time 文件
# `java.time`
`java.time`包包含用于管理日期、时间、时段和持续时间的类。包装包括以下内容:
......@@ -96,14 +96,14 @@ JCL 是实现该语言的包的集合。更简单地说,它是 JDK 中包含
我们在第 6 章、“数据结构、泛型和流行实用程序”中讨论了大多数此类。
# java.io 文件以及 java.nio 文件
# `java.io`以及`java.nio`
`java.io``java.nio`包包含支持使用流、序列化和文件系统读写数据的类和接口。这两种包装的区别如下:
* `java.io`包类允许在没有缓存的情况下读取/写入数据(我们在第 5 章、“字符串、输入/输出和文件”中讨论过),而`java.nio`包的类创建了一个缓冲区,允许在填充的缓冲区中来回移动
* `java.io`包类阻塞流直到所有数据被读写,而`java.nio`包的类以非阻塞方式实现(我们将在第 15 章、“反应式编程”中讨论非阻塞方式)
# java.sql 语言以及 javax.sql 文件
# `java.sql`以及`javax.sql`
这两个包组成了一个 **Java 数据库连接****JDBC**)API,它允许访问和处理存储在数据源(通常是关系数据库)中的数据。`javax.sql`包通过提供以下支持来补充`java.sql`包:
......@@ -129,13 +129,13 @@ JCL 是实现该语言的包的集合。更简单地说,它是 JDK 中包含
我们将讨论这个包,并在第 11 章、“网络编程”中看到代码示例。
# java.lang.math 语言以及 java.数学
# `java.lang.math`以及`java.math`
`java.lang.math`包包含执行基本数值运算的方法,例如计算两个数值的最小值和最大值、绝对值、初等指数、对数、平方根、三角函数以及许多其他数学运算。
`java.math`包通过允许使用`BigDecimal``BigInteger`类处理更大的数字,补充了`java.lang`包的 Java 基本类型和包装类。
# Java.awt, 加法计算器,和 javafx
# `Java.awt`,`javax.swing`,和 JavaFX
第一个支持为桌面应用程序构建**图形用户界面****GUI**)的 Java 库是`java.awt`包中的**抽象窗口工具包****AWT**)。它为执行平台的本机系统提供了一个接口,允许创建和管理窗口、布局和事件。它还具有基本的 GUI 小部件(如文本字段、按钮和菜单),提供对系统托盘的访问,并允许启动 Web 浏览器和通过 Java 代码向客户机发送电子邮件。它对本机代码的高度依赖使得基于 AWT 的 GUI 在不同的平台上看起来不同。
......@@ -151,7 +151,7 @@ JavaFX 基于**层叠样式表**(**CSS**),将平滑动画、Web 视图、
最常用的第三方非 JCL 库的不同列表包括 20 到 100 个库。在本节中,我们将讨论这些列表中的大多数。所有这些都是开源项目。
# 组织 junit
# `org.junit`
`org.junit`包是开源测试框架 JUnit 的根包。它可以作为以下`pom.xml`依赖项添加到项目中:
......@@ -209,7 +209,7 @@ public void multiplyByTwo() {
一个**单元**是一段可以测试的最小代码,因此它的名字。最佳测试实践将方法视为最小的可测试单元。这就是为什么单元测试通常测试方法。
# org.mockito 公司
# `org.mockito`
单元测试经常面临的问题之一是需要测试使用第三方库、数据源或其他类的方法的方法。在测试时,您希望控制所有的输入,以便可以预测测试代码的预期结果。在这一点上,模拟或模拟被测试代码与之交互的对象的行为的技术就派上了用场。
......@@ -258,7 +258,7 @@ int result = someClass.multiplyByTwoTheValueFromSomeOtherClass(mo);
```
5. mock 方法返回预定义的结果:
5. 模拟方法返回预定义的结果:
```java
Assert.assertEquals(10, result);
......@@ -281,13 +281,13 @@ public void multiplyByTwoTheValueFromSomeOtherClass() {
Mockito 有一定的局限性。例如,不能模拟静态方法和私有方法。否则,通过可靠地预测所使用的第三方类的结果来隔离正在测试的代码是一个很好的方法
# org.apache.log4j 以及组织 slf4j
# `org.apache.log4j`以及`org.slf4j`
在这本书中,我们使用`System.out`来显示结果。在实际应用程序中,也可以这样做,并将输出重定向到一个文件,例如,用于以后的分析。在做了一段时间之后,您会注意到您需要关于每个输出的更多细节:例如,每个语句的日期和时间以及生成日志语句的类名。随着代码库的增长,您会发现最好将不同子系统或包的输出发送到不同的文件,或者在一切正常时关闭一些消息,在检测到问题并且需要有关代码行为的更详细信息时再打开这些消息。您不希望日志文件的大小无法控制地增长。
您可以编写自己的代码来完成这一切。但是有几种框架是基于配置文件中的设置来实现的,您可以在每次需要更改日志记录行为时更改这些设置。最常用的两个框架是`log4j`(发音为 *LOG-FOUR-JAY*)和`slf4j`(发音为 *S-L-F-FOUR-JAY*)。
事实上,这两个框架并不是对手。`slf4j`框架是一个 facade,提供对底层实际日志框架的统一访问,其中一个也可以是`log4j`。当程序员事先不知道使用库的应用程序将使用什么样的日志框架时,这种外观在库开发期间尤其有用。通过使用`slf4j`编写代码,程序员允许稍后将其配置为使用任何日志系统。
事实上,这两个框架并不是对手。`slf4j`框架是一个外观,提供对底层实际日志框架的统一访问,其中一个也可以是`log4j`。当程序员事先不知道使用库的应用程序将使用什么样的日志框架时,这种外观在库开发期间尤其有用。通过使用`slf4j`编写代码,程序员允许稍后将其配置为使用任何日志系统。
因此,如果您的代码将仅由您的团队开发的应用程序使用,那么仅使用`log4j`就足够了。否则,请考虑使用`slf4j`
......@@ -363,9 +363,9 @@ Process finished with exit code 1
如果这对您来说还不够好,可以将配置更改为记录不同级别的消息、不同的文件等等。[阅读`log4J`文档](https://logging.apache.org)
# org.apache.commons 网站
# `org.apache.commons`
`org.apache.commons`包是另一个流行的库,它是作为一个名为 **Apache Commons** 的项目开发的。它由一个名为 **Apache 软件基金会**的开源程序员社区维护。这个组织是 1999 年由阿帕奇集团成立的。自 1993 年以来,Apache 小组一直围绕 apachehttp 服务器的开发而发展。apachehttp 服务器是一个开源的跨平台 Web 服务器,自 1996 年 4 月以来一直是最流行的 Web 服务器。
`org.apache.commons`包是另一个流行的库,它是作为一个名为 **Apache Commons** 的项目开发的。它由一个名为 **Apache 软件基金会**的开源程序员社区维护。这个组织是 1999 年由阿帕奇集团成立的。自 1993 年以来,Apache 小组一直围绕 Apache HTTP 服务器的开发而发展。Apache HTTP 服务器是一个开源的跨平台 Web 服务器,自 1996 年 4 月以来一直是最流行的 Web 服务器。
Apache Commons 项目包括以下三个部分:
......@@ -382,9 +382,9 @@ Apache Commons 项目包括以下三个部分:
但是`org.apache.commons`下还有更多的包,其中包含数千个类,这些类很容易使用,可以帮助您的代码更加优雅和高效。
# 朗和朗 3
# `lang`和`lang3`
`org.apache.commons.lang3`包实际上是`org.apache.commons.lang`包的版本 3。创建新包的决定是由于版本 3 中引入的更改是向后不兼容的,这意味着使用先前版本的`org.apache.commons.lang`包的现有应用程序在升级到版本 3 后可能会停止工作。但在大多数主流编程中,向 import 语句添加`3`(作为迁移到新版本的方法)通常不会破坏任何东西。
`org.apache.commons.lang3`包实际上是`org.apache.commons.lang`包的版本 3。创建新包的决定是由于版本 3 中引入的更改是向后不兼容的,这意味着使用先前版本的`org.apache.commons.lang`包的现有应用程序在升级到版本 3 后可能会停止工作。但在大多数主流编程中,向`import`语句添加`3`(作为迁移到新版本的方法)通常不会破坏任何东西。
据文献记载,`org.apache.commons.lang3`包提供了高度可重用的静态实用方法,主要是为`java.lang`类增加价值。这里有几个值得注意的例子:
......@@ -397,9 +397,9 @@ Apache Commons 项目包括以下三个部分:
* `RandomStringUtils`类:根据不同字符集的字符生成`String`对象
* `StringUtils`课堂:我们在第 5 章中讨论了“字符串、输入/输出和文件”
# 收藏 4
# `collections4`
尽管从表面上看,`org.apache.commons.collections4`包的内容与`org.apache.commons.collections`包(即包的版本 3)的内容非常相似,但迁移到版本 4 可能不如在 import 语句中添加“4”那么顺利。版本 4 删除了不推荐使用的类,添加了泛型和其他与以前版本不兼容的特性。
尽管从表面上看,`org.apache.commons.collections4`包的内容与`org.apache.commons.collections`包(即包的版本 3)的内容非常相似,但迁移到版本 4 可能不如在`import`语句中添加“4”那么顺利。版本 4 删除了不推荐使用的类,添加了泛型和其他与以前版本不兼容的特性。
要想得到一个在这个包或它的一个子包中不存在的集合类型或集合实用程序,必须很困难。以下只是包含的功能和实用程序的高级列表:
......
# 多线程和并发处理
在本章中,我们将讨论通过使用并发处理数据的 worker(线程)来提高 Java 应用程序性能的方法。我们将解释 Java 线程的概念并演示它们的用法。我们还将讨论并行处理和并发处理的区别,以及如何避免由于并发修改共享资源而导致的不可预知的结果。
在本章中,我们将讨论通过使用并发处理数据的工作器(线程)来提高 Java 应用程序性能的方法。我们将解释 Java 线程的概念并演示它们的用法。我们还将讨论并行处理和并发处理的区别,以及如何避免由于并发修改共享资源而导致的不可预知的结果。
本章将讨论以下主题:
......@@ -60,7 +60,7 @@ class MyThread extends Thread {
}
```
如果未重写`run()`方法,则线程不执行任何操作。在我们的示例中,只要参数不等于 string`"exit"`,线程就会每秒打印它的名称和其他属性;否则它就会退出。`pauseOneSecond()`方法如下:
如果未重写`run()`方法,则线程不执行任何操作。在我们的示例中,只要参数不等于字符串`"exit"`,线程就会每秒打印它的名称和其他属性;否则它就会退出。`pauseOneSecond()`方法如下:
```java
private static void pauseOneSecond(){
......@@ -148,7 +148,7 @@ public static void main(String... args) {
守护进程线程(名为`Two`的线程)在最后一个用户线程存在后退出,它与`MyThread`类的情况完全相同。
# 扩展线程与实现 Runnable
# 扩展线程与实现`Runnable`
`Runnable`的实现具有允许实现扩展另一个类的优点(在某些情况下是唯一可能的选择)。当您想向现有类添加类似线程的行为时,它特别有用。实现`Runnable`允许更灵活的使用。但除此之外,与`Thread`类的扩展相比,在功能上没有区别。
......@@ -316,7 +316,7 @@ class MyRunnable implements Runnable {
但是,如果应用程序可能需要的工作线程没有固定的限制,或者没有很好的方法来预测线程可能需要多少内存或可以执行多长时间,那么设置工作线程计数的上限可以防止应用程序性能的意外降级、内存不足或资源耗尽工作线程使用的任何其他资源。如果线程行为极不可预测,那么单线程池可能是唯一的解决方案,可以选择使用自定义线程池执行器。但在大多数情况下,固定大小的线程池执行器是应用程序需求和代码复杂性之间的一个很好的实际折衷方案(在本节前面,我们列出了由`Executors`工厂类创建的所有可能的池类型)
将池的大小设置得过低可能会剥夺应用程序有效利用可用资源的机会。因此,在选择池大小之前,建议花一些时间监视应用程序,以确定应用程序行为的特性。事实上,为了适应和利用代码或执行环境中发生的更改,必须在应用程序的整个生命周期中重复 cycle deploy monitor adjust
将池的大小设置得过低可能会剥夺应用程序有效利用可用资源的机会。因此,在选择池大小之前,建议花一些时间监视应用程序,以确定应用程序行为的特性。事实上,为了适应和利用代码或执行环境中发生的更改,必须在应用程序的整个生命周期中重复“循环部署监视调整”
考虑的第一个特征是系统中 CPU 的数量,因此线程池的大小至少可以与 CPU 的计数一样大。然后,您可以监视应用程序,查看每个线程占用 CPU 的时间以及占用其他资源(如 I/O 操作)的时间。如果不使用 CPU 所花费的时间与线程的总执行时间相当,则可以按以下比率增加池大小:不使用 CPU 的时间除以总执行时间。但这是在另一个资源(磁盘或数据库)不是线程间争用的主题的情况下。如果是后者,那么您可以使用该资源而不是 CPU 作为描述因子。
......@@ -739,7 +739,7 @@ class CalculatorSyncBlock implements Calculator {
}
```
如您所见,synchronized 块在`this`对象上获取一个锁,该锁由两个线程共享,并且只有在线程退出块之后才释放它。在我们的演示代码中,该块覆盖了该方法的所有代码,因此在性能上没有差异。但是想象一下这个方法中有更多的代码(我们将位置注释为`there may be some other code here`。如果是这样的话,代码的同步部分就更小,因此成为瓶颈的机会就更少。
如您所见,`synchronized`块在`this`对象上获取一个锁,该锁由两个线程共享,并且只有在线程退出块之后才释放它。在我们的演示代码中,该块覆盖了该方法的所有代码,因此在性能上没有差异。但是想象一下这个方法中有更多的代码(我们将位置注释为`there may be some other code here`。如果是这样的话,代码的同步部分就更小,因此成为瓶颈的机会就更少。
如果我们运行`invokeAllCallables(new CalculatorSyncBlock())`,结果如下:
......@@ -758,7 +758,7 @@ Java 中的每个对象都从基对象继承了`wait()`、`notify()`和`notifyAl
* `ConcurrentLinkedDeque<E>`:基于链接节点的并发队列,当多个线程共享对一个公共集合的访问时,`ConcurrentLinkedQueque``ConcurrentLinkedDeque`都是合适的选择。
* `ConcurrentSkipListMap<K,V>`:并发`ConcurrentNavigableMap`接口实现。
* `ConcurrentSkipListSet<E>`:基于`ConcurrentSkipListMap`的并发`NavigableSet`实现。`ConcurrentSkipListSet``ConcurrentSkipListMap`类,根据 *Javadoc*,对包含、添加和删除操作及其变体,提供预期平均`O(logn)`时间成本。升序视图及其迭代器的速度比降序视图快;当您需要按特定顺序快速遍历元素时,请使用它们。
* `CopyOnWriteArrayList<E>`:一种线程安全的`ArrayList`变体,所有的修改操作(add、set 等)都是通过对底层数组进行一个新的拷贝来实现的;根据 *Javadoc*`CopyOnWriteArrayList`类通常成本太高,但当遍历操作的数量远远超过修改时,它可能比其他方法更有效,当您不能或不想同步遍历,但需要排除并发线程之间的干扰时,它会很有用;当您不需要在不同位置添加新元素且不需要排序时,使用它;否则,使用`ConcurrentSkipListSet`
* `CopyOnWriteArrayList<E>`:一种线程安全的`ArrayList`变体,所有的修改操作(`add``set`等)都是通过对底层数组进行一个新的拷贝来实现的;根据 *Javadoc*`CopyOnWriteArrayList`类通常成本太高,但当遍历操作的数量远远超过修改时,它可能比其他方法更有效,当您不能或不想同步遍历,但需要排除并发线程之间的干扰时,它会很有用;当您不需要在不同位置添加新元素且不需要排序时,使用它;否则,使用`ConcurrentSkipListSet`
* `CopyOnWriteArraySet<E>`:所有操作都使用内部`CopyOnWriteArrayList`的集合。
* `PriorityBlockingQueue`:当一个自然的顺序是可以接受的,并且您需要快速向尾部添加元素和快速从队列头部移除元素时,这是一个更好的选择;**阻塞**是指队列在检索元素时等待变为非空,在存储元素时等待队列中的空间变为可用。
......@@ -811,7 +811,7 @@ System.out.println("\n" + list); //prints: [One, Two, Three, Three]
在多线程环境中,内存一致性错误可能有多种形式和原因。它们在`java.util.concurrent`包的 *Javadoc* 中有很好的讨论。在这里,我们将只提到最常见的情况,这是由于缺乏能见度造成的。
当一个线程更改属性值时,另一个线程可能不会立即看到更改,并且不能对原始类型使用 synchronized 关键字。在这种情况下,可以考虑对属性使用`volatile`关键字;它保证了不同线程之间的读/写可见性。
当一个线程更改属性值时,另一个线程可能不会立即看到更改,并且不能对原始类型使用`synchronized`关键字。在这种情况下,可以考虑对属性使用`volatile`关键字;它保证了不同线程之间的读/写可见性。
并发问题不容易解决。这就是为什么现在越来越多的开发人员采取更激进的方法也就不足为奇了。他们更喜欢在一组无状态操作中处理数据,而不是管理对象状态。我们将在第 13 章、“函数式编程”和第 14 章、“Java 标准流”中看到这些代码的示例。Java 和许多现代语言以及计算机系统似乎正朝着这个方向发展。
......
......@@ -22,7 +22,7 @@ JVM 只是根据编码逻辑执行指令的执行器。它还发现并将应用
* 运行/执行/启动/启动应用程序
* 运行/执行/启动/启动 JVM 或 Java 进程
也有几种方法。在[第一章](01.html)“Java12 入门”中,我们向您展示了如何使用 IntelliJ IDEA 运行`main(String[])`方法。在本章中,我们将重复已经说过的一些内容,并添加可能对您有所帮助的其他变体。
也有几种方法。在第一章“Java12 入门”中,我们向您展示了如何使用 IntelliJ IDEA 运行`main(String[])`方法。在本章中,我们将重复已经说过的一些内容,并添加可能对您有所帮助的其他变体。
# 使用 IDE
......@@ -40,7 +40,7 @@ JVM 只是根据编码逻辑执行指令的执行器。它还发现并将应用
![](img/2f348cdc-48fe-4447-a5f0-0d793f39820c.png)
* 打开 Run 菜单并选择类的名称。有几种不同的选项可供选择:
* 打开“运行”菜单并选择类的名称。有几种不同的选项可供选择:
![](img/285fa8b9-7e22-49e9-b3c1-0026b4b49f2b.png)
......@@ -48,7 +48,7 @@ JVM 只是根据编码逻辑执行指令的执行器。它还发现并将应用
![](img/ce7a1458-a9de-4827-95ee-5aae678ac3fd.png)
VM options 字段允许设置`java`命令选项。例如输入`-Xlog:gc`,IDE 会形成如下`java`命令:
VM 选项字段允许设置`java`命令选项。例如输入`-Xlog:gc`,IDE 会形成如下`java`命令:
```java
java -Xlog:gc -cp . com.packt.learnjava.ch09_jvm.MyApplication
......@@ -123,7 +123,7 @@ public class MyApplication {
}
```
首先,必须使用`javac`命令来编译它。命令行如下所示(前提是您打开了项目根目录中`pom.xml`所在文件夹中的 terminal 窗口):
首先,必须使用`javac`命令来编译它。命令行如下所示(前提是您打开了项目根目录中`pom.xml`所在文件夹中的终端窗口):
```java
javac src/main/java/com/packt/learnjava/ch09_jvm/MyApplication.java
......@@ -253,7 +253,7 @@ java -cp src/main/java/* com.packt.learnjava.ch09_jvm.MyApplication
# 对可执行 JAR 文件使用命令行
可以避免在命令行中指定主类。相反,我们可以创建一个可执行的`.jar`文件。它可以通过将主类的名称(您需要运行的主类和包含`main()`方法的主类)放入 manifest 文件中来实现。步骤如下:
可以避免在命令行中指定主类。相反,我们可以创建一个可执行的`.jar`文件。它可以通过将主类的名称(您需要运行的主类和包含`main()`方法的主类)放入`manifest`文件中来实现。步骤如下:
1. 创建一个文本文件,`manifest.txt`(名称实际上并不重要,但是这个名称清楚地表明了意图),其中包含以下行:
......@@ -313,7 +313,7 @@ JVM 通常被称为 **JVM 实例**。这是因为每次执行一个`java`命令
在主 JVM 进程内运行的进程包括:
* classloader 执行的进程包括:
* 类加载器执行的进程包括:
* 类加载
* 类链接
* 类初始化
......@@ -341,15 +341,15 @@ JVM 架构可以描述为有两个子系统:**类加载器**和**执行引擎*
要加载的第一个类是在命令行中传递的类,其中包含了`main(String[])`方法。类加载器读取`.class`文件,对其进行解析,并用静态字段和方法字节码填充方法区域。它还创建了一个描述类的`java.lang.Class`实例。然后类加载器链接该类(参见“类链接”部分),对其进行初始化(参见“类初始化”部分),然后将其传递给执行引擎以运行其字节码。
`main(String[])`方法是进入应用程序的入口。如果它调用另一个类的方法,则必须在类路径上找到该类,然后加载、初始化,只有这样才能执行它的方法。如果这个-just loaded-方法调用另一个类的方法,那么这个类也必须被找到、加载和初始化。等等。这就是 Java 应用程序如何启动和运行的。
`main(String[])`方法是进入应用程序的入口。如果它调用另一个类的方法,则必须在类路径上找到该类,然后加载、初始化,只有这样才能执行它的方法。如果这个刚刚加载的方法调用另一个类的方法,那么这个类也必须被找到、加载和初始化。等等。这就是 Java 应用程序如何启动和运行的。
`main(String[])`方法
每个类都可以有一个`main(String[])`方法,而且经常有。这种方法用于将类作为独立应用程序独立运行,以进行测试或演示。这种方法的存在不会使类成为 main。只有在`java`命令行或`.jar`文件清单中标识为 main 时,类才会成为 main
每个类都可以有一个`main(String[])`方法,而且经常有。这种方法用于将类作为独立应用程序独立运行,以进行测试或演示。这种方法的存在不会使类成为`main`。只有在`java`命令行或`.jar`文件清单中标识为`main`时,类才会成为`main`
也就是说,让我们继续讨论加载过程。
如果您查看`java.lang.Class`的 API,您将不会在那里看到公共构造器。classloader 自动创建它的实例,顺便说一句,它是您可以在任何 Java 对象上调用的`getClass()`方法返回的同一个实例。
如果您查看`java.lang.Class`的 API,您将不会在那里看到公共构造器。类加载器自动创建它的实例,顺便说一句,它是您可以在任何 Java 对象上调用的`getClass()`方法返回的同一个实例。
它不携带类静态数据(在方法区域中维护),也不携带状态值(它们在执行期间创建的对象中)。它也不包含方法字节码(它们也存储在方法区域中)。相反,`Class`实例提供描述类的元数据—它的名称、包、字段、构造器、方法签名等等。元数据不仅对 JVM 有用,而且对应用程序也有用。
......@@ -423,7 +423,7 @@ JVM 架构可以描述为有两个子系统:**类加载器**和**执行引擎*
* 由于未处理的异常而导致异常终止
* 带或不带错误状态代码的强制程序退出
如果没有异常和无限循环,`main(String[])`方法通过一个 return 语句或在最后一个语句执行之后完成。一旦发生这种情况,主应用程序线程就会将控制流传递给 JVM,JVM 也停止执行。这就是幸福的结局,许多应用程序在现实生活中都享受到了这一点。我们的大多数例子,除了那些演示了异常或无限循环的例子外,也成功地退出了。
如果没有异常和无限循环,`main(String[])`方法通过一个`return`语句或在最后一个语句执行之后完成。一旦发生这种情况,主应用程序线程就会将控制流传递给 JVM,JVM 也停止执行。这就是幸福的结局,许多应用程序在现实生活中都享受到了这一点。我们的大多数例子,除了那些演示了异常或无限循环的例子外,也成功地退出了。
然而,Java 应用程序还有其他退出方式,其中一些方式也非常优雅,而另一些则不那么优雅。如果主应用程序线程创建了子线程,或者换句话说,程序员编写了生成其他线程的代码,那么即使优雅地退出也可能不容易。这完全取决于创建的子线程的类型。
......@@ -431,7 +431,7 @@ JVM 架构可以描述为有两个子系统:**类加载器**和**执行引擎*
但是,如果所有子线程都是守护进程线程,或者没有子线程在运行,那么只要主应用程序线程退出,JVM 实例就会停止运行。
应用程序在异常情况下如何退出取决于代码设计。在讨论异常处理的最佳实践时,我们在第 4 章、“处理”中对此进行了讨论。如果线程捕获了`main(String[])` try-catch 块或类似高级方法中的所有异常,那么由应用程序(以及编写代码的程序员)决定如何最好地继续—尝试更改输入数据并重复生成异常的代码块,记录错误并继续,或者退出。
应用程序在异常情况下如何退出取决于代码设计。在讨论异常处理的最佳实践时,我们在第 4 章、“处理”中对此进行了讨论。如果线程捕获了`main(String[])``try-catch`块或类似高级方法中的所有异常,那么由应用程序(以及编写代码的程序员)决定如何最好地继续—尝试更改输入数据并重复生成异常的代码块,记录错误并继续,或者退出。
另一方面,如果异常保持未处理状态并传播到 JVM 代码中,则线程(发生异常的地方)停止执行并退出。接下来会发生什么,取决于线程的类型和其他一些条件。以下是四种可能的选择:
......@@ -586,7 +586,7 @@ JVM 启动时,堆为空,分为三个部分:
4. 选择所有正确的语句:
1. 每个有清单的`.jar`文件都是可执行文件
2. 如果`java`命令使用了`-jar`选项,则忽略 classpath 选项
2. 如果`java`命令使用了`-jar`选项,则忽略`classpath`选项
3. 每个`.jar`文件都有一个清单
4. 可执行文件`.jar`是带有清单的 ZIP 文件
......@@ -627,9 +627,9 @@ JVM 启动时,堆为空,分为三个部分:
4. 方法参数值不在线程之间共享
11. 选择所有正确的语句:
1. Classloader 填充方法区域
1. 类加载器填充方法区域
2. 类加载器在堆上分配内存
3. Classloader 写入`.class`文件
3. 类加载器写入`.class`文件
4. 类加载器解析方法引用
12. 选择所有正确的语句:
......@@ -640,7 +640,7 @@ JVM 启动时,堆为空,分为三个部分:
13. 选择所有正确的语句:
1. 数据库每秒可支持的事务数是一种吞吐量度量
2. 当垃圾收集器暂停应用程序时,它被称为 stop all things
2. 当垃圾收集器暂停应用程序时,它被称为“停止一切”
3. 网站返回数据的速度有多慢是一个响应性指标
4. 垃圾收集器清除 CPU 队列中的作业
......
......@@ -219,7 +219,7 @@ public class TcpServer {
}
```
让我们浏览前面的代码。在 try with resources 语句中,我们基于新创建的 socket 创建了`Socket``DataInputStream``DataOutputStream`对象,并创建了`BufferedReader`对象从控制台读取用户输入(我们将使用它输入数据)。在创建套接字时,`accept()`方法会阻塞,直到客户端尝试连接到本地服务器的端口`3333`
让我们浏览前面的代码。在资源尝试语句中,我们基于新创建的 socket 创建了`Socket``DataInputStream``DataOutputStream`对象,并创建了`BufferedReader`对象从控制台读取用户输入(我们将使用它输入数据)。在创建套接字时,`accept()`方法会阻塞,直到客户端尝试连接到本地服务器的端口`3333`
然后,代码进入无限循环。首先,它使用`DataInputStream``readUTF()`方法,将客户端发送的字节读取为以修改的 UTF-8 格式编码的 Unicode 字符串。结果以`"Client said: "`前缀打印。如果接收到的消息是一个`"end"`字符串,那么代码退出循环,服务器程序退出。如果消息不是`"end"`,则控制台上显示`"Say something: "`提示,`readLine()`方法阻塞,直到用户键入内容并点击`Enter`
......
......@@ -364,7 +364,7 @@ System.out.println(p2.getAddress().getStreet()); //prints: 25 Main Street
# 试,抓,最后
本书包含第 4 章、“处理”,专门介绍`try``catch``finally`子句的用法,这里不再赘述。我们只想再次重申,使用 try with resources 语句是释放资源的首选方法(传统上是在`finally`块中完成的)。遵从库使代码更简单、更可靠。
本书包含第 4 章、“处理”,专门介绍`try``catch``finally`子句的用法,这里不再赘述。我们只想再次重申,使用资源尝试语句是释放资源的首选方法(传统上是在`finally`块中完成的)。遵从库使代码更简单、更可靠。
# 最佳设计实践
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册