java的异常被分类为两大类,Checked异常和Runtime异常(运行时异常)。所有的RuntimeException类及其子类的实例被称为Runtime异常;不是RuntimeException类及其子类的异常实例则被称为Checked异常。

对于Checked异常的处理方式有如下两种。

1:当前方法明确知道如何处理该异常,程序应该使用try...catch块来捕获该异常,然后在对应的catch块中修复该异常。

2:当前方法不知道如何处理这种异常,应该在定义该方法时声明抛出此异常。

 

Runtime异常则更加灵活,Runtime异常无须显式声明抛出,如果程序需要捕获Runtime异常,也可以使用try...catch块来实现。

 

使用throws声明抛出异常

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

throws声明抛出只能在方法签名中使用,throws可以声明抛出多个异常类,多个异常类之间以逗号隔开。一旦使用throws语句声明抛出该异常,程序就无须使用try...catch块来捕获该异常了。

 

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

 1 public class ThrowsTest 
 2 {
 3 
 4     public static void main(String[] args) throws Exception 
 5     {    
 6         //因为test();方法声明抛出了IOException异常
 7         //所以调用该方法的代码要么处于try...catch块中,要么处于另一个带throws声明抛出的方法中。
 8         test();
 9 
10     }
11 
12     private static void test() throws IOException 
13     {    
14         //因为FileInputStream的构造器声明抛出IOException 异常
15         //所以调用IOException 的代码要么处于try...catch中,要么处于另一个带throws声明抛出的方法中。
16         FileInputStream fis = new FileInputStream("a.txt");
17         
18     }
19 
20 }

 

 使用throws声明抛出异常时有一个限制,就是方法重写时“两小”中的一条规则:子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不允许比父类方法声明抛出的异常多。

由此可见,使用Checked异常至少存在两大不便。

1:对于程序中的Checked异常,java要求必须显式捕获并处理该异常,或者显式声明抛出该异常。这样就增加了编程复杂度。

2:如果在方法中显式声明抛出Checked异常,将会导致方法签名与异常耦合,如果该方法是重写父类的方法,则该方法抛出的异常还会受到被重写方法所抛出异常的限制。

在大部分时候推荐使用Runtime异常,而不使用Checked异常。尤其当程序需要自行抛出异常时,使用Runtime异常更加简洁。当使用Runtime异常时,程序无须在方法中声明抛出Checked异常,一旦发生了自定义错误,程序只管抛出Runtime异常。

 

 

使用throw抛出异常

很多时候,系统是否要抛出异常,可能需要根据应用的业务需求来决定,如果程序中的数据,执行与既定的业务需求不符,这就是一种异常。由于与业务需求不符而产生的异常,必须由程序员来决定抛出,系统无法抛出这种异常。

如果需要在程序中自行抛出异常,则应该使用throw语句,throw语句可以单独使用,throw语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实例。

如果throw语句抛出的异常是Checked异常,则该throw语句要么处于try块里,显式捕获该异常,要么放在一个带throws声明抛出的方法中,即把该异常交给该方法的调用者处理;如果throw语句抛出的异常是Runtime异常,则该语句无须放在try块里,也无须放在带throw声明抛出的方法中;程序既可以显式使用try...catch来捕获并处理该异常,也可以完全不理会该异常,把该异常交给该方法调用者处理。例如下面这个程序。

public class ThrowsTest 
{
    public static void main(String[] args)
    {
        try
        {
            //调用声明抛出Checked异常的方法,要么显式捕获该异常,要么在main方法中再次声明抛出
            throwChecked(-3);
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
            //调用声明抛出Runtime异常的方法既可以显式捕获该异常,也可以不理会该异常
            throwRuntime(3);

    }

    private static void throwChecked(int a) throws Exception
    {
        if(a > 0)
        {
            //自行抛出Exception异常
            //该代码必须处于try块里,或处于带throws声明的方法中
            throw new Exception("a的值大于0,不符合要求");
        }
            
    }
    
    private static void throwRuntime(int a) 
    {
        if(a > 0)
        {
            //自行抛出RunTimeException异常,既可以显式捕获该异常
            //也可以完全不理会该异常,把该异常交给该方法调用者处理
            throw new RuntimeException("a的值大于0,不符合要求");
        }
        
    }
    
}

 

 

 

自定义异常类

在通常情况下,程序很少会自行抛出系统异常,因为异常的类名通常也包含了该异常的有用信息。所以在选择抛出异常时,应该选择合适的异常类,从而可以明确的描述该异常情况,在这种情况下,应用程序常常需要抛出自定义异常。

自定义异常都应该继承Exception基类,如果希望自定义Runtime异常,则应该继承RuntimeException基类。定义异常类时通常需要提供两个构造器:一个是无参数的构造器;另一个是带一个字符串参数的构造器,这个字符串将作为该异常对象的描述信息(也就是异常对象的getMessage()方法的返回值)

下面程序创建了一个自定义异常类

public class AuctionException extends Exception
{
    //无参构造器
    public AuctionException(){}
    
    //带一个字符串参数的构造器
    public AuctionException(String msg)
    {
        super(msg);
    }
}

 

posted on 2016-02-24 05:31  所谓荣耀  阅读(908)  评论(0编辑  收藏  举报