《Java黑皮书基础篇第10版》 第12章【笔记】

第十二章

12.1 引言

异常是Java中的类,构造一个异常就是在构造一个对象

12.2 异常处理概述

异常是从方法抛出的。方法的调用者可以捕获以及处理该异常。

异常可以将检测错误和处理错误分离开来,当方法检测到错误时,抛出(throw)异常并传递给调用者进行处理(catch)。

浮点数除以0不会产生异常。是因为一个正/负数除以0会得到一个无穷/小的结果,整数没有定义无穷大/小,而浮点数定义了无穷大/小,所以浮点数除以0的结果是无穷大/小

12.3 异常类型

在这里插入图片描述

其中,Error类和RuntimeException类以及他们的子类,被称为免检异常,Java不强制要求编写代码捕获或声明他们

而其他所有的异常被称为必检异常,编译器会强制要求程序员检查并通过try-catch处理他们,或者在方法头声明他们(更详细的内容在12.4)

12.4 关于异常处理的更多知识

12.4.1 声明异常

声明异常的语法如下

public void myMethod() throws Exception1, Exception2, ···
12.4.2 抛出异常

抛出异常的语法如下(以illegalArgumentException为例):

illegalArgumentException ex = new illegalArgumentException("Wrong Argument");

或者

throw new illegalArgumentException("Wrong Argument");

通常,Java API中的异常(也就是12.3章节的图示)至少有2个构造方法,1个无参构造方法和1个带有异常消息(exception message)的构造方法,这个异常消息可以用getMessage()获取

12.4.3 捕获异常

捕获异常的语法如下

try {
    
    
  
} catch (Exception1 ex) {
    
    
  
}
12.4.4 从异常中获取倍息

Java.lang.Throwable类中提供了很多实例方法,获取有关异常的信息。

在这里插入图片描述

12.4.5 理解如下(结合了廖雪峰老师官网的Java讲义)

在Java中,当前执行的语句必属于某个方法

关于必检异常抛出声明捕获,一共只有2点需要记忆:

1.

方法要拋出的必检异常都必须在方法头中使用throws和逗号显式声明

也就是说:一个方法,只要在方法体抛出了异常1;就必须在方法头声明异常1

2.

如果方法头声明了某个异常,那么调用方在调用时必须捕获他们

也就是说:一个方法,只要在方法头声明异常1,调用方就必须捕获异常1

举个例子 + 例外情况分析:

例如,方法getBytes()声明了一个必检异常
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
    
    
    ...
}
2.1 那么,getBytes()方法的调用方toGBK()必须捕获这个异常(这个调用方可以是main方法,也可以是其他的更低一级的方法,且不限于储存在哪一个类中)
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        byte[] bs = toGBK("中文");
        System.out.println(Arrays.toString(bs));
    }

    static byte[] toGBK(String s) {
    
    
        //toGBK()方法调用了getBytes()方法
        //理想情况下,toGBK()方法使用try-catch捕获getBytes()声明的异常
        try {
    
    
            // 用指定编码转换String为byte[]:
            return s.getBytes("GBK");
        } catch (UnsupportedEncodingException e) {
    
    
            // 如果系统不支持GBK编码,会捕获到UnsupportedEncodingException:
            System.out.println(e); // 打印异常信息
            return s.getBytes(); // 尝试使用用默认编码
        }
    }
}
2.2 如果当前的调用方toGBK()没有捕获这个异常,就必须在更高的调用层捕获(main方法是最高的调用层)
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        //在更高的调用层main进行捕获
        try {
    
    
            byte[] bs = toGBK("中文");
            System.out.println(Arrays.toString(bs));
        } catch (UnsupportedEncodingException e) {
    
    
            System.out.println(e);
        }
    }

    static byte[] toGBK(String s) throws UnsupportedEncodingException {
    
    
        // 用指定编码转换String为byte[]:
        return s.getBytes("GBK");
    }
}
2.3 如果不想写try-catch statement,可以直接在main方法处throws Exception,声明了可能抛出的所有的异常(或者某一类别的异常)
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Main {
    
    
    //直接声明异常
    public static void main(String[] args) throws Exception {
    
    
        byte[] bs = toGBK("中文");
        System.out.println(Arrays.toString(bs));
    }

    static byte[] toGBK(String s) throws UnsupportedEncodingException {
    
    
        // 用指定编码转换String为byte[]:
        return s.getBytes("GBK");
    }
}

12.5 finally子句

无论异常是否产生,finally 子句总是会被执行的。语法如下

try {
    
     
  statements;
}
catch (TheException ex) {
    
    
    handling ex;
 }
finally {
    
     
  finalStatements;
}

某些情况下,可以没有catch,只使用try ... finally结构。例如:

void process(String file) throws IOException {
    try {
        ...
    } finally {
        System.out.println("END");
    }
}

因为方法声明了可能抛出的异常,所以可以不写catch

12.6 何时使用异常

异常处理将错误处理代码(catch)从正常的程序设计任务中(try)分离出来,这样,可以使程序更易读 、更易修改。

但是,异常处理也会占用更多的时间和计算机资源。不要把异常处理当作简单的逻辑测试。

12.7 重新抛出异常

如果异常处理器不能处理一个异常,或者只是简单地希望它的调用者注意到该 异常,Java允许该异常处理器重新抛出异常。

12.8 链式异常

和其他异常一起抛出一个异常 ,构成了链式异常。

12.9创建自定义异常类

可以通过继承Exception或它的子类来自定义一个新的异常类,但是最好不要继承RuntimeException,只继承必检异常,这样编译器就可以强制要求程序员捕获他们

Java 提供相当多的异常类,尽量首先使用它们而不要创建自己的异常类。

12.10 File类

在程序运行时得到的数据是暂时的,程序终止运行也就意味着数据的丢失。为了将数据永久保存,可以把这些数据储存在文件中,这些文件今后就可以被其他程序访问。

对于File类,可以获取、重命名和删除文件或目录,

构建一个File实例并不会真的在计算机上创建一个文件,也不可以对文件进行输入/输出。

12.11 文件输入和输出

12.11.1 使用PrintWriter写入数据

java.io.PrintWriter 类可用来创建一个文件并向文本文件写人数据。但是一定要close(),不然会导致数据不能正确的保存

12.11.2 使用 try-with-resources 自动关闭资源

程序猿经常会忘记close(),为了避免错误,简化代码,可以使用try-with-resources自动调用close()

12.11.3 使用 Scanner 读数据

可以使用Scanner从文件中读取数据

12.11.4 Scanner 如何工作

读取用分隔符分隔开的标记。默认情况下,分隔符是空格。

对于nextInt()和nextDouble()方法,首先,他们会从当前的输入缓冲区开始,忽略所有的空格和换行符,直到遇到一个非空格非换行符的字符。

其次,开始记录接下来所有的字符,直到再次遇到空格或换行符时停止,并返回所有收集到的字符(只返回字符,不返回空格或换行符)

对于nextLine()方法,首先,他们会从当前的输入缓冲区开始,记录并返回所有的内容(包括空格和换行符),直到遇到换行符时停止,也就是读取当前行的全部剩余部分

注意,读取完成后,这个方法会将读取指针转入新的下一行

12.12 从Web上读取数据

使用URL类可以读取网页上文件的内容

12.13 示例学习:Web爬虫

开发了一个程序可以基于第一个网页的URL,爬取其后相关联的100个URL

本章拓展:

从Java 7开始,引入了java.nio.file包,其中包含了一组新的文件和目录操作类,用于替代java.io.File类。

总的来说,java.nio.file包提供了更现代和强大的文件和目录操作功能,更加符合当今的编程需求。它提供了更丰富的API,更好的异常处理和性能优势,建议在新的Java项目中使用java.nio.file包进行文件和目录操作。

猜你喜欢

转载自blog.csdn.net/weixin_40020256/article/details/130675746