• Java——异常处理


    1、java提供的异常不可能预见所有的问题,所以需要自己定义异常类,必须从已有的异常类继承,最好选择意思相近的异常类继承。

    class MyException extends Exception{}
    
    public class Tree1 {
        
        public static void f() throws MyException{
            System.out.println("throws MyException from f()");
            throw new MyException();
        }
            
        public static void main (String[] args) {
        
            try {
                f();
            }catch(MyException e){
                System.out.println("caught it");
            }
        }
    }

    try块中的代码将被监控,catch将会接受来自try块的异常。这里的f()方法将会抛出一个 MyException类的异常,然后catch将会接收到这个异常,并输出caught it4

    所以输出结果为:

    throws MyException from f()
    caught it

    可以为异常类定义一个接受字符串参数的构造器:

    class MyException extends Exception{
        public MyException() {}
        public MyException(String msg) {
            super(msg);
        }
    }
    public class Tree1 {
        public static void f() throws MyException{
            System.out.println("throws MyException from f()");
            throw new MyException();
        }
            
        public static void main (String[] args) {
            try {
                f();
            }catch(MyException e){
                e.printStackTrace(System.out);
            }
        }
    }

    这样的输出是:

    throws MyException from f()
    MyException
        at Tree1.f(Tree1.java:11)
        at Tree1.main(Tree1.java:16)

    在异常类的定义中的第二个构造器中使用了super关键字明确调用了其基类构造器,它接受一个字符串作为参数。

    在异常处理程序中,调用了Throwable类声明的printStackTrace()方法,就像输出中看到的这样,它将会打印“从方法调用处直到异常抛出处” 的方法调用序列,这里信息发送到了System.out,如果使用默认的e.printStackTrace();则信息将被输出到标准错误流。

    2、Exception类型的方法

    class MyException extends Exception{
        public MyException() {}
        public MyException(String msg) {
            super(msg);
        }
    }
    class MySecondException extends Exception{
        public MySecondException() {}
        public MySecondException(String msg) {
            super(msg);
        }
    }
    public class Tree1 {
        public static void f() throws MyException {
            System.out.println("throws MyException from f()");
            throw new MyException("name");
        }
        
        public static void g() throws MySecondException {
            System.out.println("throws MySecondException from g()");
            throw new MySecondException("name2");
        }
            
        public static void main (String[] args){
            try {
                f();
    //            g();
            }catch(Exception e){
                System.out.println(e.getMessage());
                System.out.println(e);
                System.out.println(e.getLocalizedMessage());
            }
        }
    }

    这里定义了两个继承自Exception的异常类,分别是MyException、MySecondException,由两个方法f()、g()抛出,如果在try块中只有f();,输出结果为:

    throws MyException from f()
    name
    MyException: name
    name

    Throwable类的getMessage()方法返回的是Exception的详细消息字符串,就是抛出这个异常时候传入的字符串,

    而getLocalizedMessage()方法返回的是 Exception的本地化描述。

    toString()方法返回 此对象的类的 name ": "(冒号和一个空格)

    然后如果把上面代码g();的前面的//删掉,就是try块中将运行f()和g()两个方法,输出结果为:

    throws MyException from f()
    name
    MyException: name
    name

    和刚才一样这是因为catch接收到异常后就会结束try块中的程序运行,所以g()方法并没有被运行,所以如果先运行g()的结果就是:

    throws MySecondException from g()
    name2
    MySecondException: name2
    name2

    3、栈轨迹

    public class Tree1 {
        public static void f(){
            try {
                throw new Exception();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        
        public static void g() {f();}
        public static void h() {g();}
            
        public static void main (String[] args){
            f();        
            g();
            h();
        }
    }

    输出结果为:

    java.lang.Exception
        at Tree1.f(Tree1.java:17)
        at Tree1.main(Tree1.java:27)
    java.lang.Exception
        at Tree1.f(Tree1.java:17)
        at Tree1.g(Tree1.java:23)
        at Tree1.main(Tree1.java:28)
    java.lang.Exception
        at Tree1.f(Tree1.java:17)
        at Tree1.g(Tree1.java:23)
        at Tree1.h(Tree1.java:24)
        at Tree1.main(Tree1.java:29)

    这里的调用的printStackTrace(),是Throwable类的方法,这个方法会将Throwable对象的栈轨迹信息打印到标准错误输出流上,第一行是异常类的tostring()方法输出的内容,后面几行的内容都是之前通过fillInStackTrace()方法保存的内容。

    java.lang.Exception
        at Tree1.f(Tree1.java:17)
        at Tree1.main(Tree1.java:27)

    在这个例子中,在方法f()中抛出异常,在main方法中捕获异常,并且打印栈轨迹信息。因此,输出依次展示了f—>main的过程。还打印了抛出错误,捕获错误的行数、类的名字。

    java.lang.Exception
        at Tree1.f(Tree1.java:17)
        at Tree1.g(Tree1.java:23)
        at Tree1.main(Tree1.java:28)

    这里就是在方法f()中抛出异常,方法g()中调用了f(),然后和上一个一样了,第三个也同样。

    public class Tree1 {
        public static void f(){
            try {
                throw new Exception();
            }catch(Exception e){
                for(StackTraceElement ste: e.getStackTrace()) {            
                    System.out.println(ste);
    
                }
            }
        }
        
        public static void g() {f();}
        public static void h() {g();}
            
        public static void main (String[] args){
            f();
            System.out.println("------------------");
            g();
            System.out.println("------------------");
            h();
        }
    }

    这个例子中调用了getStackTrace()方法,这个方法返回StackTraceElement类的一个对象。其输出内容为:

    Tree1.f(Tree1.java:17)
    Tree1.main(Tree1.java:30)
    ------------------
    Tree1.f(Tree1.java:17)
    Tree1.g(Tree1.java:26)
    Tree1.main(Tree1.java:32)
    ------------------
    Tree1.f(Tree1.java:17)
    Tree1.g(Tree1.java:26)
    Tree1.h(Tree1.java:27)
    Tree1.main(Tree1.java:34)

    如果这样使用:

    public class Tree1 {
        public static void f(){
            try {
                throw new Exception();
            }catch(Exception e){
                for(StackTraceElement ste: e.getStackTrace()) {            
                    System.out.print(ste.getMethodName() + "  " );
                    System.out.println(ste.getLineNumber());
                }
            }
        }
        
        public static void g() {f();}
        public static void h() {g();}
            
        public static void main (String[] args){
            f();
            System.out.println("------------------");
            g();
            System.out.println("------------------");
            h();
        }
    }

    调用StackTraceElement中的方法getMethodName()和getLineNumber()就可以值获取栈轨迹中的方法 和行:

    f  17
    main  30
    ------------------
    f  17
    g  26
    main  32
    ------------------
    f  17
    g  26
    h  27
    main  34

    所以,其实StackTraceElement是一个栈轨迹元素的数组。将这些栈轨迹元素保存在一个数组中。每个元素对应栈的一个栈帧。数组的第一个元素保存的是栈顶元素,也就是上面的f。最后一个元素保存的栈底元素,也就是main。

    public class Tree1 {
        public static void f() throws Exception{
                throw new Exception();
        }
        
        public static void g() throws Exception {
                try {
                    f();
                }catch(Exception e){
                    e.printStackTrace();
                    throw e;
                    }
        }
            
        public static void main (String[] args){
            try {
                g();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    4、重新抛出异常

    这里f()函数中抛出一个异常,然后g()方法中捕获了这个异常并打印异常栈轨迹Stack Trace,然后又再抛出了刚刚的异常,mai方法中捕获 了这个异常并打印异常栈轨迹Stack Trace,所以结果为:

    java.lang.Exception
        at Tree1.f(Tree1.java:16)
        at Tree1.g(Tree1.java:21)
        at Tree1.main(Tree1.java:30)
    java.lang.Exception
        at Tree1.f(Tree1.java:16)
        at Tree1.g(Tree1.java:21)
        at Tree1.main(Tree1.java:30)

    也就是说,捕获到异常又立即抛出,在上级方法调用中再次捕获这个异常,打印的栈轨迹信息是一样的。额,其实只看代码也知道,因为这里只是把刚刚异常e又抛出一次,

    public class Tree1 {
        public static void f() throws Exception{
                throw new Exception();
        }
        
        public static void g() throws Exception {
                try {
                    f();
                }catch(Exception e){
                    e.printStackTrace();
                    throw (Exception)e.fillInStackTrace();
                    }
        }
            
        public static void main (String[] args){
            try {
                g();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    throw (Exception)e.fillInStackTrace();

    这一行调用了Exception的fillInStackTrace()方法,首先要知道其实这个方法并不是来自于Exception类。Exception类本身除了定义了几个构造器之外,所有的方法都是从其父类继承过来的。而和异常相关的方法都是从java.lang.Throwable类继承过来的,所以其实fillInStackTrace()是Throwable类的方法,然后fillInStackTrace()方法就是将当前线程当前状态下的轨迹栈的状态保存进Throwabe中,就是更新的意思。

    public Throwable fillInStackTrace()

    这是这个方法的原型所以它的返回值是一个Throwable类对象,所以使用它更新的时候需要强制类型转换。

    throw (Exception)e.fillInStackTrace();

    所以输出内容为:

    java.lang.Exception
        at Tree1.f(Tree1.java:16)
        at Tree1.g(Tree1.java:21)
        at Tree1.main(Tree1.java:30)
    java.lang.Exception
        at Tree1.g(Tree1.java:24)
        at Tree1.main(Tree1.java:30)

    这次第二个打印的栈轨迹信息就没有f了。

    5、异常链

    class MyException1 extends Exception{} 
    
    
    class MyException2 extends Exception{}
    
    public class Tree1 {
        
        public static void f() throws MyException1{
            throw new MyException1();
        }
        
        public static void g() throws MyException2{
            try {
                f();
            } catch (MyException1 e) {
                e.printStackTrace();
                throw new MyException2();
            }
        }
    
            
        public static void main (String[] args){
            try {
                g();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    这里定义了两个异常类,f()方法抛出了MyException1,g()方法捕获了这个异常并打印了异常的栈轨链,然后抛出了另一个异常MyException2也打印了异常的栈轨链,所以输出结果为:

    MyException1
        at Tree1.f(Tree1.java:16)
        at Tree1.g(Tree1.java:21)
        at Tree1.main(Tree1.java:31)
    MyException2
        at Tree1.g(Tree1.java:24)
        at Tree1.main(Tree1.java:31)

    这没什么毛病,但是常常想要在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这就需要将原始异常的信息包装在新的异常中,这被称为异常链

    class MyException1 extends Exception{} 
    
    
    class MyException2 extends Exception{
        MyException2(Throwable throwable){
            super(throwable);
        }
        MyException2(){
            super();
        }
    }
    public class Tree1 {
        
        public static void f() throws MyException1{
            throw new MyException1();
        }
        
        public static void g() throws MyException2{
            try {
                f();
            } catch (MyException1 e) {
                e.printStackTrace();
                throw new MyException2(e);
            }
        }
    
            
        public static void main (String[] args){
            try {
                g();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    这样的输出结果就是:

    MyException1
        at Tree1.f(Tree1.java:16)
        at Tree1.g(Tree1.java:21)
        at Tree1.main(Tree1.java:31)
    MyException2: MyException1
        at Tree1.g(Tree1.java:24)
        at Tree1.main(Tree1.java:31)
    Caused by: MyException1
        at Tree1.f(Tree1.java:16)
        at Tree1.g(Tree1.java:21)
        ... 1 more

    这样的定义:

    public class MyException2 extends Exception{  
        //定义异常的原因  
        public MyException2(String message){  
            super(message);  
        }  
      
        //定义异常原因,并携带原始的异常  
        public MyException2(String message,Throwable cause){  
            super(message,cause);  
        }  
      
        //保留原始异常信息  
        publicMyException2(Throwable cause){  
            super(cause);  
        }  
    }  

    就能将原始异常传递给新异常

    或是:

    class MyException1 extends Exception{
    } 
    
    
    class MyException2 extends Exception{
        MyException2(Throwable throwable){
            super(throwable);
        }
        MyException2(){
            super();
        }
    }
    
    class MyException3 extends Exception{
        MyException3(Throwable throwable){
            super(throwable);
        }
        MyException3(){
            super();
        }
    }
    
    public class Tree1 {
        
        public static void f() throws MyException1{
            throw new MyException1();
        }
        
        public static void g() throws MyException2{
            try {
                f();
            } catch (MyException1 e) {
    //            e.printStackTrace();
                throw new MyException2(e);
            }
        }
        
        public static void h() throws MyException3{
            try {
                g();
            } catch (MyException2 e) {
    //            e.printStackTrace();
                throw new MyException3(e);
            }
        }
    
            
        public static void main (String[] args){
            try {
                h();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    这里值输出了最后一个异常的栈轨迹

    MyException3: MyException2: MyException1
        at Tree1.h(Tree1.java:44)
        at Tree1.main(Tree1.java:51)
    Caused by: MyException2: MyException1
        at Tree1.g(Tree1.java:35)
        at Tree1.h(Tree1.java:41)
        ... 1 more
    Caused by: MyException1
        at Tree1.f(Tree1.java:27)
        at Tree1.g(Tree1.java:32)
        ... 2 more

    其中还包括了MyException1和MyException2 的信息

    6、异常的限制

      a、当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。

      b、异常限制对构造器不起作用

      c、当父类与接口具有相同的方法而且方法同时抛出不同的异常,这个时候是不允许的

      d、当父类的构造方法抛出异常,子类必须有一个构造方法是抛出相同异常或者此异常的父类。

      e、当父类方法没有抛出异常,子类覆盖的方法不能够抛出异常

      f、当父类方法抛出异常,子类覆盖的方法可以不抛出异常

      b和e有一点冲突,但是实际上是是因为父类构造器必须以这样或者那样的方式被调用,子类构造器的异常说明必须包含基类构造器的异常说明。

    7、把“被检查的异常”转换为“不检查的异常”

    import java.io.*;
    
    class WrapCheckedExcetion{
        void thorwRuntimeExcetion(int type) {
            try {
                switch(type) {
                case 0: throw new FileNotFoundException();
                case 1: throw new IOException();
                case 2: throw new RuntimeException("where an I");
                default: return;
                }
            }catch(Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    
    class SomeOtherExcption extends Exception{}
    public class Tree1 {
        public static void main(String[] args) {
            WrapCheckedExcetion wce = new WrapCheckedExcetion();
            wce.thorwRuntimeExcetion(3);
            for(int i = 0; i < 4; i++) {
                try {
                    if(i < 3)
                        wce.thorwRuntimeExcetion(i);
                    else
                        throw new SomeOtherExcption();
                }catch(SomeOtherExcption e) {
                    System.out.println("SomeOtherExcption:" + e);
                }catch(RuntimeException re) {
                    try {
                        throw re.getCause();
                    }catch(FileNotFoundException e) {
                        System.out.println("FileNotFoundException:" + e);
                    }catch(IOException e) {
                        System.out.println("IOException:" + e);
                    }catch(Throwable e) {
                        System.out.println("Throwable:" + e);
                    }
                }
            }
        }
    }

    输出:

    FileNotFoundException:java.io.FileNotFoundException
    IOException:java.io.IOException
    Throwable:java.lang.RuntimeException: where an I
    SomeOtherExcption:SomeOtherExcption

    这里的WrapCheckedExcetion中的thorwRuntimeExcetion可以生成不同的异常,这些异常都被捕获并包装进了RuntimeExcetion对象中,所以它们都变成了运行时异常的“cause”了。在main中可以不用try块就可以调用thorwRuntimeExcetion(),因为它并没有抛出“被检查的异常”所以这个thorwRuntimeExcetion方法就实现了把“被检查的异常”转换为“不检查的异常”

  • 相关阅读:
    微信小程序使用wxParse解析html
    git基本操作
    thinkphp 静态缓存设置
    phpstudy 安装memcached服务和memcache扩展
    CSS超出部分显示省略号…代码
    小程序支付
    phpstorm 快捷键2
    thinkphp session设置
    cookie与session
    微信小程序 setData动态设置数组中的数据
  • 原文地址:https://www.cnblogs.com/xxbbtt/p/7632794.html
Copyright © 2020-2023  润新知