《Effective java》学习笔记 1 之 对象创建和销毁

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

你可能感兴趣的:(《Effective java》学习笔记 1 之 对象创建和销毁)