建议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. App启动加载广告页面思路

    需求 很多app(如淘宝.美团等)在启动图加载完毕后,还会显示几秒的广告,一般都有个跳过按钮可以跳过这个广告,有的app在点击广告页之后还会进入一个广告页面,点击返回进入首页.今天我们就来开发一个广告 ...

  2. TypeScript学习笔记(三):类

    类 在TypeScript中,类似于C#的结构,即一个文件中可以存在多个类,且文件名可以任意取,我们先看一个简单的类的示例. class Person { private name: string; ...

  3. Http通讯协议在.net下的实现方法

    1.HttpwebRequest and  HttpWebResponse 2.客户端访问服务端的API:HttpClient 3. .net下的Remoting 4.Web Services 5.W ...

  4. 离散信号MATLAB频谱分析程序

    from http://blog.csdn.net/u012129372/article/details/26565611 %FFT变换,获得采样数据基本信息,时域图,频域图 %这里的向量都用行向量, ...

  5. "file:///" file 协议

    [问题] 在WLW中拖入本地图片文件,然后调试过程中,选中对应图片,看到获得的对应的html源码中,图片地址是这样的: href="file:///C:/Users/CLi/AppData/ ...

  6. .Net转前端

    坚持 OR 方向 转前端,去折腾CSS JS 各种神奇的移动端框架: Web App H5 前端 前端工程师=javascript+N种技能,即一专多长: JavaScript 世界,Node.js, ...

  7. Zabbix3.0完整部署

    0. 准备工作 0.1 初始化 #!/bin/sh yum clean all systemctl stop firewalld.service systemctl disable firewalld ...

  8. CentOS6.5安装图形界面

    转载自http://www.cnblogs.com/zydev/p/5128788.html 一.使用网络安装(如果网络比较快,这个方法简单) yum groupinstall "Deskt ...

  9. contiki-main.c 中的process系列函数学习笔记 <contiki学习笔记之六>

    说明:本文依然依赖于 contiki/platform/native/contiki-main.c 文件. ---------------------------------------------- ...

  10. Codeforces Gym 100002 C "Cricket Field" 暴力

    "Cricket Field" Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/1000 ...