一.使用throws抛出异常
如果在当前方法不知道该如何处理该异常时,则可以使用throws对异常进行抛出给调用者处理或者交给JVM;JVM对异常的处理方式是:打印异常的跟踪栈信息并终止程序运行。
throws在使用时应处于方法签名之后使用,可以抛出多种异常并用英文字符逗号’,’隔开。
e.g.1
public void throwsTest() throws ExceptionClass1, ExceptionClass2 {...}
如果抛出给调用者的异常是Checked(检查)异常,这种异常是我们需要处理以来提高程序健壮性的,一般抛出则要调用者做相应处理,要么调用者对该异常进行try…catch处理,要么再次throws交给上一层。这其中需要注意一点:子类方法声明抛出的异常类型应是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不允许比父类方法声明抛出的异常多。
e.g.2
public class ThrowsTest2{
//因为test();会抛出IOException,main方法是调用者
//则需要使用try...catch进行捕获或者抛给JVM
//抛出时要遵循父类声明抛出“大于”子类声明抛出的原则
public static void main(String[] args) throws Exception {
test();
}
public static void test() throws IOException() {
//因为FileInputStream构造器会抛出IOException
//所以需要使用try...catch块进行处理或使用throws抛给调用者
FileInputStream fis = new FileInputStream("a.txt");
}
}
二.使用throw抛出异常
如果需要程序在程序中自行抛出异常,应该使用throw语句抛出,抛出的不是一个类而是一个对象且只能抛出一个对象。它可以单独使用,也可以结合catch块捕获使用。如果抛出的异常对象是Checked异常,则处于try块里被catch捕获或者放在一个带throws的方法里;如果抛出的是RuntimeException异常,则既可以显示使用try…catch捕获也可以不理会它(交由JVM处理)。
e.g.3
public class ThrowTest {
public static void main(String[] args) {
try{
throwChecked(3);
}catch(Exception e) {
System.out.println(e.getMessage());
}
throwRuntime(-3);
}
//该方法内抛出一个Exception异常对象,必须捕获或抛给调用者
public static void throwChecked(int a) throws Exception {
if(a < 0) {
throw new Exception("a的值应大于0,不符合要求")
}
}
//该方法内抛出一个RuntimeException对象,可以不理会直接交给JVM处理
public static void throwRuntime(int a) {
if(a < 0) {
throw new RuntimeException("a的值应大于0,不符合要求")
}
}
}
Java7增强的throw语句:
在Java7之前,父类和子类在声明抛出异常时应符合父类包含的异常“大于等于”子类包含的异常的规则;从Java7开始,Java编译器会检查throw语句抛出的异常的实际类型,如下代码中编译器知道throw e只能抛出FileNotFoundException,所以在方法签名上可以直接写该异常:
e.g.4
public class ThrowTest2 {
public static void main(String[] args) throws FileNotFoundException {
try {
new FileOutputStream("a.txt");
}catch(Exception e) {
e.printStackTrace();
throw e;
}
}
}
三.自定义异常类
在抛出异常时,异常类名往往包含有用的信息,所以在选择抛出异常时需要选择适合的类,从而可以明确的描述该异常情况。这时候就需要我们自己定义异常,自定义异常一定是Throwable的子类,若是检查异常就要继承自Exception,若是运行时异常就要继承自RuntimeException。
e.g.5
public class AuctionException extends Exception {
//无参构造
public AuctionException() {}
//含参构造
//通过调用父类的构造器将字符串msg传给异常对象的massage属性,
//massage属性就是对异常的描述
public AuctionException(String msg) {
super(msg);
}
}
关于自定义异常深入学习可以参考以下链接:
http://www.cnblogs.com/AlanLee/p/6104492.html
四.catch和throw同时使用
在实际应用中,在异常出现的当前方法中,程序只能对异常做部分处理,还有些处理需要在该方法的调用者中才能够完成,使用需要再次抛出异常。这时就需要将catch和throw结合使用。
e.g.6
public class AuctionTest {
private double initPrice = 30.0;
public void bid(String bidPrice) throws AuctionException {
double d = 0.0;
try{
d = Double.parseDouble(bidPrice);
}catch(Exception e) {
e.printStackTrace();
throw new AuctionException("竞拍价必须为数值,不能包含其它数值");
}
if(initPrice > d) {
throw new AuctionException("竞拍价应比起拍价高");
}
initPrice = d;
}
public static void main(String[] args) {
AuctionTest at = new AuctionTest();
try{
at.bid("..");
}catch(AuctionException ae) {
System.out.println(ae.getMessage());
}
}
}
以上代码的运行结果为:
竞拍价必须为数值,不能包含其它数值
java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
at sun.misc.FloatingDecimal.parseDouble(Unknown Source)
at java.lang.Double.parseDouble(Unknown Source)
at ExceptionTest.AuctionTest.bid(AuctionTest.java:15)
at ExceptionTest.AuctionTest.main(AuctionTest.java:28)
五.异常链
将捕获一个异常然后接着抛出另一个异常,并将原始异常信息保存下来是一种典型的链式处理,也被称作异常链。
六.Java的异常跟踪栈
如e.g.6的运行结果
除去第一行打印的自定义的提示信息之外的第一行是异常的类型和详细消息,之后的提示信息中含有确切代码行数的信息记录了所有的异常发生点,各行显示被调用方法中执行的停止位置。跟踪栈总是最内部的被调用方法逐渐上传直到外部业务操作的起点,通常就是main方法或者Thread的run方法。
七.异常处理规则
不要过度使用异常:对于完全已知的错误应编写处理这种错误代码从而提高代码的健壮性,只有外部的、不能确定的和不可预知的运行时错误使用异常,并且异常机制的效率低于正常的流程控制。
不要使用过于庞大的try块:过于庞大的try块业务也相对更复杂,会导致try块中异常的可能性大大增加,在分析发生异常的原因时难度增加。
避免使用Catch All语句:Catch All是catch(Throwable t),也会在发生异常是分析原因的复杂度增加。
不要忽略已捕获到的异常:对于捕获到的异常应该对其进行处理从而提高代码健壮性,而不是什么都不做或者只是打印跟踪栈信息。