只想把基础打好之-异常

作者: 扈扈哈嘿 | 来源:发表于2017-03-05 13:33 被阅读43次

    在这里说说一些容易忽略的知识点。

    1. 对异常来说最重要的就是异常的类名。要做到见名知义。比如NullPointerException,IllegalStateException等,它们都只是简单地继承了RuntimeException。
    public class NullPointerException extends RuntimeException {
        private static final long serialVersionUID = 5162710183389028792L;
    
        /**
         * Constructs a {@code NullPointerException} with no detail message.
         */
        public NullPointerException() {
            super();
        }
    
        /**
         * Constructs a {@code NullPointerException} with the specified
         * detail message.
         *
         * @param   s   the detail message.
         */
        public NullPointerException(String s) {
            super(s);
        }
    }
    
    1. printStackTrace()方法所提供人信息可以通过getStackTrace()方法来直接访问,这个方法将返回一个栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一帧。
    public class WhoCalled {
        static void f(){
            try {
                throw new Exception();
            }catch (Exception e){
                      for(StackTraceElement ste:e.getStackTrace()){
                          System.out.println(ste.getMethodName());
                      }
            }
        }
        static void g(){f();}
        static void h(){g();}
        public static void main(String[] args){
            f();
            System.out.println("#############################");
            g();
            System.out.println("#############################");
            h();
            System.out.println("#############################");
        }
    }
    

    运行结果:

    f
    main
    invoke0
    invoke
    invoke
    invoke
    main
    #############################
    f
    g
    main
    invoke0
    invoke
    invoke
    invoke
    main
    #############################
    f
    g
    h
    main
    invoke0
    invoke
    invoke
    invoke
    main
    #############################
    
    1. fillInStackTrack()用来更新栈信息,fillInStackTrack()方法将返回一个Throwable对象。通过下面的例子会直观一些。
    public class Rethrowing {
        public static void f() throws Exception{
            System.out.println("f方法里的原始异常");
            throw new Exception("从f方法里面抛出的异常");
        }
        public static void g() throws Exception{
            try {
                f();
            } catch (Exception e) {
                System.out.println("g方法里面的catch代码块");
                e.printStackTrace(System.out);
                throw e;
            }
        }
        public static void h()throws Exception{
            try {
                f();
            } catch (Exception e) {
                System.out.println("h方法里面的代码块");
                e.printStackTrace(System.out);
                throw (Exception) e.fillInStackTrace();
            }
        }
        public static void main(String[] args){
            try {
                g();
            } catch (Exception e) {
                System.out.println("main方法中调用g方法");
                e.printStackTrace(System.out);
            }
            System.out.println("#####################");
            try {
                h();
            } catch (Exception e) {
                System.out.println("main方法中调用h方法");
                e.printStackTrace(System.out);
            }
    
        }
    }
    

    运行结果:

    f方法里的原始异常
    g方法里面的catch代码块
    java.lang.Exception: 从f方法里面抛出的异常
        at exception.Rethrowing.f(Rethrowing.java:9)
        at exception.Rethrowing.g(Rethrowing.java:13)
        at exception.Rethrowing.main(Rethrowing.java:31)
    main方法中调用g方法
    java.lang.Exception: 从f方法里面抛出的异常
        at exception.Rethrowing.f(Rethrowing.java:9)
        at exception.Rethrowing.g(Rethrowing.java:13)
        at exception.Rethrowing.main(Rethrowing.java:31)
    #####################
    f方法里的原始异常
    h方法里面的代码块
    java.lang.Exception: 从f方法里面抛出的异常
        at exception.Rethrowing.f(Rethrowing.java:9)
        at exception.Rethrowing.h(Rethrowing.java:22)
        at exception.Rethrowing.main(Rethrowing.java:38)
    main方法中调用h方法
    java.lang.Exception: 从f方法里面抛出的异常
        at exception.Rethrowing.h(Rethrowing.java:26)
        at exception.Rethrowing.main(Rethrowing.java:38)
    

    4.异常链,Throwable的子类在构造器中都可以一个cause对象作为参数。这个cause就用来表示原始异常,这样通过把原始异常传递给新的异常,使得在当前位置创建并抛出新的异常,也能通过这个异常链追踪最初发生的位置。需要注意的是,在Throwabe的子类中,只有三种基本的异常类提供了带cause参数构造器它们是Error,Exception和RuntimeException。如果要把其它类型的异常链接起来,应该使用initCause()方法而不是构造器。比如说:

    new RuntimeException的子类(RuntimeException的其它子类的对象);
    Exception的子类的对象.initCause(RuntimeException的子类对象);
    

    5.在try块里有return,finally是会执行的。那如果finally也有return呢,会返回哪个呢?

    public class FinallyTest {
        public static void main(String[] args){
    System.out.println(testFinally());
        }
        private static String testFinally(){
            try{
                return "try";
            }finally {
                return "finally";
            }
        }
    }
    

    运行结果:

    finally
    

    这种结构会导致代码可读性变差。
    5.异常的不足就是会导致异常丢失。异常作为程序出错的标志决不应该被忽略。然而某些使用finally子句就会发生这种情况。

    public class LostException {
        class VeryImportantException extends Exception{
            @Override
            public String toString() {
                return "very important exception";
            }
        }
        class HoHumException extends Exception{
            @Override
            public String toString() {
                return "hohum exception";
            }
        }
        void f() throws VeryImportantException{
            throw new VeryImportantException();
        }
        void dispose() throws HoHumException{
            throw new HoHumException();
        }
        public static void main(String[] args){
            try{
                LostException lost=new LostException();
                try{
                    lost.f();
                }finally {
                    lost.dispose();
                }
            }catch (Exception e){
                System.out.println(e);
            }
        }
    }
    

    运行结果:

    hohum exception
    

    从输出结果中看到VeryImportantException不见了,它被finally子句中的HoHumException取代。这是相当严重的缺陷。
    6.异常的限制,当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。这些限制很有用,因为这意味着,当基类使用的代码应用到派生类对象的时候,一样能够工作,当然这是面向对象的基本概念。异常也不例外。

    public class ExceptionTest {
        class BaseException extends Exception{};
        class Foul extends BaseException{};
        class Strike extends BaseException{};
        abstract class Inning{
            public Inning () throws BaseException{}
            public void event () throws BaseException{}
            public abstract void atBat() throws Strike,Foul;
            public void walk(){};
        }
        class StormException extends Exception{};
        class RainedOut extends StormException{};
        class PopFoul extends Foul{};
        interface Storm{
            public void event() throws RainedOut;
            void rainHard() throws RainedOut;
        }
        public class StormyInning extends Inning implements Storm{
            //因为Inning的构造器抛出异常,所以它的继承类必须处理父类的异常,当然也可以抛出自己想要处理的异常
            public StormyInning()  throws BaseException,Foul{
            }
            //下面这个方法不能通过编译,因为在Inning方法中并没有抛出异常
    //        @Override
    //        public void walk() throws PopFoul{
    //
    //        }
            //接口不能基类中的方法添加异常,所以下面也不能编译
    //        @Override
    //        public void event() throws RainedOut{
    //
    //        }
            @Override
            public void event() {
            }
            //重写的方法可以抛出继承的异常
            @Override
            public void atBat() throws PopFoul {
            }
            @Override
            public void rainHard() throws RainedOut {
            }
        }
    }
    

    在Inning类中。可以看到构造器和event方法都声明将抛出异常,但实际上并没有抛出,这种方式使你强制用户去捕获可能在覆盖的event版本中增加的异常。

    7.被检查异常并不总是好的,它有时候会使问题变得复杂,因为它强制你在还没有准备好处理问题的时候加上catch子句。

    相关文章

      网友评论

        本文标题:只想把基础打好之-异常

        本文链接:https://www.haomeiwen.com/subject/njdrgttx.html