JAVA 的基本理念是 “结构不佳的代码不能运行”。
发现错误的理想时机是在编译阶段,即在程序运行之前。然而,在编译期间并不能找出所有错误,余下的问题必须在运行期间解决。
这就需要错误源通过某种方式将适当的信息传递给接收者,再由接收者正确处理这个问题。
错误恢复机制是保障代码健壮性的强有力标志。早期编程语言通常会在方法内设置特殊返回值或者标记,来判定程序是否发生了错误。
每次在调用程序之前都进行错误检查,会使得代码变得难以阅读,特使是在构建大型程序时,会造成很大的阻碍。
JAVA 提供异常处理机制来解决这个问题,异常处理程序不必在方法调用处进行检查,异常机制将保证能捕获这个错误,并在一个地方集中处理错误。
异常会阻止当前方法或作用域继续执行,之后允许我们强制停止程序运行或者强制程序处理问题,并返回稳定状态。
故异常处理理论上拥有2种基本模型:
1.终止模型
假设此错误非常关键,一旦此异常被抛出,表示错误已无法挽回,也不允许程序回来继续执行。
2.恢复模型
这类模型用来修正错误,然后重新尝试调用出问题的方法,通常认为第二次能成功。使用这类模型不能将异常向外抛出,而是调用方法修正错误。通常不建议使用此类模型,因修复错误依赖抛出异常位置的非通用性代码,不仅增加了代码的耦合性,也增加了代码编写和维护的困难。
接下来,我们来看个异常处理的例子:
public class ExceptionFeature {
public static void main(String[] args) {
try {
ArrayList<Object> arrayList = new ArrayList<>();
arrayList = null;
System.out.println(arrayList.toString());
} catch (NullPointerException e) {
e.printStackTrace();
}
System.out.println("method finish");
}
}
java.lang.NullPointerException
at mtn.baymax.charpter12.ExceptionFeature.main(ExceptionFeature.java:16)
method finish
对可能产生异常的代码块使用 try - catch 关键字包裹起来,try { } 包含预计产生异常的代码,然后在 catch(Exception e ) 方法中指定参数,即需要捕获的异常类型,所有的异常类都继承于 Exception 类。
异常在处理完成后,程序会继续执行。若有抛出的异常未处理或者异常处理中再次出现异常且未处理,程序会立即终止。
此外,我们还可以定义多个 catch 块来处理异常,异常只会进入第一个匹配的异常,后续的 catch 不会继续执行,在这里第二个 catch 使用的是 Exception 类,它可以捕获所有类型的异常,但因所有子类都会进行向上转型,不利于根据指定类型异常进行处理的情形。
public class ExceptionFeature {
public static void main(String[] args) {
try {
ArrayList<Object> arrayList = new ArrayList<>();
arrayList = null;
System.out.println(arrayList.toString());
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("method finish");
}
}
其次,我们还可以创建自定义异常,只需继承 Exception 类即可,同时还可定义多个构造器来生成自定义异常类,满足不同场景的需求。
下述例子 SimpleException(String message) 重写了父类构造器中的方法,throw 表示主动抛出异常,throws 表示该方法可能会抛出某种类型的异常,通过 throws抛出异常后,若其他程序调用此方法,编译器会强制要求处理此异常。
public class SimpleException extends Exception {
public SimpleException() {
}
public SimpleException(String message) {
super(message);
}
}
public class InheritingExceptions {
public void f() throws SimpleException {
System.out.println("throw simpleException from f()");
throw new SimpleException();
}
public void g() throws SimpleException {
System.out.println("throw simpleException from g()");
throw new SimpleException("message");
}
public static void main(String[] args) {
InheritingExceptions inheritingExceptions = new InheritingExceptions();
try {
inheritingExceptions.f();
} catch (SimpleException e) {
e.printStackTrace();
}
try {
inheritingExceptions.g();
} catch (SimpleException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
throw simpleException from f()
throw simpleException from g()
message
mtn.baymax.charpter12.SimpleException
at mtn.baymax.charpter12.InheritingExceptions.f(InheritingExceptions.java:12)
at mtn.baymax.charpter12.InheritingExceptions.main(InheritingExceptions.java:23)
mtn.baymax.charpter12.SimpleException: message
at mtn.baymax.charpter12.InheritingExceptions.g(InheritingExceptions.java:17)
at mtn.baymax.charpter12.InheritingExceptions.main(InheritingExceptions.java:28)
此外,我们通常会将异常信息记录在日志中,一般是在调用类中进行记录,而非在异常类中直接进行记录。
Exception 类中的 printStackTrace() 不会产生字符串,需调用另一个接收 java.io.StringWriter 对象的重载方法来产生字符串。
public class LoggingException extends Exception {
public LoggingException() {
}
}
public class LoggingExceptions {
private static Logger logger = Logger.getLogger("LoggingExceptions");
public static void main(String[] args) {
try {
throw new LoggingException();
} catch (LoggingException e) {
StringWriter trace = new StringWriter();
e.printStackTrace(new PrintWriter(trace));
logger.severe(trace.toString());
}
}
}
七月 10, 2021 9:06:29 下午 mtn.baymax.charpter12.LoggingExceptions main
严重: mtn.baymax.charpter12.LoggingException
at mtn.baymax.charpter12.LoggingExceptions.main(LoggingExceptions.java:18)
本次分享至此结束,希望本文对你有所帮助,若能点亮下方的点赞按钮,在下感激不尽,谢谢您的【精神支持】。
若有任何疑问,也欢迎与我交流,若存在不足之处,也欢迎各位指正!