建议34: 构造函数尽量简化

我们知道在通过new关键字生成对象时必然会调用构造函数,构造函数的简繁情况会直接影响实例对象的创建是否繁琐。在项目开发中,我们一般都会制订构造函数尽量简单,尽可能不抛异常,尽量不做复杂算法等规范,那如果一个构造函数确实复杂了会怎么样?我们来看一段代码:

 public class Client {
public static void main(String[] args) {
Server s = new SimpleServer(1000);
}
} // 定义一个服务
abstract class Server {
public final static int DEFAULT_PORT = 40000; public Server() {
// 获得子类提供的端口号
int port = getPort();
System.out.println("端口号:" + port);
/* 进行监听动作 */
} // 由子类提供端口号,并做可用性检查
protected abstract int getPort();
} class SimpleServer extends Server {
private int port = 100; // 初始化传递一个端口号
public SimpleServer(int _port) {
port = _port;
} // 检查端口号是否有效,无效则使用默认端口,这里使用随机数模拟
@Override
protected int getPort() {
return Math.random() > 0.5 ? port : DEFAULT_PORT;
}
}

该代码是一个服务类的简单模拟程序,Server类实现了服务器的创建逻辑,子类只要在生成实例对象时传递一个端口号即可创建一个监听该端口的服务,该代码的意图如下:

通过SimpleServer的构造函数接收端口参数。

子类的构造函数默认调用父类的构造函数。

父类构造函数调用子类的getPort方法获得端口号。

父类构造函数建立端口监听机制。

对象创建完毕,服务监听启动,正常运行。

貌似很合理,再仔细看看代码,确实也和我们的意图相吻合,那我们尝试多次运行看看,输出结果要么是“端口号:40000”,要么是“端口号:0”,永远不会出现“端口号:100”或是“端口号:1000”,这就奇怪了,40000还好说,但那个0是怎么冒出来的呢?代码在什么地方出现问题了?

要解释这个问题,我们首先要说说子类是如何实例化的。子类实例化时,会首先初始化父类(注意这里是初始化,可不是生成父类对象),也就是初始化父类的变量,调用父类的构造函数,然后才会初始化子类的变量,调用子类自己的构造函数,最后生成一个实例对象。了解了相关知识,我们再来看上面的程序,其执行过程如下:

子类SimpleServer的构造函数接收int类型的参数:1000。

父类初始化常变量,也就是DEFAULT_PORT初始化,并设置为40000。

执行父类无参构造函数,也就是子类的有参构造中默认包含了super()方法。

父类无参构造函数执行到“int port = getPort()”方法,调用子类的getPort方法实现。

子类的getPort方法返回port值(注意,此时port变量还没有赋值,是0)或DEFAULT_PORT(此时已经是40000)了。

父类初始化完毕,开始初始化子类的实例变量,port赋值100。

执行子类构造函数,port被重新赋值为1000。

子类SimpleServer实例化结束,对象创建完毕。

终于清楚了,在类初始化时getPort方法返回的port值还没有赋值,port只是获得了默认初始值(int类的实例变量默认初始值是0),因此Server永远监听的是40000端口了(0端口是没有意义的)。这个问题的产生从浅处说是由类元素初始化顺序导致的,从深处说是因为构造函数太复杂而引起的。构造函数用作初始化变量,声明实例的上下文,这都是简单的实现,没有任何问题,但我们的例子却实现了一个复杂的逻辑,而这放在构造函数里就不合适了。

问题知道了,修改也很简单,把父类的无参构造函数中的所有实现都移动到一个叫做start的方法中,将SimpleServer类初始化完毕,再调用其start方法即可实现服务器的启动工作,简洁而又直观,这也是大部分JEE服务器的实现方式。

注意 构造函数简化,再简化,应该达到“一眼洞穿”的境界。

[改善Java代码]构造函数尽量简化的更多相关文章

  1. [改善Java代码]不要在构造函数中抛出异常

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

  2. [改善Java代码]构造代码块会想你所想

    建议37: 构造代码块会想你所想 镜像博文:http://www.cnblogs.com/DreamDrive/p/5413408.html http://www.cnblogs.com/DreamD ...

  3. [改善Java代码]使用构造块精炼程序

    建议36: 使用构造代码块精炼程序 什么叫代码块(Code Block)?用大括号把多行代码封装在一起,形成一个独立的数据体,实现特定算法的代码集合即为代码块,一般来说代码块是不能单独运行的,必须要有 ...

  4. [改善Java代码]易变业务使用脚本语言编写

    建议16: 易变业务使用脚本语言编写 Java世界一直在遭受着异种语言的入侵,比如PHP.Ruby.Groovy.JavaScript等,这些“入侵者”都有一个共同特征:全是同一类语言—脚本语言,它们 ...

  5. [改善Java代码]建议40:匿名类的构造函数很特殊

    建议40: 匿名类的构造函数很特殊 在上一个建议中我们讲到匿名类虽然没有名字,但可以有一个初始化块来充当构造函数,那这个构造函数是否就和普通的构造函数完全一样呢?我们来看一个例子,设计一个计算器,进行 ...

  6. [改善Java代码]使用匿名类的构造函数

    建议39: 使用匿名类的构造函数 阅读如下代码,看看是否可以编译: public class Client { public static void main(String[] args) { Lis ...

  7. [改善Java代码]使用构造函数协助描述枚举项

    一.分析 一般来说,我们经常使用的枚举项只有一个属性,即排序号,其默认值是从0.1.2... ....但是除了排序号外,枚举还有一个(或多个)属性:枚举描述,它的含义是通过枚举的构造函数,声明每个枚举 ...

  8. [改善Java代码]避免在构造函数中初始化其他类

    建议35: 避免在构造函数中初始化其他类 构造函数是一个类初始化必须执行的代码,它决定着类的初始化效率,如果构造函数比较复杂,而且还关联了其他类,则可能产生意想不到的问题,我们来看如下代码: publ ...

  9. [改善Java代码]适时选择不同的线程池来实现

    Java的线程池实现从最根本上来说只有两个:ThreadPoolExecutor类和ScheduledThreadPoolExecutor类,这两个类还是父子关系,但是Java为了简化并行计算,还提供 ...

随机推荐

  1. iOS本地数据存取

    应用沙盒 1)每个iOS应用都有自己的应用沙盒(应用沙盒就是文件系统目录),与其他文件系统隔离.应用必须待在自己的沙盒里,其他应用不能访问该沙盒 2)应用沙盒的文件系统目录,如下图所示(假设应用的名称 ...

  2. jquery easyui的扩展验证

    1.扩展通过$.extends($.fn.validatebox.defaults.rules,)扩展 $.extend( $.fn.validatebox.defaults.rules, { idc ...

  3. ASP.NET MVC中Session以及处理方式

    转载原地址 http://www.cnblogs.com/darrenji/p/3951065.html

  4. HDU 5726 GCD (RMQ + 二分)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5726 给你n个数,q个询问,每个询问问你有多少对l r的gcd(a[l] , ... , a[r]) ...

  5. FPGA高级设计——时序分析和收敛(转)

    何谓静态时序分析(Static Timing Analysis,简称STA)? 它可以简单的定义为:设计者提出一些特定的时序要求(或者说是添加特定的时序约束),套用特定的时序模型,针对特定的电路进行分 ...

  6. ecshop支持手机号码登录、邮箱登录

    修改 User.php  文件找到: if ($user->login($username, $password,isset($_POST['remember']))) 在它上边增加一段我们所要 ...

  7. copy and Xcopy 复制文件到另一地址

    1. copy不能复制文件夹下的文件,而Xcopy可以. 2. Xcopy不能连接文件,而copy可以连接文件 eg : 将A盘中的A.TXT和B.TXT文件连接起来,连接后的文件名为C.TXT    ...

  8. 关于配置php源代码管理环境的几点注意

    1.如果你的项目原本就是utf-8的编码,而你设置eclipse的工作空间的默认编码为utf-8后,或者在项目文件夹上右键属性设置了编码类型后依旧没有效果,而是需要在每一个文件上右键属性设置为utf- ...

  9. char指针

    1.在C语言中,没有字符串类型,因此使用char指针表示字符串. 2.那么问题来了,使用char* 表示字符串,到哪里是结尾呢?因此需要一个特殊的字符作为哨兵,类似迭代器中的end(),这个哨兵就是' ...

  10. Codeforces Gym 100513I I. Sale in GameStore 暴力

    I. Sale in GameStore Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100513/p ...