文章目录
Java 语言提供了一套完善的异常处理机制。正确运用这套机制,有助于提高程序的健壮性。
9.1 Java 异常处理机制概述
Java 语言按照面向对象的思想来处理异常,使得程序具有更好的可维护性。Java 异常处理机制具有以下优点:
- 把各种不同类型的异常情况进行分类,用 Java 类来表示异常情况,这种类被称为异常类。把异常情况表示成异常类,可以充分发挥类的可扩展和可重用的优势。
- 异常流程的代码和正常流程的代码分离,提高了程序的可读性,简化了程序的结构。
- 可以灵活地处理异常,如果当前方法有能力处理异常,就捕获并处理它,否则只需抛出异常,由方法调用者来处理它。
下面是一个车子出故障 CarWrongException
异常类和上班迟到 LateWrongException
异常类。
public class Worker {
private Car car;
public Worker(Car car) {
this.car = car;
}
public void gotoWork() throws LateException {
try {
car.drive();
} catch (CarWrongException e) {
walk();
Date date = new Date(System.currentTimeMillis());
String reason = e.getMessage();
//处理异常的过程中产生的新异常,gotoWork()不会再处理,而是声明抛出LateException
//那谁来处理Worker类的LateException呢?显然是职工的老板
throw new LateException(data, reaseon);
}
}
public void walk() {
/* on foot */
}
}
public class Car {
public void drive() throws CarWrongException{
if (brakeFail())
throw new CarWrongException("brake failed");
if (engineFail())
throw new CarWrongException("engine failed")
}
}
public class CarWrongException extends Exception {
public CarWrongException() {}
public CarWrongException(String msg) {
super(msg);
}
}
public class LateWrongException extends Exception {
private Date arriveTime;
private String reason;
public LateException(Date arriveTime, String reason) {
this.arriveTime = arriveTime;
this.reason = reason;
}
public Date getArriveTime() {
return arriveTime;
}
public Strng getReason() {
return reason;
}
}
Java 虚拟机的方法调用栈
Java 虚拟机用方法调用栈(method invocation stack)来跟踪每个线程中一系列的方法调用过程。该堆栈保存了每个调用方法的本地信息(比如方法的局部变量)。每个线程都有一个独立的方法调用栈。
如果方法中的代码块可能抛出异常,有两种处理办法:
- 在当前方法中通过
try-catch
语句捕获并处理异常,例如public class Sample { public void methodA (int money) { if (--money <= 0) throw new SpecialException("Out of Money :( "); } catch (SpecialException e) { /* deal exception: earn Money! :| */ } }
- 在方法的声明处通过
throws
语句声明抛出异常,例如public class Sample { public void methodA(int money) throws SpecialException { if (--money <= 0) throw new SpecialException("Out of Money :( "); } }
当一个方法正常执行完毕后, Java 虚拟机会从调用栈中弹出该方法的栈结构,然后继续处理前一个方法。如果在执行方法的过程中抛出异常, Java 虚拟机必须找到能捕获该异常的 catch
代码块 。它首先查看当前方法是否存在这样的 catch 代码块,如果存在,就执行该 catch 代码块;否则,Java 虚拟机会从调用栈中弹出该方法的栈结构,继续到前一个方法中查找合适的 catch 代码块。
当 Java 虚拟机追溯到调用栈的最底部的方法时,如果仍然没有找到处理该异常的代码块, 将按以下步骤处理
- 调用异常对象的
printStackTrace()
方法,打印来自方法调用栈的异常信息。Exception in thread "main" Specia!Exception: Out of money at Sample.methodA(Sample.java:3) at Sample.main(Sample.java:11)
- 如果该线程不是主线程,那么终止这个线程,其他线程继续正常运行。如果该线程是主线程(即方法调用栈的最底部为 main() 方法),那么整个应用程序被终止。
一般说来,在 Java 程序中使用 try-catch
语句不会对应用的性能造成很大的影响。仅当异常发生时, Java 虚拟机需要执行额外的操作,来定位处理异常的代码块,这时会对性能产生负面影响。如果抛出异常的代码块和捕获异常 的代码块位千同一个方法中,那么这种影响就会小一 些;如果 Java 虚拟机必须搜索方法调用栈来寻找异常处理代码块,对性能的影响就比较大了。尤其是当异常处理代码块位于调用栈的最底部时,Java 虚拟机定位异常处理代码块需要大量的工作。
9.2 运用 Java 异常处理机制
本节介绍如何在应用程序中运用这种机制,来处理实际的异常情况。
try-catch 语句:捕获异常
try {
/* code that may cause exception */
} catch (Exception e) {
/* deal with exception */
}
finally 语句:任何情况下必须执行的代码
由于异常会强制中断正常流程,这会使得某些不管在任何情况下都必须执行的步骤被忽略,从而影响程序的健壮性。
public class Sample {
public void work() throws LeaveEarlyException {
try {
openDoor();
slack();
} catch (DischargeException e) {
throw new LeaveEarlyException();
} finally {
closeDoor();
}
}
}
throws 子句:声明可能会出现的异常
如果一个方法可能会出现异常,但没有能力处理这种异常,可以在 方法声明处 用 throws
子句来声明抛出异常。一个方法可能会出现多种异常, throws 子句允许声明抛出多个异常。
throw 语句:抛出异常
throw 语句用于抛出异常。值得注意的是,由 throw
语句抛出的对象必须是 java.lang.Throwable
类或者其子类的实例。
try {
checkBalance();
} catch (NoMoneyException e) {
buyLotteryTicket();
if (money < 0) throw e; //若果异常没能处理则继续抛出
}
异常处理语句语法规则
- try 代码块后面可以有零个或多个 catch 代码块,还可以有零个或至多一个 finally 代码块。如果 catch 代码块和 finally 代码块并存, finally 代码块必须在 catch 代码块后面。
- try 代码块后面可以只跟 finally 代码块。
public class Sample { public static void main(String[] args) throws SpecailException { try { new Sample().methodA(1); System.out.println("main"); } finally { System.out.println("finally"); } } }
- 在 try 代码块中定义的变量的作用域为 try 代码块,在 catch 代码块和 finally 代码块中不能访问该变量。
- 当 try 代码块后面有多个 catch 代码块时,Java 虚拟机会把实际抛出的异常对象依次和各个 catch 代码块声明的异常类刑匹配,如果异常对象为某个异常类型或其子类的实例,就执行这个 catch 代码块,不会再执行其他的 catch 代码块。
- 为了简化编程,从 JDK7 开始,允许在一个 catch 子句中同时捕获多个不同类型的异常,用符号
|
来分割。 - 如果一个方法可能出现受检查异常,要么用 try-catch 语句捕获,要么用 throws 子句声明将它抛出,否则会导致编译错误。
void m1() throws IOException{} //合法 void m2() { m1(); } //编译错误,必须捕获或声明抛出异常 void m3() throws IOException{ m1(); } //合法 void m4() throws Exception{ m1(); } //合法 void m5() {try { m1();} catch(IOException e) {..} } //合法
- 针对前面一条语法规则,从 JDK7 开始,如果在 catch 子句中捕获的异常被声明为 final 类型,那么当 catch 代码块中继续抛出该异常时,可以不用在定义方法时用 throws 子句声明将它抛出。
void method () { //无需声明catch代码中抛出的用final修饰的异常 try { .. } catch (final Throwable ex) { //被捕获异常使用final修饰 .. throw ex; //继续抛出异常 } }
- throw 语句后面不允许紧跟其他语句,因为这些语句永远不会被执行。
异常流程的运行过程
异常流程由 try-catch-finally
语句来控制。如果程序中还包含 return
和 System.exit()
语句,会使流程变得更加复杂。本节结合具体例子来说明异常流程的运行过程。
- finally 语句不被执行的唯一情况是先执行了用于终止程序的
System.exit()
方法。public static void main(String[] args) { try { System.out.println("begin"); System.exit(0); } finally { System.out.println("finally"); //不会执行 } System.out.println("end"); //不会执行 } //最终打印解果:begin //调用了java.lang.System类静态方法exit()终止当前的java虚拟机进程
public class Sample { public static void main(String[] args) throws Exception { try { System.out.println("begin"); new Sample().method(); //假设出现异常 System.exit(0); } catch (Exception e) { System.out.println("Wrong"); throw e; //如果把此行注释掉,将得到不同的运行结果 } finally { System.out.println("finally"); } System.out.println("end"); } } //保留该行,打印结果:begin wrong finally java.lang.SpecialException .. //注释改行,打印结果:begin wrong finally end
- return 语句用于退出本方法。 在执行 try 或 catch 代码块中的 return 语句时,假如有 finally 代码块,会先执行 finally 代码块。
public class WithReturn { public int methodA(int money) throws SpecialException{ if(--money <= 0) throw new SpecialException("Out of money"); return money; } public int methodB(int money){ try{ System.out.println("begin"); int result=methodA(money); //假设抛出异常 return result; //没有执行 } catch (SpecialException e) { System.out.println(e.getMessage()); return -100; }finally{ System.out.println("Finally"); } } public static void main(String[] args) { System.out.println(new WithReturn().methodB(1)); } } //打印结果:begin, out of money, finally, -100
- finally 代码块虽然在 return 语句之前被执行,但 finally 代码块不能通过重新给变量赋值的方式来改变 return 语句的返回值。
public class Sample { public static int test() { int a = 0; try { return a; } finally { a = 1; } } public static void main(String[] args) throws Exception { System.out.println(test()); } } //打印结果:0
- 建议不要在 finally 代码块中使用 return 语句,因为它会导致以下两种潜在的错误。第一种错误是覆盖 try 或 catch 代码块的 return 语句;第二种错误是丢失异常。
public class FinallyReturn { public int methodA(int money) throws SpecialException{ if(--money <= 0) throw new SpecialException("Out of money"); return money; } public int methodB(int money){ try { return methodA(money); //可能抛出异常 } catch(SpecialException e){ return -100; } finally { return 100; //会覆盖 try 和 catch 代码块的 return 语句 } } public static void main(String[] args) { FinallyReturn s = new FinallyReturn(); System.out.println(s.methodB(1)); //打印 100 System.out.println(s.methodB(2)); //打印 100 } }
public class ExLoss{ public int methodA(int money) throws SpecialException { if(--money <= 0) throw new SpecialException("Out of money"); return money; } public int metbodB(int money) throws Exception { try{ return methodA(money); //可能抛出异常 } catch (SpecialException e) { throw new Exception("Wrong"); }finally{ return 100; } } public static void main(String[] args) { try { System.out.println(new ExLoss().methodB(1)); //打印 100 System.out.println("No Exception"); }catch (Exception e) { System.out.println(e.getMessage()); } } } //methodB()方法的 catch 代码块继续抛出异常,按理说 main() 方法的 catch 代码块应该捕获并处理该异常 //但由于 methodB() 方法的 finally 代码块有返回值,异常被丢失了, //main()方法没有捕获到 methodB()方法的异常。 //以上程序的打印结果为:100, No Exception
9.3 Java 异常类
在程序运行中,任何中断正常流程的因素都被认为是异常。按照面向对象的思想,Java 语言用 Java 类来描述异常。所有异常类的祖先类为 Java.lang.Throwable
类,它的实例表示具体的异常对象,可以通过 throw
语句抛出。
Throwable 类提供了访问异常信息的一些方法:getMessage()
: 返回 String 类型的异常信息。printStackTrace()
:打印跟踪方法调用栈而获得的详细异常信息。在程序调试阶段,此方法可用于跟踪错误。
Throwable 类有两个直接子类:
- Error 类:表示单靠程序本身无法恢复的严重错误,比如内存空间不足,或者 Java 虚拟机的方法调用栈溢出。在大多数情况下,遇到这样的错误时,建议让程序终止。
- Exception类:表示程序本身可以处理的异常,本章所有例子都针对这类异常。当程序运行时出现这类异常,应该尽可能地处理异常,并且使程序恢复运行 ,而不应该随意终止程序。
Error 类及其子类表示程序本身无法修复的错误,它和运行时异常的相同之处是:Java 编译器都不会检查它们,当程序运行时出现它们,都会终止程序。两者的不同之处在于:Error 类及其子类表示的错误通常是由 Java 虚拟机抛出的,在 JDK 中预定义了一些错误类,比如 OutOfMemoryError 和 StackOutotMemoryError。在应用程序中,一般不会扩展 Error 类,来创建用户自定义的错误类。而 RuntimeException 类表示程序代码中的错误,它是可以扩展的,用户可以根据特定的问题领域,来创建相关的运行时异常类。
下面对一些常见的异常做简要的介绍:
IOException
:操作输入流和输出流时可能出现的异常。ArithmeticException
: 数学异常。如果把整数除以 0,就会出现这种异常。NullPointerException
:空指针异常。当引用变量为 null 时,试图访问对象的属性或方法,就会出现这种异常。IndexOutOfBoundsException
:下标越界异常,它的子类ArrayIndexOutOfBoundsException
表示数组下标越界异常。ClassCastException
: 类型转换异常。IllegalArgumentException
:非法参数异常,可用来检查方法的参数是否合法。
Exception 类还可分为两种:运行时异常 和 受检查异常。运行时异常是应该尽量避免的,在程序调试阶段,遇到这种异常时,正确的做法是改进程序的设计和实现方式,修改程序中的错误,从而避免这种异常。捕获它并且使程序恢复运行并不是明智的办法。
RuntimeException
类及其子类都称为运行时异常,这种异常的特点是 Java 编译器不会检查它, 也就是说,当程序中可能出现这类异常时,即使没有用 try-catch 语句捕获它 ,也没有用 throws 子句声明抛出它,也会编译通过。由于程序代码不会处理运行时异常,因此当程序在运行时出现了这种异常时,就会导致程序异常终止。
除了 RuntimeException 及其子类以外,其他的 Exception 类及其子类都属于受检查异常(Checked Exception)。这种异常的特点是 Java 编译器会检查它,当程序中可能出现这类异常时,要么用 try-catch 语句捕获它,要么用 throws 子句声明抛出它,否则编译不会通过。
9.4 用户自定义异常
在特定的问题领域,可以通过扩展 Exception 类或 RuntimeException 类来创建自定义的异常,异常类包含和异常相关的信息,这有助于负责捕获异常的 catch 代码块正确地分析并处理异常。
//以下代码定义了 一个服务器超时异常:
public class ServerTimedOutException extends Exception {
private String reason;
private int port;
public ServerTimedOutException (String reason, int port) {
this.reason = reason;
this.port = port;
}
public String getReason() {
return reason;
}
public int getPort() {
return port;
}
}
//使用 throw语句抛出异常:不能连接80端口
throw new ServerTimedOutException("Could not connect", 80);
异常转译和异常链
在分层的软件结构中,会存在自上而下的依赖关系,也就是说上层的子系统会访问下层子系统的 API。当位于最上层的子系统不需要关心来自底层的异常的细节时,常见的做法是捕获原始的异常,把它转换为一个新的不同类型的异常,再抛出新的异常,这种处理异常的办法称为 异常转译。
例如上传文件时可能会捕获 IOException 或者SQLException 。但是用户没有必要关心异常的底层细节,他们仅需要知道上传图像失败,具体的调试和排错由系统管理员或者软件开发人员来处理。因此上传操作捕获原始异常后,在 catch 代码块吧原始异常信息记入日志,然后向用户抛出 UploadException 异常。
JDK1.4 以上版本中的 Throwable
类支持异常链机制。所谓 异常链 就是把原始异常包装为新的异常类,也就是说在新的异常类中封装了原始异常类,这有助于查找产生异常的根本原因。
//支持异常链的异常类 BaseException.java
import java.io.*;
public class BaseException extends Exception {
protected Throwable cause = null; //保存原始Java异常
public BaseException() {}
public BaseException(String msg) {
super(msg); //调用父类构造方法
}
public BaseException(Throwable cause) { //参数cause指定原始异常
this.cause = cause;
}
public BaseException(String msg, Throwable cause) { //参数cause指定原始异常
super(msg);
this.cause = cause;
}
public Throwable initCause(Throwable cause) {
this.cause = cause;
return this;
}
public Throwable getCause() {
return cause;
}
public void printStackTrace() {
printStackTrace(System.err);
}
public void printStackTrace(PrintStream outStream) {
printStackTrace(new PrintWriter(outStream));
}
public void printStackTrace(PrintWriter writer) {
super.printStackTrace(writer);
if (getCause() != null)
getCause().printStackTrace(writer);
writer.flush()
}
}
//假设 UploadException 类扩展了 BaseException 类
public class UploadException extends BaseException{
public UploadException(Throwable cause) {
super(cause);
}
public UploadException(String msg) {
super(msg);
}
}
//以下是把 IOException 包装为 UploadException 的代码
try {
//上传图像文件
} catch(IOException e) {
//把原始异常信息记录到日志中 ,便于排错
...
//把原始异常包装为 UploadException
UploadException ue = new UploadException(e);
throw ue;
}
处理多样化异常
在实际应用中,有时需要一个方法同时抛出多个异常。例如,用户提交的 HTML 表单上有多个字段域,业务规则要求每个字段域的值都符合特定规则,如果不符合规则,就抛出相应的异常。有效的做法是每次当用户提交表单后,验证所有的字段域, 然后向用户显示所有的验证错误信息。不幸的是,在 Java 方法中一次只能抛出一个异常对象。因此需要开发者自行设计支持多样化异常的异常类。
//提供了一种支持多样化异常的异常类 BaseException.java
package multiex;
import java.util.List;
import java.util.ArrayList;
import java.io.PrintStream;
import java.io.PrintWriter;
public class BaseException extends Exception {
protected Throwable cause= null;
private List<Throwable> exceptions = new ArrayList<Throwable>();
public BaseException() {}
public BaseException(String msg){
super(msg);
}
public BaseException(Throwable cause) {
this.cause= cause;
}
public BaseException(String msg, Throwable cause){
super(msg);
this.cause = cause;
}
public List getExceptions() {
return exceptions;
}
public void addException(BaseException ex){
exceptions.add(ex);
}
public Throwable initCause(Throwable cause) {
this.cause = cause;
return this;
}
public Throwable getCause() { //返回原始的异常
return cause;
}
public void printStackTrace() {
printStackTrace(System.err);
}
public void printStackTrace(PrintStream outStream) {
printStackTrace(new Print Writer(outStream));
}
public void printStackTrace(PrintWriter writer) {
super.printStackTrace(writer);
if (getCause() != null)
getCause().printStackTrace(writer);
writer.flush();
}
}
// BaseException 用法
public void check() throws BaseException {
BaseException be = new BaseException();
try {
checkField1();
} catch (FieldException e) {
be.addException(e);
}
try {
checkField2();
} catch (FieldException e) {
be.addException(e);
}
if (be.getException().size() > 0)
throw be;
}
9.5 异常处理原则
- 异常只能用于非正常情况,不能用异常来控制程序的正常流程。
- 在 JavaDoc 文档中应该为方法可能抛出的所有异常提供说明文档。无论是受检查异常,还是运行时异常,都应该通过 JavaDoc 的
@throws
标签来描述产生异常的条件。 - 应该尽可能地避免异常,尤其是运行时异常。
- 许多运行时异常是由于程序代码中的错误引起的,只要修改了程序代码的错误,或者改进了程序的实现方式,就能避免这种错误。
- 有些异常是由于当对象处于某种状态下,不适合某种操作造成的,通过提供状态测试方法来避免此类异常。
- 应该尽力保持异常的原子性。异常的原子性是指当异常发生后,各个对象的状态能够恢复到异常发生前的初始状态,而不至于停留在某个不合理的中间状态。
- 最常见的办法是先检查方法的参数是否有效,确保当异常发生时还没有改变对象的初始状态。
- 编写一段恢复代码,由它来解释操作过程中发生的失败,并且使对象状态回滚到初始状态。
- 在对象的临时副本上进行操作,当操作成功后, 把临时副本中的内容复制到原来的对象中。
- 避免过于庞大的 try 代码块。
- 在 catch 子句中指定昙体的异常类型。
- 不要在 catch 代码块中忽略被捕获的异常
9.6 记录日志
在程序中输出日志总的来说有 3 个作用:
- 监视代码中变量的变化情况,把数据周期性地记录到文件中供其他应用进行统计分析工作。
- 跟踪代码运行时轨迹,作为日后审计的依据。
- 承担集成开发环境中的调试器的作用,向文件或控制台打印代码的调试信息。
要在程序中输出日志,最普通的做法是在代码中嵌入许多打印语句,这些打印语句可以把日志输出到控制台或文件中。比较好的做法是构造一个日志操作类来封装此类操作,而不是让一系列的打印语句充斥代码的主体。
在强调可重用组件开发的今天,可以直接使用 Java 类库的 java.util.logging
日志操作包。这个包中主要有 4 个类:
Logger
类:负责生成日志,并能够对日志信息进行分级别筛选,通俗地讲,就是决定什么级别的日志信息应该被输出,什么级别的日志信息应该被忽略。Handler
类:负责输出日志信息,它有两个子类:ConcoleHandler
类(把日志输出到 DOS 命令行控制台)和FileHandler
类(把日志输出到文件中)。Formatter
类:指定日志信息的输出格式。它有两个子类:SimpleFormatter
类(表示常用的日志格式)和XMLFormatter
类(表示基于 XML 的日志格式)。Level
类:表示日志的各种级别,它的静态常量如Level.INFO
、Level.WARNING
和Level.CONFIG
等分别表示不同的日志级别。
import java.util.logging.*;
class Logger Tester1{
public static void main(String[] args) {
Logger myLogger = Logger.getLogger("mylogger"); //声明一个日志记录器对象
myLogger.setLevel(Level.WARNING);
myLogger.info("this is a normal msg"); //INFO 级别的日志
myLogger.warning("this is a warning msg"); //WARNING 级别的日志
myLogger.severe("this is a severe msg"); //SEVERE 级别的日志
}
}
以上代码将 myLogger 对象的日志级别设为 WARNING, 因此,只有 warning() 方法和 server()方法生成的日志会被输出。DOS控制台输出:
警告:this is a warning msg
严重:this is a severe msg
//如果要把日志输出到文件中,可以采用以下方式:
import java.util.logging.*;
import java.io.IOException;
class Logger Tester2{
public static void main(String[] args) throws IOException {
Logger myLogger = Logger.getLogger("mylogger"); //声明一个日志记录器对象
FileHandler fileHandler = new FileHandler("C:\\test.log");
fileHandler.setLevel(Level.INFO); //设定向文件中写日志的级别
myLogger.addHandler(fileHandler); //把 FileHandler 与 Logger 对象关联
myLogger.info("this is a normal msg"); //INFO 级别的日志
myLogger.warning("this is a warning msg"); //WARNING 级别的日志
myLogger.severe("this is a severe msg"); //SEVERE 级别的日志
}
}
运行以上程序,将在 C:\test.log 文件中记录以下 XML 格式的日志:
<?xml version="l.0" encoding="GBK" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
...
<message>this is a normal msg</message>
</record>
<record>
...
<message>this is a warning msg</message>
</record>
<record>
...
<message>this is a severe msg</message>
</record>
</1og>
默认情况下, FileHandler
类与 XMLFormatter
类关联,即向文件中输出的日志是基于 XML 格式的。也可以自定义一个继承 Fomatter
类的子类,然后覆盖它的 format()
方法,在该方法中指定客制化的日志输出格式。
import java.util.logging.*;
import java.io.IOException;
public class LoggerTester3 {
static class MyFormatter extends Formatter { //自定义的日志输出格式类
public String format(LogRecord record) { //覆盖 format()方法
return "<" + record.getLevel() + ">:" + record.getMessage()+ "\n";
}
}
public static void main(String[] args) throws IOException {
Logger myLogger = Logger.getLogger("mylogger");
FileHandler fileHandler = new FileHandler("C: \\test log");
fileHandler.setFormatter(new MyFormatter()); //设置自定义的日志输出格式
myLogger.addHancller(fileHancller);
myLogger.info("this is a normal msg");
myLogger.warning("this is a waring msg");
}
}
运行以上程序,将在 C:\test.log 文件中输出以下格式的日志:
<INFO>:this is a normal msg
<WARNING>:this is a waring msg
9.7 使用断言
从 JDKl.4 开始,引入了断言机制,异常处理的一种高级形式。使用断言的语法有两种形式为:
assert 条件表达式
assert 条件表达式 : 包含错误信息的表达式
以上 assert
为 Java 关键字。条件表达式的值是一个布尔值。当条件表达式的值为 false 时,就会抛出 一个 Assertion Error
,这是一个错误,而不是一个异常。在第二种形式中,第二个表达式会被转换成作为错误消息的字符串。
public class AssertTester {
public int divide(int a, int b) {
assert b != 0 : "CANNOT divide zero!!!"; //使用断言
return a / b;
}
public static void main(String[] args) {
AssertTester t = new AssertTester();
System.out.println(t.divide(1, 0));
}
}
当程序运行时,断言在默认情况下是关闭的,这意味着程序中的断言语句不会被执行。在 Java 命令中启用断言需要使用 -ca
参数,禁用断言使用 -da
参数。例如,用命令 java -ea AssertTester
来执行 AssertTester
类,将会输出以下错误信息:
Exception in thread "main" java.lang.AssertionError: CANNOT divide zero!!!
at AssertTester.divide(AssertTester.java:4)
at AssertTester.main(AssertTester.java:10)