本文是Java异常篇,学习于廖雪峰的Java教程

Java异常

1.异常类型

  • 异常体系的根为Throwable,它继承自Object
  • Throwable有两个体系ErrorException
  • Error
    • OutOfMemoryError
    • NoClassDefFounderError
    • StackOverError
    • 等等
  • Exception
    • RuntimeException
      • IndexOutOfBoundsException
      • NullPointerException
      • 等等
    • 非RuntimeException(也叫Checked Exception异常)
      • IOException
      • ReflectiveOperationException
      • NumberFormatException
      • FileNotFoundException
      • SocketException
      • 等等
  • 非RuntimeException异常必须被捕获,RuntimeExcept异常不强制被捕获。
  • 异常捕获使用try{}···catch{}语句。
  • 其中try声明可能出现异常的方法,catch捕获异常Exception及其子类。
  • thorws语句声明可能出现的异常,凡是用throws声明异常的方法,最终必须被捕获,如若不捕获,可在最高调用层throws异常,代价是发送异常的话程序会闪退。
  • printStackTrace()方法可以打印异常栈。

2.多catch语句与finally

  • catch语句可以捕获多个异常,如果这些异常有继承关系,则需要分开捕获,例如如下代码:
public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException e) {
        System.out.println(e);
    } catch (NumberFormatException e) {
        System.out.println(e);
    }
}
  • 注意子类要放在父类异常前面,否则如果匹配到父类异常,子类异常将不会被捕获到。
  • 如果无继承关系,则多个异常可以用|同时合并。
  • finally语句可以保证无论是否发生异常都会被执行。
  • 可以存在try{}···finally{}语句,前提是方法已经用throws语句声明异常。

3.抛出异常

  • 异常的传播:当某个方法抛出了异常时,如果当前方法没有捕获异常,异常就会被抛到上层调用方法,直到遇到某个try{}···catch{}语句被捕获为止。
  • 如何抛出异常:创建某个异常实例,用throws语句抛出。
  • 异常类型转换,如果在try语句中抛出异常,又在catch语句抛出新的异常,就会把异常类型转换。
  • 获取原始异常的办法:
    • try捕获的原始异常的实例传入catch语句中抛出的新类型异常的参数中。
    • 使用getCause()方法。
  • 如果在try或者catch语句中抛出异常,JVM会先执行finally语句,然后抛出异常。
  • 异常屏蔽(Suppressed Exception):如果在finally语句抛出异常,则catch语句的异常不会被抛出,由于JVM只能抛出一个异常。
  • 打印输出被屏蔽的异常SuppressedException的方法:创建一个Exception 实例,保存被屏蔽的异常的信息,判断存在SuppressedException后,用addSuppressed()方法把刚才创建的Exception实例添加进来,最后抛出异常。

4.自定义异常类型&NullPointerException

  • 在一个大型项目中,通常的做法是自定义一个BaseException,并且建议继承自RuntimeException,最后由BaseEduixception派生出其他自定义异常类型。

  • 对于NullPointerException异常,应当遵循早暴露早修复的原则。

  • 为了防止出现NullPointerException异常,应有较好的编码习惯,比如用" "空字符串代替null

5.断言

  • 断言(Assertion)是一种调试程序的方式。
  • 作用处:断言通常用在不在可恢复程序中,因为断言失败会导致程序结束提出。
  • 断言的用法: 例如assert x>=0,如果x<0,则断言失败,返回AssertionError;可以在断言的时候附带错误信息,例如assert x>=0 : "x must > 0"
  • 开启方式: 断言默认是关闭的,可以在命令行开启java -enableassertions Main.java

6.JDK Logging

  • JDK Logging可用于有针对性地调试代码。

  • JDK Logging的使用:

  • 位于java.util.logging包中。

  • 有八种日志级别,默认级别为INFO,并且默认级别以下的日志不会被打印,但可设定默认级别。

    • SEVERE
      • WARNING
      • INFO
      • CONFIG
      • FINE
      • FINER
      • FINEST
  • 示例代码

    package com.itranswarp.learnjava;
    import java.util.logging.Logger;
    public class Main {
        public static void main(String[] args) {
            Logger logger = Logger.getGlobal();
            logger.info("start process...");
            logger.warning("memory is running out...");
            logger.fine("ignored.");
            logger.severe("process will be terminated...");
        }
    }
    
    ### 7.Commons Logging
  • Commons Logging是一种第三方日志库,它由Apache创建,可作为日志接口来调用其他日志系统。

  • 特点:可以挂载不同的日志系统,默认情况下先搜索Log4j,如果没找到,再使用JDK Logging

  • 使用

    • 首先可通过静态工厂方法LogFactory.getLog()方法(方法内的参数可以为getclass()当前类名.class,Main)创建一个Log实例,再使用Log的实例方法打印日志。
    • 如果在静态方法中引用Log示例,则需要定义静态Log实例,如果在实例方法中引用实例,则定义一个实例变量。
    • 示例代码
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Main {
    public static void main(String[] args) {
        Log log = LogFactory.getLog(Main.class);
        log.info("start...");
        log.warn("end.");m
    }
}
  • 六种日志级别,其中INFO为默认级别。
    • FATAL
    • ERROR
    • WARNING
    • INFO
    • DEBUG
    • TRACE
  • 注意:由于为第三方库,所以需要提前下载。

8.Log4j

  • Log4j是一种日志系统,就跟JDK Logging一样。
  • Log4j的特点:可自动通过不同的Appender把同一条日志输出到不同的目的地。
  • 使用:可通过修改配置文件,最后通过Common Logging来实现调用。

9.SLF4J和Logback

  • SLF4J相当于Common Logging
  • Logback相当于Log4j
  • 使用:SLF4J的接口实际上和Commons Logging几乎一模一样,不同之处就是Log变成了LoggerLogFactory变成了LoggerFactory