day10 10、异常的一些注意点

10、异常的一些注意点

10.1 Java.util.logging(JDK自带的记录日志类)

10.1.1 简介

日志用来记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题。下面介绍 Java 自带的日志工具类 java.util.logging 的使用。

如果要生成简单的日志记录,可以使用全局日志记录器并调用其 info 方法,代码如下:

Logger.getGlobal().info("打印信息");

JDK Logging 把日志分为如下表 7 个级别,等级依次降低。

级别 SEVERE WARNING INFO CONFIG FINE FINER FINEST
调用方法 severe() warning() info() config() fine() finer() finest()
含义 严重 警告 信息 配置 良好 较好 最好

Logger 的默认级别是 INFO,比 INFO 级别低的日志将不显示。Logger 的默认级别定义在 jre 安装目录的 lib 下面。

Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO

所以在默认情况下,日志只显示前三个级别,对于所有的级别有下面几种记录方法:

logger.warning(message);
logger.fine(message);

同时,还可以使用 log 方法指定级别,例如:

logger.log(Level.FINE, message);

例 1

package exception_use;

import java.util.logging.Logger;

public class TestLog {

        private static Logger log = Logger.getLogger(TestLog.class.toString());
        public static void main(String[] args) {
            // 级别依次升高,后面的日志级别会屏蔽之前的级别
            log.finest("finest");
            log.finer("finer");
            log.fine("fine");
            log.config("config");
            log.info("info");
            log.warning("warning");
            log.severe("server");
        }
    }


输出结果为:
在这里插入图片描述

可以使用 setLevel 方法设置级别,例如logger.setLevel(Level.FINE);可以将 FINE 和更高级别的都记录下来。另外,还可以使用 Level.ALL 开启所有级别的记录,或者使用 Level.OFF 关闭所有级别的记录。

注意:如果将记录级别设计为 INFO 或者更低,则需要修改日志处理器的配置。默认的日志处理器不会处理低于 INFO 级别的信息。

10.1.2 修改日志管理器配置

在这里插入图片描述
可以通过编辑配置文件来修改日志系统的各种属性。在默认情况下,配置文件存在于 jre 安装目录下“jre/lib/logging.properties”。要想使用另一个配置文件,就要将 java.util.logging.config.file 特性设置为配置文件的存储位置,并用下列命令启动应用程序。

java -D java.util.logging.config.file = configFile MainClass

日志管理器在 JVM 启动过程中初始化,这在 main 执行之前完成。如果在 main 中调用System.setProperty("java.util.logging.config.file",file),也会调用LogManager.readConfiguration()来重新初始化日志管理器。

要想修改默认的日志记录级别,就需要编辑配置文件,并修改以下命令行。

.level=INFO

可以通过添加以下内容来指定自己的日志记录级别

Test.Test.level=FINE

也就是说,在日志记录器名后面添加后缀 .level。

在稍后可以看到,日志记录并不将消息发送到控制台上,这是处理器的任务。另外,处理器也有级别。要想在控制台上看到 FINE 级别的消息,就需要进行下列设置。

java.util.logging.ConsoleHandler.level=FINE

注意:在日志管理器配置的属性设置不是系统属性,因此,用-Dcom.mycompany.myapp.level=FINE启动应用程序不会对日志记录器产生任何影响

10.2 自动资源管理(java7后加,java9增强)

10.2.1 介绍

public static void main(String[] args) {
    FileInputStream fis = null;
    try {
        fis = new FileInputStream("a.txt");
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        // 关闭磁盘文件,回收资源
        if (fis != null) {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Java 7 以前,上面程序中的 finally 代码块是不得不写的“臃肿代码”,为了解决这种问题,Java 7 增加了一个新特性,该特性提供了另外一种管理资源的方式,这种方式能自动关闭文件,被称为自动资源管理(Automatic Resource Management)。该特性是在 try 语句上的扩展,主要释放不再需要的文件或其他资源。

自动资源管理替代了 finally 代码块,并优化了代码结构和提高程序可读性。语法如下:

try (声明或初始化资源语句) {//加()来声明!
    // 可能会生成异常语句
} catch(Throwable e1){
    // 处理异常e1
} catch(Throwable e2){
    // 处理异常e1
} catch(Throwable eN){
    // 处理异常eN
}

当 try 代码块结束时,自动释放资源。不再需要显式的调用 close() 方法,

该形式也称为“带资源的 try 语句”。

注意:

  1. try 语句中声明的资源被隐式声明为 final,资源的作用局限于带资源的 try 语句。
  2. 可以在一条 try 语句中声明或初始化多个资源,每个资源以;隔开即可。
  3. 需要关闭的资源必须实现了 AutoCloseable 或 Closeable 接口。
  4. Closeable 是 AutoCloseable 的子接口,Closeable 接口里的 close() 方法声明抛出了 IOException,因此它的实现类在实现 close() 方法时只能声明抛出 IOException 或其子类;AutoCloseable 接口里的 close() 方法声明抛出了 Exception,因此它的实现类在实现 close() 方法时可以声明抛出任何异常。

下面示范如何使用自动关闭资源的 try 语句。

public class AutoCloseTest {
    public static void main(String[] args) throws IOException {
        try (
                // 声明、初始化两个可关闭的资源
                // try语句会自动关闭这两个资源
                BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java"));
                PrintStream ps = new PrintStream(new FileOutputStream("a.txt"))) {
            // 使用两个资源
            System.out.println(br.readLine());
            ps.println("使用带资源的try");
        }
    }
}

上面程序中分别声明、初始化了两个 IO 流,BufferedReader 和 PrintStream 都实现了 Closeable 接口,并在 try 语句中进行了声明和初始化,所以 try 语句会自动关闭它们。

自动关闭资源的 try 语句相当于包含了隐式的 finally 块(这个 finally 块用于关闭资源),因此这个 try 语句可以既没有 catch 块,也没有 finally 块。

Java 7 几乎把所有的“资源类”(包括文件 IO 的各种类、JDBC 编程的 Connection 和 Statement 等接口)进行了改写,改写后的资源类都实现了 AutoCloseable 或 Closeable 接口。

如果程序需要,自动关闭资源的 try 语句后也可以带多个 catch 块和一个 finally 块。

10.2.2 java9的增强自动资源管理(不用写()只要声明了final或有效final)

Java 9 再次增强了这种 try 语句。Java 9 不要求在 try 后的圆括号内声明并创建资源,只需要自动关闭的资源有 final 修饰或者是有效的 final (effectively final),Java 9 允许将资源变量放在 try 后的圆括号内。上面程序在 Java 9 中可改写为如下形式。

public class AutoCloseTest {
    public static void main(String[] args) throws IOException {
        // 有final修饰的资源
        final BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java"));
        // 没有显式使用final修饰,但只要不对该变量重新赋值,该变量就是有效的
        final PrintStream ps = new PrintStream(new FileOutputStream("a. txt"));
        // 只要将两个资源放在try后的圆括号内即可
        try (br; ps) {
            // 使用两个资源
            System.out.println(br.readLine());
            ps.println("使用带资源的try");
        }
    }
}

10.3 final and return执行顺序

在 finally 代码块中改变返回值并不会改变最后返回的内容。且它一定会被执行!

总结为以下几条:

  • 当 try 代码块和 catch 代码块中有 return 语句时,finally 仍然会被执行。
  • 执行 try 代码块或 catch 代码块中的 return 语句之前,都会先执行 finally 语句。
  • 无论在 finally 代码块中是否修改返回值,返回值都不会改变,仍然是执行 finally 代码块之前的值。finally 代码块中的 return 语句一定会执行。

10.4 异常规范

10.4.1 C++异常规范(抛弃)

概念
C++ 规定,异常规范在函数声明和函数定义中必须同时指明,并且要严格保持一致,不能更加严格或者更加宽松。

注意
C++里边我们知道异常规范是抛弃的,异常规范是 C++98 新增的一项功能,但是后来的 C++11 已经将它抛弃了,不再建议使用。
java里边的异常规范不同!

10.4.2 java的异常规范

参考大佬的博客,作记录:Java 异常处理基本规则,Java异常处理的基本规范

1)不要捕获运行时异常
不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类,如:IndexOutOfBoundsException / NullPointerException ,这类异常由程序员预检查来规避,保证程序健壮性。

2) try-catch 作用域(现有代码出现率较高)
对大段代码进行try-catch ,这是不负责任的表现。catch 时请细分各种类型进行捕获!分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。
3)异常的捕捉 & 异常的处理(现有代码出现率较高)
捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。
4)异常 & finally
如果有对IO 流和资源做操作,必须逐一关闭IO 流和资源对象(从里层到外层),有异常也要做处理。

JDK 7 以上可以使用try-with-resources 方式。 (java7的自动资源管理,java9的增强自动资源管理)
5)finally & return
不能在 finally 块中使用 return ,finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句。
6)异常需要精确
捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。

7)程序员的基本修养 & NPE

  1. 方法(接口)的返回值可以为 null ,但不推荐返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回 null 值。调用方需要进行 null 判断防止 NPE 问题。

  2. 防止 NPE ,是程序员的基本修养,注意 NPE 产生的场景。

    a.查询数据库返回null ,包括null 对象和null 集合。

    b.集合内元素有null 对象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章