zoukankan      html  css  js  c++  java
  • [改善Java代码]不要在构造函数中抛出异常

    Java的异常机制有三种:

    一.Error类以及其子类表示的是错误,它是不需要程序员处理也不能处理的异常.比如VirtualMachineError虚拟机错误,ThreadDeath线程僵尸等.

    二.RuntimeException类及其子类表示的是非受检查异常,是系统可能会抛出的异常,程序员可以去处理,也可以不去处理,最经典的就是NullPointerException空指针异常和IndexOutOfBoundsException越界异常.

    三.Exception类及其子类(不包含非受检查异常)表示的是受检异常,这是程序员必须处理的异常,不处理则程序不能通过编译,比如IOException表示I/O异常,SQLException表示数据库访问异常.

    我们知道 一个对象的创建,需要经过内存分配,静态代码初始化,构造函数执行等过程,对象生成的关键步骤是构造函数,那是不是允许在构造函数中抛出异常呢?从Java语法上来说,完全可以在构造函数中抛出异常,三类 异常都可以,

    但是从系统设计和开发的角度来分析,则尽量不要在构造函数中抛出异常,我们以三种不同类型的异常来说明:

    (1)构造函数抛出错误是程序员无法处理的

    (2)构造函数不应该抛出非受检查异常

    看如下的例子:

    class Person{
        public Person(int _age){
            //不满18岁得用户对象不能建立
            if(_age<18){
                //throw new RuntimeException("年龄必须大于18岁。");
            }        
        }
        //看限制级的电影
        public void seeMovie(){
            System.out.println("看限制级电影");
        }
    }

    代码的意图很明显,不满18岁的用户根本就不会生成一个Person实例对象,没有对象,类行为seeMovie方法就不可以执行,想法很好,但这会导致不可预测的结果,比如我们这样引用Person类.

        public static void main(String[] args) {
            while(true){
                Person p = new Person(17);
                p.seeMovie();
            }
    
            /*其他的逻辑处理*/
        }

    很明显,p对象不能建立,因为是一个RuntimeException异常,开发人员可以捕捉也可以不捕捉,代码看上去逻辑很正确,没有任何的瑕疵,但是事实上,这段程序会抛出异常,无法执行,这段代码给了我们两个警示: 

      ①加重了上层代码编写者的负担.

      ②后续代码不会执行.

    (3)构造函数尽可能不要抛出受检查异常

    //父类
    class Base{
        //父类抛出IOException
        public Base() throws IOException{
            throw new IOException();
        }
        //父类方法抛出Exception
        public void method() throws Exception{
            
        }
    }
    //子类
    class Sub extends Base{
        //子类抛出Exception异常
        public Sub() throws Exception {
        }
        //子类方法的异常类型必须是覆写方法的子类型
        @Override
        public void method() throws IOException{
            
        }
    }

    就这么一段代码,展示了在构造函数中抛出受检查异常的三个不利方面.

    ①导致子类代码膨胀

      上面的例子中,子类的无参构造函数不能省略,原因是父类的无参构造函数抛出了IOException异常,子类的无参构造函数默认调用的是父类的构造函数,所以子类的无参构造函数必须抛出IOException或其父类.

     ②违背了里氏替换原则

        里氏替换原则说"父类能出现的地方子类就可以出现,而且将父类替换为子类也不会产生任何异常",那我们回过头来看看Sub类是否可以替换Base类,比如我们的上层代码是这样写的:

        public static void main(String[] args) {
            try{
                Base base = new Base();
            }catch(IOException e){
                //异常处理
            }
        }

    然后我们希望把new Base()替换成new Sub(),而且代码能够正常编译和运行.非常可惜编译不通过..原因是Sub的构造函数抛出了Exception异常,它比父类的构造函数抛出的异常范围要宽,必须增加新的catch块才能解决.

     1 import java.io.IOException;
     2 
     3 public class Client {
     4     public static void main(String[] args) {
     5         try{
     6             Base base = new Base();
     7             //Base base = new Sub();这样是编译不通过的,因为Sub的构造抛出Exception,它比父类的构造函数
     8             //抛出的异常范围要宽,必须增加新的catch块才能解决.
     9         }catch(IOException e){
    10             //异常处理
    11         }
    12     }
    13 }
    14 
    15 class Base{
    16     //父类抛出IOException
    17     public Base() throws IOException{
    18         throw new IOException();
    19     }
    20 }
    21 
    22 class Sub extends Base{
    23     //子类抛出Exception异常
    24     public Sub() throws Exception {
    25 
    26     }
    27 }

    可能读者会问,为什么Java的构造函数允许子类的构造函数抛出更广泛的异常类呢?这正好与类方法的异常机制相反,类方法的异常是这样要求的:

    class Base{
    
        //父类方法抛出Exception
        public void method() throws Exception{
            
        }
    }
    
    class Sub extends Base{
        //子类方法的异常类型必须是覆写方法的子类型
        @Override
        public void method() throws IOException{
            
        }
    }

    子类方法可以抛出多个异常,但是都必须是覆写方法的子类型,对我们的例子来说,Sub类的method方法抛出的异常必须是Exception的子类或者Exception类,这是Java覆写的要求.构造函数之所以与此相反,是因为构造函数没有覆写的概念,只是构造函数间的引用调用而已,所以在构造函数中抛出受检查异常会违背里氏替换原则,使我们的程序缺乏灵活性. 

       ③子类构造函数扩展有限

    以上汇总起来就是:非受检查异常不要抛出,抛出了对人对己都是有害的...受检查异常尽量不抛出.总之一句话:在构造函数中尽可能的不出现异常.

  • 相关阅读:
    67. Add Binary
    66. Plus One
    64. Minimum Path Sum
    63. Unique Paths II
    How to skip all the wizard pages and go directly to the installation process?
    Inno Setup打包之先卸载再安装
    How to change the header background color of a QTableView
    Openstack object list 一次最多有一万个 object
    Openstack 的 Log 在 /var/log/syslog 里 【Ubuntu】
    Git 分支
  • 原文地址:https://www.cnblogs.com/DreamDrive/p/5621276.html
Copyright © 2011-2022 走看看