Java之 Exception和Error
定义
An exception is an unwanted or unexpected event, which occurs during the execution of a program i.e at run time, that disrupts the normal flow of the program’s instructions.
Java 语言在设计之初就提供了相对完善的异常处理机制,这也是 Java 得以大行其道的原因之一,因为这种机制大大降低了编写和维护可靠程序的门槛。
Error vs Exception
Java exception和Error有什么区别,运行时异常与一般异常有什么区别?
Error: An Error indicates serious problem that a reasonable application should not try to catch.
Exception: Exception indicates conditions that a reasonable application might try to catch.
首先可以看一下Throwable 类图,如下:
可以看出,Exception和Error都继承自Throwable,在Java中只有Throwable类型的实例才可以被抛出(throw)或捕获(catch),它是异常处理机制的基本组成类型。
Exception
Checked Exceptions
正确的程序在运行中,很容易出现的、情理可容的异常状况。除了Exception中的RuntimeException及其子类以外,其他的Exception类及其子类(例如:IOException和ClassNotFoundException)都属于可查异常。
Checked exceptions are exceptions that the Java compiler requires us to handle。
Unchecked Exceptions
包括运行时异常(RuntimeException与其子类)和错误(Error),RuntimeException发生的时候,表示程序中出现了编程错误,所以应该找出错误修改程序,而不是去捕获RuntimeException。
Unchecked exceptions are exceptions that the Java compiler does not require us to handle.
Handling Exceptions
/**
* @exception FileNotFoundException ...
*/
public Scanner(String fileName) throws FileNotFoundException {
...
}
throws
public int getPlayerScore(String playerFile)
throws FileNotFoundException {
Scanner contents = new Scanner(new File(playerFile));
return Integer.parseInt(contents.nextLine());
}
FileNotFoundException 是一个可检测异常,最简单的方式就是抛出。
try–catch
public int getPlayerScore(String playerFile) {
try {
Scanner contents = new Scanner(new File(playerFile));
return Integer.parseInt(contents.nextLine());
} catch (FileNotFoundException noFile) {
throw new IllegalArgumentException("File not found");
}
}
finally
它表示无论是否出现异常,都应当执行的内容。
public int getPlayerScore(String playerFile)
throws FileNotFoundException {
Scanner contents = null;
try {
contents = new Scanner(new File(playerFile));
return Integer.parseInt(contents.nextLine());
} finally {
if (contents != null) {
contents.close();
}
}
}
上面的代码对于一个经验深的人来说,仍旧可能会有问题
public int getPlayerScore(String playerFile) {
Scanner contents;
try {
contents = new Scanner(new File(playerFile));
return Integer.parseInt(contents.nextLine());
} catch (FileNotFoundException noFile ) {
logger.warn("File not found, resetting score.");
return 0;
} finally {
try {
if (contents != null) {
contents.close();
}
} catch (IOException io) {
logger.error("Couldn't close the reader!", io);
}
}
}
因为close本身也是一个高风险的方法,我们也要同时抓取。
Multiple catch Blocks
public int getPlayerScore(String playerFile) {
try (Scanner contents = new Scanner(new File(playerFile))) {
return Integer.parseInt(contents.nextLine());
} catch (IOException e) {
logger.warn("Player file wouldn't load!", e);
return 0;
} catch (NumberFormatException e) {
logger.warn("Player file was corrupted!", e);
return 0;
}
}
上面这段代码有没有问题,如果我修改成下面的代码会怎么样?
public int getPlayerScore(String playerFile) {
try {
Scanner contents = new Scanner(new File(playerFile));
return Integer.parseInt(contents.nextLine());
} catch (Exception e) {
logger.warning("Player file wouldn't load!");
e.printStackTrace();
return 0;
}
}
Union catch Blocks
catch 代码块可以集中一起,如下:
public int getPlayerScore(String playerFile) {
try (Scanner contents = new Scanner(new File(playerFile))) {
return Integer.parseInt(contents.nextLine());
} catch (IOException | NumberFormatException e) {
logger.warn("Failed to load score!", e);
return 0;
}
}
Throwing Exceptions
当出现了异常以后,我们并不知道如何处理时,或者压根就不想处理当前异常时,我看可以使用throws关键词,将异常抛给调用者,让调用者去处理异常。
创建两个可以检查异常,如下:
public class TimeoutException extends Exception {
public TimeoutException(String message) {
super(message);
}
}
Throwing a Checked Exception
public List<Player> loadAllPlayers(String playersFile) throws TimeoutException {
while ( !tooLong ) {
...
}
throw new TimeoutException("This operation took too long");
}
如果中间执行时间过长的话,调用者得到异常后可以知道下一步该如何处理。
抛出一个不可检查异常:
public List<Player> loadAllPlayers(String playersFile) throws TimeoutException {
if(!isFilenameValid(playersFile)) {
throw new IllegalArgumentException("Filename isn't valid!");
}
return new ArrayList<Player>();
}
public boolean isFilenameValid(String f){
return true;
}
public List<Player> loadAllPlayers(String playersFile)
throws PlayerLoadException {
try {
...
} catch (IOException io) {
throw new PlayerLoadException(io);
}
}
上面代码是什么意思?
Swallowing Exceptions
public int getPlayerScore(String playerFile) {
try {
// ...
} catch (Exception e) {}
return 0;
}
如果采取这样的话,很可能会引发难以诊断的诡异情况,生吞异常,往往是基于假设这段代码不会发生或者感觉忽略异常是无所谓的。但是如果我们不把异常抛出来,或者没有输出到日志,程序可能在后续的代码以不可控的方式结束。没人能够轻易判断究竟是哪里抛出了异常,以及是什么原因产生了异常。
try {
// 业务代码
// …
} catch (IOException e) {
e.printStackTrace();
}
我们来查看一下printStackTrace() 代码可以看到 "Prints this throwable and its backtrace to the standard error stream."。问在这里,在稍微复杂的生产系统中。
Common Exceptions and Errors
Checked
- Checked Exceptions
This exception is typically a way to say that something on the network, filesystem, or database failed.
RuntimeExceptions
- ArrayIndexOutOfBoundsException
this exception means that we tried to access a non-existent array index, like when trying to get index 5 from an array of length 3. - ClassCastException
this exception means that we tried to perform an illegal cast, like trying to convert a String into a List. We can usually avoid it by performing defensive instanceof checks before casting. - IllegalStateException
This exception is a generic way for us to say that our internal state, like the state of our object, is invalid. - NullPointerException
This exception means we tried to reference a null object. We can usually avoid it by either performing defensive null checks or by using Optional. - NumberFormatException
This exception means that we tried to convert a String into a number, but the string contained illegal characters, like trying to convert “5f3” into a number.
Errors
- StackOverflowError
this exception means that the stack trace is too big. This can sometimes happen in massive applications; however, it usually means that we have some infinite recursion happening in our code. - NoClassDefFoundError
this exception means that a class failed to load either due to not being on the classpath or due to failure in static initialization. - OutOfMemoryError
this exception means that the JVM doesn’t have any more memory available to allocate for more objects. Sometimes, this is due to a memory leak.
参考
Exception Handling in Java
docs.oracle
Java - Exceptions
Exception和Error有什么区别?
Java:详解Java中的异常(Error与Exception)
Java:简述try-catch-finally异常捕获