课程咨询 :186 8716 1620      qq:2066486918

昆明Java培训 > 达内新闻 > Java培训:不要在构造函数中抛出异常
  • Java培训:不要在构造函数中抛出异常

    发布:昆明Java培训      来源:达内新闻      时间:2016-10-23

  • 昆明Java培训机构的老师知道Java异常的机制有三种:

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

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

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

    昆明Java培训机构的老师知道,一个对象的创建过程经过内存分配,静态代码初始化、构造函数执行等过程,对象生成的关键步骤是构造函数,那是不是也允许在构造函数中抛出异常呢?从Java语法上来说,完全可以在构造函数中 抛出异常,三类异常都可以,但是从系统设计和开发的角度来分析,则尽量不要在构造函数中抛出异常,我们以三种不同类型的异常来说明之。

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

    在构造函数执行时,若发生了VirtualMachineError虚拟机错误,那就没招了,只能抛出,程序员不能预知此类错误的发生,也就不能捕捉处理。

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

    我们来看这样一个例子,代码如下:

    class Person {

    public Person(int _age) {

    //不满18岁的用户对象不能建立

    if (_age < 18) {

    throw new RuntimeException("年龄必须大于18岁.");

    }

    }

    public void doSomething() {

    System.out.println("doSomething......");

    }

    }

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

    public static void main(String[] args) {

    Person p = new Person(17);

    p.doSomething();

    /*其它的业务逻辑*/

    }

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

    加重了上层代码编写者的负担:捕捉这个RuntimeException异常吧,那谁来告诉我有这个异常呢?只有通过文档约束了,一旦Person类的构造函数经过重构后再抛出其它非受检异常,那main方法不用修改也是可以测试通过的,但是这里就 可能会产生隐藏的缺陷,而写还是很难重现的缺陷。不捕捉这个RuntimeException异常,这个是我们通常的想法,既然已经写成了非受检异常,main方法的编码者完全可以不处理这个异常嘛,大不了不执行Person的方法!这是非常危险的 一旦产生异常,整个线程都不再继续执行,或者链接没有关闭,或者数据没有写入数据库,或者产生内存异常,这些都是会对整个系统产生影响。

    后续代码不会执行:main方法的实现者原本是想把p对象的建立作为其代码逻辑的一部分,执行完doSomething方法后还需要完成其它逻辑,但是因为没有对非受检异常进行捕捉,异常最终会抛出到JVM中,这会导致整个线程执行结束后 后面所有的代码都不会继续执行了,这就对业务逻辑产生了致命的影响。

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

    我们来看下面的例子,代码如下:

    //父类

    class Base {

    //父类抛出IOException

    public Base() throws IOException {

    throw new IOException();

    }

    }

    //子类

    class Sub extends Base {

    //子类抛出Exception异常

    public Sub() throws Exception {

    }

    }

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

    导致子类膨胀:在我们的例子中子类的无参构造函数不能省略,原因是父类的无参构造函数抛出了IOException异常,子类的无参构造函数默认调用的是父类的构造函数,所以子类无参构造函数也必须抛出IOException或其父类。

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

    public static void main(String[] args) {

    try {

    Base base = new Base();

    } catch (Exception e) {   

    e.printStackTrace();

    }

    }

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

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

    //父类

    class Base {

    //父类方法抛出Exception

    public void testMethod() throws Exception {

    }

    }

    //子类

    class Sub extends Base {

    //父类方法抛出Exception

    @Override

    public void testMethod() throws IOException {

    }

    }

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

    3.子类构造函数扩展受限:子类存在的原因就是期望实现扩展父类的逻辑,但父类构造函数抛出异常却会让子类构造函数的灵活性大大降低,例如我们期望这样的构造函数。

    //父类

    class Base {

    public Base() throws IOException{

    }

    }

    //子类

    class Sub extends Base {

    public Sub() throws Exception{

    try{

    super();

    }catch(IOException e){

    //异常处理后再抛出

    throw e;

    }finally{

    //收尾处理

    }

    }

    }

    很不幸,这段代码编译不通过,原因是构造函数Sub没有把super()放在第一句话中,想把父类的异常重新包装再抛出是不可行的(当然,这里有很多种“曲线”的实现手段,比如重新定义一个方法,然后父子类的构造函数都调用该方 法,那么子类构造函数就可以自由处理异常了),这是Java语法机制。

    将以上三种异常类型汇总起来,对于构造函数,错误只能抛出,这是程序人员无能为力的事情;非受检异常不要抛出,抛出了"对己对人"都是有害的;受检异常尽量不抛出,能用曲线的方式实现就用曲线方式实现,总之一句话: 在构造函数中尽可能不出现异常。

    昆明Java培训机构的老师提醒注意:在构造函数中不要抛出异常,尽量曲线实现。

    推荐文章

上一篇:Java Spring mvc操作Redis及Redis集群

下一篇:java程序:使用Throwable获得栈信息

最新开班日期  |  更多

Java--零基础全日制班

Java--零基础全日制班

开班日期:11/30

Java--零基础业余班

Java--零基础业余班

开班日期:11/30

Java--周末提升班

Java--周末提升班

开班日期:11/30

Java--零基础周末班

Java--零基础周末班

开班日期:11/30

  • 网址:http://km .java.tedu.cn      地址:昆明市官渡区春城路62号证券大厦附楼6楼
  • 课程培训电话:186 8716 1620      qq:2066486918    全国服务监督电话:400-827-0010
  • 服务邮箱 ts@tedu.cn
  • 2001-2016 达内国际公司(TARENA INTERNATIONAL,INC.) 版权所有 京ICP证08000853号-56