一、创建和销毁对象
1、用静态工厂代替构造器
public static Service newInstance(String name) {
Provider p = providers.get(name);
if (p == null)
throw new IllegalArgumentException(
"No provider registered with name: " + name);
return p.newService();
}
不需要每次都新建一个对象,亦可从缓存中取
关键词:static newInstance
2、遇到多个构造器参数考虑构建器
public class Person {
private final String name;
private final int age;
private final String address;
private final String phone;
public static class Builder{
private final String name;
private final int age;
private String address = null;
private String phone = null;
public Builder(String name,int age){
this.name = name;
this.age = age;
}
public Builder address(String val){
address = val;
return this;
}
public Builder phone(String val){
phone = val;
return this;
}
public Person build(){
return new Person(this);
}
}
private Person(Builder builder){
this.name = builder.name;
this.age = builder.age;
this.address = builder.address;
this.phone = builder.phone;
}
@Override
public String toString() {
return "name:"+name+" age:"+age+" address:"+address+" phone:"+phone;
}
}
public class PersonTest {
public static void main(String[] args) {
Person p = new Person.Builder("tom", 18).address("深圳").phone("110").build();
System.out.println(p.toString());
}
}
关键词: 多个参数 、 Builder 、 new SpecialClass.Builder(“必须参数”).method(“可选参数”).build();
3、用私有构造器或枚举类型强化singleton属性
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {};
public static Elvis getInstance() {
return INSTANCE;
}
public void leaveTheBuilding() {};
}
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {};
}
关键词:私有构造器单例 、 枚举单例(支持序列化)
4、通过私有构造器强化不可实例化的能力
// Noninstantiable utility class
public class UtilityClass {
// Suppress default constructor for noninstantiability
private UtilityClass() {
throw new AssertionError();
}
}
关键词:工具类使用私有构造器强化不能被实例化
5、避免创建不必要的对象
①重用不可变对象,如Boolean.valueOf(boolean)
②重用已知不会修改的可变对象,如static静态块
public class Item5 {
private static long birthTimeMillis;
static {
Calendar calendar = Calendar.getInstance();
calendar.set(1978, Calendar.NOVEMBER, 10, 0, 0, 0);
birthTimeMillis = calendar.getTime().getTime();
}
public long getFoundedTimeMillisFromNow() {
return System.currentTimeMillis() - birthTimeMillis;
}
}
③自动装箱问题
//本代码因为把long改为Long,每次加操作都会生成一个Long对象,所以优先使用基本类型
Long sum = 0L;
long startTime = System.currentTimeMillis();
for (long i = 0; i <= Integer.MAX_VALUE; i++) {
sum += i;
}
6、消除过期的对象引用
解决预防内存泄露问题。
①过期引用。如jdk在实现栈时,pop操作后会执行:
elementData[elementCount] = null;
显示地把引用清空。
②缓存。只要缓存之外存在对某个项的键的引用,该项就有意义,那么就可以用WeakHashMap代替缓存;当缓存中的项过期之后,它们就会自动被删除。记住只有当所要的缓存项的生命周期是由该键的外部引用而不是由值决定时,WeakHashMap才有用处。
③监听器和其他回调。如果你实现了一个API,客户端在这个API注册回调,却没有显式地取消注册,那么除非你采取某些动作,否则他们就会积聚。确保回调立即被当做垃圾回收的最佳方法是只保存它们的弱引用(weak reference),例如只将它们保存成WeakHashMap中的键。
7、避免使用终结方法(finalizer)
- 所谓的终结方法其实是指finalize()。
- Java的垃圾回收机制只负责内存相关清理,其他资源的清理(释放文件、释放DB连接)需要程序员手动完成。
- 调用System.gc() 只是建议JVM执行垃圾回收(GC),但什么时候执行、是否要执行由JVM决定。
- 用户可以自己调用对象的finalize方法,但是这种调用是正常的方法调用,和对象的销毁过程无关。
- 如果一个类实现了finalize方法,那么当JVM执行垃圾回收的时候,该方法一定会被调用。
public class C {
public static void main(String[] args) {
A a = new A();
a.b = new B();
a = null;
System.gc();
}
}
class A {
B b;
public void finalize() {
System.out.println("method A.finalize at " + System.nanoTime());
}
}
class B {
public void finalize() {
System.out.println("method B.finalize at " + System.nanoTime());
}
}
6.finalize的执行过程。
当对象不可达时,GC会判断该对象是否覆盖了finalize方法,如没有覆盖则直接将其回收,否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完后,GC会再次判断该对象是否可达,若不可达则进行回收。否则对象“复活”。
7.jdk中代码例子。
为防止用户忘记关闭资源,JDK中FileInputStream类中覆盖了finalize方法:
/**
* Ensures that the <code>close</code> method of this file input stream is
* called when there are no more references to it.
*
* @exception IOException if an I/O error occurs.
* @see java.io.FileInputStream#close()
*/
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
close();
}
}
-
何时使用finalize
①尽量少用finalize,最好由系统管理,我们可以定义其他的方法来释放非内存资源。
②如果一定要用,那么可以参考FileInputStream类,在finalize检查费内存资源是否释放。