Effective Java 总结 (持续更新中)

创建和销毁对象

1. 考虑用静态工厂方法代替构造器

优势

  1. 它们有名称
  2. 不必在每次调用他们的时候都创建一个新对象
  3. 它们可以返回原返回类型的任何子类型的对象
interface Service {
	
}
interface Provider {
	Service newService();
}
class Test implements Provider {
	@Override
	public Service newService() {
		// TODO Auto-generated method stub
		return null;
	}
	
}
public class Services {
	private Services() {}
	
	private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
	private static final String DEFAULT_PROVIDER_NAME = "<def>";
	
	public static void registerDefaultProvider(Provider p) {
		registerProvider(DEFAULT_PROVIDER_NAME, p);
	}
	
	public static void registerProvider(String name, Provider p) {
		providers.put(name, p);
	}
	
	public static Service newInstance() {
		return newInstance(DEFAULT_PROVIDER_NAME);
	}
	
	public static Service newInstance(String name) {
		Provider p = providers.get(name);
		if(p == null) 
			throw new IllegalArgumentException("");
		return p.newService();
	}
}
  1. 在创建参数化类型实例的时候,它们使代码变得更简洁

缺点

  1. 类如果不含公有的或者受保护的构造器,就不能被子类化.
  2. 它们与其他的静态方法实际上没有任何区别.

静态工厂方法惯用名称

valueOf ------------ 类型转换方法
of --------------valueOf 简写
getInstance ------------ 返回唯一实例
newInstance ----------- 返回新的实例
getType ------------ 跟getInstance一样, 返回对象类型
newType -----------跟newInstance一样,返回新的对象类型

2. 遇到多个构造器参数时考虑用构造器

  • 重叠构造器模式可行,但是当有许多参数的时候,客户端代码很难编写,并且仍然较难以阅读
  • 如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是种不错的选择,特别是当大多数参数都是可选的时候
public class NutritionFacts {
	private final int servingSize;
	private final int servings;
	private final int calories;
	private final int cat;
	
	public static class Builder {
		private int servingSize;
		private int servings;
		private int calories = 0;
		private int cat = 0;
		
		public Builder(int servingSize, int servings) {
			this.servingSize = servingSize;
			this.servings = servings;
		}
		public Builder calories(int val) {
			this.calories = val;
			return this;
		}
		public Builder cat(int val) {
			this.cat = val;
			return this;
		}
		public NutritionFacts build() {
			return new NutritionFacts(this);
		}
	}
	private NutritionFacts(Builder builder) {
		servingSize = builder.servingSize;
		servings = builder.servings;
		calories= builder.calories;
		cat = builder.cat;
	}
}

3. 用私有构造器或者枚举类型强化Singleton属性

public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() {}
    public static Elvis getInstance() {
        return INSTANCE;
    }
    // 防止序列化
    private Object readResolve() {
        return INSTANCE;
    }
}

单元素的枚举类型已经成为实现Singleton的最佳方法

public enum Elvis {
    INSTANCE;
}

4. 通过私有构造器强化不可实例化的能力

企图通过将类做成抽象类来强制该类不可被实例化,这是行不通的

public class UtilityClass {
    private UtilityClass() {
       throw new AssertionError(); 
    }
}

5. 避免创建不必要的对象

要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱.

6. 消除过期对象的引用

  • 如果一个栈先是增长,然后再收缩,那么,从栈中弹出来的对象将不会被当做垃圾回收,即使使用栈的程序不再引用这些对象,他们也不会被回收.这是因为,栈内部维护着对这些对象的过期引用(指的是永远也不会再被解除的引用)
public class Stack {
	
	private Object[] elements;
	public 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();
		return elements[--size];
	}*/
	//清空过期引用
	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);
	}
	
	public Object[] getElements() {
		return this.elements;
	}
	
	public static void main(String[] args) {
		Stack s = new Stack();
		for(int i = 0; i < 15; i++) {
			s.push(i);
		}
		for(int i = 0; i < s.elements.length; i++) {
			System.out.print(s.elements[i] + " ");
		}
		System.out.println();
		System.out.println(s.pop());
		System.out.println(s.pop());
		for(int i = 0; i < s.elements.length; i++) {
			System.out.print(s.elements[i] + " ");
		}
	}
}
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 null 
14
13
0 1 2 3 4 5 6 7 8 9 10 11 12 null null null 
  • 清空对象引用应该是一种例外,而不是一种规范行为

内存溢出问题

  • 只要类是自己管理内存,程序员就应该警惕内存泄漏问题
  • 内存泄漏的另一个常见来源是缓存
  • 内存泄漏的第三个常见来源是监听器和其他回调

7. 避免使用终结方法

终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的.

  • 不应该依赖终结方法来更新重要的持久状态.
  • System.gc和System.runFinalization它们确实增加了终结方法被执行的机会,但是它们并不保证终结方法一定会被执行。唯一声称保证终结方法被执行的方法是System.runFinalizersOnExit和Runtime.runFinalizersOnExit这两个方法都有致命缺陷
  • 可以使用显式的终止方法通常与try-finally 结构结合起来使用, 以确保及时终止.

终结方法好处

  1. 当对象的所有者忘记调用显式终止方法时,终结方法可以当"安全网".
  2. 与对象的本地对等体(native peer)有关,本地对等体是一个本地对象(native object),普通对象通过本地方法(native method) 委托给一个本地对象.因为本地对等体不是一个普通的对象,所以垃圾回收不会知道它,当它的java对等体被回收的时候,它不会被回收.

总之,除非是作为安全网,或者是为了终止非关键的本地资源,否则不要使用终结方法.在这些很少见的情况下,既然使用了终结方法,纪要记住调用super.finalize.如果用终结方法作为安全网,要记得记录终结方法的非法用法.最后,如果需要把终结方法与公有的非final类关联起来,请考虑使用终极方法守卫者,以确保即使子类的终结方法未能调用super.finalize该终结方法也会被执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值