用户在遇到错误时会感觉到很不爽,如果一个用户在运行程序期间,由于程序的一些错误或者外部环境的影响造成了用户数据的丢失,用户就有可能不在使用这个程序了。为了避免此类事情发生,至少应该做到:
Java提供的异常捕获机制来改善这种情况。
某个方法不能采用正常的途径完成它的任务,就可能通过另一个路径退出该方法。这种情况下,方法并不返回任何值,而是抛出( throw
)一个封装了错误信息的对象。这个方法会 立刻退出,并不返回任何值,调用这个方法的代码也将无法执行,而是异常处理机制开始搜索能够处理这种异常状况的异常处理器(exception handler)。
Exception
JavaAPI里是这样描述的:
The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch.
The class Exception and any subclasses that are not also subclasses of RuntimeException are checked exceptions . Checked exceptions need to be declared in a method or constructor's throws clause if they can be thrown by the execution of the method or constructor and propagate outside the method or constructor boundary.
RuntimeException
派生出 NullPointerException ClassCastException IllegalArgumentException ArithmeticException ArrayStoreException IndexOutOfBoundsException NegativeArraySizeException NumberFormatException SecurityException UnsupportedOperationException
JavaAPI里是这样描述的:
RuntimeException is the superclass of those exceptions that can be thrown during the normal operation of the Java Virtual Machine.
RuntimeException and its subclasses are unchecked exceptions . Unchecked exceptions do not need to be declared in a method or constructor's throws clause if they can be thrown by the execution of the method or constructor and propagate outside the method or constructor boundary.
IOException
有 Class
Java把所有的 Error
& RuntimeException
称为 未检查(unchecked) 异常,其他的异常称为 已检查异常(checked) ,编译器会检查是否未所以的已检查异常提供了异常处理器。
如果出现了 RuntimeException
,那么一定是我的问题
如标准类库提供的 FileInputStream
类的一个构造器的声明:
public FileInputStream(String name) throws FileNotFoundException 复制代码
String readData(Scanner in) throws EOFException { ... while(...) { if(!in.hasNext()) /*EOF encountered*/ { if(n < len) throw new EOFException(); } ... } return s; } 复制代码
其中为了更加细致的描述这个异常, EOFException
类提供了一个含义字符串类型参数的构造器:
String gripe = "Conten - length: " + len + ", Received: " + n; throw new EOFException(gripe); 复制代码
try { code more code } catch(Exception) { handler for this type } 复制代码
有时候需要捕获多个异常:
try { code that might throw exceptions } catch(FileNotFoundException | UnknowHostException) { emergency action for missing file and unkonw hosts } catch(IOException) { emergency action for all other I/O problems } 复制代码
注意这里两个异常有相同的处理动作时可以合并,合并采用的 逻辑或( |
) 而不是短路或( ||
)
在catch中可以再抛出一个异常,这样做的目的是改变异常的类型。比如执行servlet的代码可能不想知道发生错误的细节原因,但希望明确的知道servlet是否又问题。
try { access the database } catch(SQLExeption e) { Throwable se = new ServletException(); se.initCause(e); throw se; } 复制代码
这样做在捕获到异常时就可以使用:
Throwable e = se.getCause(); 复制代码
重新得到原始异常而补丢失细节。
@Test public void myFinally() { try { InputStream in = new FileInputStream("/home/lighk/test.txt"); try { in.read(); /*take care! There is might throw other Exception*/ } finally { in.close(); } } catch (IOException e) { e.printStackTrace(); } } 复制代码
这里又一个问题,如果内层的try只是抛出 IOException
那么这种结构很适合。 但是如果内层的try抛出了非IOException,这些异常只有这个方法的调用者才能处理。 执行这个finally语句块,并调用close。而close()方法本身也可能抛出IOException。 当这种情况出现,原始异常会丢失,转而抛出close()方法的异常。然而第一个异常可能会更有意思。 若嵌套多层try语句也可以解决这个问题,但这会让代码变得很繁琐。 Java 7 未这种代码模式提供了一个很又用的快捷方式--带资源的try语句。
try(Scanner in = new Scanner(new FileInputStream("/home/lighk/test.txt")); PrintWriter out = New PrintWriter("out.txt")) { while(in.hasNext()) out.println(in.next().toUpperCase()); } 复制代码
前提是这个资源必须实现了AutoCloseable接口,或者其子接口Closeable.
不论这这个方法如何退出,in & out 都会关闭。若try抛出一个异常,close()抛出一个异常,close() 的异常会被`抑制`。这些异常会被自动捕获,并由addSuppressed方法增加到原来的异常。若对这个异常感兴趣, 可以调用getSuppressed方法获取close抛出并被抑制的异常列表。
if(! s.empty()) s.pop(); 复制代码
try { s.pop(); } catch(EmptyStackException e) { /* do something */ } 复制代码
前者的性能要比后者高的多,因此只在异常情况下使用异常机制。
Throw a EmptyStackException is better than throw a NullPointerException in the following code to the error.