美文网首页
Java-面向对象-异常

Java-面向对象-异常

作者: 码农随想录 | 来源:发表于2018-07-16 18:01 被阅读5次

    异常和错误

    Java运行时期发生的问题就是异常。

    Java中运行时发生的除了异常Exception还有错误Error。

    异常:通常发生可以有针对性的处理方式的。
    错误:通常发生后不会有针对性的处理方式。
    Error的发生往往都是系统级别的问题,都是jvm所在系统发生的并反馈给jvm的。
    无法针对处理,只能修正代码。

    class ExceptionDemo 
    {
        public static void main(String[] args) 
        {
            int[] arr = new int[1024*1024*100];//OutOfMemoryError  错误
    
            System.out.println(arr[0]);
            System.out.println(arr[3]);//该句运行时发生了异常 ArrayIndexOutOfBoundsException,导致程序无法继续执行。程序结束。
            System.out.println("over");
        }
    }
    

    自定义异常

    /
    //自定义异常。描述Person的年龄数值非法。
    /**
    只要是本项目的Person的年龄出现非法值就会发生该异常。
    */
    class NoAgeException
    {
        /*
        为什么要定义构造函数,因为看到Java中的异常描述类中有提供对问题对象的初始化方法。
        */
        NoAgeException()
        {}
    
        NoAgeException(String message)
        {}
    }
    
    class Person
    {
        private String name;
        private int age;
        Person(String name,int age)
        {
            //加入逻辑判断。
            if(age<0 || age>200)
            {
                throw new NoAgeException(age+",年龄数值非法");
            }
            this.name = name;
            this.age = age;
        }
        //定义Person对象对应的字符串表现形式。覆盖Object中的toString方法。
        public String toString()
        {
            return "Person[name="+name+",age="+age+"]";
        }
    }
    class ExceptionDemo5 
    {
        public static void main(String[] args) 
        {
            Person p = new Person("xiaoming",-20);
            System.out.println(p);
        }
    }
    

    发生编译失败:
    ExceptionDemo5.java:43: 错误: 不兼容的类型
    throw new NoAgeException(age+",年龄数值非法");
    ^
    需要: Throwable
    找到: NoAgeException
    1 个错误

    不兼容,不明白,查阅,提示中说需要Throwable,啥东西?
    搜索API。看到Throwable描述。发现。它是异常和错误的超类(父类)
    原来它是异常体系的顶层类。
    Throwable
    |--Error
    |--Exception

    通过阅读:自定义异常被抛出,必须是继承Throwable,或者继承Throwable的子类。
    该对象才可以被throw抛出。

    原来这个异常体系具备一个特有的特性:可抛性:可以被throw关键字操作。

    继承选择父类时,更为确切是继承Exception。

    /
    //自定义异常。描述Person的年龄数值非法。
    /**
    只要是本项目的Person的年龄出现非法值就会发生该异常。
    */
    class NoAgeException extends Exception
    {
        /*
        为什么要定义构造函数,因为看到Java中的异常描述类中有提供对问题对象的初始化方法。
        */
        NoAgeException()
        {
            super();
        }
    
        NoAgeException(String message)
        {
            super(message);// 如果自定义异常需要异常信息,可以通过调用父类的带有字符串参数的构造函数即可。
        }
    }
    
    class Person
    {
        private String name;
        private int age;
        Person(String name,int age)
        {
            //加入逻辑判断。
            if(age<0 || age>200)
            {
                throw new NoAgeException(age+",年龄数值非法");
            }
            this.name = name;
            this.age = age;
        }
        //定义Person对象对应的字符串表现形式。覆盖Object中的toString方法。
        public String toString()
        {
            return "Person[name="+name+",age="+age+"]";
        }
    }
    class ExceptionDemo5 
    {
        public static void main(String[] args) 
        {
            Person p = new Person("xiaoming",-20);
            System.out.println(p);
        }
    }
    

    但是发现编译又一次失败了。

    ExceptionDemo5.java:62: 错误: 未报告的异常错误NoAgeException; 必须对其进行捕获或
    声明以便抛出
    throw new NoAgeException(age+",年龄数值非法");
    ^
    1 个错误

    通过这个编译失败提示,发现自定义的异常和之前所使用的异常(空指针异常,角标越界异常,无效参数异常有不同)
    抛出哪些异常没有这个失败提示?那么之前的异常和自定义的异常有什么区别呢?
    通过查看api的继承体系发现,之前的异常都是Exception下面的RuntimeException子类的子类。
    阅读RuntimeException描述中有明确说明,这个运行时异常以及其子类都无需进行声明。

    可以将自定义的异常继承RuntimeException.

    /
    //自定义异常。描述Person的年龄数值非法。
    /**
    只要是本项目的Person的年龄出现非法值就会发生该异常。
    */
    class NoAgeException extends RuntimeException
    {
        /*
        为什么要定义构造函数,因为看到Java中的异常描述类中有提供对问题对象的初始化方法。
        */
        NoAgeException()
        {
            super();
        }
    
        NoAgeException(String message)
        {
            super(message);// 如果自定义异常需要异常信息,可以通过调用父类的带有字符串参数的构造函数即可。
        }
    }
    
    class Person
    {
        private String name;
        private int age;
        Person(String name,int age)
        {
            //加入逻辑判断。
            if(age<0 || age>200)
            {
                throw new NoAgeException(age+",年龄数值非法");
            }
            this.name = name;
            this.age = age;
        }
        //定义Person对象对应的字符串表现形式。覆盖Object中的toString方法。
        public String toString()
        {
            return "Person[name="+name+",age="+age+"]";
        }
    }
    class ExceptionDemo5 
    {
        public static void main(String[] args) 
        {
            Person p = new Person("xiaoming",-20);
            System.out.println(p);
        }
    }
    

    继承Exception和继承RuntimeExcpetion为什么差距这么大?

    class Demo
    {
        void show()
        {
            //如果在函数内抛出Exception//编译失败,因为编译器在检查语法时发生了错误。
            /*
            该程序已经出现问题,Java认为这个程序本身存在隐患,
            需要捕获或者声明出来(你要么把问题处理,要么把问题标识出来让调用知道)
            */
    //      throw new Exception();
    
            /*
            为什么抛出RuntimeException,不需要捕获,不要声明呢?
            不是功能本身发生的异常,而是因为比如调用者传递参数错误而导致功能运行失败。
            这时也是问题,需要通过异常来体现,但是这个异常不要声明出来的。
            声明的目的是为了让调用者进行处理。
            不声明的目的是不让调用者进行处理,就是为了让程序停止,让调用者看到现象,并进行代码的修正。★★★★★
    
            原理异常分两种:
            1,编译时异常:编译器会检测的异常。
            2,运行时异常:编译器不会检测的异常。不需要声明。声明也可以,如果声明了,无外乎就是让调用者给出处理方式。
            ArrayIndexOutOfBoundsException
            IllegalArgumentException
            NullPointerException
            ClassCastException
            */
            throw new RuntimeException();
        }
    }
    class ExceptionDemo6 
    {
        public static void main(String[] args) 
        {
            System.out.println("Hello World!");
        }
    }
    

    声明和捕获

    声明:将问题标识出来,报告给调用者。
    如果函数内通过throw抛出了编译时异常,而捕获,那么必须通过throws进行声明,让调用者去处理。
    
    捕获:Java中对异常有针对性的语句进行捕获。
    语句:
    try
    {
        //需要被检测的语句。
    }
    catch(异常类 变量)//参数。
    {
        //异常的处理语句。
    }
    finally
    {
        //一定会被执行的语句。
    }
    
    class Demo
    {
        /*
        如果定义功能时有问题发生需要报告给调用者。可以通过在函数上使用throws关键字进行声明。
        */
        void show(int x)throws Exception
        {
            if(x>0)
                throw new Exception();
            else
                System.out.println("show run");
        }
    }
    
    class ExceptionDemo7 
    {
        public static void main(String[] args)//throws Exception//在调用者上继续声明。 
        {
            Demo d = new Demo();
            try
            {
                d.show(1);//当调用了声明异常的方法时,必须有处理方式。要么捕获,要么声明。
            }
            catch (Exception ex)//括号中需要定义什么呢?对方抛出的是什么问题,在括号中就定义什么问题的引用。
            {
                System.out.println("异常发生了");
            }
            System.out.println("Hello World!");
        }
    }
    

    继承Exception呢?还是继承RuntimeException呢?

    
    
    class NoAgeException extends RuntimeException
    {
        /*
        为什么要定义构造函数,因为看到Java中的异常描述类中有提供对问题对象的初始化方法。
        */
        NoAgeException()
        {
            super();
        }
    
        NoAgeException(String message)
        {
            super(message);// 如果自定义异常需要异常信息,可以通过调用父类的带有字符串参数的构造函数即可。
        }
    }
    
    class Person
    {
        private String name;
        private int age;
        /*
        构造函数到底抛出这个NoAgeException是继承Exception呢?还是继承RuntimeException呢?
        继承Exception,必须要throws声明,一声明就告知调用者进行捕获,一旦问题处理了调用者的程序会继续执行。
        但是如果使用到了Person对象的数据,导致都失败的。
        继承RuntimeExcpetion,不需要throws声明的,这时调用是不可能编写捕获代码的,因为调用根本就不知道有问题。
        一旦发生NoAgeException,调用者程序会停掉,并有jvm将信息显示到屏幕,让调用者看到问题,修正代码。哦耶。
    
        */
        Person(String name,int age)//throws NoAgeException
        {
            //加入逻辑判断。
            if(age<0 || age>200)
            {
                throw new NoAgeException(age+",年龄数值非法");
            }
            this.name = name;
            this.age = age;
        }
        //定义Person对象对应的字符串表现形式。覆盖Object中的toString方法。
        public String toString()
        {
            return "Person[name="+name+",age="+age+"]";
        }
    }
    class ExceptionDemo8
    {
        public static void main(String[] args) 
        {
    //      try
    //      {
                Person p = new Person("xiaoming",20);
                System.out.println(p);
    //      }
    //      catch (NoAgeException ex)
    //      {
    //          System.out.println("异常啦");
    //      }
            System.out.println("over");
        }
    }
    

    编译时异常的应用

    /*
    案例:老师用电脑讲课。
    两个对象:
        老师:
            属性:姓名。
            行为:讲课。
        电脑:
            行为:运行。
    
    考虑问题。
    1,电脑蓝屏-->异常。
    
    2,电脑冒烟-->异常。
    
    */
    // 可以声明,让调用给出处理方式。
    class LanPingException extends Exception
    {
        LanPingException()
        {
            super();
        }
        LanPingException(String message)
        {
            super(message);
        }
    }
    
    class MaoYanException extends Exception
    {
        MaoYanException()
        {
            super();
        }
        MaoYanException(String message)
        {
            super(message);
        }
    }
    /*
    讲课中冒烟,问题可以临时解决,是冒烟问题没有直接处理,所以就使用throws声明。
    但是发现,这个问题不应该属于讲课的问题。调用讲课方法的调用者是处理不了这个冒烟问题的。
    该调用者能处理的应该是冒烟导致的课程进行不下去的问题。
    应该在列出一个异常。课时停止异常。
    
    */
    class NoPlanException extends Exception
    {
        NoPlanException()
        {
            super();
        }
        NoPlanException(String message)
        {
            super(message);
        }
    }
    
    
    class NoteBook
    {
        private int state = 2;
        public void run()throws LanPingException,MaoYanException//方法上可以通过throws声明多个异常。
        {
            System.out.println("笔记本电脑运行");
            if(state == 1)
                throw new LanPingException("电脑蓝屏了");
    
            if(state == 2)
                throw new MaoYanException("电脑冒烟了");
        }
        public void reset()
        {
            state = 0;
            System.out.println("电脑重启");
        }
    }
    class Teacher
    {
        private String name;
        private NoteBook book;
        Teacher(String name)
        {
            this.name = name;
            book = new NoteBook();
        }
        //讲课。
        public void prelect()throws NoPlanException
        {
            /*
            调用到了声明异常的方法,在这里到底是捕获好呢?还是声明好呢?
            有具体的捕获处理方式吗?有,那就捕获,没有,那么声明。
            我可以处理,重启就可以了,重启是电脑的功能。
            */
            try
            {
                book.run();//对于声明多个异常的方法,在处理,需要定义多个catch与之对应。
            }
            
            catch (LanPingException e)//LanPingException e = new LanPingException("电脑蓝屏了");
            {
                //重启。
                System.out.println(e.toString());//异常的名称+异常的信息。
    //          e.printStackTrace();
                book.reset();
            }
            catch(MaoYanException e)//MaoYanException e = new MaoYanException("电脑冒烟了");
            {
                System.out.println(e.toString());
                test();
                //冒烟问题没有解决,继续声明throws出去。
    //          throw e;
    //          new 电脑维修部(book);//去修电脑。
    //          book = new NoteBook();
                throw new NoPlanException(e.getMessage()+",课时停止");//异常转换。
    
    
            }
            System.out.println(name+"....讲课");
        }
        //留练习。
        public void test()
        {
            System.out.println("做练习");
        }
    }
    
    class ExceptionTest2 
    {
        public static void main(String[] args) 
        {
            Teacher t = new Teacher("老师");
            try
            {
                t.prelect();
            }
            catch (NoPlanException e)
            {
                System.out.println(e.toString());
                System.out.println("换老师");
            }
            
            System.out.println("Hello World!");
        }
    }
    

    throw和throws的区别

    • throw用在函数内。
      throws用在函数上。

    • thorw抛出的是异常对象。
      throws用于进行异常类的声明,后面异常类可以有多个,用逗号隔开。

    finally的使用

    需求:有一些特定的代码无论异常是否发生,都需要执行。
    因为异常会引发程序跳转,导致有写语句执行不到。无法满足这个需求。
    异常捕获处理时java提供解决方案。
    try catch finally
    finally就是解决这个问题的,这个代码块中存放的代码都是一定会被执行的。

    应用场景;
    
    定义一个功能往数据库中添加数据。
    
    void add(Data data)throws NoAddException
    {
        //1,连接数据库。
        try{
        //2,添加数据。//添加数据时发生了异常情况。throw new SQLException();程序跳转,就执行不到断开连接。
                    //而断开连接必须要执行,因为不执行,连接资源在浪费。
                    //无论是否发生问题,都需要执行断开连接的动作,从而释放资源。
        }catch(SQLException e)
        {
            //解决数据库的问题。
            //同时将问题告诉调用者。
            throw new NoAddException();
        }
        finally
        {
            //3,断开连接。
        }
    
    }
    
    总结:finally到底什么时候用?
    只要程序中使用到了具体的资源(数据库连接,IO资源,网络连接socket等)
    需要释放,都必须定义在finally中。你在定义程序,只要问题发生与否,
    指定程序都需要执行时,就定义finally中。
    
    • 注意: return后finall也会执行, 有一种情况发生(System.exit(0);//退出Jvm。),finally不执行。
    class NoShowException extends Exception
    {
        NoShowException(String message)
        {
            super(message);
        }
    }
    
    class Demo
    {
        void show(int num)throws NoShowException
        {
            if(num<0)
                throw new NoShowException(num+",数值是非法的");
            System.out.println("show ....."+num);
        }
    }
    
    class ExceptionDemo9
    {
        public static void main(String[] args)
        {
            Demo d  = new Demo();
            //因为调用到了声明异常的show方法,所以调用者要给处理方式,要么继续声明,要么捕获。
            try
            {
                d.show(-5);
            }
            catch (NoShowException ex)
            {
                System.out.println(ex.toString());//打印是异常名称+异常信息。
    
                //如果异常发生,处理完毕后,希望功能结束。
                            //return后finall也会执行
                return ;
    
                //注意:有一种情况发生,finally也不执行。
                //System.exit(0);//退出Jvm。
            }
            finally
            {
                System.out.println("hello");
            }
            System.out.println("over");
            */
    
            Test t = new Test();
            int num = t.show(-4);
            System.out.println("num="+num);
    
    
        }
    }
    

    catch和finally都return后,结果?

    由于catch中的return会在finally后执行,如果finally有return,就以finally中return为结果

    class ExceptionDemo9
    {
        public static void main(String[] args)
        {
            Test t = new Test();
            int num = t.show(-4);//100
            System.out.println("num="+num);
        }
    }
    class Test
    {
        int show(int num)
        {
        
            try{
                if(num<0)
                    throw new Exception();
                return 4;
            }catch(Exception e)
            {
                System.out.println(e.toString());
                return 200;
            }
            finally
            {
                System.out.println("finally run");
                return 100;
            }
        }
    }
    

    try catch finally 组合方式

    1,
    try catch : 对代码进行异常检测,并对检测的异常传递给catch处理。
                异常捕获处理。
    void show()//不用throws 
    {
        try{
        throw new Exception();
        }catch(Exception e)
        {
        
        }
    }
    
    2,
    try finally : 对代码进行异常检测,检测到异常后因为没有catch,所以一样会被默认jvm抛出。
                异常是没有捕获处理的。但是功能所开启资源需要进行关闭,所有finally。
                只为关闭资源。
    
    void show()//需要throws 
    {
        try{
        throw new Exception();
        }finally
        {
        
        }
    }
    
    3,
    try catch finally
    检测异常,并传递给catch处理,并定义资源释放。
    
    4,try catch1 catch2 catch3......
    

    异常在继承或者实现中的使用细节

    • 细节总结
    1,子类覆盖父类方法时,如果父类的方法声明异常,子类只能声明父类异常或者该异常的子类,或者不声明。
    2,当父类方法声明多个异常时,子类覆盖时只能声明多个异常的子集。
    3,当被覆盖的方法没有异常声明时,子类覆盖时是无法声明异常的。
        举例:父类存在这种情况,接口也有这种情况,
        问题:接口中没有声明异常,而实现的子类覆盖方法时发生了异常,怎么办?
             无法进行throws声明,只能catch的捕获。万一问题处理不了呢?catch中继续throw抛出,但是只能将异常转换成RuntimeException子类抛出。
    
    • 子类覆盖父类方法时,如果父类的方法声明异常,子类不声明异常,子类调用覆盖方法不需要try...catch...
    /*
    Exception
        |--AException
            |--AAException
        |--BException
    */
    class AException extends Exception
    {
    }
    class BException extends Exception
    {
    }
    class AAException extends AException
    {
    }
    class Fu
    {
        void show() throws AException
        {
        }
    }
    class Zi extends Fu
    {
        void show()
        {
            
        }
    }
    class ExceptionDemo11 
    {
        public static void main(String[] args) 
        {
            Zi z = new Zi();
            z.show();
        }
    }
    
    • 子类覆盖父类方法时,如果父类的方法声明异常AException,子类覆盖方法时可以声明异常AException或者异常AException的子类,子类调用覆盖方法需要try...catch...
    class AException extends Exception
    {
    }
    class BException extends Exception
    {
    }
    class AAException extends AException
    {
    }
    class Fu
    {
        void show() throws AException
        {
        }
    }
    class Zi extends Fu
    {
        void show() throws AException
        {
            
        }
    }
    class Zi1 extends Fu
    {
        void show() throws AAException
        {
            
        }
    }
    class ExceptionDemo11 
    {
        public static void main(String[] args) 
        {
            Zi z = new Zi();
        try{
                z.show();
        }catch(Exception e){};
    
        Zi1 z1 = new Zi1();
        try{
                z.show();
        }catch(Exception e){};
        }
    }
    

    如果没有trc...catch...,就会变异报错


    编译会报错
    • 子类覆盖父类方法时,如果父类的方法声明异常AException,子类不声明异常BException,编译会报错
    class AException extends Exception
    {
    }
    class BException extends Exception
    {
    }
    class AAException extends AException
    {
    }
    class Fu
    {
        void show() throws AException
        {
        }
    }
    class Zi extends Fu
    {
        void show() throws BException
        {
            
        }
    }
    class ExceptionDemo11 
    {
        public static void main(String[] args) 
        {
            Zi z = new Zi();
        try{
                z.show();
        }catch(Exception e){};
        }
    }
    
    编译错误
    • 当被覆盖的方法没有异常声明时,子类覆盖时是无法声明异常的
    class Fu
    {
        void show()
        {
        }
    }
    class Zi extends Fu
    {
        void show()throws Exception
        {
        
        }
    }
    class ExceptionDemo11 
    {
        public static void main(String[] args) 
        {
            Zi z = new Zi();
            try{
            z.show();
            }catch(Exception e)
            {}
        }
    }
    
    编译错误
    • 接口中没有声明异常,而实现的子类覆盖方法时发生了异常,怎么办?无法进行throws声明,只能catch的捕获。万一问题处理不了呢?catch中继续throw抛出,但是只能将异常转换成RuntimeException子类抛出。
        Interface Inter
        {
            public void show();
        }
        class Demo implement Inter
        {
            public void show()
            {
                try{
                    throw new Exception();
                }catch(Exception e)
                {
                    code...;
                    throw new RuntimeException("");// 告知调用者问题所在。
                }
            }
        }
    

    相关文章

      网友评论

          本文标题:Java-面向对象-异常

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