[改善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块才能解决.
import java.io.IOException; public class Client {
public static void main(String[] args) {
try{
Base base = new Base();
//Base base = new Sub();这样是编译不通过的,因为Sub的构造抛出Exception,它比父类的构造函数
//抛出的异常范围要宽,必须增加新的catch块才能解决.
}catch(IOException e){
//异常处理
}
}
} class Base{
//父类抛出IOException
public Base() throws IOException{
throw new IOException();
}
} class Sub extends Base{
//子类抛出Exception异常
public Sub() throws Exception { }
}
可能读者会问,为什么Java的构造函数允许子类的构造函数抛出更广泛的异常类呢?这正好与类方法的异常机制相反,类方法的异常是这样要求的:
class Base{ //父类方法抛出Exception
public void method() throws Exception{ }
} class Sub extends Base{
//子类方法的异常类型必须是覆写方法的子类型
@Override
public void method() throws IOException{ }
}
子类方法可以抛出多个异常,但是都必须是覆写方法的子类型,对我们的例子来说,Sub类的method方法抛出的异常必须是Exception的子类或者Exception类,这是Java覆写的要求.构造函数之所以与此相反,是因为构造函数没有覆写的概念,只是构造函数间的引用调用而已,所以在构造函数中抛出受检查异常会违背里氏替换原则,使我们的程序缺乏灵活性.
③子类构造函数扩展有限
以上汇总起来就是:非受检查异常不要抛出,抛出了对人对己都是有害的...受检查异常尽量不抛出.总之一句话:在构造函数中尽可能的不出现异常.
[改善Java代码]不要在构造函数中抛出异常的更多相关文章
- [改善Java代码]避免在构造函数中初始化其他类
建议35: 避免在构造函数中初始化其他类 构造函数是一个类初始化必须执行的代码,它决定着类的初始化效率,如果构造函数比较复杂,而且还关联了其他类,则可能产生意想不到的问题,我们来看如下代码: publ ...
- [改善Java代码] 提倡异常的封装
JavaAPI提供的异常都是比较低级别的,低级别是指只有开发人员才能看懂的异常.而对于终端用户来说基本上就是天书,与业务无关,是纯计算机语言的描述. 异常封装的三方面的好处: 1)提高系统的友好性 ...
- [改善Java代码]异常只为异常服务
异常原本是正常逻辑的补充,但是有时候会被当做主逻辑使用.看如下代码: public class Client { enum Color { Red, Blue; } public static voi ...
- [改善Java代码]构造代码块会想你所想
建议37: 构造代码块会想你所想 镜像博文:http://www.cnblogs.com/DreamDrive/p/5413408.html http://www.cnblogs.com/DreamD ...
- [改善Java代码]使用构造块精炼程序
建议36: 使用构造代码块精炼程序 什么叫代码块(Code Block)?用大括号把多行代码封装在一起,形成一个独立的数据体,实现特定算法的代码集合即为代码块,一般来说代码块是不能单独运行的,必须要有 ...
- [改善Java代码]不要让四舍五入亏了一方
建议25: 不要让四舍五入亏了一方 本建议还是来重温一个小学数学问题:四舍五入.四舍五入是一种近似精确的计算方法,在Java 5之前,我们一般是通过使用Math.round来获得指定精度的整数或小数的 ...
- [改善Java代码]易变业务使用脚本语言编写
建议16: 易变业务使用脚本语言编写 Java世界一直在遭受着异种语言的入侵,比如PHP.Ruby.Groovy.JavaScript等,这些“入侵者”都有一个共同特征:全是同一类语言—脚本语言,它们 ...
- 【HBase】通过Java代码实现HBase数据库中数据的增删改查
目录 创建maven工程,导入jar包 java代码实现创建hbase表 java代码实现向hbase表中插入数据 java代码查询hbase数据 使用rowKey查询指定列族指定列的值 通过star ...
- [改善Java代码]推荐使用String直接量赋值
建议52:推荐使用String直接量赋值 一.建议 String对象的生成方式有两种: 1.通过new关键字生成,String str3 = new String(“中国”); 2.直接声明,如:St ...
随机推荐
- Yii CModel中rules验证规则
array( array(‘username’, ‘required’), array(‘username’, ‘length’, ‘min’=>3, ‘max’=>12), array( ...
- UVALive 7279 Sheldon Numbers (暴力打表)
Sheldon Numbers 题目链接: http://acm.hust.edu.cn/vjudge/contest/127406#problem/H Description According t ...
- CodeForces 682C Alyona and the Tree (树+dfs)
Alyona and the Tree 题目链接: http://acm.hust.edu.cn/vjudge/contest/121333#problem/C Description Alyona ...
- Spring MVC ControllerClassNameHandlerMapping example
handler mapping是把url跟控制器关联起来. In Spring MVC, ControllerClassNameHandlerMapping use convention to map ...
- Spring SimpleJdbcTemplate batchUpdate() example
In this tutorial, we show you how to use batchUpdate() in SimpleJdbcTemplate class. See batchUpdate( ...
- zoj 1610 Count the Colors
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=610 Count the Colors Time Limit:2000MS ...
- ASP.NET网站如何显示自己的网页图标
转载自 http://www.webtag123.com/dotnet/17238.html 1. 直接放个ico图标到你网站的根目录,并命名为favicon.ico就可以了.favicon.ico应 ...
- HTML5几种常见的错误写法
本文介绍了HTML5常见的6种错误写法,包括:1.不要使用section作为div的替代品 2.只在需要的时候使用header和hgroup 3.不要把所有列表式的链接放在nav里 4.figure元 ...
- Unity3D之Mecanim动画系统学习笔记(十):Mecanim动画的资源加载相关
资源加载是必备的知识点,这里就说说Mecanim动画的资源如何打包及加载. 注意,Unity4.x和Unity5.x的AssetBundle打包策略不一样,本笔记是基于Unity4.x的AssetBu ...
- ASP.NET MVC- 数据验证机制
ASP.NET MVC的数据验证机制,比起ASP.NET WEBFORM那种高效很多.下面记录以下两个示例,以便日后方便查阅. 方式一:在Controller里通过AddModelError方法返回错 ...