Java - 避免不必要的对象
通常,我们更喜欢重用一个对象而不是重新创建一个。
如果对象是不可变的,它就始终可以被重用。
下面是一个反面例子:
String s = new String("stringette");
该语句每次执行时都创建一个新的实例。
String构造器中的参数"stringette"本身是一个实例,功能方面等同于那些通过构造器创建的对象。
如果这种语句放到循环里,效果会变得更糟。
于是我们只需要:
String s = "stringette";
这样就永远是同一个string实例,并且可以保证同一个VM中会重用相同字符串字面量的对象。
(the object will be reused by any other code running in the same vitual machine that happens to contain the same string literal.)
如果类中同时提供了静态工厂方法和构造器,客户端通常可以使用静态工厂方法来防止创建不必要的对象。
比如java.lang.Boolean:
public static final Boolean TRUE = new Boolean(true); public static final Boolean FALSE = new Boolean(false); public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
而在构造器的javadoc中也做了相应的说明:
/**
* Allocates a {@code Boolean} object representing the
* {@code value} argument.
*
* <p><b>Note: It is rarely appropriate to use this constructor.
* Unless a <i>new</i> instance is required, the static factory
* {@link #valueOf(boolean)} is generally a better choice. It is
* likely to yield significantly better space and time performance.</b>
*
* @param value the value of the {@code Boolean}.
*/
public Boolean(boolean value) {
this.value = value;
}
除了重用不可变的对象,我们也可以重用那些不会再有变化的可变对象。
(Also use mutable object if you know they won't be modified.)
下面是一个反面例子,检查某人是否出生于生育高峰期(1946~1964):
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone; public class Person {
private final Date birthDate; public Person(Date birthDate) {
// Defensive copy - see Item 39
this.birthDate = new Date(birthDate.getTime());
} // Other fields, methods 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;
}
}
一次Calendar.getInstance和两次getTime,这些都会创建新的实例:
public static Calendar getInstance(TimeZone zone,Locale aLocale){
return createCalendar(zone, aLocale);
} private static Calendar createCalendar(TimeZone zone,Locale aLocale){
Calendar cal = null; String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype == null) {
// Calendar type is not specified.
// If the specified locale is a Thai locale,
// returns a BuddhistCalendar instance.
if ("th".equals(aLocale.getLanguage())
&& ("TH".equals(aLocale.getCountry()))) {
cal = new BuddhistCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
} else if (caltype.equals("japanese")) {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else if (caltype.equals("buddhist")) {
cal = new BuddhistCalendar(zone, aLocale);
} else {
// Unsupported calendar type.
// Use Gregorian calendar as a fallback.
cal = new GregorianCalendar(zone, aLocale);
} return cal;
} public final Date getTime() {
return new Date(getTimeInMillis());
}
于是我们做一下改进,将对象声明为private static final,并在静态初始化块中把需要的实例都准备好:
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone; class Person {
private final Date birthDate; public Person(Date birthDate) {
// Defensive copy - see Item 39
this.birthDate = new Date(birthDate.getTime());
} // Other fields, methods /**
* The starting and ending dates of the baby boom.
*/
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;
}
}
这样不仅提高了效率,而且更加明确。
但是,我们还可以再改进一些。
如果上面的例子中的isBabyBoomer没有被调用过,那我们在静态初始化块中创建的实例就白白浪费了(可能例子中的对象太少了,多了就爽了)。
对于这个问题,我们可以用延迟初始化(lazily initializing)来解决,也就是在isBabyBoomer被第一次调用时进行初始化。
但是作者并不建议这样做,因为这可能只会让方法实现变得更复杂,而没有显著提高效率。
上面的例子中我们把重用的对象在实例化以后不会再有变化,那么能不能重用可变对象?
比如Map.keySet方法返回的是同一个Set实例,这个Set实例的状态依赖于其backing Object,即Map实例。
当Map发生变化时Set也跟着变化就可以了。
虽然每次调用时重新创建一个Set实例也是可行的,但实在没必要。
另外,autoboxing这个特性也为用户提供了创建不必要的对象的新方法。
试试基本类型和封装类型混用,并让他们超出封装类型的缓存范围。
比如这样做:
public class Sum {
// Hideously slow program! Can you spot the object creation?
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,造成了巨大的浪费。
(prefer primitives to boxed primitives,and watch out for unintentional autoboxing.)
Java - 避免不必要的对象的更多相关文章
- JAVA基础知识之IO——对象序列化
对象序列化 Java对象序列化(Serialize)是指将Java对象写入IO流,反序列化(Deserilize)则是从IO流中恢复该Java对象. 对象序列化将程序运行时内存中的对象以字节码的方式保 ...
- 转!! Java中如何遍历Map对象的4种方法
在Java中如何遍历Map对象 How to Iterate Over a Map in Java 在java中遍历Map有不少的方法.我们看一下最常用的方法及其优缺点. 既然java中的所有map都 ...
- java根据url获取json对象
package test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; ...
- java中基本类型封装对象所占内存的大小(转)
这是一个程序,java中没有现成的sizeof的实现,原因主要是java中的基本数据类型的大小都是固定的,所以看上去没有必要用sizeof这个关键字. 实现的想法是这样的:java.lang.Runt ...
- 【转】Java中如何遍历Map对象的4种方法
原文网址:http://blog.csdn.net/tjcyjd/article/details/11111401 在Java中如何遍历Map对象 How to Iterate Over a Map ...
- Java反射获取类和对象信息全解析
反射可以解决在编译时无法预知对象和类是属于那个类的,要根据程序运行时的信息才能知道该对象和类的信息的问题. 在两个人协作开发时,你只要知道对方的类名就可以进行初步的开发了. 获取类对象 Class.f ...
- java中将list、map对象写入文件
链接地址:http://blog.sina.com.cn/s/blog_4a4f9fb50101p6jv.html 推荐:凤爪女瓜子男怪象该谁反思伦敦房价为什么持续暴涨 × wvqusrtg个 ...
- java基础面向对象之类与对象
java基础面向对象之类与对象 2017-01-14 1.面向对象的基本概念 以一种组建化的形式进行代码设计 1)在面向对象程序设计中包含有如下几种特性 •封装性:保护内部结构的安全性 •继承性:在已 ...
- Java 1.0 类与对象
1.Java中main()的作用: a.测试真正的类 b.启动Java应用程序 2. Java程序只会让对象与对象交互 3.创建对象时存储在堆中,自动回收. 4.Java无全局变量 5.Java程序由 ...
- Java 反射 分析类和对象
Java 反射 分析类和对象 @author ixenos 摘要:优化程序启动策略.在运行时使用反射分析类的结构和对象 优化程序启动策略 在启动时,包含main方法的类被加载.它会加载所有它需要的类. ...
随机推荐
- 为什么程序员都不喜欢使用switch而使用if来做条件跳转
请用5秒钟的时间查看下面的代码是否存在bug. OK,熟练的程序猿应该已经发现Bug所在了,在第8行和第10行下面我没有添加关键字break; 这就导致这段代码的行为逻辑与我的设计初衷不符了. 缺 ...
- kali linux之msf客户端渗透
在无法通过网络边界的情况下转而攻击客户端----进行社会工程学攻击,进而渗透线上业务网络 含有漏洞利用代码的web站点--利用客户端漏洞 含有漏洞利用代码的doc,pdf等文档----诱使被害者执行p ...
- 基于DALN方案C/S架构运用
今天闲来无事,看到笔记本的蓝牙设备想着:可不可以实现电脑端播放手机端的音频. 刚刚开始想着基于蓝牙,尝试多次无解(主要原因是没有找到支持此功能的软件) 最后:有朋友建议可以研究下DALN方案解决这个需 ...
- ArchLinux 下 virtualbox 报错 libQtCore.so.4: cannot open shared object file
VirtualBox: supR3HardenedMainGetTrustedMain: dlopen("/usr/lib/virtualbox/VirtualBox.so",) ...
- android开发如何获取res/raw和assets文件夹的路径
---恢复内容开始--- android开发如何获取res/raw和assets文件夹的路径,主要分为两种情况: 1.如果你只是拷贝动作,那么你只需要得到res/raw和assets文件输入流就可以, ...
- java内存模型(jMM)(二)
volatile关键字 volatile是一个类型修饰符(type specifier),就像大家更熟悉的const一样,它是被设计用来修饰被不同线程访问和修改的变量.volatile的作用是作为指令 ...
- openstack 的horizon的结构
openstack的控制台应用horizon 特点: 1.界面的显示元素进行深度封装: 将元素的后台与前台模板结合在一起.制作html整个界面有点类似搭积木组合的方式. 2.模块加载: 根据权限对ur ...
- Bootrap 项目实战(微金所前端首页)第一部分
微金所前端首页成果图:(这是本人自己按照微金所官网首页,采用Bootrap,JS,JQuery,css制作的网页效果图,在第二部分我会公布网页源代码) 如需网页源代码,请在下方留言,备注你的qq邮箱. ...
- Rstudio常用且不熟快捷键 “原版+中文” 整理
- 【算法笔记】B1049 数列的片段和
1049 数列的片段和 (20 分) 给定一个正数数列,我们可以从中截取任意的连续的几个数,称为片段.例如,给定数列 { 0.1, 0.2, 0.3, 0.4 },我们有 (0.1) (0.1, ...