• [改善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覆写的要求.构造函数之所以与此相反,是因为构造函数没有覆写的概念,只是构造函数间的引用调用而已,所以在构造函数中抛出受检查异常会违背里氏替换原则,使我们的程序缺乏灵活性. 

       ③子类构造函数扩展有限

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

  • 相关阅读:
    linux 查看安装软件位置(持续跟新)
    POJ 3661 Running (DP思想)
    stringstream的用法
    关于实验室搬家
    POJ 搜索(2)
    Treap的插入删除实现
    碰到一道不错的扩展欧几里德题目 whu 1338
    SRM 144
    关于无向图双连通问题
    POJ 动态规划(2)
  • 原文地址:https://www.cnblogs.com/DreamDrive/p/5621276.html
Copyright © 2020-2023  润新知