最近看知乎上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