`
guitar427
  • 浏览: 4558 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

第5条--避免创建不必要的对象

 
阅读更多

先说几句题外话,重新看这本书,真的是有很多新的收获,而且明显地感觉比第一次学习时轻松很多,所以永远不要放弃学习,即使过程再艰难再晦涩,只要坚持去做去学总会有提升,而一点一点的提升最终就会产生一个巨大的质变,再回头去看的时候就会惊讶地发现其实自己已经走了很远。对于初学者来说,仔细读JDK的源码和一些优秀框架的源码对于编程能力的提高是非常有帮助的,先去读一些常用接口和类的源码,比如java.lang、java.util、java.io包下面的一些东西,学习java访问数据库时候可以参考java.sql包,学习网络编程可以参考java.net包去看,学习并发编程可以再去研究java.util.concurrent包,学习算法和设计模式等知识时也可以拿jdk甚至junit、spring这些优秀的设计来当例子看。我想在EffectiveJava的学习记录之后继续写几个源码解读的系列,希望对初学者有所帮助。

好了,进入正题吧,这一条我想把作者的讲解顺序做一个调整,我觉得这样大家会快速理解这一条的中心思想。
避免创建不必要的对象,不是说尽量少地创建对象,也不是说重用对象一定是好的选择,而是避免创建那些不需要创建的对象。
实际上,小对象的创建和回收是非常廉价的,通过创建小对象来提升程序的逻辑性、简洁性和功能,这通常是件好事;而通过维护对象池避免重复创建对象的做法只在极少的情况下是被鼓励的,除非创建对象的开销非常大,例如创建数据库连接这样的操作。另外,在某些情况下是必须创建新对象的,这和后面“保护性拷贝”所讲的内容相对应。在需要使用保护性拷贝的时候,复用对象会造成安全隐患。
那究竟哪些对象是不必要创建的呢?
我们来看一个极端反面的例子:
String s = new String("string");
很多公司的笔试题都会举这个例子,"string"本身就是一个实例,new String又会创建一个String的实例。String的域都是final修饰的,所以String类型的实例一旦创建后就是不可变的,对String的操作而产生的实例都是新创建的实例,如果这种操作非常频繁的话尽量使用StringBuffer或者StringBuilder来代替。


再来看另外一个例子,有一个Person类,它有一个isBabyBoomer方法,用来检验这个人是否为一个"baby boomer(生育高峰期出生的小孩)",换句话说,就是检验这个人是否出生于1946至1964年间:

public class Person {
    private final Date birthDate;
    
    public Person(Date birthDate){
        this.birthDate = birthDate;
    }

    // Other fields, methods, and constructor omitted
    // DON'T DO THIS!
    public boolean isBabyBoomer() {
        // Unnecessary allocation of expensive object
        Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomStart = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomEnd = gmtCal.getTime();
        return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0;
    }
}

 
在isBabyBoomer中创建的两个Date对象虽然是可变的,但是在意义上是不可变的,所以完全没必要每次执行isBabyBoomer方法都创建对象,可以将boomStart和boomEnd作为常量在类的第一次实例化时初始化。

  上面的两个例子中的对象都是明显能够被重用的,还有些情况是不那么明显的,考虑适配器的情形。适配器是指这样一个对象:它把功能委托给一个后备对象(backing object),从而为后备对象提供一个可以替代的接口。由于适配器除了后备对象之外,没有其他的状态信息,所以针对某个给定对象的特定适配器而言,它不需要创建多个适配器实例。
  上面这段话不是太容易理解,我们来对照Map的keySet方法来分析,先来看看HashMap中keySet方法的实现:
   

public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }

    private final class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return newKeyIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            return HashMap.this.removeEntryForKey(o) != null;
        }
        public void clear() {
            HashMap.this.clear();
        }
    }

 
  我们发现keySet被实例化以后每次调用keySet返回的都是同一个KeySet实例,这里KeySet类就是一个适配器,它的所有方法只是对Map接口中方法的一种适配或者说是替代方案。所以,对于同一个Map来说,没必要创建多个KeySet的实例。
 
  JDK1.5中引入了一种新的机制,自动装箱(autoboxing),它允许基本类型和装箱类型的混用,按需要自动装箱和拆箱,对于这个机制,还是慎用为好,看看这个例子:
  

Long sum = 0L;
  for(long i=0; i<Integer.MAX_VALUE; i++){
    sum +=i;
  }

 
  这段程序只因sum的类型被设置成装箱类型Long,执行效率受到了很大的影响,当然这也是一个比较极端的例子。
  最后还是重复那句话,避免创建不必要的对象,不是说尽量少地创建对象,也不是说重用对象一定是好的选择,而是避免创建那些不需要创建的对象。而这里发现哪些是不需要创建的对象是重点。

分享到:
评论

相关推荐

    day020-继承加强和设计模式代码和笔记.rar

    控制创建对象的数量 =&gt; 创建对象通过new 调用构造方法 =&gt; 控制构造方法就能控制创建对象 控制调用构造方法 =&gt; 用private修饰 =&gt; 需要给外部提供一个对象 =&gt; 先在类中创建一个对象 (联想到封装) =&gt; ...

    易语言程序免安装版下载

    各参数声明要与其定义一致。  请参考易语言安装目录内的例程:samples\静态编译\调用LIB和OBJ 1. 静态编译方面的改进和优化。 2. 符号重命名程序(resym.exe)增加对.obj文件的支持,并更新了文档(sdk\static_docs...

    C++MFC教程

    +-- 第五章 对话框 |------ 5.1 使用资源编辑器编辑对话框 |------ 5.2 创建有模式对话框 |------ 5.3 创建无模式对话框 |------ 5.4 在对话框中进行消息映射 |------ 5.5 在对话框中进行数据交换和数据检查 |------...

    ExtAspNet v2.2.1 (2009-4-1) 值得一看

    -这将会影响所有的aspx页面,一定要将工具条(Toolbars)和Items区分开来。 -祝你生日快乐 - 小师妹妹。 +2009-08-29 v2.0.8 -ExtAspNet支持多语言(en,zh_CN,zh_TW),可以在Web.config中修改。 -将所有的...

    数据库设计经验谈.pdf

    1 第 1 部分 - 设计数据库之前 3 第 2 部分 - 设计数据库表 3 第 3 部分 - 选择键 3 第 4 部分 - 保证数据完整性 3 第 5 部分 - 各种小技巧 3 第 1 部分 - 设计数据库之前 3 考察现有环境 3 定义标准的对象命名规范 ...

    SQL Server 2008数据库设计与实现

     4.1.2 避免编写不必要的代码  4.1.3 给表瘦身  4.1.4 最大化聚集索引的使用  4.1.5 降低每张表中索引的数量  4.2 规范化应该走多远  4.3 规范化过程  4.4 实体和属性的形式:第一范式  4.4.1 所有...

    effective-swift:阅读Effective Java 3E,了解编程中的习惯用法和有效用法,并提出在Swift中使用它的方法。

    避免不必要的对象创建 德玛 项目7 释放使用的对象参考 莉娜 项目8 避免使用终结剂和清洁剂 莉娜 项目9 使用try-with-resources而不是try-finally 林 第三章所有对象的通用方法 项目编号 标题 副标题 经理 项目...

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

    8.5.1 避免不必要的复制操作 389 8.5.2 应用rvalue引用形参 392 8.5.3 命名的对象是lvalue 394 8.6 类模板 399 8.6.1 定义类模板 400 8.6.2 根据类模板创建对象 402 8.6.3 使用有多个形参的类模板 405 8.6.4...

    Java的六大问题你都懂了吗

    同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都new一个String.例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做:  public class Demo {  private ...

    超级有影响力霸气的Java面试题大全文档

    当客户机第一次调用一个Stateful Session Bean 时,容器必须立即在服务器中创建一个新的Bean实例,并关联到客户机上,以后此客户机调用Stateful Session Bean 的方法时容器会把调用分派到与此客户机相关联的Bean实例...

    ExtAspNet_v2.3.2_dll

    -这将会影响所有的aspx页面,一定要将工具条(Toolbars)和Items区分开来。 -祝你生日快乐 - 小师妹妹。 +2009-08-29 v2.0.8 -ExtAspNet支持多语言(en,zh_CN,zh_TW),可以在Web.config中修改。 -将所有的...

    Oracle SQL高级编程(资深Oracle专家力作,OakTable团队推荐)--随书源代码

    第5章 关于问题 116 5.1 问出好的问题 116 5.2 提问的目的 117 5.3 问题的种类 117 5.4 关于问题的问题 119 5.5 关于数据的问题 121 5.6 建立逻辑表达式 126 5.7 小结 136 第6章 SQL执行计划 137 6.1 解释...

    java设计模式选择题复习题.doc

    工厂系列模式的优缺点: 让用户的代码和某个特定类的子类的代码解耦 用户不必知道它所使用的对象是怎样创建的,只需知道该对象有哪些方法 抽象工厂模式可以为用户创建一系列相关的对象,使用户和创建这些对象的类脱...

    支持多数据库的ORM框架ef-orm.zip

    阅读推荐:第10章 特点五,常用DDL操作的封装 从数据库元数据访问,到建表,创建约束,创建sequence等各种DDL操作进行了封装,用户无需编写各种SQL,可以直接通过API操作数据库结构。 尤其是ALTER TABLE等修改...

    亮剑.NET深入体验与实战精要2

    第5章 数据库开发 199 5.1 ADO.NET与抽水的故事 200 5.1.1 ADO.NET的定义 200 5.1.2 趣味理解ADO.NET对象模型 202 5.1.3 进水笼头——建立Connection 204 5.1.4 抽水机——Command 206 5.1.5 输水管——...

    亮剑.NET深入体验与实战精要3

    第5章 数据库开发 199 5.1 ADO.NET与抽水的故事 200 5.1.1 ADO.NET的定义 200 5.1.2 趣味理解ADO.NET对象模型 202 5.1.3 进水笼头——建立Connection 204 5.1.4 抽水机——Command 206 5.1.5 输水管——...

    JSTL详细标签库介绍

    要避免使用异常处理来代替错误处理,若这样就会降低程序的清晰性。&lt;BR&gt;3、 尽管可以使用异常处理来代替错误处理,但会降低性能&lt;BR&gt;4、 异常处理会占用程序的格外执行时间&lt;BR&gt;5、 异常处理能够提高程序的容错性&lt;BR&gt;6...

    入门学习Linux常用必会60个命令实例详解doc/txt

    在使用mount这个指令时,至少要先知道下列三种信息:要加载对象的文件系统类型、要加载对象的设备名称及要将设备加载到哪个目录下。 (1)Linux可以识别的文件系统 ◆ Windows 95/98常用的FAT 32文件系统:vfat ;...

    PHP培训教程之AJAX技术.docx

    2、使用Ajax的好处 1、通过异步模式,提升了用户体验 2、优化了浏览器和器之间的传输,减少不必要的数据往返,减少了带宽占用 3、Ajax引擎在客户端运行,承担了一部分本来由器承担的工作,从而减少了用户量下的器...

Global site tag (gtag.js) - Google Analytics