Java错误处理、Java断言和日志

版权声明:站在巨人的肩膀上学习。 https://blog.csdn.net/zgcr654321/article/details/83214002

Java错误处理:

在计算机运行过程中,错误总是会出现:

用户输入的错误;

读写文件的错误;

网络错误,内存耗尽等等。

如果一个方法调用出错,调用方如何得知这个错误呢?

异常:

Java使用异常来表示错误。异常是class,本身带有类型信息。异常可以在任何地方抛出。异常只需要在上层捕获,和方法调用分离。

如:

Java的异常体系:

Java规定,必须捕获的异常包括Exception及其子类,但不包括RuntimeException及其子类

我们把Exception及其子类称为Checked Exception。

不需要捕获的异常包括Error及其子类,RuntimeException及其子类。

因为Error是发生了严重错误,程序一般对此无能为力。而Exception是发生了运行时的逻辑错误,应该捕获异常并处理。

捕获并处理错误:IOException,NumberFormatException;

修复程序:NullPointerException,IndexOutOfBoundsException。

我们使用try...catch捕获异常,可能发生异常的语句放在try{...}中,使用catch捕获对应的Exception及其子类。

如:

对可能抛出Checked Exception的方法调用,我们可以捕获Exception处理,如果不捕获则可以用过throws声明,但通过throws声明后仍然需要在上层捕获。main()方法是最后捕获Exception的机会。如果仍没有捕获,JVM就会报错并退出。

如:

总结:

Java使用异常来表示错误,并通过try{...}catch{...}捕获异常;

Java的异常是class,并且从Throwable继承;

Error是无需捕获的严重错误;

Exception是应该捕获的可处理的错误;

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

捕获异常:

我们可以通过try{...}catch{...}捕获异常。

还可以使用多个catch子句来捕获不同的Exception及其子类。

如:

catch捕获时可以用或操作符“|”在一条catch子句中同时捕获多种错误。

如:

写的时候注意catch的顺序,子类必须写前面,因为是按从上到下的顺序捕获的。如果父类在前,由于某个子类也属于这个父类,那么就会先被父类的catch给捕获。

如:

finally语句可以保证有无错误都会执行。finally语句不是必须的,finally总是最后才执行。

如:

总结:

catch子句的匹配顺序非常重要,子类必须放前面;

finally子句保证有无异常都会执行,finally是可选的;

catch可以匹配多个非继承关系的异常(JDK>=1.7)。

抛出异常:

当某个方法抛出异常时,如果当前方法没有捕获,异常就会被抛到上层调用方法,直到遇到某个try...catch被捕获。

如:

printStackTrace()可以打印出方法的调用栈,对于调试错误非常有用。

如:

这样的就是方法调用栈。从中我们可以一步一步找出异常最初是从哪里来的。

观察调用栈,我们可以找出异常是从哪里被抛出的。

如何抛出异常?

创建某个Exception的实例,用throw语句抛出。

如:

转换异常:

如果一个方法捕获了某个异常后,又在catch中抛出新的异常,就相当于把抛出的异常类型给“转换”了。

如:

新的异常会丢失原有的异常信息,我们可以让新的Exception持有原始异常信息。

如:

注意:

在抛出异常前,finally语句会保证执行。如果finally语句抛出异常,则catch语句将要抛出的异常不再执行。

如:

箭头所指的没有被抛出的异常称为“被屏蔽”的异常(suppressed exception)。

如何保存所有的异常信息?

我们可以用origin变量保存原始异常;如果存在原始异常,用addSuppressed()添加新异常;如果存在原始异常,或者新异常,最后在finally抛出。

如:

我们可以用getSupperssed()获取所有的Suppressed Exception。

如:

总结:

printStackTrace()可以打印异常的传播栈,对调试很有用;

捕获异常并再次抛出新的异常时,应该持有原始异常信息;

如果在finally中抛出异常,应该把新抛出的异常添加到原有异常中;

用getSuppressed()可以获取所有添加的Supperssed Exception;

处理Suppressed Exception要求JDK>=1.7。

自定义异常:

首先看看JDK已有的异常:

我们还可以定义新的异常类型,新的异常类型可以从合适的Exception派生,推荐从RuntimeException派生,这样我们就不需要强制捕获自定义的异常。也不需要在方法中声明需要抛出异常。

我们可以定义新的异常关系树:从适合的Exception派生BaseException,其他Exception从BaseException派生。

如:

自定义的异常应该提供多个构造方法。

总结:

自定义异常应该从合适的Exception派生;

推荐用RuntimeException;

自定义异常应该提供多个构造方法;

可以使用IDE根据父类快速创建构造方法。

Java断言和日志:

断言:

断言(Assertion)是一种程序调试的方式,使用assert关键字,断言条件预期为true,如果断言失败,抛出AssertionError。我们还可以设置一个断言消息。

如:

断言的特点:

断言失败时会抛出AssertionError,导致程序结束退出;

不能用于可恢复的程序错误(可恢复的程序错误不应该使用断言);

只应该用于开发和测试阶段。

JVM默认关闭断言指令:

给Java虚拟机传递-ea参数启用断言;

可以指定特定的类启用断言;

可以指定特定的包启用断言。

总结:

断言是调试方式,断言失败会抛出AssertionError;

只能在开发和测试阶段启用断言;

对可恢复的错误不能使用断言,应该抛出异常;

断言很少被使用,更好地方法是编写单元测试。

日志:

日志(logging)是为了取代System.out.println()语句。

日志可以设置输出样式;可以设置输出级别,禁止某些级别输出;可以被重定向到文件;可以按包名控制日志级别。

JDK内置了Logging(即JDK Logging),在java.util.logging包中。

如:

JDK Logging定义了7个日志级别:

如果设置为INFO,则输出INFO以上的3个级别的日志。

JDK Logging的局限:

JVM启动时读取配置文件并完成初始化;

JVM启动后无法修改配置;

需要在JVM启动时传递参数-Djava.util.logging.config.file=config-file-name。

总结:

日志是为了替代System.out.println(),可以定义格式,重定向到文件等;

日志可以存档,便于追踪问题;

日志记录可以按级别分类,便于打开或者关闭某些级别;

可以根据配置文件调整日志,无需修改代码;

JDK提供了Logging:java.util.logging。

Commons Logging:

Commons Logging是Apache创建的日志模块:

可以挂接不同的日志系统;

可以通过配置文件指定挂接的日志系统;

自动搜索并使用Log4j;

使用JDK Logging(JDK>=1.4)。

如:

Commons Logging定义了6个日志级别:

如何在Eclipse中引入jar包:

Apache Commons Logging的包下载地址http://commons.apache.org/proper/commons-logging/

Project -> Property -> Java Build Path -> Libraries -> Add Jars...

建议在工程目录下创建一个lib文件夹,把所有引用的jar包都放到这个文件夹内。

初始化Log对象:

final Log log = LogFactory.getLog(getClass());

总结:

Commons Logging是使用最广泛的日志模块;

Commons Logging的API非常简单;

Commons Logging可以自动使用其他日志模块。

Log4j:

Log4j是目前最流行的日志框架。目前已有2.X版本。

Appender可以把同一个log输出到不同的目的地:Console(屏幕)、File、Socket(远程);Filter是过滤器,决定哪些log需要被输出;Layout用来格式化log信息。

Commons Logging如果在classpath中发现了log4j,就会使用log4j。

最佳使用方法:

使用Commons Logging接口来写入日志;

开发阶段无需引用Log4j;

使用Log4j只需要把正确的配置文件和相关jar包放入工程的classpath中;

使用配置文件可以灵活修改日志的输出,无需修改代码。

总结:

通过Commons Logging实现日志,不需要修改代码即可使用Log4j;

使用Log4j只需要把log4j2.xml和相关jar放入classpath;

如果要更换Log4j,只需要移除log4j2.xml和相关jar;

只有扩展Log4j时,才需要引用Log4j的接口。

猜你喜欢

转载自blog.csdn.net/zgcr654321/article/details/83214002