最近看知乎上Android成长之路的帖子推荐了一本书《Effective java》于是下了个中文版的pdf开始学习,鉴于自己英文太差阅读英文版太吃力,所以下了中文版。
1.使用静态工厂方法来代替构造器(此处静态工厂方法和设计模式的工厂方法不同)
优点:
1 它有名称,可以更加确切的描述创建的对象。例如BigInteger(int,int,Random)可以使用 BigInteger.probablePrime(int,Random)代替
2 不必每次都调用他们的时候都创建一个新的对象
3 可以返回原返回类型的任何子类型的对象
4 在创建参数化类型实例的时候,它们使代码变得更加简洁
缺点:
1 如果不含共有的或者受保护的构造器,就不能被子类化。
2 它们于其他的静态方法实际上没有任何区别。
2.遇到了多个构造器参数时要考虑使用构造器
1 使用重叠构造器
缺点:当参数有许多的时候,客户端代码会很难写,并且难以阅读。
2 使用javaBeans的模式,通过setter方式来设置参数
缺点:构造过程中被分到几个调用中,在构造过程中javaBean可能处于不一致的状态。不安全
3 使用Builder模式
package com.effetive.creation;
import java.util.WeakHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
/**
* 营养成分
*
* @author Administrator
*
*/
public class NutritionFast {
// 必选参数
private int servingSize;
private int servings;
// 可选参数
private int calories;
private int fat;
private int sodium;
private int catbohydrate;
private NutritionFast(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
catbohydrate = builder.catbohydrate;
}
public static class Builder {
// 必选参数
private int servingSize;
private int servings;
// 可选参数
private int calories;
private int fat;
private int sodium;
private int catbohydrate;
public Builder(int servingSize, int servings) {
super();
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public Builder catbohydrate(int val) {
catbohydrate = val;
return this;
}
public NutritionFast build() {
return new NutritionFast(this);
}
}
public static void main(String[] args) {
// 调用
NutritionFast cocaCola = new NutritionFast.Builder(240, 8)
.calories(100).sodium(35).build();
}
}
如果构造器中或静态工厂中具有多个参数,可以选择Builder模式,如果参数比较少还是选择重叠的方式。
3.用私有的构造器或者枚举强化singleton属性(个人感觉使用枚举来做单例模式,我好像没见过)
public class Elvis{
private static final Elvis instance = new Elvis();
private Elvis(){}
public static Elvis getInstance(){
return instance;
}
public void leavTheBuilding(){}
}
public enum Elvis{
INSTANCE;
public void leavTheBuilding(){}
}
4.避免创建不必要的对象
String s = new String("abcd");
String s = "abcd";
我们选择第二种方式,因为第一中方式会创建出2个对象。
Long sum =0l;
for(long i =0; i<Integer.MAX_VALUE;i++){
sum +=i;
}
如果我们把Long 改为long 可以少创建出2^31个Long对象。所以尽量使用基本数据类型。
5.消除过期的对象引用
//自己维护一个栈
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(Object e){
if(size==0){
throw new EmptyStackException();
}
Object result = elements[--size];
//elements[--size] = null;
return result;
}
//如果容量不够进行扩充
public void ensureCapacity(){
if(elements.length == size){
elements = Array.copy(elements,2*size+1);
}
}
}
这段程序运行起来可能没有什么错误,但是确存在这严重的内存泄露,如果对Stack的进行压栈操作,然后再进行弹栈,但是弹栈的对象是不能被垃圾回收站回收,因为是个强引用(还在elements数组中)。如果这种过期的引用对象(obsolete reference)没有被回收,可能会出现OutOfMemory的情况.修改方式 :就是把弹栈的对象给制空。
引用过期对象容易出现位置:
1 自己管理的Stack 、存储池、数组
解决方案:一旦容器元素变成非活动部分的一部分,我们就要手动清空这些容器元素
2 缓存
一旦你把对象放在缓存中,它很容易被遗忘,从而使它在很长一段时间不使用的时候仍然保留在内存中。
解决方案:可以使用WeakHashMap来当做缓存容器;还可以使用使用Timer,ScheduledThreadPoolExecutor定时器来定时清理
3 内存泄漏还可能出现在监听器或其他回调中 ,可以使用WeakReference作为WeakhashMap的键
例如,在Android中有很多地方Context对象,我们很多时候会在Activity直接用this代表Context,如果这个Activity已经关闭就应该被垃圾回收器回收,但是我们的通过参数的来传递Activity对象,可能其他地方还引用着这个activity对象。我们解决方案可以用this.getApplicationContext()这样可以避免了Activity内存泄漏
我可以使用工具来查找内存泄漏比如Heap Profiler