Java 异常处理
Java面向对象设计 - Java异常处理
异常是在没有定义正常执行路径时在Java程序的执行期间可能出现的条件。
Java通过将执行操作的代码与处理错误的代码分离来处理错误。
当发生异常时,Java会创建一个包含有关异常的所有信息的对象,并将其传递给相应的异常处理代码。
有关异常的信息包括异常的类型,发生异常的代码中的行号等。
try-catch块
要处理异常,请将代码放在try块中。try块如下所示:
try { // Code for the try block }
try块以关键字try开头,后面是开括号和结束括号。
try块的代码放在开口和关闭大括号内。
try块本身不能使用。
它必须后跟一个或多个catch块,或一个finally块,或两者的组合。
要处理可能在try块中抛出的异常,请使用catch块。
一个catch块可用于处理多种类型的异常。
catch块的语法与方法的语法相似。
catch (ExceptionClassName parameterName) { // Exception handling code }
catch块的声明就像一个方法声明。
它从关键字catch开始,后面跟一对括号。
在括号中,它声明了一个参数。
参数类型是应该捕获的异常类的名称。
parameterName是用户给定的名称。括号后面是开口括号和结束括号。异常处理代码放在大括号中。
当抛出异常时,异常对象的引用将复制到parameterName。
我们可以使用parameterName从异常对象中获取信息。
我们可以将一个或多个catch块与try块关联。
try-catch块的一般语法如下。
try { // Your code that may throw an exception } catch (ExceptionClass1 e1){ // Handle exception of ExceptionClass1 type } catch (ExceptionClass2 e2){ // Handle exception of ExceptionClass2 type } catch (ExceptionClass3 e3){ // Handle exception of ExceptionClass3 type }
例子
下面的代码显示了如何处理除零异常。
public class Main { public static void main(String[] args) { int x = 10, y = 0, z; try { z = x / y; System.out.println("z = " + z); } catch (ArithmeticException e) { String msg = e.getMessage(); System.out.println("The error is: " + msg); } System.out.println("The end."); } }
上面的代码生成以下结果。
Java 异常类
Java面向对象设计 - Java异常类
异常类层次结构
异常类层次结构从java.lang.Throwable类开始。
当抛出异常时,它必须是Throwable类的对象或其任何子类。
catch块的参数必须是Throwable类型,或其子类之一,例如Exception,ArithmeticException,IOException等。
我们可以通过从一个异常类继承我们的类来创建我们自己的异常类。
使用多个catch块
Java运行时选择适当的catch块,并从第一个catch块开始顺序寻找合适的catch时钟。
try块的多个catch块必须从最具体的异常类型排列为最通用的异常类型。
以下代码使用多个catch块的有效序列。
ArithmeticException类是RuntimeException类的子类。
如果这两个异常都在同一个try块的catch块中处理,那么最具体的类型,即ArithmeticException,必须出现在最通用的类型之前,即RuntimeException。
try { // Do something, which might throw Exception } catch(ArithmeticException e1) { // Handle ArithmeticException first } catch(RuntimeException e2) { // Handle RuntimeException after ArithmeticException }
已检查和未检查异常
有三种类型的异常:
- 有一些例外,有更高的可能发生。我们可以在try-catch块中处理它们。或者我们可以在调用方法/构造函数声明中指定它可能抛出异常。
- 错误是可能发生的异常,并且我们几乎不能处理它。例如,java.lang.OutOfMemeoryError。我们不能做任何事情从内存不足错误中恢复。
异常类层次结构中的类,它们是Error类的子类和Error类本身,属于此类别的异常。
编译器不坚持对我们的代码采取行动。如果在运行时抛出此类异常,运行时将通过显示详细的错误消息并暂停应用程序来处理它。 - 在运行时可能会发生异常,我们可能会从异常条件中恢复。
异常类层次结构中的类,它们是RuntimeException类的子类和RuntimeException类本身,属于此类别。
编译器不坚持对我们的代码采取行动。
第一类中的异常称为检查异常,因为编译器检查它们是否在代码中处理。
Throwable类,Exception类和Exception类的子类(不包括RuntimeException类及其子类)称为检查异常。
所有未检查异常的异常称为未检查异常,因为编译器不检查它们是否在代码中处理。
Error类,Error类的所有子类,RuntimeException类及其所有子类都是未检查的异常。
我们可以处理未检查的异常,如果我们想,编译器不会强迫我们这样做。
用于处理已检查或未检查异常的程序结构是相同的。
以下代码显示如何处理已检查的异常:
import java.io.IOException; public class Main { public static void main(String[] argv) { try { int input = System.in.read(); if (input != -1) { System.out.println(input); } } catch (IOException e) { System.out.print("IOException occurred."); } } }
Java 异常抛出
Java面向对象设计 - Java异常抛出
如果一段代码可能抛出一个已检查的异常,我们有两个选择:
- 使用try-catch块处理已检查的异常。
- 在方法/构造函数声明中用throws子句指定。
语法
throws子句的一般语法是
<modifiers> <return type> <method name>(<params>) throws<List of Exceptions>{ }
关键字throws用于指定throws子句。
throws子句放在方法参数列表的右括号之后。
throws关键字后面是以逗号分隔的异常类型列表。
例子
下面的代码展示了如何在方法的声明中使用throws子句。
import java.io.IOException; public class Main { public static void readChar() throws IOException { int input = System.in.read(); } }
这里是显示如何使用它的代码。
例2
import java.io.IOException; public class Main { public static void readChar() throws IOException { int input = System.in.read(); System.out.println(input); } public static void main(String[] args) { try { readChar(); } catch (IOException e) { System.out.println("Error occurred."); } } }
上面的代码生成以下结果。
例3
我们可以继续抛出异常。
import java.io.IOException; public class Main { public static void readChar() throws IOException { int input = System.in.read(); System.out.println(input); } public static void main(String[] args) throws IOException { readChar(); } }
上面的代码生成以下结果。
抛出异常
我们可以使用throw语句在我们的代码中抛出异常。
throw语法的语法是
throw <A throwable object reference>;
throw是一个关键字,后面是一个对可抛出对象的引用。
throwable对象是一个类的实例,它是Throwable类的子类,或Throwable类本身。
以下是throw语句的示例,它抛出一个IOException:
// Create an object of IOException IOException e1 = new IOException("File not found"); // Throw the IOException throw e1;
以下是throw语句的示例,它抛出一个IOException:
// Throw an IOException throw new IOException("File not found");
如果我们抛出一个被检查的异常,我们必须使用try-catch块来处理它,或者在方法或构造函数声明中使用throws子句。
如果您抛出未经检查的异常,这些规则不适用。
Java 自定义异常
Java面向对象设计 - Java自定义异常
我们可以创建我们自己的异常类。
它们必须扩展现有的异常类。
<Class Modifiers> class <Class Name> extends <Exception Class Name> { }
<Class Name> is the exception class name.
创建一个MyException类,它扩展了java.lang.Exception类。
语法
语法如下:
public class MyException extends Exception { }
异常类与Java中的任何其他类一样。通常,我们不向我们的异常类中添加任何方法。
许多有用的方法可以用来查询异常对象的状态在Throwable类中声明。
自定义异常类构造函数
通常,我们为我们的异常类包括四个构造函数。
所有构造函数将使用super关键字调用其超类的相应构造函数。
class MyException extends Exception { public MyException() { super(); } public MyException(String message) { super(message); } public MyException(String message, Throwable cause) { super(message, cause); } public MyException(Throwable cause) { super(cause); } }
第一个构造函数创建一个具有null的异常作为其详细消息。
第二个构造函数创建一个具有详细消息的异常。
第三和第四个构造函数允许您通过包装/不包含详细消息的另一个异常来创建异常。
您可以抛出类型MyException的异常。
throw new MyException("Your message goes here");
我们可以在方法/构造函数声明中的throws子句中使用MyException类,或者在catch块中使用参数类型。
public void m1() throws MyException { }
或捕获异常类
try { }catch(MyException e) { }
Throwable
下面的列表显示了Throwable类的一些常用方法。
Throwable类是Java中所有异常类的超类。此表中显示的所有方法在所有异常类中都可用。
- Throwable getCause()
返回异常的原因。如果未设置异常的原因,则返回null。 - String getMessage()
返回异常的详细消息。 - StackTraceElement[] getStackTrace()
返回堆栈跟踪元素的数组。 - Throwable initCause(Throwable cause)
设置异常的原因。
有两种方法可以将异常设置为异常的原因。其他方法是使用构造函数,它接受原因作为参数。 - void printStackTrace()
在标准错误流上打印堆栈跟踪。 - void printStackTrace(PrintStream s)
将堆栈跟踪打印到指定的PrintStream对象。 - void printStackTrace(PrintWriter s)
将堆栈跟踪打印到指定的PrintWriter对象。 - String toString()
返回异常对象的简短描述。
例外
以下代码演示了对异常类使用printStackTrace()方法。
public class Main { public static void main(String[] args) { try { m1(); } catch (MyException e) { e.printStackTrace(); // Print the stack trace } } public static void m1() throws MyException { m2(); } public static void m2() throws MyException { throw new MyException("Some error has occurred."); } } class MyException extends Exception { public MyException() { super(); } public MyException(String message) { super(message); } public MyException(String message, Throwable cause) { super(message, cause); } public MyException(Throwable cause) { super(cause); } }
上面的代码生成以下结果。
例2
以下代码显示了如何将异常的堆栈跟踪写入字符串。
import java.io.PrintWriter; import java.io.StringWriter; public class Main { public static void main(String[] args) { try { m1(); } catch (MyException e) { String str = getStackTrace(e); System.out.println(str); } } public static void m1() throws MyException { m2(); } public static void m2() throws MyException { throw new MyException("Some error has occurred."); } public static String getStackTrace(Throwable e) { StringWriter strWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(strWriter); e.printStackTrace(printWriter); // Get the stack trace as a string String str = strWriter.toString(); return str; } } class MyException extends Exception { public MyException() { super(); } public MyException(String message) { super(message); } public MyException(String message, Throwable cause) { super(message, cause); } public MyException(Throwable cause) { super(cause); } }
上面的代码生成以下结果。
Java 终止块
Java 面向对象设计 - Java 终止块
try
块也可以有零个或一个 finally
块。 finally
块总是与 try
块一起使用。
语法
使用 finally
块的语法是
finally {
// Code for finally block
}
finally
块以关键字 finally
开始,后面紧跟一对大括号。
finally
块的代码放在大括号内。
try
,catch
和 finally
块有两种可能的组合: try - catch - finally
或 try - finally
。
try
块可以后跟零个或多个 catch
块。
try
块最多可以有一个 finally
块。
try
块必须有一个 catch
块,一个 finally
块,或者两者兼而有之。
try-catch-finally
块的语法是:
try {
// Code for try block
}
catch(Exception1 e1) {
// Code for catch block
}
finally {
// Code for finally block
}
try - finally
块的语法是:
try {
// Code for try block
}
finally {
// Code for finally block
}
无论在相关联的 try
和 /
或 catch
块中发生什么,finally
块都被保证被执行。
通常,我们使用 finally
块来写清理代码。
例如,我们可能获得一些资源,当我们完成它们时,必须释放。
try - finally
块允许你实现这个逻辑。
您的代码结构将如下所示:
try {
// Obtain and use some resources here
}
finally {
// Release the resources that were obtained in the try block
}
例子
下面的代码演示了 finally
块的使用。
public class Main {
public static void main(String[] args) {
int x = 10, y = 0, z = 0;
try {
System.out.println("Before dividing x by y.");
z = x / y;
System.out.println("After dividing x by y.");
} catch (ArithmeticException e) {
System.out.println("Inside catch block a.");
} finally {
System.out.println("Inside finally block a.");
}
try {
System.out.println("Before setting z to 2.");
z = 2;
System.out.println("After setting z to 2.");
}
catch (Exception e) {
System.out.println("Inside catch block b.");
} finally {
System.out.println("Inside finally block b.");
}
try {
System.out.println("Inside try block c.");
}
finally {
System.out.println("Inside finally block c.");
}
try {
System.out.println("Before executing System.exit().");
System.exit(0);
System.out.println("After executing System.exit().");
} finally {
// This finally block will not be executed
// because application exits in try block
System.out.println("Inside finally block d.");
}
}
}
上面的代码生成以下结果。
重新引用异常
捕获的异常可以重新引用。
public class Main {
public static void main(String[] args) {
try {
m1();
} catch (MyException e) {
// Print the stack trace
e.printStackTrace();
}
}
public static void m1() throws MyException {
try {
m2();
} catch (MyException e) {
e.fillInStackTrace();
throw e;
}
}
public static void m2() throws MyException {
throw new MyException("An error has occurred.");
}
}
class MyException extends Exception {
public MyException() {
super();
}
public MyException(String message) {
super(message);
}
public MyException(String message, Throwable cause) {
super(message, cause);
}
public MyException(Throwable cause) {
super(cause);
}
}
上面的代码生成以下结果。
Java 异常使用
Java面向对象的设计 - Java异常使用
访问线程的堆栈
以下代码显示了如何获取线程的堆栈帧。
Throwable对象在创建线程的点处捕获线程的堆栈。
public class Main { public static void main(String[] args) { m1(); } public static void m1() { m2(); } public static void m2() { m3(); } public static void m3() { Throwable t = new Throwable(); StackTraceElement[] frames = t.getStackTrace(); printStackDetails(frames); } public static void printStackDetails(StackTraceElement[] frames) { System.out.println("Frame count: " + frames.length); for (int i = 0; i < frames.length; i++) { int frameIndex = i; // i = 0 means top frame System.out.println("Frame Index: " + frameIndex); System.out.println("File Name: " + frames[i].getFileName()); System.out.println("Class Name: " + frames[i].getClassName()); System.out.println("Method Name: " + frames[i].getMethodName()); System.out.println("Line Number: " + frames[i].getLineNumber()); } } }
上面的代码生成以下结果。
try-with-resources块
Java 7添加了一个名为try-with-resources的新结构。
使用Java 7中的新的try-with-resources构造,上面的代码可以写成
try (AnyResource aRes = create the resource...) { // Work with the resource here. // The resource will be closed automatically. }
当程序退出构造时,try-with-resources构造自动关闭资源。
资源尝试构造可以具有一个或多个catch块和/或finally块。
我们可以在try-with-resources块中指定多个资源。两个资源必须用分号分隔。
最后一个资源不能后跟分号。
以下代码显示了try-with-resources使用一个和多个资源的一些用法:
try (AnyResource aRes1 = getResource1()) { // Use aRes1 here } try (AnyResource aRes1 = getResource1(); AnyResource aRes2 = getResource2()) { // Use aRes1 and aRes2 here }
我们在try-with-resources中指定的资源是隐式最终的。
在try-with-resources中的资源必须是java.lang.AutoCloseable类型。
Java 7添加了AutoCloseable接口,它有一个close()方法。
当程序退出try-with-resources块时,将自动调用所有资源的close()方法。
在多个资源的情况下,按照指定资源的相反顺序调用close()方法。
class MyResource implements AutoCloseable { public MyResource() { System.out.println("Creating MyResource."); } @Override public void close() { System.out.println("Closing MyResource..."); } } public class Main { public static void main(String[] args) { try (MyResource mr = new MyResource(); MyResource mr2 = new MyResource()) { } } }
上面的代码生成以下结果。
Multi-Catch块
Java 7增加了对多catch块的支持,以便在catch块中处理多种类型的异常。
我们可以在multi-catch块中指定多个异常类型。多个异常由竖线(|)分隔。
捕获三个异常:Exception1,Exception2和Exception3。
try { // May throw Exception1, Exception2, or Exception3 } catch (Exception1 | Exception2 | Exception3 e) { // Handle Exception1, Exception2, and Exception3 }
在multi-catch块中,不允许有通过子类化相关的替代异常。
例如,不允许以下multi-catch块,因为Exception1和Exception2是Throwable的子类:
try { // May throw Exception1, Exception2, or Exception3 } catch (Exception1 | Exception2 | Throwable e) { // Handle Exceptions here }