java异常机制

目录

1、自定义异常类

2、try/catch 捕获异常

2.1 一个异常

2.2 多个异常

2.3 catch抛出异常

3. finally

4. 带资源的try语句

5. 断言


java的异常对象都是Throwable类派生出的,又分为两类:Error和Exception,Error是java运行时系统内部错误或者资源耗尽错误,Exception则是java程序本身的错误。各种异常子类多如牛毛,没有必要刻意去记,知道异常机制原理即可。

对于任何一个抛出异常的方法,如果没有处理器捕获这个异常,那么当前线程就会结束。java发展至今,已经总结出了很多已知的异常,并给出了相应的处理器,所以,我们应该去尽量避免这些已查异常的出现,对于未知的异常,应该在代码中做好异常处理,避免当前线程终止。

1、自定义异常类

异常类的目的就是用来描述问题的,当遇到任何标准异常类都不适合的问题时,就可以自己创建异常类,自定义的异常类派生于Exception类或者Exception类的子类,一般有两个构造方法:默认构造方法和带有一个字符串参数的构造方法, 字符串用于描述异常。

举个例子:定义异常类MyException 和 MyException2

public class MyException extends IOException {
    public MyException(){}
    public MyException(String description){
        super(description);
    }
}
public class MyException2 extends IOException {
    public MyException2(){}
    public MyException2(String description){
        super(description);
    }
}
public class Test {
    //测试自定义异常类
    public void customizedException(int a) throws MyException{
        if(a < 10){
            throw new MyException("我是自定义异常类");
        }else {
            throw new MyException();
        }
    }

    public static void main(String[] args) throws MyException {
        //测试自定义异常类
        Test test = new Test();
        test.customizedException(11);
    }
}

运行结果:

 这是抛出的默认构造方法创建的异常类对象。

 这是抛出的带参构造方法创建的异常类对象。

由此可见,两个构造方法不同所带来的区别,且程序是以状态码1结束的,说明是非正常结束,那是因为程序中只是抛出了异常,并没有异常处理器捕获,因此导致程序直接终止。

2、try/catch 捕获异常

2.1 一个异常

try{
    test.customizedException(9);
}catch (Exception e){
    System.out.println(e.getMessage());
}

try{ }里面的代码是执行代码,如果执行过程中,抛出了异常,则看catch( )是否能够捕捉该异常(即抛出的异常类对象能否赋值给catch的参数,如果能,则捕捉到,否则,捕捉不到),如果能捕捉到该异常,则执行catch( ){ }部分的代码。

抛出的异常类对象能否赋值给catch的参数:意思是catch的参数类型要么就是异常类,要么就是异常类的父类或祖类。

还有一点:catch( )的参数变量隐含是final类型的,即不可更改。

2.2 多个异常

        try{
            test.customizedException(9);
        }catch (MyException e){
            System.out.println(e.getMessage());
        }catch (MyException2 e){
            System.out.println(e.getMessage());
        }

如果try{ }里的代码可能会抛出多种异常,那么就用多个catch分别捕获即可,catch捕获了几个异常,就执行几个catch的代码,catch( )的参数的有效域仅限于当前catch。

如果try会抛出多种异常,但是有多种异常的意思是一样的(即多种异常都代表同一个东西出错了),假设(仅仅是假设)RuntimeException 和 IOException 都代表了同一种异常,那么可以写法如下,这样的写法需要注意的是,异常类之间没有父子关系或者继承关系。

        try{
            test.customizedException(9);
        }catch (RuntimeException  | IOException e){
            System.out.println(e.getMessage());
        }

2.3 catch抛出异常

1、在catch( ){ }里面再抛出异常,可以达到异常类型的转变,对原异常的描述信息进行覆盖或者追加。

        Test test = new Test();
        try{
            test.customizedException(9);
        }catch (MyException e){
            throw new MyException2(e.getMessage()+"我是异常类2222");
        }

运行结果:

分析:可以看出,上面的这种异常链是用新的异常直接覆盖掉旧的异常,所以运行结果中,只抛出了一个异常MyException2,但是MyException异常却没有抛出,只不过我们把两个异常的描述信息拼接起来了,如果代码中我们把throw new MyException2(e.getMessage() + "我是异常类2222")中的e.getMessage()去掉的话,那么MyException的异常描述信息是不会打印出来的。

2、上面的异常链会导致覆盖问题,就是我们只能看到打印出来的是被抛出的最新异常,而不能追根溯源,为此,有另外一种方法,异常包装技术,强烈推荐这个,如下:

Test test = new Test();
try{
    test.customizedException(9);
}catch (MyException e){
    Throwable se = new MyException2("哈哈哈");
    se.initCause(e);
    throw se;
}

运行结果:

分析:我们不仅把最新的MyException2抛出了,而且还把MyException抛出了,这就是追根溯源,将整个异常链从新到旧抛出异常。对于这句代码,Throwable se = new MyException2("哈哈哈"),不是定死的,就是简单的子类对象赋值给父类或者祖类引用,不一定非要是Throwable类,用Exception类也行,反正都是MyException2类的祖类,但是没有必要纠结于此,用Throwable类挺好的,因为Throwable类是异常机制中最高的类,可以将任何异常对象赋值给Throwable引用,而不担心代码出问题。

3. finally

1、当抛出异常的时候,如果没有捕捉并处理好异常,那么程序会退出执行,但是我们有时候需要在退出执行之前做一些操作,finally为我们提供了方法。注意:如果try里面没有抛出异常,finally模块也会执行。其实准确地说,finally模块应该是try和catch退出当前方法之前执行的,所以try和catch里面有异常抛出(没有捕捉器),有return语句,等等,这些都会导致退出当前方法之前执行finally。

Test test = new Test();
        try{
            test.customizedException(9);
        }catch (MyException e){
            throw new MyException2(e.getMessage()+"我是异常类2222");
        }finally {
            System.out.print("finally模块的语句被执行了");
        }

运行结果:

分析:try里面抛出了异常,被catch捕捉到,然后catch又抛出个异常,这个异常没有相应的catch捕捉,本来程序就应该结束了,但是由于finally模块的存在,所以在结束之前,执行了finally模块的内容,然后才打印出的异常信息(个人认为,是先执行抛出异常,然后查询该异常有没有对应的捕捉器,结果没有,自然就要退出执行,那么就要打印出异常信息——打印异常信息是日志机制的事情,不是try的工作,然后退出执行,但是因为finally存在,所以先执行完finally的内容,在打印出异常信息,最后退出)。

2、强烈建议独立使用try/catch和try/finally,即三个模块不在同一级使用,因为如下的代码风格看起来思路会清晰一些,finally到底是针对哪个try,catch到底是捕捉的哪块内容,一目了然:

try{
    try{
        //try代码
    }finally{
        //finally代码
    }
}catch{
    //catch代码
}

3、还有一个有趣的知识点,当try和finally中都有return语句,比如try要return 7,finally要return 9,运行结果如何呢,结果是try真正返回7之前,要执行finally里的return语句,返回值9覆盖掉了7,最后try返回的是9。

4. 带资源的try语句

有些时候,我们需要对资源进行操作,在操作资源的过程中可能发生异常,假设为异常1,导致退出当前方法,在退出之前,我们往往希望能够关闭资源。可以使用try 和 finally来实现,在退出之前关闭资源,但是关闭资源这个操作也可能抛出异常,假设为异常2,那么此时异常2就会覆盖掉异常1,导致打印出来的异常信息只有异常2。为此,带资源的try语句可以避免这样的问题。

try(资源1,资源2,...){
    //操作资源的语句,不用写关闭资源的语句
}

try里面只需要写操作资源的语句即可,不需要特意去关闭资源,因为try模块执行结束之后(无论是正常运行结束,还是抛出异常),自然后将模块包含的资源全部关闭。

5. 断言

断言是为了方便测试的,在代码中使用断言,在测试阶段,这些断言代码是保留在程序中的,但是代码发布时,这些断言代码会自动被清除。断言适用于程序员在开发和调试阶段使用。

两种形式:asset 条件;     assert 条件:表达式;

第一种形式,如果条件为false,就抛出AssertErro异常,第二种形式,如果条件为false,抛出AssertErro异常(带有异常信息字符串的,字符串的内容就是表达式)。如果条件为true,那么什么事都没有,正常继续执行。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值