From 3991bd955b9b6e76eb99c49d56ceb4f84047975f Mon Sep 17 00:00:00 2001 From: unclesesame Date: Sun, 19 Apr 2020 18:45:15 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E7=AC=AC15=E7=AB=A0=20=E5=BC=82=E5=B8=B8:?= =?UTF-8?q?=20=E9=94=99=E5=88=AB=E5=AD=97=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit update typos --- docs/book/15-Exceptions.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/book/15-Exceptions.md b/docs/book/15-Exceptions.md index 5ee290bb..f114f4e8 100644 --- a/docs/book/15-Exceptions.md +++ b/docs/book/15-Exceptions.md @@ -914,7 +914,7 @@ DynamicFields.setField(DynamicFields.java:67) 至于返回值,setField() 将用 getField() 方法把此位置的旧值取出,这个操作可能会抛出 NoSuchFieldException 异常。如果客户端程序员调用了 getField() 方法,那么他就有责任处理这个可能抛出的 NoSuchFieldException 异常,但如果异常是从 setField0 方法里抛出的,这种情况将被视为编程错误,所以就使用接受 cause 参数的构造器把 NoSuchFieldException 异常转换为 RuntimeException 异常。 -你会注意到,toString0 方法使用了一个 StringBuilder 来创建其结果。在 [字符串](./Strings.md) 这章中你将会了解到更多的关于 StringBuilder 的知识,但是只要你编写设计循环的 toString() 方法,通常都会想使用它,就像本例一样。 +你会注意到,toString() 方法使用了一个 StringBuilder 来创建其结果。在 [字符串](./Strings.md) 这章中你将会了解到更多的关于 StringBuilder 的知识,但是只要你编写设计循环的 toString() 方法,通常都会想使用它,就像本例一样。 主方法中的 catch 子句看起来不同 - 它使用相同的子句处理两种不同类型的异常,并结合“或(|)”符号。此 Java 7 功能有助于减少代码重复,并使你更容易指定要捕获的确切类型,而不是简单地捕获基本类型。你可以通过这种方式组合多种异常类型。 @@ -1100,7 +1100,7 @@ on off ``` -程序的目的是要确保 main() 结束的时候开关必须是关闭的,所以在每个 try 块和异常处理程序的末尾都加入了对 sw.offo 方法的调用。但也可能有这种情况:异常被抛出,但没被处理程序捕获,这时 sw.off() 就得不到调用。但是有了 finally,只要把 try 块中的清理代码移放在一处即可: +程序的目的是要确保 main() 结束的时候开关必须是关闭的,所以在每个 try 块和异常处理程序的末尾都加入了对 sw.off() 方法的调用。但也可能有这种情况:异常被抛出,但没被处理程序捕获,这时 sw.off() 就得不到调用。但是有了 finally,只要把 try 块中的清理代码移放在一处即可: ```java // exceptions/WithFinally.java @@ -1272,7 +1272,7 @@ public class LostMessage { A trivial exception ``` -从输出中可以看到,VeryImportantException 不见了,它被 finally 子句里的 HoHumException 所取代。这是相当严重的缺陷,因为异常可能会以一种比前面例子所示更微妙和难以察党的方式完全丢失。相比之下,C++把“前一个异常还没处理就抛出下一个异常”的情形看成是糟糕的编程错误。也许在 Java 的未来版本中会修正这个问题(另一方面,要把所有抛出异常的方法,如上例中的 dispose() 方法,全部打包放到 try-catch 子句里面)。 +从输出中可以看到,VeryImportantException 不见了,它被 finally 子句里的 HoHumException 所取代。这是相当严重的缺陷,因为异常可能会以一种比前面例子所示更微妙和难以察觉的方式完全丢失。相比之下,C++把“前一个异常还没处理就抛出下一个异常”的情形看成是糟糕的编程错误。也许在 Java 的未来版本中会修正这个问题(另一方面,要把所有抛出异常的方法,如上例中的 dispose() 方法,全部打包放到 try-catch 子句里面)。 一种更加简单的丢失异常的方式是从 finally 子句中返回: @@ -1953,7 +1953,7 @@ try { ## 其他可选方式 异常处理系统就像一个活门(trap door),使你能放弃程序的正常执行序列。当“异常情形” -发生的时候,正常的执行已变得不可能或者不需要了,这时就要用到这个“活门"。异常代表了当前方法不能继续执行的情形。开发异常处理系统的原因是,如果为每个方法所有可能发生的错误都进行处理的话,任务就显得过于繁重了,程序员也不愿意这么做。结果常常是将错误忽格。应该注意到,开发异常处理的初衷是为了方便程序员处理错误。 +发生的时候,正常的执行已变得不可能或者不需要了,这时就要用到这个“活门"。异常代表了当前方法不能继续执行的情形。开发异常处理系统的原因是,如果为每个方法所有可能发生的错误都进行处理的话,任务就显得过于繁重了,程序员也不愿意这么做。结果常常是将错误忽略。应该注意到,开发异常处理的初衷是为了方便程序员处理错误。 异常处理的一个重要原则是“只有在你知道如何处理的情况下才捕获异常"。实际上,异常处理的一个重要目标就是把错误处理的代码同错误发生的地点相分离。这使你能在一段代码中专注于要完成的事情,至于如何处理错误,则放在另一段代码中完成。这样一来,主要代码就不会与错误处理逻辑混在一起,也更容易理解和维护。通过允许一个处理程序去处理多个出错点,异常处理还使得错误处理代码的数量趋于减少。 @@ -2056,7 +2056,7 @@ public class MainException { 在编写你自己使用的简单程序时,从主方法中抛出异常是很方便的,但这不是通用的方法。 -问题的实质是,当在一个普通方法里调用别的方法时,要考虑到“我不知道该这样处理这个异常,但是也不想把它‘吞’了,或若打印一些无用的消息”。异常链提供了一种新的思路来解决这个问题。可以直接把“被检查的异常”包装进 RuntimeException 里面,就像这样: +问题的实质是,当在一个普通方法里调用别的方法时,要考虑到“我不知道该这样处理这个异常,但是也不想把它‘吞’了,或者打印一些无用的消息”。异常链提供了一种新的思路来解决这个问题。可以直接把“被检查的异常”包装进 RuntimeException 里面,就像这样: ```java try { @@ -2163,7 +2163,7 @@ WrapCheckedException.throwRuntimeException() 的代码可以生成不同类型 异常是 Java 程序设计不可分割的一部分,如果不了解如何使用它们,那你只能完成很有限的工作。正因为如此,本书专门在此介绍了异常——对于许多类库(例如提到过的 I/O 库),如果不处理异常,你就无法使用它们。 -异常处理的优点之一就是它使得你可以在某处集中精力处理你要解决的问题,而在另一处处理你编写的这段代码中产生的错误。尽管异常通常被认为是一种工具,使得你可以在运行时报告错误并从错误中恢复,但是我一直怀疑到底有多少时候“恢复”真正得以实现了,或者能够得以实现。我认为这种情况少于 10%,并且即便是这 10%,也只是将栈展开到某个已知的稳定状态,而并没有实际执行任何种类的恢复性行为。无论这是否正确,我一直相信“报告”功能是异常的精髓所在. Java 坚定地强调将所有的错误都以异常形式报告的这一事实,正是它远远超过语如 C++ 这类语言的长处之一,因为在 C++ 这类语言中,需要以大量不同的方式来报告错误,或者根本就没有提供错误报告功能。一致的错误报告系统意味着,你再也不必对所写的每一段代码,都质问自己“错误是否正在成为漏网之鱼?”(只要你没有“吞咽”异常,这是关键所在!)。 +异常处理的优点之一就是它使得你可以在某处集中精力处理你要解决的问题,而在另一处处理你编写的这段代码中产生的错误。尽管异常通常被认为是一种工具,使得你可以在运行时报告错误并从错误中恢复,但是我一直怀疑到底有多少时候“恢复”真正得以实现了,或者能够得以实现。我认为这种情况少于 10%,并且即便是这 10%,也只是将栈展开到某个已知的稳定状态,而并没有实际执行任何种类的恢复性行为。无论这是否正确,我一直相信“报告”功能是异常的精髓所在. Java 坚定地强调将所有的错误都以异常形式报告的这一事实,正是它远远超过如 C++ 这类语言的长处之一,因为在 C++ 这类语言中,需要以大量不同的方式来报告错误,或者根本就没有提供错误报告功能。一致的错误报告系统意味着,你再也不必对所写的每一段代码,都质问自己“错误是否正在成为漏网之鱼?”(只要你没有“吞咽”异常,这是关键所在!)。 就像你将要在后续章节中看到的,通过将这个问题甩给其他代码-即使你是通过抛出 RuntimeException 来实现这一点的--你在设计和实现时,便可以专注于更加有趣和富有挑战性的问题了。 From 50c7fdf6d42657a556f6f1d9d40c1a7b177ffcfe Mon Sep 17 00:00:00 2001 From: unclesesame Date: Sun, 26 Apr 2020 15:51:52 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E7=AC=AC19=E7=AB=A0=20=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=20=E7=BA=A0=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 第19章 类型信息 纠错 --- docs/book/19-Type-Information.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/book/19-Type-Information.md b/docs/book/19-Type-Information.md index 3c70fe47..f013b51a 100644 --- a/docs/book/19-Type-Information.md +++ b/docs/book/19-Type-Information.md @@ -785,11 +785,11 @@ public class ForNameCreator extends PetCreator { } ``` -`loader()` 方法使用 `Class.forName()` 创建了 `Class` 对象的 `List`。这可能会导致 `ClassNotFoundException` 异常,因为你传入的是一个 `String` 类型的参数,它不能再编译期间被确认是否合理。由于 `Pet` 相关的文件在 `typeinfo` 包里面,所以使用它们的时候需要填写完整的包名。 +`loader()` 方法使用 `Class.forName()` 创建了 `Class` 对象的 `List`。这可能会导致 `ClassNotFoundException` 异常,因为你传入的是一个 `String` 类型的参数,它不能在编译期间被确认是否合理。由于 `Pet` 相关的文件在 `typeinfo` 包里面,所以使用它们的时候需要填写完整的包名。 为了使得 `List` 装入的是具体的 `Class` 对象,类型转换是必须的,它会产生一个编译时警告。`loader()` 方法是分开编写的,然后它被放入到一个静态代码块里,因为 `@SuppressWarning` 注解不能够直接放置在静态代码块之上。 -为了对 `Pet` 进行计数,我们需要一个能跟踪不同类型的 `Pet` 的工具。`Map` 的是这个需求的首选,我们将 `Pet` 类型名作为键,将保存 `Pet` 数量的 `Integer` 作为值。通过这种方式,你就看可以询问:“有多少个 `Hamster` 对象?”我们可以使用 `instanceof` 来对 `Pet` 进行计数: +为了对 `Pet` 进行计数,我们需要一个能跟踪不同类型的 `Pet` 的工具。`Map` 是这个需求的首选,我们将 `Pet` 类型名作为键,将保存 `Pet` 数量的 `Integer` 作为值。通过这种方式,你就可以询问:“有多少个 `Hamster` 对象?”我们可以使用 `instanceof` 来对 `Pet` 进行计数: ```java // typeinfo/PetCount.java @@ -1052,7 +1052,7 @@ EgyptianMau=2, Rodent=5, Hamster=1, Manx=7, Pet=20} ### 递归计数 -`PetCount3.Counter` 中的 `Map` 预先加载了所有不同的 `Pet` 类。我们可以使用 `Class.isAssignableFrom()` 而不是预加载地图,并创建一个不限于计数 `Pet` 的通用工具: +`PetCount3.Counter` 中的 `Map` 预先加载了所有不同的 `Pet` 类。我们可以使用 `Class.isAssignableFrom()` 而不是预加载 `Map` ,并创建一个不限于计数 `Pet` 的通用工具: ```java // onjava/TypeCounter.java @@ -1336,7 +1336,7 @@ x.getClass().equals(Derived.class)) true 如果你不知道对象的确切类型,RTTI 会告诉你。但是,有一个限制:必须在编译时知道类型,才能使用 RTTI 检测它,并对信息做一些有用的事情。换句话说,编译器必须知道你使用的所有类。 -起初,这看起来并没有那么大的限制,但是假设你引用了一个对不在程序空间中的对象。实际上,该对象的类在编译时甚至对程序都不可用。也许你从磁盘文件或网络连接中获得了大量的字节,并被告知这些字节代表一个类。由于这个类在编译器为你的程序生成代码后很长时间才会出现,你如何使用这样的类? +起初,这看起来并没有那么大的限制,但是假设你引用了一个不在程序空间中的对象。实际上,该对象的类在编译时甚至对程序都不可用。也许你从磁盘文件或网络连接中获得了大量的字节,并被告知这些字节代表一个类。由于这个类在编译器为你的程序生成代码后很长时间才会出现,你如何使用这样的类? 在传统编程环境中,这是一个牵强的场景。但是,当我们进入一个更大的编程世界时,会有一些重要的情况发生。第一个是基于组件的编程,你可以在应用程序构建器*集成开发环境*中使用*快速应用程序开发*(RAD)构建项目。这是一种通过将表示组件的图标移动到窗体上来创建程序的可视化方法。然后,通过在编程时设置这些组件的一些值来配置这些组件。这种设计时配置要求任何组件都是可实例化的,它公开自己的部分,并且允许读取和修改其属性。此外,处理*图形用户界面*(GUI)事件的组件必须公开有关适当方法的信息,以便 IDE 可以帮助程序员覆写这些事件处理方法。反射提供了检测可用方法并生成方法名称的机制。 @@ -1826,7 +1826,7 @@ caught EmptyTitleException `EmptyTitleException` 是一个 `RuntimeException`,因为它意味着程序存在错误。在这个方案里边,你仍然可能会得到一个异常。但不同的是,在错误产生的那一刻(向 `setTitle()` 传 `null` 值时)就会抛出异常,而不是发生在其它时刻,需要你通过调试才能发现问题所在。另外,使用 `EmptyTitleException` 还有助于定位 BUG。 -`Person` 字段的限制又不太一样:如果你把它的值设为 `null`,程序会自动把将它赋值成一个空的 `Person` 对象。先前我们也用过类似的方法把字段转换成 `Option`,但这里我们是在返回结果的时候使用 `orElse(new Person())` 插入一个空的 `Person` 对象替代了 `null`。 +`Person` 字段的限制又不太一样:如果你把它的值设为 `null`,程序会自动把将它赋值成一个空的 `Person` 对象。先前我们也用过类似的方法把字段转换成 `Optional`,但这里我们是在返回结果的时候使用 `orElse(new Person())` 插入一个空的 `Person` 对象替代了 `null`。 在 `Position` 里边,我们没有创建一个表示“空”的标志位或者方法,因为 `person` 字段的 `Person` 对象为空,就表示这个 `Position` 是个空缺位置。之后,你可能会发现你必须添加一个显式的表示“空位”的方法,但是正如 YAGNI[^2] (You Aren't Going to Need It,你永远不需要它)所言,在初稿时“实现尽最大可能的简单”,直到程序在某些方面要求你为其添加一些额外的特性,而不是假设这是必要的。 From a86fc088504b802c1a061e5310c62ac3d0cba061 Mon Sep 17 00:00:00 2001 From: unclesesame Date: Sun, 26 Apr 2020 15:58:29 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E7=AC=AC17=E7=AB=A0=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=20=E7=BA=A0=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 第17章 文件 纠错 --- docs/book/17-Files.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/book/17-Files.md b/docs/book/17-Files.md index c92cb291..7a22b051 100644 --- a/docs/book/17-Files.md +++ b/docs/book/17-Files.md @@ -5,7 +5,7 @@ # 第十七章 文件 >在丑陋的 Java I/O 编程方式诞生多年以后,Java终于简化了文件读写的基本操作。 -这种"困难方式"的全部细节都在 [Appendix: I/O Streams](./Appendix-IO-Streams.md)。如果你读过这个部分,就会认同 Java 设计者毫不在意他们的使用者的体验这一观念。打开并读取文件对于大多数编程语言来是非常常用的,由于 I/O 糟糕的设计以至于 +这种"困难方式"的全部细节都在 [Appendix: I/O Streams](./Appendix-IO-Streams.md)。如果你读过这个部分,就会认同 Java 设计者毫不在意他们的使用者的体验这一观念。打开并读取文件对于大多数编程语言来说是非常常用的,由于 I/O 糟糕的设计以至于 很少有人能够在不依赖其他参考代码的情况下完成打开文件的操作。 好像 Java 设计者终于意识到了 Java 使用者多年来的痛苦,在 Java7 中对此引入了巨大的改进。这些新元素被放在 **java.nio.file** 包下面,过去人们通常把 **nio** 中的 **n** 理解为 **new** 即新的 **io**,现在更应该当成是 **non-blocking** 非阻塞 **io**(**io**就是*input/output输入/输出*)。**java.nio.file** 库终于将 Java 文件操作带到与其他编程语言相同的水平。最重要的是 Java8 新增的 streams 与文件结合使得文件操作编程变得更加优雅。我们将看一下文件操作的两个基本组件: @@ -124,7 +124,7 @@ true 我已经在这一章第一个程序的 **main()** 方法添加了第一行用于展示操作系统的名称,因此你可以看到不同操作系统之间存在哪些差异。理想情况下,差别会相对较小,并且使用 **/** 或者 **\\** 路径分隔符进行分隔。你可以看到我运行在Windows 10 上的程序输出。 当 **toString()** 方法生成完整形式的路径,你可以看到 **getFileName()** 方法总是返回当前文件名。 -通过使用 **Files** 工具类(我们接下类将会更多地使用它),可以测试一个文件是否存在,测试是否是一个"普通"文件还是一个目录等等。"Nofile.txt"这个示例展示我们描述的文件可能并不在指定的位置;这样可以允许你创建一个新的路径。"PathInfo.java"存在于当前目录中,最初它只是没有路径的文件名,但它仍然被检测为"存在"。一旦我们将其转换为绝对路径,我们将会得到一个从"C:"盘(因为我们是在Windows机器下进行测试)开始的完整路径,现在它也拥有一个父路径。“真实”路径的定义在文档中有点模糊,因为它取决于具体的文件系统。例如,如果文件名不区分大小写,即使路径由于大小写的缘故而不是完全相同,也可能得到肯定的匹配结果。在这样的平台上,**toRealPath()** 将返回实际情况下的 **Path**,并且还会删除任何冗余元素。 +通过使用 **Files** 工具类(我们接下来将会更多地使用它),可以测试一个文件是否存在,测试是否是一个"普通"文件还是一个目录等等。"Nofile.txt"这个示例展示我们描述的文件可能并不在指定的位置;这样可以允许你创建一个新的路径。"PathInfo.java"存在于当前目录中,最初它只是没有路径的文件名,但它仍然被检测为"存在"。一旦我们将其转换为绝对路径,我们将会得到一个从"C:"盘(因为我们是在Windows机器下进行测试)开始的完整路径,现在它也拥有一个父路径。“真实”路径的定义在文档中有点模糊,因为它取决于具体的文件系统。例如,如果文件名不区分大小写,即使路径由于大小写的缘故而不是完全相同,也可能得到肯定的匹配结果。在这样的平台上,**toRealPath()** 将返回实际情况下的 **Path**,并且还会删除任何冗余元素。 这里你会看到 **URI** 看起来只能用于描述文件,实际上 **URI** 可以用于描述更多的东西;通过 [维基百科](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) 可以了解更多细节。现在我们成功地将 **URI** 转为一个 **Path** 对象。 @@ -465,7 +465,7 @@ test\Hello.txt 我们尝试使用 **createDirectory()** 来创建多级路径,但是这样会抛出异常,因为这个方法只能创建单级路径。我已经将 **populateTestDir()** 作为一个单独的方法,因为它将在后面的例子中被重用。对于每一个变量 **variant**,我们都能使用 **createDirectories()** 创建完整的目录路径,然后使用此文件的副本以不同的目标名称填充该终端目录。然后我们使用 **createTempFile()** 生成一个临时文件。 -在调用 **populateTestDir()** 之后,我们在 **test** 目录下面下面创建一个临时目录。请注意,**createTempDirectory()** 只有名称的前缀选项。与 **createTempFile()** 不同,我们再次使用它将临时文件放入新的临时目录中。你可以从输出中看到,如果未指定后缀,它将默认使用".tmp"作为后缀。 +在调用 **populateTestDir()** 之后,我们在 **test** 目录下面创建一个临时目录。请注意,**createTempDirectory()** 只有名称的前缀选项。与 **createTempFile()** 不同,我们再次使用它将临时文件放入新的临时目录中。你可以从输出中看到,如果未指定后缀,它将默认使用".tmp"作为后缀。 为了展示结果,我们首次使用看起来很有希望的 **newDirectoryStream()**,但事实证明这个方法只是返回 **test** 目录内容的 Stream 流,并没有更多的内容。要获取目录树的全部内容的流,请使用 **Files.walk()**。 @@ -589,7 +589,7 @@ evt.kind(): ENTRY_DELETE 此时,**watcher.take()** 将等待并阻塞在这里。当目标事件发生时,会返回一个包含 **WatchEvent** 的 **Watchkey** 对象。展示的这三种方法是能对 **WatchEvent** 执行的全部操作。 -查看输出的具体内容。即使我们正在删除以 **.txt** 结尾的文件,在 **Hello.txt** 被删除之前,**WatchService** 也不会被触发。你可能认为,如果说"监视这个目录",自然会包含整个目录和下面子目录,但实际上的:只会监视给定的目录,而不是下面的所有内容。如果需要监视整个树目录,必须在整个树的每个子目录上放置一个 **Watchservice**。 +查看输出的具体内容。即使我们正在删除以 **.txt** 结尾的文件,在 **Hello.txt** 被删除之前,**WatchService** 也不会被触发。你可能认为,如果说"监视这个目录",自然会包含整个目录和下面子目录,但实际上:只会监视给定的目录,而不是下面的所有内容。如果需要监视整个树目录,必须在整个树的每个子目录上放置一个 **Watchservice**。 ```java // files/TreeWatcher.java @@ -851,4 +851,4 @@ Java 7 和 8 对于处理文件和目录的类库做了大量改进。如果您 -
\ No newline at end of file +
From 4c072abe5fd4c363155aaa12203505e127dc1508 Mon Sep 17 00:00:00 2001 From: unclesesame Date: Sun, 26 Apr 2020 16:04:04 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E7=AC=AC16=E7=AB=A0=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=20=E7=BA=A0=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 第16章 代码校验 纠错 --- docs/book/16-Validating-Your-Code.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/book/16-Validating-Your-Code.md b/docs/book/16-Validating-Your-Code.md index 46306059..527eef2e 100644 --- a/docs/book/16-Validating-Your-Code.md +++ b/docs/book/16-Validating-Your-Code.md @@ -180,7 +180,7 @@ Cleaning up 4 **gradlew test** -尽管可以用最简单的方法,如 **CountedListTest.java** 所示没那样,JUnit 还包括大量的测试结构,你可以到[官网](junit.org)上学习它们。 +尽管可以用最简单的方法,如 **CountedListTest.java** 所示那样,JUnit 还包括大量的测试结构,你可以到[官网](junit.org)上学习它们。 JUnit 是 Java 最流行的单元测试框架,但也有其它可以替代的。你可以通过互联网发现更适合的那一个。 @@ -1240,7 +1240,7 @@ public class SLF4JLogging { **Aug 16, 2016 5:40:31 PM InfoLogging main** **INFO: hello logging** -日志系统会检测日志消息处所在的的类名和方法名。 但它不能保证这些名称是正确的,所以不要纠结于其准确性。 +日志系统会检测日志消息处所在的类名和方法名。 但它不能保证这些名称是正确的,所以不要纠结于其准确性。 ### 日志等级 @@ -1654,7 +1654,7 @@ N:数组的大小:**10^(2*k)**,通常来说,**k=1..7** 足够来练习 Q:setter 的操作成本 -这个 C/P/N/Q 模型在早期 JDK 8 的 Lambda 开发期间付出水面,大多数并行的 Stream 操作(**parallelSetAll()** 也基本相似)都满足这些结论:**N*Q**(主要工作量)对于并发性能尤为重要。并行算法在工作量较少时可能实际运行得更慢。 +这个 C/P/N/Q 模型在早期 JDK 8 的 Lambda 开发期间浮出水面,大多数并行的 Stream 操作(**parallelSetAll()** 也基本相似)都满足这些结论:**N*Q**(主要工作量)对于并发性能尤为重要。并行算法在工作量较少时可能实际运行得更慢。 在一些情况下操作竞争如此激烈使得并行毫无帮助,而不管 **N*Q** 有多大。当 **C** 很大时,**P** 就变得不太相关(内部并行在大量的外部并行面前显得多余)。此外,在一些情况下,并行分解会让相同的 **C** 个客户端运行得比它们顺序运行代码更慢。