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

本条主要讲的是一些反面教材,希望大家引以为鉴。

①无意中使用自动装箱导致多创建对象。

public class Sum {
public static void main(String[] args) {
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println(sum);
}
}

sum被声明为Long而不是long,意味着每次i都要被自动装箱(创建Long实例),因此速度会慢很多。

②可以重用不会被修改的可变对象。

也就是说,有些看起来是变量,但是实际上它的值一旦计算出来之后就不再变化,这种也应该视作常量,而不是每次都计算。

下面先看一段代码。

public class Person {
//生日
private final Date birthDate; public Person(Date birthDate) {
this.birthDate = new Date(birthDate.getTime());
} public boolean isBabyBoomer() {
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方法,根据生日判断是否属于婴儿潮时间。看起来并没有什么问题。

但实际上,每次调用这个方法时,都会新建一个Calendar,一个TimeZone和两个Date实例,这是没有必要的(所以,在平时写方法时也要注意,有很多对象是可以提取出来的)。下面看如何改进。

public class Person {
private final Date birthDate; public Person(Date birthDate) {
this.birthDate = new Date(birthDate.getTime());
}
private static final Date BOOM_START;
private static final Date BOOM_END; static {
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_START = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_END = gmtCal.getTime();
} public boolean isBabyBoomer() {
return birthDate.compareTo(BOOM_START) >= 0
&& birthDate.compareTo(BOOM_END) < 0;
}
}

改进之后Person类仅在初始化时创建了实例,后面调用isBabyBoomer方法时,就没有生成对象的消耗了。

第6条:消除过期的对象引用

下面的例子对java中的栈进行一个简单的模拟,看起来逻辑上没什么问题,却隐藏着一个问题。

public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
} public void push(Object e) {
ensureCapacity();
elements[size++] = e;
} public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
//如果没有这一步,会产生很大的问题
//elements[size] = null;
return result;
} private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}

问题就在于,当栈先增长,然后再收缩。从栈中弹出来的对象并不会被当做垃圾回收,这是因为栈内还维护着这些对象的引用。弹出之后,使用栈的程序不再需要它,只能人为清理掉。一般而言,只要类是自己管理内存,程序员就应该警惕内存泄漏问题。

文中还还提到在使用回调时,确保回调立即被当作垃圾回收的最佳方法时只保存它们的弱引用,例如,只将它们保存成WeakHashMap中的键(这个平时很少用到,只作为了解)。

弱引用与强引用的区别在于,即使内存不足,强引用也不会被回收掉。而弱引用不管内存足不足,一旦被垃圾回收线程发现,就会被回收掉。

Effective Java2读书笔记-创建和销毁对象(三)的更多相关文章

  1. Effective Java2读书笔记-创建和销毁对象(一)

    第1条:考虑用静态工厂方法代替构造器 通常情况下,我们创建一个对象采取new的形式,但是还有一种方法也是经常使用到的,它的名称叫做静态工厂方法. 例如,java中基本类型boolean的包装类Bool ...

  2. Effective Java2读书笔记-创建和销毁对象(四)

    第7条:避免使用终结方法 这一条讲的简直是不知所云.先简单记下来其中说出的几条: ①显式终止方法的典型例子有InputStream.OutputStream和java.sql.Connection上的 ...

  3. Effective Java2读书笔记-创建和销毁对象(二)

    第3条:用私有构造器或者枚举类型强化Singleton属性 这一条,总体来说,就是讲了一个小技巧,将构造器声明为private,可以实现单例.具体有以下几种实现的方式. ①最传统的单例实现模式,可能有 ...

  4. Effective Java读书笔记--创建和销毁对象

    1.优先考虑用静态工厂方法代替构造器2.遇到多个构造器参数时要考虑使用构建器Builder解决参数过多,不可变类型.私有构造方法,静态类的构造方法提供必要参数,剩下可选.new xxx.build() ...

  5. Effective Java学习笔记--创建和销毁对象

    创建和销毁对象 一.静态工厂方法代替构造器 静态工厂方法的优缺点 优点: 1.可以自定义名称(可以将功能表述的更加清晰) 2.不必每次调用都创建新的对象(同一对象重复使用) 3.返回的类型可以是原返回 ...

  6. Effective Java2读书笔记-类和接口(三)

    第17条:要么为继承而设计,并提供文档说明,要么就禁止继承 第18条:接口优于抽象类 这两条中,提到了一个很重要的概念骨架实现.也就是说,抽象类实现接口的形式.这样的好处是,接口本来不能提供默认的实现 ...

  7. [Effective Java]第二章 创建和销毁对象

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  8. Effective Java2读书笔记-对于所有对象都通用的方法(二)

    第10条:始终要覆盖toString 这一条没什么好讲的,就是说默认的toString方法打印出来的是类名+@+十六进制哈希码的值.我们应该覆盖它,使它能够展示出一些更为详细清晰的信息,这个看实际情况 ...

  9. Effective Java2读书笔记-对于所有对象都通用的方法(一)

    第8条:覆盖equals时请遵守通用约定 ①约定的内容 自反性.对于任何非null的引用值x.x.equals(x)必须返回true. 对称性.对于任何非null的引用值x和y.当且仅当y.equal ...

随机推荐

  1. LibSvm介绍---调用方法及参数介绍

        libsvm是著名的SVM开源组件,目前有JAVA.C/C++,.NET 等多个版本,本人使用的是2.82 libsvm命名空间下主要使用类: svm_model 为模型类,通过训练或加载训练 ...

  2. MyEclipse8.5自动生成注册码

    package com; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamRe ...

  3. ng-repeat-start ng-repeat-end 的使用

    ng-repeat-start与ng-repeat-end时AngularJS(1.2.x)扩展的, 使用这两个指令可以灵活控制遍历形式. 例如: index.html <div class=& ...

  4. DHTMLEdit

    DHTMLEdit   POP3研究了一会,发现如果要写一个类似FOXMAIL的东西,还需要解决一个编辑的问题.以为邮件是支持HTML编辑的. 网上查了一会发现可以使用WINDOWS自带的控件:DHT ...

  5. JS获取按下的键盘字符

    <html> <head> KeyPress Test!<hr> <script language="javascript"> fu ...

  6. poj 2342 Anniversary party_经典树状dp

    题意:Ural大学有n个职员,1~N编号,他们有从属关系,就是说他们关系就像一棵树,父节点就是子节点的直接上司,每个职员有一个快乐指数,现在要开会,职员和职员的直接上司不能同时开会,问怎才能使开会的快 ...

  7. Eclipse使用之杂七杂八

    使用Eclipse的Marketplace安装1.12x版本的Subclipse插件(SVN)后,新建SVN资源库时出现如下报错: “Can't create session svn: Unable ...

  8. xagrs

    find /tmp/ -name "*.log" -mtime +4 | xargs -i -t mv {} /home/ find /tmp/ -name "*.log ...

  9. LVM(1)

    DM: DM: Device Mapper    逻辑设备        RAID, LVM2        DM: LVM2    快照    多路径

  10. 解决一个maven在eclipse中M2_HOME不能调整的问题

    在坚持了许久之后,依然还是没能抵住maven的各种攻击,终于从0开始maven. 开始由于有关小项目,时间紧任务重,没仔细研究maven,便匆匆上手了,导致maven库的位置放在了c盘当前用户的数据目 ...