使用枚举类型强化Singleton属性
Singleton
指的是仅被实例化一次的类,即我们常说的单例.我们可以通过枚举类型来实现单例.1
2
3
4public enum Elvis{
INSTANCE;
public void leaveTheBuilding(){......)
}
这种方法是最简单的方法,而且它提供了序列化的机制,绝对防止多次实例化,避免通过反射利用无参构造器获取实例,这种方法可能是目前实现单例的最佳方法.
通过私有构造器强化不可实例化
有些类并不希望被实例化,例如Java内置的工具类,它们只提供一些静态的工具方法.实例化并无任何的意义,当这种类没有显示的构造器时,编译器会自动提供一个公有的无参构造器,这让就会有被实例化的危险了.为了保证不被实例化,可以提供一个私有的构造器
,这让就可以避免外部实例化该类.1
2
3
4
5
6public class UtilityClass{
private UtilityClass(){
throw new AssertionError();
}
// other method
}
上面的代码中在私有构造器中抛出的AssertionError
可以避免在类的内部被实例化.这种方法的缺点就是无法子类化.
避免创建不必要的对象
尽量的重用对象,而不是每次都创建新的对象.下面是一个反面例子:1
String s = new String("stringette");
这个语句每次执行都会创建一个新的String对象,而且作为参数的"stringette"
本身就是一个String实例,若把该语句放到一个循环当中,那将会创建出大量相同的对象.我们应该使用下面的语句代替:1
String s = "stringette";
这样只用了一个String实例,而且在同一台虚拟机运行,只要包含相同的字符串字面常量,对象都会被重用.对于同时提供构造器
和静态工厂方法
的不可变类,应该优先选择静态工厂方法
,避免创建不必要的对象.例如优先使用Boolean.valueOf(String)
而不是使用Boolean(String)
.
重用那些已知不会被修改的可变对象.下面我们举一个例子,我们先看反面的代码1
2
3
4
5
6
7
8
9
10
11
12public class Person{
private final Date birthDate;
public boolean isBabyBoomer(){
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946,Calendar.JANUARY,1,0,0,0);
Date boomStart = gtmCal.getTime();
gtmCal.set(1965,Calendar.JANUARY,1,0,0,0);
Date boomEnd = gtmCal.getTime();
return birthDate.compareTo(boomStart) >=0 && birthDate.compareTo(boomEnd) < 0;
}
}
上面的代码中,isBabyBoomer()
每次调用都会新建一个Calendar,一个TimeZone和两个Date实例,而这些实例的值都是确定的,不会被修改,这样的代码效率会大大的降低.下面我们用静态初始化块
对代码进行优化.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Person{
private final Date birthDate;
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 = gtmCal.getTime();
gtmCal.set(1965,Calendar.JANUARY,1,0,0,0);
BOOM_END = gtmCal.getTime();
}
public boolean isBabyBoomer(){
return birthDate.compareTo(BOOM_START) >=0 &&
birthDate.compareTo(BOOM_END) < 0;
}
}
改进后的代码,只在初始化时创建Calendar,TimeZone,和Date实例一次,大大的提高了效率.
优先使用基本数据类型,而不是相对应的装箱基本类型.因为创建对象的代价远远比使用基本数据类型的代价大.
这里提倡我们应该尽可能的避免创建对象,但是对于小对象的构造器只做少量的工作,而且会提示程序的清晰和简洁.通过维护现有的对象池而避免创建对象并不是一种好的做法,除法对象池中的对象是重量级的,而且维护对象池会导致代码难以阅读,有时还会损害性能.所有我们要通过自己的衡量来决定是否要创建对象.