From a517eb2108cccf32faa640ee6f05e4e0c8ec50d7 Mon Sep 17 00:00:00 2001 From: sjsdfg <736777445@qq.com> Date: Mon, 13 May 2019 13:41:18 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20Java=20=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E6=A0=87=E5=87=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/15-Exceptions.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/book/15-Exceptions.md b/docs/book/15-Exceptions.md index 7ded0c3..d671011 100644 --- a/docs/book/15-Exceptions.md +++ b/docs/book/15-Exceptions.md @@ -919,8 +919,23 @@ DynamicFields.setField(DynamicFields.java:67) ## Java 标准异常 +Throwable这个Java类被用来表示任何可以作为异常被抛出的类。Throwable对象可分为两种类型(指从Throwable继承而得到的类型):Error用来表示编译时和系统错误(除特殊情况外,一般不用你关心);Exception是可以被抛出的基本类型,在Java类库、用户方法以及运行时故障中都可能抛出Exception型异常。所以Java程序员关心的基类型通常是Exception。要想对异常有全面的了解,最好去浏览一下HTML格式的Java文档(可以从java.sun.com下载)。为了对不同的异常有个感性的认识,这么做是值得的。但很快你就会发现,这些异常除了名称外其实都差不多。同时,Java中异常的数目在持续增加,所以在书中简单罗列它们毫无意义。所使用的第三方类库也可能会有自己的异常。对异常来说,关键是理解概念以及如何使用。 + +异常的基本的概念是用名称代表发生的问题,并且异常的名称应该可以望文知意。异常并非全是在java.lang包里定义的;有些异常是用来支持其他像util、net和io这样的程序包,这些异常可以通过它们的完整名称或者从它们的父类中看出端倪。比如,所有的输入/输出异常都是从java.io.IOException继承而来的。 + +### 特例:RuntimeException + +在本章的第一个例子中: + +```java +if(t == null) + throw new NullPointerException(); +``` + + + ## finally 关键字 From fd67d82a870fab9316bafc23b30c87601713a2ae Mon Sep 17 00:00:00 2001 From: Abel-Huang <497720975@qq.com> Date: Mon, 13 May 2019 00:38:46 +0800 Subject: [PATCH 2/7] first commit on CP 17.1 --- .gitignore | 3 + docs/book/17-Files.md | 297 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 298 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 6d3e913..851523c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ _book ._* */.DS_Store */._* + +# IDEA +/.idea/* \ No newline at end of file diff --git a/docs/book/17-Files.md b/docs/book/17-Files.md index 688d4bf..018f4ba 100644 --- a/docs/book/17-Files.md +++ b/docs/book/17-Files.md @@ -2,17 +2,310 @@ # 第十七章 文件 +在丑陋的Java I/O编程方式诞生多年以后,Java终于简化了文件读写的基本操作。这种"困难方式"的全部细节都在[Appendix: I/O Streams]()。如果你读过这个部分,就会认同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与文件结合使得文件操作编程变得更加优雅。我们将看一下文件操作的两个基本组件: +1. 文件或者目录的路径; +2. 文件本身。 ## 文件和目录路径 +### `Paths` +一个**Path**对象表示一个文件或者目录的路径,是一个跨操作系统(OS)和文件系统的抽象,目的是在构造路径时不必关注底层操作系统,代码可以在不进行修改的情况下运行在不同的操作系统上。**java.nio.file.Paths**类包含一个重载方法**static get()**,该方法方法接受一系列**Strings**字符串或一个*统一资源标识符*(URI)作为参数,并且进行转换返回一个**Path**对象: +```java +// files/PathInfo.java +import java.nio.file.*; +import java.net.URI; +import java.io.File; +import java.io.IOException; +public class PathInfo { + static void show(String id, Object p) { + System.out.println(id + ": " + p); + } + + static void info(Path p) { + show("toString", p); + show("Exists", Files.exists(p)); + show("RegularFile", Files.isRegularFile(p)); + show("Directory", Files.isDirectory(p)); + show("Absolute", p.isAbsolute()); + show("FileName", p.getFileName()); + show("Parent", p.getParent()); + show("Root", p.getRoot()); + System.out.println("******************"); + } + public static void main(String[] args) { + System.out.println(System.getProperty("os.name")); + info(Paths.get("C:", "path", "to", "nowhere", "NoFile.txt")); + Path p = Paths.get("PathInfo.java"); + info(p); + Path ap = p.toAbsolutePath(); + info(ap); + info(ap.getParent()); + try { + info(p.toRealPath()); + } catch(IOException e) { + System.out.println(e); + } + URI u = p.toUri(); + System.out.println("URI: " + u); + Path puri = Paths.get(u); + System.out.println(Files.exists(puri)); + File f = ap.toFile(); // Don't be fooled + } +} + +/* 输出: +Windows 10 +toString: C:\path\to\nowhere\NoFile.txt +Exists: false +RegularFile: false +Directory: false +Absolute: true +FileName: NoFile.txt +Parent: C:\path\to\nowhere +Root: C:\ +****************** +toString: PathInfo.java +Exists: true +RegularFile: true +Directory: false +Absolute: false +FileName: PathInfo.java +Parent: null +Root: null +****************** +toString: C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples\files\PathInfo.java +Exists: true +RegularFile: true +Directory: false +Absolute: true +FileName: PathInfo.java +Parent: C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples\files +Root: C:\ +****************** +toString: C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples\files +Exists: true +RegularFile: false +Directory: true +Absolute: true +FileName: files +Parent: C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples +Root: C:\ +****************** +toString: C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples\files\PathInfo.java +Exists: true +RegularFile: true +Directory: false +Absolute: true +FileName: PathInfo.java +Parent: C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples\files +Root: C:\ +****************** +URI: file:///C:/Users/Bruce/Documents/GitHub/onjava/ +ExtractedExamples/files/PathInfo.java +true +*/ +``` + +我已经在这一章第一个程序的**main()**方法添加了第一行用于展示操作系统的名称,因此你可以看到不同操作系统之间存在哪些差异。理想情况下,差别会相对较小,并且使用**/**或者**\\**路径分隔符进行分隔。你可以看到我运行在Windows 10上的程序输出。 + +当**toString()**方法生成完整形式的路径,你可以看到**getFileName()** 方法总是返回当前文件名。 +通过使用**Files**工具类(我们接下类将会更多的使用它),可以测试一个文件是否存在,测试是否是一个"真正"的文件还是一个目录等等。"Nofile.txt"这个示例展示我们描述的文件可能并不在指定的位置;这样可以允许你创建一个新的路径。"PathInfo.java"存在于当前目录中,最初它只是没有路径的文件名,但它仍然被检测为"存在"。一旦我们将其转换为绝对路径,我们将会得到一个从"C:"盘(因为我们是在Windows机器下进行测试)开始的完整路径,现在它也拥有一个父路径。“真实”路径的定义在文档中有点模糊 + 因为它取决于具体的文件系统。例如,如果文件名不区分大小写,即使路径由于大小写的缘故而不是完全相同,也可能得到肯定的匹配结果。 + +```java +// files/PartsOfPaths.java +import java.nio.file.*; + +public class PartsOfPaths { + public static void main(String[] args) { + System.out.println(System.getProperty("os.name")); + Path p = Paths.get("PartsOfPaths.java").toAbsolutePath(); + for(int i = 0; i < p.getNameCount(); i++) + System.out.println(p.getName(i)); + System.out.println("ends with '.java': " + + p.endsWith(".java")); + for(Path pp : p) { + System.out.print(pp + ": "); + System.out.print(p.startsWith(pp) + " : "); + System.out.println(p.endsWith(pp)); + } + System.out.println("Starts with " + p.getRoot() + " " + p.startsWith(p.getRoot())); + } +} + +/* 输出: +Windows 10 +Users +Bruce +Documents +GitHub +on-java +ExtractedExamples +files +PartsOfPaths.java +ends with '.java': false +Users: false : false +Bruce: false : false +Documents: false : false +GitHub: false : false +on-java: false : false +ExtractedExamples: false : false +files: false : false +PartsOfPaths.java: false : true +Starts with C:\ true +*/ +``` + +```java +// files/PathAnalysis.java +import java.nio.file.*; +import java.io.IOException; + +public class PathAnalysis { + static void say(String id, Object result) { + System.out.print(id + ": "); + System.out.println(result); + } + + public static void main(String[] args) throws IOException { + System.out.println(System.getProperty("os.name")); + Path p = Paths.get("PathAnalysis.java").toAbsolutePath(); + say("Exists", Files.exists(p)); + say("Directory", Files.isDirectory(p)); + say("Executable", Files.isExecutable(p)); + say("Readable", Files.isReadable(p)); + say("RegularFile", Files.isRegularFile(p)); + say("Writable", Files.isWritable(p)); + say("notExists", Files.notExists(p)); + say("Hidden", Files.isHidden(p)); + say("size", Files.size(p)); + say("FileStore", Files.getFileStore(p)); + say("LastModified: ", Files.getLastModifiedTime(p)); + say("Owner", Files.getOwner(p)); + say("ContentType", Files.probeContentType(p)); + say("SymbolicLink", Files.isSymbolicLink(p)); + if(Files.isSymbolicLink(p)) + say("SymbolicLink", Files.readSymbolicLink(p)); + if(FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) + say("PosixFilePermissions", + Files.getPosixFilePermissions(p)); + } +} + +/* 输出: +Windows 10 +Exists: true +Directory: false +Executable: true +Readable: true +RegularFile: true +Writable: true +notExists: false +Hidden: false +size: 1631 +FileStore: SSD (C:) +LastModified: : 2017-05-09T12:07:00.428366Z +Owner: MINDVIEWTOSHIBA\Bruce (User) +ContentType: null +SymbolicLink: false +*/ +``` + +```java +// files/AddAndSubtractPaths.java +import java.nio.file.*; +import java.io.IOException; + +public class AddAndSubtractPaths { + static Path base = Paths.get("..", "..", "..").toAbsolutePath().normalize(); + + static void show(int id, Path result) { + if(result.isAbsolute()) + System.out.println("(" + id + ")r " + base.relativize(result)); + else + System.out.println("(" + id + ") " + result); + try { + System.out.println("RealPath: " + result.toRealPath()); + } catch(IOException e) { + System.out.println(e); + } + } + + public static void main(String[] args) { + System.out.println(System.getProperty("os.name")); + System.out.println(base); + Path p = Paths.get("AddAndSubtractPaths.java").toAbsolutePath(); + show(1, p); + Path convoluted = p.getParent().getParent() + .resolve("strings").resolve("..") + .resolve(p.getParent().getFileName()); + show(2, convoluted); + show(3, convoluted.normalize()); + Path p2 = Paths.get("..", ".."); + show(4, p2); + show(5, p2.normalize()); + show(6, p2.toAbsolutePath().normalize()); + Path p3 = Paths.get(".").toAbsolutePath(); + Path p4 = p3.resolve(p2); + show(7, p4); + show(8, p4.normalize()); + Path p5 = Paths.get("").toAbsolutePath(); + show(9, p5); + show(10, p5.resolveSibling("strings")); + show(11, Paths.get("nonexistent")); + } +} + +/* 输出: +Windows 10 +C:\Users\Bruce\Documents\GitHub +(1)r onjava\ +ExtractedExamples\files\AddAndSubtractPaths.java +RealPath: C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples\files\AddAndSubtractPaths.java +(2)r on-java\ExtractedExamples\strings\..\files +RealPath: C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples\files +(3)r on-java\ExtractedExamples\files +RealPath: C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples\files +(4) ..\.. +RealPath: C:\Users\Bruce\Documents\GitHub\on-java +(5) ..\.. +RealPath: C:\Users\Bruce\Documents\GitHub\on-java +(6)r on-java +RealPath: C:\Users\Bruce\Documents\GitHub\on-java +(7)r on-java\ExtractedExamples\files\.\..\.. +RealPath: C:\Users\Bruce\Documents\GitHub\on-java +(8)r on-java +RealPath: C:\Users\Bruce\Documents\GitHub\on-java +(9)r on-java\ExtractedExamples\files +RealPath: C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples\files +(10)r on-java\ExtractedExamples\strings +RealPath: C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples\strings +(11) nonexistent +java.nio.file.NoSuchFileException: +C:\Users\Bruce\Documents\GitHub\onjava\ +ExtractedExamples\files\nonexistent +*/ +``` + ## 目录 - - ## 文件系统 From 4568f95483d060cab6759cdfdd51bed18db7b6cf Mon Sep 17 00:00:00 2001 From: Abel-Huang <497720975@qq.com> Date: Tue, 14 May 2019 00:04:15 +0800 Subject: [PATCH 3/7] =?UTF-8?q?17=E7=AB=A0=E7=AC=AC=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E5=88=9D=E7=A8=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/17-Files.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/book/17-Files.md b/docs/book/17-Files.md index 018f4ba..ef3003c 100644 --- a/docs/book/17-Files.md +++ b/docs/book/17-Files.md @@ -119,8 +119,14 @@ true 我已经在这一章第一个程序的**main()**方法添加了第一行用于展示操作系统的名称,因此你可以看到不同操作系统之间存在哪些差异。理想情况下,差别会相对较小,并且使用**/**或者**\\**路径分隔符进行分隔。你可以看到我运行在Windows 10上的程序输出。 当**toString()**方法生成完整形式的路径,你可以看到**getFileName()** 方法总是返回当前文件名。 -通过使用**Files**工具类(我们接下类将会更多的使用它),可以测试一个文件是否存在,测试是否是一个"真正"的文件还是一个目录等等。"Nofile.txt"这个示例展示我们描述的文件可能并不在指定的位置;这样可以允许你创建一个新的路径。"PathInfo.java"存在于当前目录中,最初它只是没有路径的文件名,但它仍然被检测为"存在"。一旦我们将其转换为绝对路径,我们将会得到一个从"C:"盘(因为我们是在Windows机器下进行测试)开始的完整路径,现在它也拥有一个父路径。“真实”路径的定义在文档中有点模糊 - 因为它取决于具体的文件系统。例如,如果文件名不区分大小写,即使路径由于大小写的缘故而不是完全相同,也可能得到肯定的匹配结果。 +通过使用**Files**工具类(我们接下类将会更多的使用它),可以测试一个文件是否存在,测试是否是一个"真正"的文件还是一个目录等等。"Nofile.txt"这个示例展示我们描述的文件可能并不在指定的位置;这样可以允许你创建一个新的路径。"PathInfo.java"存在于当前目录中,最初它只是没有路径的文件名,但它仍然被检测为"存在"。一旦我们将其转换为绝对路径,我们将会得到一个从"C:"盘(因为我们是在Windows机器下进行测试)开始的完整路径,现在它也拥有一个父路径。“真实”路径的定义在文档中有点模糊,因为它取决于具体的文件系统。例如,如果文件名不区分大小写,即使路径由于大小写的缘故而不是完全相同,也可能得到肯定的匹配结果。在这样的平台上,**toRealPath()** 将返回实际情况下的**Path**,并且还会删除任何冗余元素。 + +这里你会看到**URI**看起来只能用于描述文件,实际上**URI**可以用于描述更多的东西;通过[维基百科]()可以了解更多细节。现在我们成功地将**URI**转为一个**Path**对象。 + +最后,你会在**Path**中看到一些有点欺骗的东西,这就是调用**toFile()**方法会生成一个**File**对象。听起来似乎可以得到一个类似文件的东西(毕竟被称为**File**),但是这个方法的存在仅仅是为了向后兼容。虽然看上去应该被称为"路径",实际上却应该表示目录或者文件本身。这是个非常草率并且令人困惑的命名,但是由于**java.nio.file**的存在我们可以安全的忽略它的存在。 + +### 选取路径部分片段 +**Path**对象可以非常容易的生成路径的某一部分: ```java // files/PartsOfPaths.java @@ -164,8 +170,13 @@ files: false : false PartsOfPaths.java: false : true Starts with C:\ true */ -``` +``` +可以通过**getName()**来索引**Path**的各个部分,直到达到上限**getNameCount()**。**Path**也继承了**Iterable**接口,因此我们也可以通过增强的for循环进行遍历。请注意,即使路径以 **.java**结尾,使用**endsWith()** 方法也会返回**false**。这是因为使用**endsWith()** 比较的是整个路径部分,而不会包含文件路径的后缀。通过使用**startsWith()** 和**endsWith()**也可以完成路径的遍历。但是我们可以看到,遍历**Path**对象并不包含根路径,只有使用 +**startsWith()**检测根路径时才会返回**true**。 + +### 路径分析 +**Files**工具类包含一系列完整的方法用于获得**Path**相关的信息。 ```java // files/PathAnalysis.java import java.nio.file.*; @@ -220,6 +231,11 @@ ContentType: null SymbolicLink: false */ ``` +在调用最后一个测试方法**getPosixFilePermissions()** 之前我们需要确认一下当前文件系统是否支持**Posix**接口,否则会抛出运行时异常。 + +### **Paths**的增减修改 +我们必须能通过对**Path**对象增加或者删除一部分来构造一个新的**Path**对象。我们使用**relativize()**构造一个路径与给定路径的相对路径,使用**resolve()**解析为一个新的**Path**对象(不一定是“可发现”的名称)。对于下面代码中的示例,我使用**relativize()** 方法从所有的输出中移除根路径,部分原因是为了示范,部分原因是为了简化输出结果,这说明你可以使用该方法将绝对路径转为相对路径。 +这个版本的代码中包含**id**,以便于跟踪输出结果: ```java // files/AddAndSubtractPaths.java @@ -301,6 +317,8 @@ C:\Users\Bruce\Documents\GitHub\onjava\ ExtractedExamples\files\nonexistent */ ``` +我还为**toRealPath()** 添加了进一步的测试,这是为了扩展和规则化,防止路径不存在以免产生运行时异常。 + From 3853e0b82ab8523fd95462838f183df46d954290 Mon Sep 17 00:00:00 2001 From: sjsdfg <736777445@qq.com> Date: Tue, 14 May 2019 16:44:33 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B0=8F=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/15-Exceptions.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/book/15-Exceptions.md b/docs/book/15-Exceptions.md index d671011..5f8e3b5 100644 --- a/docs/book/15-Exceptions.md +++ b/docs/book/15-Exceptions.md @@ -383,7 +383,7 @@ getMessage() 方法,以产生更详细的信息。对于异常类来说,getM 既然异常也是对象的一种,所以可以继续修改这个异常类,以得到更强的功能。但要记住,使用程序包的客户端程序员可能仅仅只是查看一下抛出的异常类型,其他的就不管了(大多数 Java 库里的异常都是这么用的),所以对异常所添加的其他功能也许根本用不上。 -## 异常规范 +## 异常声明 Java 鼓励人们把方法可能会抛出的异常告知使用此方法的客户端程序员。这是种优雅的做法,它使得调用者能确切知道写什么样的代码可以捕获所有潜在的异常。当然,如果提供了源代码,客户端程序员可以在源代码中查找 throw 语句来获知相关信息,然而程序库通常并不与源代码一起发布。为了预防这样的问题,Java 提供了相应的语法(并强制使用这个语法),使你能以礼貌的方式告知客户端程序员某个方法可能会抛出的异常类型,然后客户端程序员就可以进行相应的处理。这就是异常说明,它属于方法声明的一部分,紧跟在形式参数列表之后。 @@ -932,7 +932,16 @@ if(t == null) throw new NullPointerException(); ``` +如果必须对传递给方法的每个引用都检查其是否为nul(因为无法确定调用者是否传入了非法引用),这听起来着实吓人。幸运的是,这不必由你亲自来做,它属于Java的标准运行时检测的一部分。如果对null引用进行调用,Java会自动抛出NullPointerException异常,所以上述代码是多余的,尽管你也许想要执行其他的检查以确保NullPointerException不会出现。 +属于运行时异常的类型有很多,它们会自动被lava虚拟机抛出,所以不必在异常说明中把它们列出来。这些异常都是从RuntimeException类继承而来,所以既体现了继承的优点,使用起来也很方便。这构成了一组具有相同特征和行为的异常类型。并且,也不再需要在异常说明中声明方法将抛出RuntimeException类型的异常(或者任何从RuntimeException继承的异常),它们也被称为“不受检查异常”。这种异常属于错误,将被自动捕获,就不用你亲自动手了。要是自己去检查RuntimeException的话,代码就显得太混乱了。不过尽管通常不用捕获RuntimeException异常,但还是可以在代码中抛出RuntimeException类型的异常。 + +RuntimeException代表的是编程错误: + +1. 无法预料的错误。比如从你控制范围之外传递进来的mull引用。 +2. 作为程序员,应该在代码中进行检查的错误。(比如对于ArrayIndexOutOfBoundsException,就得注意一下数组的大小了。)在一个地方发生的异常,常常会在另一个地方导致错误。 + +在这些情况下使用异常很有好处,它们能给调试带来便利。 From 636d28d6827d11f73315908ef6fde4001f7d25e5 Mon Sep 17 00:00:00 2001 From: sjsdfg <736777445@qq.com> Date: Mon, 13 May 2019 13:41:18 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20Java=20=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E6=A0=87=E5=87=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/15-Exceptions.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/book/15-Exceptions.md b/docs/book/15-Exceptions.md index 7ded0c3..d671011 100644 --- a/docs/book/15-Exceptions.md +++ b/docs/book/15-Exceptions.md @@ -919,8 +919,23 @@ DynamicFields.setField(DynamicFields.java:67) ## Java 标准异常 +Throwable这个Java类被用来表示任何可以作为异常被抛出的类。Throwable对象可分为两种类型(指从Throwable继承而得到的类型):Error用来表示编译时和系统错误(除特殊情况外,一般不用你关心);Exception是可以被抛出的基本类型,在Java类库、用户方法以及运行时故障中都可能抛出Exception型异常。所以Java程序员关心的基类型通常是Exception。要想对异常有全面的了解,最好去浏览一下HTML格式的Java文档(可以从java.sun.com下载)。为了对不同的异常有个感性的认识,这么做是值得的。但很快你就会发现,这些异常除了名称外其实都差不多。同时,Java中异常的数目在持续增加,所以在书中简单罗列它们毫无意义。所使用的第三方类库也可能会有自己的异常。对异常来说,关键是理解概念以及如何使用。 + +异常的基本的概念是用名称代表发生的问题,并且异常的名称应该可以望文知意。异常并非全是在java.lang包里定义的;有些异常是用来支持其他像util、net和io这样的程序包,这些异常可以通过它们的完整名称或者从它们的父类中看出端倪。比如,所有的输入/输出异常都是从java.io.IOException继承而来的。 + +### 特例:RuntimeException + +在本章的第一个例子中: + +```java +if(t == null) + throw new NullPointerException(); +``` + + + ## finally 关键字 From 60dbefb59eb592b264ec7b7da7398f2076e640b9 Mon Sep 17 00:00:00 2001 From: sjsdfg <736777445@qq.com> Date: Tue, 14 May 2019 16:44:33 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B0=8F=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/15-Exceptions.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/book/15-Exceptions.md b/docs/book/15-Exceptions.md index d671011..5f8e3b5 100644 --- a/docs/book/15-Exceptions.md +++ b/docs/book/15-Exceptions.md @@ -383,7 +383,7 @@ getMessage() 方法,以产生更详细的信息。对于异常类来说,getM 既然异常也是对象的一种,所以可以继续修改这个异常类,以得到更强的功能。但要记住,使用程序包的客户端程序员可能仅仅只是查看一下抛出的异常类型,其他的就不管了(大多数 Java 库里的异常都是这么用的),所以对异常所添加的其他功能也许根本用不上。 -## 异常规范 +## 异常声明 Java 鼓励人们把方法可能会抛出的异常告知使用此方法的客户端程序员。这是种优雅的做法,它使得调用者能确切知道写什么样的代码可以捕获所有潜在的异常。当然,如果提供了源代码,客户端程序员可以在源代码中查找 throw 语句来获知相关信息,然而程序库通常并不与源代码一起发布。为了预防这样的问题,Java 提供了相应的语法(并强制使用这个语法),使你能以礼貌的方式告知客户端程序员某个方法可能会抛出的异常类型,然后客户端程序员就可以进行相应的处理。这就是异常说明,它属于方法声明的一部分,紧跟在形式参数列表之后。 @@ -932,7 +932,16 @@ if(t == null) throw new NullPointerException(); ``` +如果必须对传递给方法的每个引用都检查其是否为nul(因为无法确定调用者是否传入了非法引用),这听起来着实吓人。幸运的是,这不必由你亲自来做,它属于Java的标准运行时检测的一部分。如果对null引用进行调用,Java会自动抛出NullPointerException异常,所以上述代码是多余的,尽管你也许想要执行其他的检查以确保NullPointerException不会出现。 +属于运行时异常的类型有很多,它们会自动被lava虚拟机抛出,所以不必在异常说明中把它们列出来。这些异常都是从RuntimeException类继承而来,所以既体现了继承的优点,使用起来也很方便。这构成了一组具有相同特征和行为的异常类型。并且,也不再需要在异常说明中声明方法将抛出RuntimeException类型的异常(或者任何从RuntimeException继承的异常),它们也被称为“不受检查异常”。这种异常属于错误,将被自动捕获,就不用你亲自动手了。要是自己去检查RuntimeException的话,代码就显得太混乱了。不过尽管通常不用捕获RuntimeException异常,但还是可以在代码中抛出RuntimeException类型的异常。 + +RuntimeException代表的是编程错误: + +1. 无法预料的错误。比如从你控制范围之外传递进来的mull引用。 +2. 作为程序员,应该在代码中进行检查的错误。(比如对于ArrayIndexOutOfBoundsException,就得注意一下数组的大小了。)在一个地方发生的异常,常常会在另一个地方导致错误。 + +在这些情况下使用异常很有好处,它们能给调试带来便利。 From 5b0480d61dad96a8a147049ed08f1a4dddeb0a46 Mon Sep 17 00:00:00 2001 From: sjsdfg <736777445@qq.com> Date: Tue, 14 May 2019 16:57:09 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20Java=20=E6=A0=87?= =?UTF-8?q?=E5=87=86=E5=BC=82=E5=B8=B8=E7=AB=A0=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/book/15-Exceptions.md | 40 +++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/book/15-Exceptions.md b/docs/book/15-Exceptions.md index 5f8e3b5..8b10b8f 100644 --- a/docs/book/15-Exceptions.md +++ b/docs/book/15-Exceptions.md @@ -938,11 +938,49 @@ if(t == null) RuntimeException代表的是编程错误: -1. 无法预料的错误。比如从你控制范围之外传递进来的mull引用。 +1. 无法预料的错误。比如从你控制范围之外传递进来的null引用。 2. 作为程序员,应该在代码中进行检查的错误。(比如对于ArrayIndexOutOfBoundsException,就得注意一下数组的大小了。)在一个地方发生的异常,常常会在另一个地方导致错误。 在这些情况下使用异常很有好处,它们能给调试带来便利。 +如果不捕获这种类型的异常会发生什么事呢?因为编译器没有在这个问题上对异常说明进行强制检查,RuntimeException类型的异常也许会穿越所有的执行路径直达main()方法,而不会被捕获。要明白到底发生了什么,可以试试下面的例子: + +```java +// exceptions/NeverCaught.java +// Ignoring RuntimeExceptions +// {ThrowsException} +public class NeverCaught { + static void f() { + throw new RuntimeException("From f()"); + } + static void g() { + f(); + } + public static void main(String[] args) { + g(); + } +} +``` + +输出结果为: + +```java +___[ Error Output ]___ +Exception in thread "main" java.lang.RuntimeException: +From f() +at NeverCaught.f(NeverCaught.java:7) +at NeverCaught.g(NeverCaught.java:10) +at NeverCaught.main(NeverCaught.java:13) +``` + +如果RuntimeException没有被捕获而直达main(),那么在程序退出前将调用异常的printStackTrace()方法。 + +你会发现,RuntimeException(或任何从它继承的异常)是一个特例。对于这种异常类型,编译器不需要异常说明,其输出被报告给了System.err。 + +请务必记住:只能在代码中忽略RuntimeException(及其子类)类型的异常,因为所有受检查类型异常的处理都是由编译器强制实施的。 + +值得注意的是:不应把Java的异常处理机制当成是单一用途的工具。是的,它被设计用来处理一些烦人的运行时错误,这些错误往往是由代码控制能力之外的因素导致的;然而,它对于发现某些编译器无法检测到的编程错误,也是非常重要的。 + ## finally 关键字