Exception 和Error的相同点和区别

相同点

Exception和Error都是继承了Throwable类,在java中只有Throwable类型的实例才可以被抛出(throw)或者捕获(catch),他是异常处理机制的基本组成类型。

不同点

Exception是在程序正常运行中,可以预料到的意外情况,应该捕获并进行异常处理。
Exception分为可检查异常(checked)和不检查异常(unchecked)

  • 可检查异常在源代码里面需要try,catch,这是编译时检查的一部分。常见的Checked Exception有IOException
  • 不可检查异常就是运行时异常,例如NullPointerException,ArrayIndexOfBoundsException,ClassCastException,SecurityException,通常是因为代码有逻辑错误。

error是正常情况下基本不会出现的情况,会对程序运行造成威胁,error不需要捕获,比如OutOfMemoryError,它就是Error的子类。

  • LinkageError:常见的LinkageError有NoClassDefFoundError,UnsatisfiedLinkError,ExceptionInInitializerError。
  • VirtualMachineError:另外还有另一类Error是VirtualMachineError,他下属的有些Error也挺常见,比如OutOfMemoryError,StackOverflowError。

NoClassDefFoundError和ClassNotFoundException有什么区别?

  1. ClassNotFoundException的产生原因:
    • java支持使用class.forName方法动态加载类,如果一个类的类名作为参数传递给这个方法,这个类就会被加载到JVM内存中,如果这个类在类路径中没有找到就会在运行时抛出ClassNotFoundException异常。解决办法就是把类和他所依赖的包都存放在类路径中。除了Class.forName,ClassLoader.loadClass、ClassLoader.findSystemClass在动态加载类到内存中的时候也可能会抛出这个异常。
    • 另一个导致ClassNotFoundException的原因就是:当一个类已经某个类加载器加载到内存中了,此时另一个类加载器又尝试着动态地从同一个包中加载这个类。
    • 总结:加载时从外存储器找不到需要的class就出ClassNotFoundException
  2. NoClassDefFoundError产生的原因
    • JVM或者ClassLoader尝试加载(可以通过正常的方法调用,也可能是使用new来创建新的对象)类的时候却找不到类的定义。要查找的类在编译的时候是存在的,运行的时候却找不到了。这个错误往往是你使用new操作符来创建一个新的对象但却找不到该对象对应的类。这个Error不应该写代码去捕获,他是由JVM引起的。NoClassDefFoundError: 当目前执行的类已经编译,但是找不到它的定义时解决办法:找到那些在开发期间存在于类路径下但在运行期间却不在类路径下的类

实践中的异常处理

异常处理需要我们写捕获代码或者在finally里面做一些资源回收的工作。可以考虑使用一些java好用的特性。比如try-with-resources和multiple catch,在编译时期,自动生成相应的处理逻辑,比如关闭哪些扩展了AutoCloseable的对象或者是扩展了Closeable的对象。

try(BufferedReader br = new BufferedReader(...);
	BufferWriter bw = new BufferedWriter(...)) {  //Try-with-resources
		//do something
	} catch (IOException | XException e) { //Multiple-catch
		//handle it
	}

下面举一个不正确的异常处理例子

try {
	// 业务代码
	// …
	Thread.sleep(1000L);
} catch (Exception e) {
	// Ignore it
}

上面的代码违反了异常处理的两个基本原则

  • 一,尽量不要类似Exception这样的通用异常,而是应该捕获特定的异常,例如Thread.sleep()抛出的InterrruptedException。这样便于理解这一段代码的目的。
  • 二,不能假设某一段代码不会发生,要把异常抛出或输出到日志(Logger)中,如果程序出问题,便于查找分析问题。

代码二

try {
	// 业务代码
	// …
} catch (IOException e) {
	e.printStackTrace();
}

这段代码作为实验代码可以,但是在产品代码中不允许,因为printStackTrace的输出选项是标准出错,难以判断到底输出到哪个地方去了。尤其是对于分布式系统,发生异常的时候,没法找到堆栈轨迹stacktrace,很难诊断问题,所以最好输出到日志系统中去。

代码三

public void readPreferences(String fileName){
	//...perform operations...
	InputStream in = new FileInputStream(fileName);
	//...read the preferences file...
}

如果fileName是null,程序就会抛出NullPointerException,但是由于没有第一时间暴露出问题,堆栈信息可能非常令人费解,往往需要相对复杂的定位。可以修改一下,让问题“throw early”

public void readPreferences(String filename) {
	Objects. requireNonNull(filename);
	//...perform other operations...
	InputStream in = new FileInputStream(filename);
	//...read the preferences file...
}

止于“catch late”,如果捕获异常之后不知道如何处理,就保留原来的异常信息,直接再次抛出异常,到了更高的层面,懂得了更多的业务逻辑,往往更清楚怎么处理。

猜你喜欢

转载自blog.csdn.net/shida_hu/article/details/84074272