0%

10.3.1 使用throws声明抛出异常

10.3.1 使用throws声明抛出异常

使用throws声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由上级调用者处理;
如果main方法也不知道如何处理这种类型的异常,也可以使用throws声明抛出异常,该异常将交给JVM处理。
JVM对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到异常后自动结束的原因。

throws声明抛出异常的语法格式

throws声明抛出只能在方法签名中使用, throws可以声明抛出多个异常类,多个异常类之间以逗号隔开。

throws声明抛出的语法格式仅跟在方法签名之后,如下例子程序使用了throws来声明抛出IOException异常,一旦使用throws语句声明抛出该异常,程序就无须使用try-catch块来捕获该异常了

1
2
3
4
5
6
7
8
9
10
import java.io.*;

public class ThrowsTest
{
public static void main(String[] args)
throws IOException
{
FileInputStream fis = new FileInputStream("a.txt");
}
}

上面程序声明不处理IOException异常,将该异常交给JVM处理,所以程序一旦遇到该异常,JVM就会打印该异常的跟踪栈信息,并结束程序。运行上面程序,效果如下所示:

1
2
3
4
5
6
Exception in thread "main" java.io.FileNotFoundException: a.txt (系统找不到指定的文件。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at ThrowsTest.main(ThrowsTest.java:8)

如果某段代码中调用了一个带throws声明的方法,该方法声明抛出了Checked异常,则表明该方法希望它的调用者来处理该异常。也就是说,调用该方法时要么放在try块中显式捕获该异常,要么放在另一个带throws声明抛出的方法中。如下例子程序示范了这种用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.io.*;

public class ThrowsTest2
{
public static void main(String[] args)
throws Exception
{
// 因为test()方法声明抛出IOException异常,
// 所以调用该方法的代码要么处于try...catch块中,
// 要么处于另一个带throws声明抛出的方法中。
test();
}
public static void test()throws IOException
{
// 因为FileInputStream的构造器声明抛出IOException异常,
// 所以调用FileInputStream的代码要么处于try...catch块中,
// 要么处于另一个带throws声明抛出的方法中。
FileInputStream fis = new FileInputStream("a.txt");
}
}

使用throws声明抛出异常时有一个限制,就是方法重写时子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不允许比父类方法声明抛出的异常多。看如下程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.*;

public class OverrideThrows
{
public void test()throws IOException
{
FileInputStream fis = new FileInputStream("a.txt");
}
}
class Sub extends OverrideThrows
{
// 子类方法声明抛出了比父类方法更大的异常
// 所以下面方法出错
public void test()throws Exception
{
}
}

上面程序中Sub子类中的test()方法声明抛出Exception,该Exception是其父类声明抛出异常EXception类的父类,这将导致程序无法通过编译。

Checked异常的缺点

使用Checked异常至少存在如下两大不便之处。

  • 对于程序中的Checked异常,Java要求必须显式捕获并处理该异常,或者显式声明抛出该异常这样就增加了编程复杂度。
  • 如果在方法中显式声明抛出Checked异常,将会导致方法签名与异常耦合,如果该方法是重写父类的方法,则该方法抛出的异常还会受到被重写方法所抛出异常的限制。

推荐使用Runtime异常

在大部分时候推荐使用Runtime异常,而不使用Checked异常。尤其当程序需要自行抛出异常时,使用Runtime异常将更加简洁。

不需要在方法声明中抛出Runtime异常

当使用Runtime异常时,程序无须在方法中声明抛出Runtime异常,一旦发生了自定义错误,程序只管抛出Runtime异常即可。

可以使用try-catch捕获Runtime异常

如果程序需要在合适的地方捕获异常并对异常进行处理,则一样可以使用try-catch块来捕获Runtime异常。

使用Runtime异常是比较省事的方式,使用这种方式既可以享受”正常代码和错误处理代码分离”,”保证程序具有较好的健壮性”的优势,又可以避免因为使用Checked异常带来的编程烦琐性。因此,C#RubyPython等语言没有所谓的Checked异常,所有的异常都是Runtime异常。

Checked异常也有其优势Checked异常能在编译时提醒程序员代码可能存在的问题,提醒程序员必须注意处理该异常,或者声明该异常由该方法调用者来处理,从而可以避免程序员因为粗心而忘记处理该异常的错误。

原文链接: 10.3.1 使用throws声明抛出异常