Java异常处理学习笔记(抛出、捕获、finally、异常传播、NPE、断言、日志)

Java中的异常是什么?

Java异常本质上一种class,继承关系如下图所示,Error是严重的错误,程序无能为力,RuntimeException是在运行过程中发生的异常,其余的异常在编写程序的时候就应该检查并处理。

异常继承树

Java异常处理学习笔记(抛出、捕获、finally、异常传播、NPE、断言、日志)_第1张图片
哪些异常必须捕获?

除了ErrorRuntimeException和他们的子类,其余的异常都必须被捕获。

try{}、throws抛出异常catch(){}捕获

如果不写try{}catch(){}会怎样?

// try...catch
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) {
		try {
			// 用指定编码转换String为byte[]:
			return s.getBytes("GBK");
		} catch (UnsupportedEncodingException e) {
			// 如果系统不支持GBK编码,会捕获到UnsupportedEncodingException:
			System.out.println(e); // 打印异常信息
			return s.getBytes(); // 尝试使用用默认编码
		}
	}
//  这种写法是错误的,编码异常必须捕获,否则无法通过编译
//	static byte[] toGBK(String s) {
//		return s.getBytes("GBK");
//	}
}

有一些异常必须要处理!否则根本无法通过编译

也可以通过throws抛出异常

try后面的代码如果出现异常了默认会抛出异常,如果不写try也可以直接使用throws来抛出异常,无论是通过try还是通过throws抛出的异常,都必须使用catch去处理异常。

如果当前调用层没有捕获异常,也必须在更高的调用层处理,main函数是最后捕获异常的机会,由编译器保证不会出错。

如果偷懒在main函数中直接抛出异常不处理,则程序遇到异常会立刻退出。

RuntimeException无需强制捕获,非RuntimeException(Checked Exception)需强制捕获,或者用throws声明;

多catch和finally语句

如果有许多语句要执行,可以全部放在try语句中。

如果有多个异常要捕获,可以并列写多个catch语句,注意

  • 子类一定要写在前面!
  • 多个相似的异常可以使用|放到一起。

如果在出现异常之后还需要执行某些语句,可以放在finally中,可以确保一定会被执行到。

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException | NumberFormatException e) { // IOException或NumberFormatException
        System.out.println("Bad input");
    } catch (Exception e) {
        System.out.println("Unknown error");
    } finally {
        System.out.println("Finally end!");
    }
}

异常的传播过程

新建异常和抛出异常

void process2(String s) {
    if (s==null) {
        // 1. 创建一个Exception实例
        NullPointerException e = new NullPointerException();
        // 2. 抛出这个具体的异常
        throw e;
        
        // 简略写法,直接抛出新建的异常
        throw new NullPointerException();
    }
}

在多个函数调用之间,可以将异常转换类型。

printStackTrace打印异常栈

如果异常在多个调用函数之间传播,则可以使用printStackTrace()方法打印异常栈。

异常和finally的关系

异常不会影响finally的执行,即使出错了,finally也一样会执行。

如果finally又抛出了异常怎么办?

fianlly中的异常会把之前的异常给屏蔽掉,虽然可以保存原始异常再输出,但是一般不建议这样做!

怎样避免NullPointerException?

俗称NPE,在引用一个null的引用变量或方法时经常出现。

怎样避免NPE?

  1. 成员变量在定义的时候就初始化
  2. 初始化的时候,string选择为“”空字符串或String[0]

如果出现多层的调用,如何确定Null出现在哪一层?

Java14中,在JVM运行时添加如下参数获得增强异常信息

java -XX:+ShowCodeDetailsInExceptionMessages Main.java

什么时候用assert?

在开发调试的时候,可以使用断言来检查一些数据是否超出正常范围。

public static void main(String[] args) {
    double x = Math.abs(-123.45);
    assert x >= 0:"x must >=0";		// 可以加一个冒号,增加说明信息
    System.out.println(x);
}

但是,一旦断言失败,抛出AssertionError会导致程序直接退出,因此断言只能用于开发和测试阶段。断言很少使用,尽量使用单独的测试单元代码。

特备注意,JVM默认是关闭断言的,所以在具体使用的时候,需要加上一句:

java -ea Main.java

上面的-ea表示-enableassertions

为什么使用log日志调试程序?

日志最大的好处,就是可以控制日志输出的级别。

同时,日志还会自动打印时间、调用信息等。

  • Java内置了标准库JDK Logging
  • 第三方日志库Commons Logging(Apache),一般配合log4j日志框架使用
  • 第三方日志库SLF4J日志接口,一般配合Logback框架实现,是目前的趋势。

接口对比

Commons Logging SLF4J
org.apache.commons.logging.Log org.slf4j.Logger
org.apache.commons.logging.LogFactory org.slf4j.LoggerFactory

第三方日志库的使用方法,是把下载的jar包放到classpath下,修改xml配置文件,然后编译运行。

你可能感兴趣的:(JAVA学习笔记)