Java的异常机制有三种:java
一.Error类以及其子类表示的是错误,它是不须要程序员处理也不能处理的异常.好比VirtualMachineError虚拟机错误,ThreadDeath线程僵尸等.程序员
二.RuntimeException类及其子类表示的是非受检查异常,是系统可能会抛出的异常,程序员能够去处理,也能够不去处理,最经典的就是NullPointerException空指针异常和IndexOutOfBoundsException越界异常.数据库
三.Exception类及其子类(不包含非受检查异常)表示的是受检异常,这是程序员必须处理的异常,不处理则程序不能经过编译,好比IOException表示I/O异常,SQLException表示数据库访问异常.ide
咱们知道 一个对象的建立,须要通过内存分配,静态代码初始化,构造函数执行等过程,对象生成的关键步骤是构造函数,那是否是容许在构造函数中抛出异常呢?从Java语法上来讲,彻底能够在构造函数中抛出异常,三类 异常均可以,函数
可是从系统设计和开发的角度来分析,则尽可能不要在构造函数中抛出异常,咱们以三种不一样类型的异常来讲明:spa
(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类.code
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覆写的要求.构造函数之因此与此相反,是由于构造函数没有覆写的概念,只是构造函数间的引用调用而已,因此在构造函数中抛出受检查异常会违背里氏替换原则,使咱们的程序缺少灵活性.
③子类构造函数扩展有限
以上汇总起来就是:非受检查异常不要抛出,抛出了对人对己都是有害的...受检查异常尽可能不抛出.总之一句话:在构造函数中尽量的不出现异常.