From 71a47fc80f1f631673bb320515f6cc76eba0783a Mon Sep 17 00:00:00 2001 From: wizardforcel <562826179@qq.com> Date: Tue, 26 Nov 2019 22:46:17 +0800 Subject: [PATCH] 2019-11-26 22:46:17 --- docs/41.md | 26 +++++++++++++------------- docs/42.md | 42 +++++++++++++++++++++--------------------- docs/43.md | 20 ++++++++++---------- docs/44.md | 36 ++++++++++++++++++------------------ docs/45.md | 52 ++++++++++++++++++++++++++-------------------------- docs/46.md | 26 +++++++++++++------------- docs/47.md | 16 ++++++++-------- docs/48.md | 38 +++++++++++++++++++------------------- docs/49.md | 15 +++++++++------ docs/50.md | 39 +++++++++++++++++++-------------------- 10 files changed, 156 insertions(+), 154 deletions(-) diff --git a/docs/41.md b/docs/41.md index e0b9d89..b50ada0 100644 --- a/docs/41.md +++ b/docs/41.md @@ -2,22 +2,22 @@ > 原文: [https://javatutorial.net/java-future-example](https://javatutorial.net/java-future-example) -异步计算的结果称为 Future ,更具体地说,它具有此名称,因为它(结果)将在将来的中稍后的时间点完成。 创建异步任务后,将创建将来的对象。 提供了许多方法来帮助检索结果(使用 **get** 方法(这是唯一的检索方法)),使用**取消**方法进行取消以及检查是否检查结果的方法。 任务成功完成或被取消。 +异步计算的结果称为`Future`,更具体地说,它具有此名称,因为它(结果)将在将来的中稍后的时间点完成。 创建异步任务后,将创建将来的对象。 提供了许多方法来帮助检索结果(使用`get`方法(这是唯一的检索方法)),使用`cancel`方法进行取消以及检查是否检查结果的方法。 任务成功完成或被取消。 ![java-featured-image](img/e0db051dedc1179e7424b6d998a6a772.jpg) -Future 的主要优点在于,它使我们能够同时执行其他进程,同时等待 Future 中嵌入的主要任务完成。 这意味着当我们面对大型计算时,使用 Future 是个好主意。 +`Future`的主要优点在于,它使我们能够同时执行其他进程,同时等待`Future`中嵌入的主要任务完成。 这意味着当我们面对大型计算时,使用`Future`是个好主意。 -**方法** +## 方法 -1. boolean cancel(boolean mayInterruptIfRunning) -2. V get() -3. V get(长时间超时,TimeUnit 单位) +1. `boolean cancel(boolean mayInterruptIfRunning)` +2. `V get()` +3. `V get(long timeout, TimeUnit unit) ` 1. 这与上面的 get 方法不同,因为在此方法中,指定了超时作为等待条件 -4. boolean isCancelled() -5. boolean isDone() +4. `boolean isCancelled()` +5. `boolean isDone()` -**使用 get(timeout,unit)**的 Future 的基本实现 +## 使用`get(timeout, unit)`的`Future`的基本实现 ```java import java.util.concurrent.ExecutionException; @@ -65,7 +65,7 @@ public class FutureDemo ``` -**输出:** +## 输出: ```java Task completion in progress... @@ -83,8 +83,8 @@ Done ``` -**细分** +## 细分 -在 ExecutorService 上调用 commit()方法,返回 Future。 现在有了 Future,我们可以调用上面的方法了。 但是,您应该特别注意的是,它在提交的任务正在“运行”时打印“正在完成任务…”。 +在`ExecutorService`上调用`commit()`方法,返回`Future`。 现在有了`Future`,我们可以调用上面的方法了。 但是,您应该特别注意的是,它在提交的任务正在“运行”时打印“正在完成任务…”。 -如果任务没有在指定的时间内完成(如注释所示),将抛出 TimeoutException catch。 \ No newline at end of file +如果任务没有在指定的时间内完成(如注释所示),将抛出`TimeoutException catch`。 \ No newline at end of file diff --git a/docs/42.md b/docs/42.md index 35ac78a..23274bf 100644 --- a/docs/42.md +++ b/docs/42.md @@ -2,15 +2,15 @@ > 原文: [https://javatutorial.net/java-equals-method-example](https://javatutorial.net/java-equals-method-example) -Java equals()方法和“ ==”运算符都用于比较对象是否相等。 但是,他们以非常不同的方式进行检查,从而产生不同的结果。 +Java `equals()`方法和`==`运算符都用于比较对象是否相等。 但是,他们以非常不同的方式进行检查,从而产生不同的结果。 ![java-featured-image](img/e0db051dedc1179e7424b6d998a6a772.jpg) -它们之间的主要区别是“ ==”检查两个对象是否都指向相同的内存位置,并且 equals()求和对象中包含的实际值的比较。 +它们之间的主要区别是`==`检查两个对象是否都指向相同的内存位置,并且`equals()`求和对象中包含的实际值的比较。 一个示例将为您提供更多线索: -**Animal.java** +`Animal.java` ```java public class Animal { @@ -25,7 +25,7 @@ public class Animal { ``` -**EqualsDemo.java** +`EqualsDemo.java` ```java public class EqualsDemo { @@ -51,11 +51,11 @@ These objects are not equal. ``` -即使两个对象都是同一 Class 的实例并包含相同的值,但它们并不引用相同的对象。 每当您键入新的**关键字**时,它都会自动创建一个新的对象引用。 当我们使用**新的**关键字创建两个对象时,即使它们包含相同的值,它们也不相同。 它们指向不同的存储位置。 +即使两个对象都是同一个类的实例并包含相同的值,但它们并不引用相同的对象。 每当您键入`new`的关键字时,它都会自动创建一个新的对象引用。 当我们使用`new`关键字创建两个对象时,即使它们包含相同的值,它们也不相同。 它们指向不同的存储位置。 ## 使用`equals()`方法和`==`运算符比较字符串 -**EqualsDemo.java** +`EqualsDemo.java` ```java public class EqualsDemo { @@ -80,19 +80,19 @@ public class EqualsDemo { Equal ``` -如果您说“等于”,那么您将是正确的。 当字符串包含相同的续 +如果您说“等于”,那么您将是正确的。 当字符串包含相同的内容 ```java Not equal ``` -ent,它们指向相同的存储位置。 +它们指向相同的存储位置。 -现在让我们做与上面完全相同的示例,但改用**新的**关键字。 +现在让我们做与上面完全相同的示例,但改用`new`关键字。 ## 使用`new`关键字创建相同内容的字符串 -**EqualsDemo.java** +`EqualsDemo.java` ```java public class EqualsDemo { @@ -117,17 +117,17 @@ public class EqualsDemo { Not equal ``` -如我上面指出的,打印不等于的原因是因为,当您使用 **new** 关键字创建对象时,会创建一个指向其自身存储位置的新 Pointer。 +如我上面指出的,打印不等于的原因是因为,当您使用`new`关键字创建对象时,会创建一个指向其自身存储位置的新指针。 ![equals() versus == operator reference memory point](img/85fe40a72078993858421e98b86cbb5f.jpg) -这是一个直观的示例。 内存位置刚刚组成。 但是从示例中可以看到,当创建 str1 和 str2 时,它们指向不同的内存位置。 因此,当您使用==运算符比较它们时,无论如何,您都会得到假。 +这是一个直观的示例。 内存位置刚刚组成。 但是从示例中可以看到,当创建`str1`和`str2`时,它们指向不同的内存位置。 因此,当您使用`==`运算符比较它们时,无论如何,您都会得到假。 ## 覆盖`equals()`方法以符合条件 -假设您要在两个对象上调用.equals(),并且如果它们包含相同的名称和年龄,则应返回 true。 +假设您要在两个对象上调用`.equals()`,并且如果它们包含相同的名称和年龄,则应返回`true`。 -**Animal.java** +`Animal.java` ```java public class Animal { @@ -158,7 +158,7 @@ public class Animal { ``` -**EqualsDemo.java** +`EqualsDemo.java` ```java public class EqualsDemo { @@ -181,11 +181,11 @@ public class EqualsDemo { These objects are equal. ``` -我们在 Animal 类中重写 equals 方法,因此它符合我们自己的条件。 如果我们不重写它,而只是对两个对象调用 equals 方法,它将不会返回 true。 +我们在`Animal`类中重写`equals`方法,因此它符合我们自己的条件。 如果我们不重写它,而只是对两个对象调用`equals`方法,它将不会返回`true`。 ## 在字符串上调用`equals()` -**EqualsDemo.java** +`EqualsDemo.java` ```java public class EqualsDemo { @@ -209,11 +209,11 @@ public class EqualsDemo { equal ``` -在字符串上调用 equals()时,它将检查每个字符是否在两个字符串中都相同。 意思是,比较字符串时应始终使用`equals`,而不是 **==** 。 +在字符串上调用`equals()`时,它将检查每个字符是否在两个字符串中都相同。 意思是,比较字符串时应始终使用`equals`,而不是`==`。 ## 对使用`new`关键字创建的字符串的调用`equals` -**EqualsDemo.java** +`EqualsDemo.java` ```java public class EqualsDemo { @@ -238,8 +238,8 @@ public class EqualsDemo { equal ``` -如您所见,使用 equals 时两个对象(字符串)是否指向不同的存储位置并不重要。 如果两个字符串中的内容相同,则返回 true。 +如您所见,使用`equals`时两个对象(字符串)是否指向不同的存储位置并不重要。 如果两个字符串中的内容相同,则返回`true`。 ## 结论 -比较字符串时,应始终使用.equals()而不是==。 \ No newline at end of file +比较字符串时,应始终使用`.equals()`而不是`==`。 \ No newline at end of file diff --git a/docs/43.md b/docs/43.md index 5d1a149..5a03c12 100644 --- a/docs/43.md +++ b/docs/43.md @@ -10,9 +10,9 @@ Java 8 引入了 Lambda 表达式,它是引入的最大(甚至不是最大 ### 让我们比较一下使用 Lambda 表达式和不使用 Lambda 表达式 -假设有一个类,该类具有默认情况下按升序对元素进行排序的方法(例如 Comparator)。 此类使用 compare 来对元素进行排序。 让我们覆盖它。 +假设有一个类,该类具有默认情况下按升序对元素进行排序的方法(例如`Comparator`)。 此类使用`compare`来对元素进行排序。 让我们覆盖它。 -**不使用 Lambda 表达式** +## 不使用 Lambda 表达式 ```java Comparator sorter = new Comparator() { @@ -23,7 +23,7 @@ Comparator sorter = new Comparator() { }; ``` -**使用 Lambda 表达式** +## 使用 Lambda 表达式 ```java Comparator lambdaComparator = (x, y) -> y.compareTo(x); @@ -31,15 +31,15 @@ Comparator lambdaComparator = (x, y) -> y.compareTo(x); 我相信您会注意到两种方法之间的区别。 两者都导致同一件事,这颠倒了该方法对元素进行排序的顺序(从升序到降序,对您的比较器开玩笑!)。 -因此,仅通过查看此示例,使用 lambda 表达式的一个关键优势就是可以减少键入的代码。 对于我们懒惰的程序员来说,这是一个很好的例子。 但是,人们可能会争辩说,由于这种简单性,实际上使记住语法变得更加困难。 没错,这将是一个缺点。 最初,语法可能有点难以记住,但是一旦您记住它,就可以编写简洁的代码,该代码也允许您实现 Functional 接口。 +因此,仅通过查看此示例,使用 lambda 表达式的一个关键优势就是可以减少键入的代码。 对于我们懒惰的程序员来说,这是一个很好的例子。 但是,人们可能会争辩说,由于这种简单性,实际上使记住语法变得更加困难。 没错,这将是一个缺点。 最初,语法可能有点难以记住,但是一旦您记住它,就可以编写简洁的代码,该代码也允许您实现`Functional`接口。 让我们看看 Lambda 表达式的更多示例。 首先,如我之前所说,语法起初有点令人生畏。 -如果您要“覆盖”的函数没有任何参数,则只需键入()-> {body}。 另一方面,如果只有一个参数,则键入(x)-> {body}。 对于两个或更多参数,您将具有(x,y,z ..)-> {body}。 +如果您要“覆盖”的函数没有任何参数,则只需键入`() -> {body}`。 另一方面,如果只有一个参数,则键入`x -> {body}`。 对于两个或更多参数,您将具有`(x, y, z -> {body}`。 -**循环播放** +## 循环播放 ```java import java.util.*; @@ -62,7 +62,7 @@ public class LambdaExpressionDemo { } ``` -注意,在 forEach 循环的主体中,我省略了“ {}”花括号。 这是因为主体仅包含一行。 如果我有两行或更多行,假设另一个打印语句,那么我将拥有大括号,如下所示: +注意,在`forEach`循环的主体中,我省略了`{}`花括号。 这是因为主体仅包含一行。 如果我有两行或更多行,假设另一个打印语句,那么我将拥有大括号,如下所示: ```java import java.util.*; @@ -87,9 +87,9 @@ public class LambdaExpressionDemo { } ``` -**从 Lambda 表达式返回值** +## 从 Lambda 表达式返回值 -您也可以像使用方法一样使用 lambda 表达式返回值。 这里要注意的一个关键是 return 关键字是可选的。 您可以忽略它,编译器知道它的工作。 +您也可以像使用方法一样使用 lambda 表达式返回值。 这里要注意的一个关键是`return`关键字是可选的。 您可以忽略它,编译器知道它的工作。 语法类似于我们之前使用的语法。 @@ -101,4 +101,4 @@ public class LambdaExpressionDemo { } ``` -因此,如果我们再次查看 Lambda 表达式提供的内容,我们可以得出结论,它为我们提供了可选的类型声明(无需声明参数的类型),可选的花括号,可选的 return 关键字(因为编译器自动返回值) (如果主体具有返回值的符号表达式)和参数周围的可选括号。 \ No newline at end of file +因此,如果我们再次查看 Lambda 表达式提供的内容,我们可以得出结论,它为我们提供了可选的类型声明(无需声明参数的类型),可选的花括号,可选的`return`关键字(因为编译器自动返回值) (如果主体具有返回值的符号表达式)和参数周围的可选括号。 \ No newline at end of file diff --git a/docs/44.md b/docs/44.md index fa0e380..331bab2 100644 --- a/docs/44.md +++ b/docs/44.md @@ -2,13 +2,13 @@ > 原文: [https://javatutorial.net/java-optional-example](https://javatutorial.net/java-optional-example) -Java 8 引入了 Optional 类,该类用于根据值是否存在来操纵数据。 您可以不使用此类而具有相同的功能,但是最终会产生混乱的代码。 换句话说,您的空检查将更少,而 NullPointerException 则不会。 +Java 8 引入了`Optional`类,该类用于根据值是否存在来操纵数据。 您可以不使用此类而具有相同的功能,但是最终会产生混乱的代码。 换句话说,您的空检查将更少,而`NullPointerException`则不会。 ![java-featured-image](img/e0db051dedc1179e7424b6d998a6a772.jpg) -让我举一个例子,说明为什么不使用 Optional 会导致问题。 +让我举一个例子,说明为什么不使用`Optional`会导致问题。 -**未使用`Optional`** +## 未使用`Optional` ```java public class NonOptionalImplementation { @@ -43,9 +43,9 @@ public class NonOptionalImplementation { } ``` -您能发现哪里会出问题吗? 显然,当我们到达 print 语句时,由于对象名称被故意设置为 null,它将抛出 NullPointerException。 +您能发现哪里会出问题吗? 显然,当我们到达`print`语句时,由于对象名称被故意设置为`null`,它将抛出`NullPointerException`。 -**输出** +## 输出 ```java Exception in thread "main" java.lang.NullPointerException @@ -53,7 +53,7 @@ Exception in thread "main" java.lang.NullPointerException ``` -**使用`Optional`**解决了此问题 +## 使用`Optional`解决了此问题 ```java import java.util.Optional; @@ -91,25 +91,25 @@ public class OptionalImplementation { } ``` -**输出** +## 输出 ```java Name not provided ``` -**上述实现**的简要分类 +## 上述实现的简要拆分 -* 我们正在创建一个非常简单的类,称为 Animal,我们有 2 个具有构造函数的 getter 方法 -* 在我们的主方法中,我们正在创建该 Animal 类的实例,但是我们还将第一个参数(恰好是名称)分配给 null -* Optional.ofNullable 检查传递的字符串是否为 null +* 我们正在创建一个非常简单的类,称为`Animal`,我们有 2 个具有构造函数的 getter 方法 +* 在我们的主方法中,我们正在创建该`Animal`类的实例,但是我们还将第一个参数(恰好是名称)分配给`null` +* `Optional.ofNullable`检查传递的字符串是否为`null` * 之后,我们尝试打印值 -* 我们有这个.orElse()方法调用,这基本上意味着:如果您要在其上调用该方法的字符串为 null,那么我将使用给出的参数进行打印 +* 我们有这个`.orElse()`方法调用,这基本上意味着:如果您要在其上调用该方法的字符串为 null,那么我将使用给出的参数进行打印 * 如果不为空,那么我将打印原始字符串 -* 换句话说,如果字符串为 null,它将打印默认值 +* 换句话说,如果字符串为`null`,它将打印默认值 是不是很酷? 只要看看我们检查空值时代码的外观如何优雅即可。 而且安全! -为了进行比较,让我们尝试不使用 Optional 类而获得相同的结果。 +为了进行比较,让我们尝试不使用`Optional`类而获得相同的结果。 ```java public class OptionalImplementation { @@ -150,7 +150,7 @@ public class OptionalImplementation { } ``` -**输出** +## 输出 ```java Name not provided @@ -158,7 +158,7 @@ Name not provided 它有效..但是干净吗? 我不这么认为! -您也可以使用 isPresent()方法检查对象中是否存在值: +您也可以使用`isPresent()`方法检查对象中是否存在值: ```java import java.util.Optional; @@ -174,11 +174,11 @@ public class OptionalImplementation { } ``` -**输出** +## 输出 ```java false Optional.empty ``` -而已。 不是那么难,不是吗? 甚至它都不是很容易理解,但是它也大大简化了您的代码,并且也非常易于阅读和维护。 如果要浏览 Optional 提供的所有方法,请查看官方 [Oracle 文档](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html)。 \ No newline at end of file +而已。 不是那么难,不是吗? 甚至它都不是很容易理解,但是它也大大简化了您的代码,并且也非常易于阅读和维护。 如果要浏览`Optional`提供的所有方法,请查看官方 [Oracle 文档](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html)。 \ No newline at end of file diff --git a/docs/45.md b/docs/45.md index 72daea9..e6007d2 100644 --- a/docs/45.md +++ b/docs/45.md @@ -2,52 +2,52 @@ > 原文: [https://javatutorial.net/java-11-http-client-example](https://javatutorial.net/java-11-http-client-example) -Java 11 引入了 HTTP 客户端,该客户端可用于通过网络发送请求并检索其响应。 HTTP 客户端取代了旧的 HttpUrlConnection 类,并且不支持易用性。 HTTP 客户端 API 同时支持 HTTP / 1.1 和 HTTP / 2。 +Java 11 引入了 HTTP 客户端,该客户端可用于通过网络发送请求并检索其响应。 HTTP 客户端取代了旧的`HttpUrlConnection`类,并且不支持易用性。 HTTP 客户端 API 同时支持 HTTP/1.1 和 HTTP/2。 ![java-featured-image](img/e0db051dedc1179e7424b6d998a6a772.jpg) -HttpClient 也是不可变的,这意味着它可以用于发送多个请求。 +`HttpClient`也是不可变的,这意味着它可以用于发送多个请求。 -每个 HttpRequest 必须提供一个 [BodyHandler](https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpResponse.BodyHandler.html) ,并且其(BodyHandler)函数用于确定如何处理响应(如果有的话)。 +每个`HttpRequest`必须提供一个[`BodyHandler`](https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpResponse.BodyHandler.html),并且其(`BodyHandler`)函数用于确定如何处理响应(如果有的话)。 可以同步或异步发送请求 -* [send(HttpRequest,BodyHandler)](https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html#send(java.net.http.HttpRequest,java.net.http.HttpResponse.BodyHandler))阻塞,直到发送了请求并接收到响应为止 -* [sendAsync(HttpRequest,BodyHandler)](https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html#sendAsync(java.net.http.HttpRequest,java.net.http.HttpResponse.BodyHandler))发送请求并同时接收响应(异步)。 此方法返回 [CompletableFuture](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/CompletableFuture.html) ,仅当响应可用时才完成。 +* [`send(HttpRequest, BodyHandler)`](https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html#send(java.net.http.HttpRequest,java.net.http.HttpResponse.BodyHandler))阻塞,直到发送了请求并接收到响应为止 +* [`sendAsync(HttpRequest, BodyHandler)`](https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html#sendAsync(java.net.http.HttpRequest,java.net.http.HttpResponse.BodyHandler))发送请求并同时接收响应(异步)。 此方法返回[`CompletableFuture`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/CompletableFuture.html),仅当响应可用时才完成。 ![HTTP Client, java example](img/1aa21d8e38b9ac10b8226f3d074661bd.jpg) -**HttpResponse.BodyHandler** +`HttpResponse.BodyHandler` * 允许在收到实际的响应主体之前检查响应代码 -**HttpResponse.BodyHandlers** +`HttpResponse.BodyHandlers` -存在 BodyHandlers 的唯一目的是处理响应主体类型。 一些例子: +存在`BodyHandlers`的唯一目的是处理响应主体类型。 一些例子: -* BodyHandlers.ofByteArray() -* BodyHandlers.ofString() -* BodyHandlers.ofFile() -* BodyHandlers.ofInputStream() +* `BodyHandlers.ofByteArray()` +* `BodyHandlers.ofString()` +* `BodyHandlers.ofFile()` +* `BodyHandlers.ofInputStream()` * 等等 -**数据作为反应流** +## 数据作为响应流 -* HttpClient 实际上是请求正文的订阅者和响应正文字节的发布者 -* 请求和响应主体作为反应流(具有无阻塞背压的数据流)公开 +* `HttpClient`实际上是请求正文的订阅者和响应正文字节的发布者 +* 请求和响应主体作为响应流(具有无阻塞背压的数据流)公开 -**HttpRequest.BodyPublisher** +`HttpRequest.BodyPublisher` * 将 Java 对象转换为适合作为请求正文发送的字节缓冲区 -**HttpRequest.BodyPublishers** +`HttpRequest.BodyPublishers` -* BodyPublishers :: ofByteArray(byte []) -* BodyPublishers :: ofFIle(Path) -* BodyPublishers :: ofString(String) +* `BodyPublishers::ofByteArray(byte[])` +* `BodyPublishers::ofFIle(Path)` +* `BodyPublishers::ofString(String)` * 等等 -**HttpRequest.BodySubscriber** +`HttpRequest.BodySubscriber` * 使用响应主体字节并将其转换为 Java 类型 @@ -61,7 +61,7 @@ HttpRequest request = HttpRequest.newBuilder() 现在,让我们发送一个 HTTP 请求。 我将向您展示同步和异步示例: -**同步(在 HttpResponse 可用之前将一直阻塞)GET** +## 同步(在`HttpResponse`可用之前将一直阻塞)GET ```java HttpClient client = HttpClient.newHttpClient(); @@ -76,7 +76,7 @@ System.out.println(response.statusCode()); System.out.println(response.body()); ``` -**异步 GET** +## 异步 GET ```java HttpClient client = HttpClient.newHttpClient(); @@ -90,7 +90,7 @@ client.sendAsync(request, BodyHandlers.ofString()) ``` -**POST** +## POST ```java HttpClient client = HttpClient.newBuilder().build(); @@ -101,6 +101,6 @@ HttpRequest request = HttpRequest.newBuilder() HttpResponse response = client.send(request, discarding()); ``` -**摘要** +## 总结 -HTTP 客户端的角色是替换 URLConnection API,并且在 Java 11 中作为 Java SE 平台的一部分进行了标准化,并且位于 java.net.http 包中。 一个主要优点是它使用了现代 Java 语言以及 API 功能。 \ No newline at end of file +HTTP 客户端的角色是替换`URLConnection` API,并且在 Java 11 中作为 Java SE 平台的一部分进行了标准化,并且位于`java.net.http`包中。 一个主要优点是它使用了现代 Java 语言以及 API 功能。 \ No newline at end of file diff --git a/docs/46.md b/docs/46.md index 46b3391..3c5052c 100644 --- a/docs/46.md +++ b/docs/46.md @@ -8,9 +8,9 @@ Java 类加载器是 [Java 虚拟机(JVM)](https://javatutorial.net/jvm-expl 本文旨在解释类加载器的工作方式,并列出 Java 类加载器的关键组件。 您下次 Java 面试时可能会遇到有关类加载器的问题。 -## 什么是 Java `ClassLoader` +## 什么是 Java 类加载器 -我们知道 Java 程序在 [Java 虚拟机(JVM)](https://javatutorial.net/jvm-explained)上运行。 当我们编译 Java 类时,它将转换为平台和机器无关的字节码。 编译的类存储为.class 文件。 当我们尝试使用类时,Java ClassLoader 将该类加载到内存中。 在已经运行的类中通过名称引用类时,这些类将引入 Java 环境。 一旦第一个类运行,以后将由类加载器完成加载类的尝试。 通常,通过声明并使用静态 main()方法来完成第一类的运行。 +我们知道 Java 程序在 [Java 虚拟机(JVM)](https://javatutorial.net/jvm-explained)上运行。 当我们编译 Java 类时,它将转换为平台和机器无关的字节码。 编译的类存储为`.class`文件。 当我们尝试使用类时,Java 类加载器将该类加载到内存中。 在已经运行的类中通过名称引用类时,这些类将引入 Java 环境。 一旦第一个类运行,以后将由类加载器完成加载类的尝试。 通常,通过声明并使用静态`main()`方法来完成第一类的运行。 ![hierarchy of class loaders](img/36169e47d4b201de13b0869599da1ab7.jpg) @@ -18,18 +18,18 @@ Java 类加载器是 [Java 虚拟机(JVM)](https://javatutorial.net/jvm-expl ## Java 类加载器的类型 -1. **Bootstrap Class Loader** -它加载 JDK 内部类,通常加载 rt.jar 和其他核心类,例如 java.lang。*包类 -2. **Extensions Class Loader** -它从 JDK 扩展目录(通常是 JRE 的 lib / ext 目录)加载类。 -3. **系统类加载器**-从系统类路径加载类,可以在使用-cp 或-classpath 命令行选项调用程序时进行设置。 +1. **自举类加载器** - 它加载 JDK 内部类,通常加载`rt.jar`和其他核心类,例如`java.lang.*`包类 +2. **扩展类加载器** - 它从 JDK 扩展目录(通常是 JRE 的`lib / ext`目录)加载类。 +3. **系统类加载器** - 从系统类路径加载类,可以在使用`-cp`或`-classpath`命令行选项调用程序时进行设置。 ## 何时以及如何加载类 何时加载类? 确实有两种情况: -1. 当执行新的字节码时(例如, **MyClass** _mc_ = new **MyClass()**😉 -2. 当字节码静态引用一个类时(例如 **System。** out )。 +1. 当执行新的字节码时(例如,`MyClass mc = new MyClass()`😉 +2. 当字节码静态引用一个类时(例如`System.out`)。 -类加载器是分层的。 第一个类是在类中声明的静态 main()方法的帮助下专门加载的。 所有随后加载的类均由已加载并正在运行的类加载。 +类加载器是分层的。 第一个类是在类中声明的静态`main()`方法的帮助下专门加载的。 所有随后加载的类均由已加载并正在运行的类加载。 进一步的类加载器在加载类时遵循以下规则: @@ -39,15 +39,15 @@ Java 类加载器是 [Java 虚拟机(JVM)](https://javatutorial.net/jvm-expl ## 静态与动态类加载 -使用 Java 的`new`运算符静态加载类。 动态加载是一种使用 Class.forName()在运行时以编程方式调用类加载器的功能的技术。 +使用 Java 的`new`运算符静态加载类。 动态加载是一种使用`Class.forName()`在运行时以编程方式调用类加载器的功能的技术。 ## `loadClass`和`Class.forName`之间的区别 -**loadClass** 仅加载类,但不初始化对象,而 **Class.forName** 在加载对象后初始化对象。 例如,如果您使用 **ClassLoader.loadClass** 加载 JDBC 驱动程序,则该驱动程序将无法注册,并且您将无法使用 JDBC +`loadClass`仅加载类,但不初始化对象,而`Class.forName`在加载对象后初始化对象。 例如,如果您使用`ClassLoader.loadClass`加载 JDBC 驱动程序,则该驱动程序将无法注册,并且您将无法使用 JDBC -**java.lang.Class.forName(String className)**方法返回与具有给定字符串名称的类或接口关联的 Class 对象。 如果找不到该类,则此方法引发 ClassNotFoundException +`java.lang.Class.forName(String className)`方法返回与具有给定字符串名称的类或接口关联的`Class`对象。 如果找不到该类,则此方法引发`ClassNotFoundException` -下面的示例演示 Class.forName 的用法 +下面的示例演示`Class.forName`的用法 ```java package net.javatutorial; @@ -74,7 +74,7 @@ public class ClassForNameExample { } ``` -加载 java.awt.Point 类。 比我们打印类名称,包和该类所有可用方法的名称。 这是在 Java 8 中执行程序的结果: +加载`java.awt.Point`类。 比我们打印类名称,包和该类所有可用方法的名称。 这是在 Java 8 中执行程序的结果: ```java name = java.awt.Point diff --git a/docs/47.md b/docs/47.md index 896b409..815958d 100644 --- a/docs/47.md +++ b/docs/47.md @@ -2,7 +2,7 @@ > 原文: [https://javatutorial.net/java-enum-example](https://javatutorial.net/java-enum-example) -枚举类型是一种特殊的数据类型,它具有不同的常量,例如 WHITE,BLACK,RED。 约定是,它们应以大写字母命名,因为它们又是常量。 在 Java 中,您可以使用 **枚举** 关键字定义枚举类型。 +枚举类型是一种特殊的数据类型,它具有不同的常量,例如`WHITE`,`BLACK`,`RED`。 约定是,它们应以大写字母命名,因为它们又是常量。 在 Java 中,您可以使用`enum`关键字定义枚举类型。 ![java-featured-image](img/e0db051dedc1179e7424b6d998a6a772.jpg) @@ -16,9 +16,9 @@ public enum Macronutrients { Java 在 1.5 中引入了 enum 数据类型。 -其他编程语言(例如 C++甚至 C)也具有枚举数据类型,但是在 Java 中,它更强大。 例如,在 C / C++中,枚举只是整数值的列表,而在 Java 中,它是扩展 Enum 的类本身,并且通常对读取和写入都更好。 最重要的是,由于枚举是一个类的事实,它还提供了允许在枚举成员上进行迭代的不同方法。 +其他编程语言(例如 C++ 甚至 C)也具有枚举数据类型,但是在 Java 中,它更强大。 例如,在 C/C++ 中,枚举只是整数值的列表,而在 Java 中,它是扩展`Enum`的类本身,并且通常对读取和写入都更好。 最重要的是,由于枚举是一个类的事实,它还提供了允许在枚举成员上进行迭代的不同方法。 -在 Java 中,您还可以使用 name()方法,该方法将为您提供枚举数据类型的成员值: +在 Java 中,您还可以使用`name()`方法,该方法将为您提供枚举数据类型的成员值: ```java public class Main { @@ -85,9 +85,9 @@ public class Main { NONE ``` -每个枚举都是枚举类型的对象。 还记得我说的枚举赋予我们灵活性吗? 好吧,它也可以作为参数传递给 switch 语句。 +每个枚举都是枚举类型的对象。 还记得我说的枚举赋予我们灵活性吗? 好吧,它也可以作为参数传递给`switch`语句。 -**使用 switch 语句**的示例 +**使用`switch`语句**的示例 ```java enum Macronutrients @@ -163,6 +163,6 @@ You have not chosen anything. You must be starving.. **类和枚举之间的主要区别** -* Enums 扩展了 java.land.Enum,并为它提供了人类可读的.toString 方法,.name 和.ordinal 方法等。 -* 枚举可用于 switch 语句 -* 枚举构造函数为我们提供了一些额外的支持方法,例如 values(),valueOf()等,这些方法被证明确实有用。 \ No newline at end of file +* 枚举扩展了`java.land.Enum`,并为它提供了人类可读的`.toString`方法,`.name`和`.ordinal`方法等。 +* 枚举可用于`switch`语句 +* 枚举构造函数为我们提供了一些额外的支持方法,例如`values()`,`valueOf()`等,这些方法被证明确实有用。 \ No newline at end of file diff --git a/docs/48.md b/docs/48.md index 22f1edd..c0e3fbb 100644 --- a/docs/48.md +++ b/docs/48.md @@ -2,30 +2,30 @@ > 原文: [https://javatutorial.net/java-hashcode-method-example](https://javatutorial.net/java-hashcode-method-example) -Java **java.lang.Object** 中的超类提供了两种比较对象的重要方法: **equals()**和 **hashcode()。** 当面对实现类之间的交互时,这些方法被广泛使用。 在本教程中,我们仅看一下 hashCode()。 +Java `java.lang.Object`中的超类提供了两种比较对象的重要方法:`equals()`和`hashcode()`。当面对实现类之间的交互时,这些方法被广泛使用。 在本教程中,我们仅看一下`hashCode()`。 ![java-featured-image](img/e0db051dedc1179e7424b6d998a6a772.jpg) ## 方法定义与实现 -**hashCode():**默认情况下,此方法返回每次都是唯一的随机整数。 例如,如果第二次执行应用程序,则该值将不同。 hashCode 值主要用于哈希格式的集合中,例如 [HashSet](https://javatutorial.net/java-hashset-example) , [HashMap](https://javatutorial.net/java-hashmap-example) 等。请注意,在覆盖 equals()方法的每个类中都必须覆盖此方法**。** +`hashCode()`:默认情况下,此方法返回每次都是唯一的随机整数。 例如,如果第二次执行应用程序,则该值将不同。`hashCode`值主要用于哈希格式的集合中,例如 [HashSet](https://javatutorial.net/java-hashset-example),[`HashMap`](https://javatutorial.net/java-hashmap-example)等。请注意,在覆盖`equals()`方法的每个类中都必须覆盖此方法。 ![Different objects are stored with different ids(or the same)](img/395f4e2a8bc926fcc4992cf6a6e7cdf9.jpg) -hashCode 的简单说明 +`hashCode`的简单说明 ## `hashCode()`可以使用的集合列表 -* [哈希集](https://docs.oracle.com/javase/7/docs/api/java/util/HashSet.html) -* [树集](https://docs.oracle.com/javase/7/docs/api/java/util/TreeSet.html) -* [LinkedHashSet](https://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashSet.html) -* [CopyOnWriteArraySet](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CopyOnWriteArraySet.html) +* [`HashSet`](https://docs.oracle.com/javase/7/docs/api/java/util/HashSet.html) +* [`TreeSet`](https://docs.oracle.com/javase/7/docs/api/java/util/TreeSet.html) +* [`LinkedHashSet`](https://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashSet.html) +* [`CopyOnWriteArraySet`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CopyOnWriteArraySet.html) ## 什么时候应该使用`hashCode()`方法 -如果我们想执行 equals()方法,则需要确保这些对象具有相同的**唯一哈希码 ID。** 当哈希码 ID 不同时,我们永远不要执行 equals()。 +如果我们想执行`equals()`方法,则需要确保这些对象具有相同的**唯一哈希码 ID**。当哈希码 ID 不同时,我们永远不要执行`equals()`。 -**注意**:当 hashCode()比较返回**为假**时,equals()方法还必须返回**为假**。 如果哈希码与**不同**,则对象**不等于**。 +**注意**:当`hashCode()`比较返回**假**时,`equals()`方法还必须返回**假**。 如果哈希码**不同**,则对象**不等**。 ## `hashCode()`和`equals()`的实际示例 @@ -77,20 +77,20 @@ public class hashCodeExample { ### 上面代码的简要分解 -在前几行中,我们将创建两个“ Car”对象,并传递一个 id 和一个品牌名称。 +在前几行中,我们将创建两个`Car`对象,并传递一个`id`和一个品牌名称。 ```java Car BMW = new Car(1, "BMW"); // 1 --> ID, 2 --> the name of the car brand Car mercedes = new Car(2, "Mercedes"); // 1 --> ID, 2 --> the name of the car brand ``` -然后,我们将一个布尔值存储在名为 isHashCodeEqual 的变量中,该变量根据两个对象的 ID 是否相等而为 true 或 false。 +然后,我们将一个布尔值存储在名为`isHashCodeEqual`的变量中,该变量根据两个对象的 ID 是否相等而为`true`或`false`。 ```java boolean isHashcodeEqual = BMW.hashCode() == mercedes.hashCode(); ``` -在那之后,我们有一个条件可以检查 isHashCodeEqual 是 true 还是 false。 如果为 true,则表示两个对象的 ID 相等。 如果不是,则意味着相反。 如果它们相等,我们只需打印“相等”。 如果不是,我们将打印一条有用的消息,该消息基本上会说,如果它们不相等,则无需使用相等进行检查,因为这两个对象不共享相同的 ID。 +在那之后,我们有一个条件可以检查`isHashCodeEqual`是`true`还是`false`。 如果为`true`,则表示两个对象的 ID 相等。 如果不是,则意味着相反。 如果它们相等,我们只需打印“相等”。 如果不是,我们将打印一条有用的消息,该消息基本上会说,如果它们不相等,则无需使用相等进行检查,因为这两个对象不共享相同的 ID。 ```java if (isHashcodeEqual) { @@ -101,7 +101,7 @@ boolean isHashcodeEqual = BMW.hashCode() == mercedes.hashCode(); } ``` -接下来是我们的静态类“汽车”。 +接下来是我们的静态类`Car`。 ```java static class Car { @@ -134,13 +134,13 @@ boolean isHashcodeEqual = BMW.hashCode() == mercedes.hashCode(); ## `hashCode()`的常见错误 -* 在 hashCode()方法中返回一个常量值,而不是为每个对象返回唯一的值。 -* 使用 HashMap 之类的哈希集合时,不覆盖 equals()和 hashCode()。 -* 忘记使用 equals()方法或其他方法覆盖 hashCode()。 +* 在`hashCode()`方法中返回一个常量值,而不是为每个对象返回唯一的值。 +* 使用`HashMap`之类的哈希集合时,不覆盖`equals()`和`hashCode()`。 +* 忘记使用`equals()`方法或其他方法覆盖`hashCode()`。 ## 有关`hashCode()`的重要注意事项 1. 使用有效的算法,以便生成唯一的哈希码 -2. 覆盖 equals()方法时,请始终确保也覆盖了 hashCode()方法。 -3. 如果比较两个对象的哈希码的结果为 false,则 equals()方法也应为 false。 (请参见上面的代码示例) -4. 如果在起诉哈希集合时没有覆盖 equals()和 hashCode(),则该集合将具有重复项。 \ No newline at end of file +2. 覆盖`equals()`方法时,请始终确保也覆盖了`hashCode()`方法。 +3. 如果比较两个对象的哈希码的结果为`false`,则`equals()`方法也应为`false`。 (请参见上面的代码示例) +4. 如果在起诉哈希集合时没有覆盖`equals()`和`hashCode()`,则该集合将具有重复项。 \ No newline at end of file diff --git a/docs/49.md b/docs/49.md index b637002..e245f20 100644 --- a/docs/49.md +++ b/docs/49.md @@ -39,13 +39,13 @@ JProfiler 界面概述 如果您之前已经编写过 Java 应用程序,则可能会遇到这个普遍存在的问题。 真正的实质是尚未释放回池中的内存。 -考虑以下示例:想象您必须创建所需的对象。 但是,当您不再需要它时,请继续前进,而不是从内存中“释放”它。 幕后发生的事情是,仍然引用该对象。 当应用程序开始消耗更多资源时,它很快就会耗尽资源,并导致 OutOfMemoryError。 +考虑以下示例:想象您必须创建所需的对象。 但是,当您不再需要它时,请继续前进,而不是从内存中“释放”它。 幕后发生的事情是,仍然引用该对象。 当应用程序开始消耗更多资源时,它很快就会耗尽资源,并导致`OutOfMemoryError`。 这是[垃圾收集](https://javatutorial.net/java-garbage-collection)无法从内存中删除这些未使用的对象的时候。 ![Memory Leak Diagram](img/4c9b96ca2cbd880236a553e493c932f8.jpg) -上图给出的主要结论是– **未使用的对象仍然占据运行中的内存,应用程序拥有的资源越多,它得到的性能越差,最终导致一个结果– OutOfMemoryError。** +上图给出的主要结论是 – **未使用的对象仍然占据运行中的内存,应用程序拥有的资源越多,它得到的性能越差,最终导致一个结果 – `OutOfMemoryError`。** 既然您知道什么是内存泄漏,并且实际上在编写应用程序时要考虑到这一点,那么让我们看看性能分析如何帮助我们识别并消除内存泄漏。 @@ -65,18 +65,20 @@ JProfiler 界面概述 您应该会看到以下窗口: ![leak memory window jprofiler](img/3b47639758ca3c34569b35ee76711d3e.jpg) -我们感兴趣的是``泄漏记忆''。但是在单击它之前,请选择``标记堆''以表明我们对新分配的对象感兴趣。 +我们感兴趣的是“内存泄露”。但是在单击它之前,请选择“标记堆”以表明我们对新分配的对象感兴趣。 ![Mark heap jprofiler](img/44c66fe7391db53641cdb9b4c447b77f.jpg) 现在,这是应用程序开始创建未进行垃圾收集的对象的过程。 -现在我们已经完成了,单击 Memory Leak,然后等待一些时间来创建新对象。 +现在我们已经完成了,单击“Memory Leak”,然后等待一些时间来创建新对象。 现在,让我们进行堆快照: + ![Heap snapshot](img/e571048a036815d5470e7a6779863875.jpg) -现在,当我们单击“ Heap Walker”时,我们将看到自上次标记堆操作以来已创建了多少个实例: +现在,当我们单击“Heap Walker”时,我们将看到自上次标记堆操作以来已创建了多少个实例: + ![heap snapshot allocated objects](img/4adb7e78dabfb70e77e28232e0d5f355.jpg) 通过查看此窗口,我们不知道哪些对象正完全参与内存泄漏。 为此,我们需要单击“使用新的”。 @@ -85,7 +87,8 @@ JProfiler 界面概述 ![General class memory leak](img/be432cec6f94b91d231a39c5dfcdbe76.jpg) -选择最通用的类​​并选择传入的引用,然后单击确定: +选择最通用的类​​并选择传入的引用,然后单击“确定”: + ![Incoming references](img/638833728edb022bd3be3f4904b2d5d1.jpg) 从那里开始,查找内存泄漏的最佳方法是在“显示 GC 根目录的路径”上进行选择: diff --git a/docs/50.md b/docs/50.md index 6bf8778..cbc4445 100644 --- a/docs/50.md +++ b/docs/50.md @@ -2,25 +2,25 @@ > 原文: [https://javatutorial.net/swing-jframe-basics-create-jframe](https://javatutorial.net/swing-jframe-basics-create-jframe) -本教程介绍了从创建到定制的 JFrame 基础知识。 +本教程介绍了从创建到定制的`JFrame`基础知识。 ## 什么是`JFrame`? -JFrame 是 java.awt.frame 扩展的 javax.swing 包类,它增加了对 JFC / SWING 组件架构的支持。 这是顶层窗口,带有边框和标题栏。 JFrame 类具有许多可用于自定义它的方法。 +`JFrame`是`java.awt.frame`扩展的`javax.swing`包类,它增加了对 JFC/SWING 组件架构的支持。 这是顶层窗口,带有边框和标题栏。`JFrame`类具有许多可用于自定义它的方法。 ## 创建一个`JFrame` -JFrame 类具有许多用于创建 JFrame 的构造函数。 以下是描述。 +`JFrame`类具有许多用于创建`JFrame`的构造函数。 以下是描述。 -**JFrame():**创建一个不可见的框架 +`JFrame()`:创建一个不可见的框架 -**JFrame(GraphicsConfiguration gc):**创建带有空白标题和屏幕设备的图形配置的框架。 +`JFrame(GraphicsConfiguration gc)`:创建带有空白标题和屏幕设备的图形配置的框架。 -**JFrame(字符串标题):**创建带有标题的 JFrame。 +`JFrame(String title)`:创建带有标题的`JFrame`。 -**JFrame(字符串标题,GraphicsConfiguration gc):**创建具有特定 Graphics 配置和指定标题的 JFrame。 +`JFrame(String title, GraphicsConfiguration gc)`:创建具有特定`Graphics`配置和指定标题的`JFrame`。 -这是创建 JFrame 的最简单示例。 +这是创建`JFrame`的最简单示例。 ```java package Example; @@ -44,11 +44,10 @@ public class JFrameExample { ![Simple JFrame window](img/7dbf2b5152eb8b7b4cdeebb9d50725d4.jpg) -J 框架 ## 设置`JFrame`的标题 -要设置 JFrame 的标题,可以使用 JFrame.setTitle(String title)。 +要设置`JFrame`的标题,可以使用`JFrame.setTitle(String title)`。 这是代码 @@ -75,11 +74,11 @@ public class JFrameExample { ![Set title of a JFrame](img/fe4c44d606053d077d298134a23a6d70.jpg) -设置 JFrame 的标题 +设置`JFrame`的标题 ## 更改`JFrame`的窗口大小 -要调整框架的大小,JFrame 提供了 JFrame.setSize(int width,int height)方法,它需要两个参数 width 和 height。 这是现在的代码外观 +要调整框架的大小,`JFrame`提供了`JFrame.setSize(int width, int height)`方法,它需要两个参数`width`和`height`。 这是现在的代码外观 ```java package Example; @@ -103,25 +102,25 @@ public class JFrameExample { ## 调整`JFrame`的大小 -设置 JFrame 的大小后,您会注意到您仍然可以通过简单地将光标放在角落并拖动它来更改它的大小。 或者,如果按右上角关闭旁边的“调整大小”选项,它将最大化到全屏大小。 发生这种情况是因为默认情况下调整大小设置为 true。 您可以简单地将 false 设置为 +设置`JFrame`的大小后,您会注意到您仍然可以通过简单地将光标放在角落并拖动它来更改它的大小。 或者,如果按右上角关闭旁边的“调整大小”选项,它将最大化到全屏大小。 发生这种情况是因为默认情况下调整大小设置为`true`。 您可以简单地将`false`设置为 -JFrame.setResizable(false),现在它将根据您在代码中给定的尺寸显示,并且不会通过图形界面调整大小。 +`JFrame.setResizable(false)`,现在它将根据您在代码中给定的尺寸显示,并且不会通过图形界面调整大小。 ## 更改屏幕上的位置 -要更改 JFrame 在屏幕上的位置,JFranme 提供了一种方法 JFrame.setlocation(int x,int),它使用两个参数 x 表示沿 x 轴的位置,y 表示沿 y 轴的位置。 屏幕的左上角是(0,0)。 +要更改`JFrame`在屏幕上的位置,`JFranme`提供了一种方法`JFrame.setlocation(int x, int y)`,它使用两个参数`x`表示沿`x`轴的位置,`y`表示沿`y`轴的位置。 屏幕的左上角是`(0, 0)`。 ## 关闭`JFrame` -您可以通过单击 JFrame 左上角的 X(十字)来轻松关闭 JFrame。 但是 JFrame.setDefaultCloseOperation(int)是 JFrmae 类提供的方法,您可以设置当用户单击叉号时将发生的操作。 如果给定参数“ 0”,则即使单击十字,JFrame 也不会关闭。 +您可以通过单击`JFrame`左上角的`X`(十字)来轻松关闭`JFrame`。 但是`JFrame.setDefaultCloseOperation(int)`是`JFrmae`类提供的方法,您可以设置当用户单击叉号时将发生的操作。 如果给定参数 0,则即使单击十字,`JFrame`也不会关闭。 -最佳实践是使用 JFrame.EXIT_ON_CLOSE,它退出应用程序(JFrame)并释放内存。 +最佳实践是使用`JFrame.EXIT_ON_CLOSE`,它退出应用程序(`JFrame`)并释放内存。 -JFrame.HIDE_ON_CLOSE:它不会关闭 JFrame,只是将其隐藏。 +`JFrame.HIDE_ON_CLOSE`:它不会关闭`JFrame`,只是将其隐藏。 -JFrame.DISPOSE_ON_CLOSE:关闭框架,但它继续运行并消耗内存。 +`JFrame.DISPOSE_ON_CLOSE`:关闭框架,但它继续运行并消耗内存。 -JFrame.DO_NOTHING_ON_CLOSE:当用户单击关闭时,它什么也不做。 +`JFrame.DO_NOTHING_ON_CLOSE`:当用户单击关闭时,它什么也不做。 最终代码如下所示 -- GitLab