Java中的异常是什么?
Java异常本质上一种class,继承关系如下图所示,Error
是严重的错误,程序无能为力,RuntimeException
是在运行过程中发生的异常,其余的异常在编写程序的时候就应该检查并处理。
异常继承树
哪些异常必须捕获?
除了Error
,RuntimeException
和他们的子类,其余的异常都必须被捕获。
try{}、throws抛出异常catch(){}捕获
如果不写try{}catch(){}会怎样?
// try...catchimport 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或NumberFormatExceptionSystem.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?
成员变量在定义的时候就初始化初始化的时候,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框架实现,是目前的趋势。
接口对比
第三方日志库的使用方法,是把下载的jar包放到classpath下,修改xml配置文件,然后编译运行。