Java的异常机制以及自定义异常的讲解和使用

什么是异常?

请看以下代码。

public static void main(String[] args) {
        int a=5;
        int b=0;
        int c=a/b;
        System.out.println(c);
    }

在这个程序中,我们可以看到,c=0,因此c=a/b是不合法的,此时执行程序会出现ArithmeticException异常(也就是数学运算异常)。
通过这个例子,我们可以看出,异常其实就是不合法的程序运行而导致的,比如在IO流章节,我们在对文件内容进行输出的时候,如果路径不对,则会出现异常(这个异常就是文件找不到而导致的异常)。

异常的分类

异常可以分为三类,分别是,Error,Exception和RuntimeException。

Error:是指程序在运行的时候出现了十分严重,不可恢复的错误,程序只能终止运行(编译器不会报错,运行的时候才会报错)。

RutimeException:是运行时异常,编译器不会检查是否对运行时异常处理了没有,所以编译可以通过,但是在运行程序的时候会出现异常情况,例如:空指针异常(对象(或者说引用)为空是调用其方法会出现)。

Exception:是编译时异常,这种异常编译器会直接报错,必须要处理之后才可编译,例如:文件内容输出的时候要处理路径找不到的异常(IO流章节会进一步学习)。

异常的处理方式

异常的处理方式一共有两种:上抛异常捕捉异常

一、上抛异常
关键字:throws
用法: thorws 异常类(在方法声明的时候上抛)
在遇到异常或者可能有异常的情况,我们可以选择不在此处处理此异常,而是将异常抛给上一级,比如一个方法出现了异常,可以将其抛给调用其方法的部分,让其进行解决,当然,这个时候如果不想处理还可以继续上抛,一直到抛到JVM虚拟机为止(也就是以下代码中在main方法的后面继续上抛)。请看以下代码。

public class ExceptionTest01 {
    public static void main(String[] args) throws Exception{
        
        //调用除法方法,可继续上抛异常(在main方法上抛)。
        int c=ExceptionTest01.division(5,0);
        System.out.println(c);
    }
    //上抛数学运算异常
    public static int division(int a,int b)throws ArithmeticException{
        return a/b;
    }
}

这里说一下异常的继承结构:
异常的超级父类是Throwable类,Error异常类和Exception类继承了Throwable类,RuntimeException类和IOException类继承了Exception类,IOException和RuntimeException还有很多的子类,IOException是编译时异常(就是编译时需要处理的异常),RuntimeException异常是运行时的异常,编译时可以不处理,RuntimeException异常下有很多的运行时异常类,比如:NullPointerException、ArithmeticException等多种编译时异常。
在异常上抛的时候只能抛给父类或者父类下与此异常同等级的子类,比如:NullPointerException、ArithmeticException就是同一父类下同等级的子类,也就是说可以将以上代码中throws ArithmeticException换为throws NullPointerException。

二、捕捉异常
关键字:try…catch…
用法: 在异常可能发生的地方对异常进行捕捉。
在我们遇到异常的时候如果选择捕捉此异常,那么需要在异常发生的地方利用try…catch语句对其进行捕捉。请看以下代码。

public class ExceptionTest01 {
    public static void main(String[] args) {
        int c=ExceptionTest01.division(5,0);
        System.out.println(c);
    }
    //捕捉异常
    public static int division(int a,int b){
        /**
         *try表示尝试去执行try语句块中的语句,如果没发生异常就不会执行catch的语句
         * 如果发生异常则会执行catch中的语句块,ArithmeticException e表示的是
         * 异常的类型,这个类型可以是同等级的子类异常也可以是其父类的异常,e表示的是
         * 异常对象,在出现异常的时候底层会创建一个异常对象。
         * e.printStackTrace();表示这个异常对象调用了异常类中的方法,打印了异常的
         * 堆栈信息,例如:Exception in thread "main" java.lang.ArithmeticException: / by zero
         * 就是异常的堆栈信息,作用是让我们知道发生了什么异常,如何修改等.
         * 更多的详情请看异常类的源码
         */
        try{
            return a/b;
        }catch(ArithmeticException e){
            //此处的异常信息也可以不打印,除此之外,我们还可以在里面添加其它语句
            //但是建议打印堆栈信息,这样方便程序的维护
            e.printStackTrace();
            //这个输出语句是我们添加的语句
            System.out.println("异常已捕捉!");
        }
        return 0;  //返回了0则说明了程序出现了异常
    }
}
/*
//以下是程序执行的结果:
java.lang.ArithmeticException: / by zero
	at JavaTestClass.ExceptionTest01.division(ExceptionTest01.java:21)
	at JavaTestClass.ExceptionTest01.main(ExceptionTest01.java:5)
异常已捕捉!
c=0
 */

关于try…catch语句的使用,以上代码已经讲解清楚了,请仔细看代码。
再补充一句:try…catch语句可以多次进行catch捕捉异常,加到上一个catch语句的后面即可(适用于出现了两个及以上的异常需要捕捉的时候)
finally语句的使用
finally语句是一个常于try…catch连用的一个语句,通常都是加在try…catch语句后面,表示此语句块一定会执行,就算try中的语句块出现了return语句也会执行(System.exit(0);除外,这个语句会直接关闭程序,除此之外,无论发生什么情况,finally中的语句一定会执行,切记。)请看代码。

try{
            return a/b;
        }catch(ArithmeticException e){
            e.printStackTrace();
        }finally{
            System.out.println("这是一定执行的语句!");
        }

我们来看下运行结果
在这里插入图片描述
我们可以看到程序出现了异常,本应该return结束,但是finally中的语句仍然执行了,这就表明了,finally中的语句一定会执行。
三、自定义异常类
我们平常接触到的都是SUN公司已经写好了的异常类,我们也可以自己定义异常类,方法如下,请看代码。

//这是定义了一个运行时异常
//如果要定义编译时异常的话,继承Exception类即可
public class MyException3 extends RuntimeException{
    public MyException3(){
    }
    public MyException3(String c){
        //调用父类的方法,在new对象的时候传入字符串
        //此字符串就是发生异常时显示在控制台上的字符串
        //字符串是由自己定义的
        super(c);
    }
}
	自定义异常类的时候只需要提供一个无参构造器和一个有String参数的构造器即可,在有参构造器中调用父类的方法。

我们来个测试类,测试一下自定义的异常类。

public class TestMyException3 {
    public static void main(String[] args) {
        try{
            //调用test()方法传入0测试
            new TestMyException3().test(0);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
			System.out.println("最后执行的语句!");
		}
    }
    public int test(int a){
        //我们假定若传来的参数是a的话让它发生异常
        if(a==0){
            //抛出异常对象
            throw new MyException3("这是测试自定义的异常!");
        }
        return 0;
    }
}

程序运行结果如下
在这里插入图片描述
我们可以看到运行结果打印出了我们所传入的字符串,说明我们自定义的异常类成功了。

总结: 1.异常类的处理要么是上抛异常(throws 异常类 在方法声明部分),要么就是捕捉异常(try…catch语句配合使用)。
2.异常类的自定义就是定义一个类继承Exception(编译时异常,在编译时必须处理,不处理的话编译器报错)或者RuntimeException(编译时不会报错,但运行时会报错),然后一个无参构造器和有参构造器,有参构造器传入字符串并用super()方法将字符串传入,在需要抛出异常的地方throw new 自定义异常对象名();可以传参也可以不传参,传参的话运行输出的结果就是传入的字符串。


码字不易,不要白嫖,觉得有用的,可以给我点个赞。感谢!
因技术能力有限,如文中有不合理的地方,希望各位大佬指出,在下方评论区留言,谢谢,希望大家一起进步,一起成长。
如需转载请注明来源,谢谢!

发布了5 篇原创文章 · 获赞 13 · 访问量 261

猜你喜欢

转载自blog.csdn.net/weixin_46521681/article/details/105430280