effective java读书小记(一)创建和销毁对象

  • 序言

《effective java》可谓是java学习者心中的一本绝对不能不拜读的好书,她对于目标读者(有一点编程基础和开发经验)的人来说,由浅入深,言简意赅。每一章节都分为若干的条目,完全可以利用平时的零碎时间片来阅读和思考。仅仅是阅读了第二章,创建和销毁对象,从静态工厂、构造器等基础得不能再基础的知识,却使我有一种特别的感觉,就如同见到一位会使我怦然心动的女生,那种惊喜,是那么的美妙,却也是那么的震撼。原来女生还能够如此地美,原来书,所谓让人头晕的编程类书籍,可以写得如此地好,直击心灵,让人有相见恨晚的感觉。正如我在朋友圈写到的,阅书如品茗,品酒,如人生。她完全不同于《21天精通xxoo》的粗制滥造,也不像《Think in java》一样对于谁都高不可攀(我读起来有丝丝的苦涩)。

好了说太多了,现在开始正文吧。

更多精彩,请关注:http://muscle1990.com/?p=295

  • 创建和销毁对象

本章主题是讲述创建和销毁对象,何时以及如何创建,何时以及如何销毁对象,如何确保他们能够适时读销毁,,以及如何管理对象销毁之前必须进行的各种清理工作。

  • 第一条:考虑用静态工厂

我们先来看看这个静态工厂方法,返回累的实例:

public static Boolean valueOf(boolean b){
	return b?Boolean.TRUE:Boolean.FALSE;
    }

通过调用该方法,就可以非常方便地利用工厂得到实例。

静态工厂和构造器不同的优势在于:

  1. 她们有名称,as what you can see。
  2. 不需要每次调用他们的时候都创建一个新的对象。
  3. 她们可以灵活地返回原类型的任何子类型对象。
  4. 在创建参数化实例的时候,她使得代码变得更加的简洁。

for example:

在我们使用带参构造器创建一个map时,需要这样:

Map<String,String> stringMap=new HashMap<String,String>();

但是,假设HashMap提供以下静态工厂:

public static <K,V> HashMap<K,V> newInstance(){
	return new HashMap<K,V>();
    }

那么我们在创建map的时候,就可以这样写了:

Map<String,String> stringMap =HashMap.newInstance();

倘若你在工作中常需要做创建对象的事,会不会有一丝的惊喜呢?

 

  • 第二条:遇到多个构造器参数时需要考虑使用构建器:

静态工厂和构造器有个共同的局限性,都不能很好地扩增到大量可选属性。

考虑一个人的资料,有些是必须要的,如Id,name,有些是可选的,如地区、身高,体重等。

方法一,带参构造器

 

package new_and_destory_object;
/*
 * @author 莫仕豪  
 * moshihao@gmail.com  muscle1990.com  
 * @version:2013-7-31 下午9:53:36
 *
 */

public class Person {
    private String id;//必选
    private String name;//必选

    private int height;//可选
    private int weight;//可选
    //private xxx ooo;//可选
    //and so on...//可选

    public Person(String id,String name){
	this.id=id;
	this.name=name;
    }

    public Person(String id,String name,int height){
	this.id=id;
	this.name=name;
	this.height=height;
    } 

    public Person(String id,String name,int height,int weight){
	this.id=id;
	this.name=name;
	this.height=height;
	this.weight=weight;
    } 
    public Person(String id,String name,int height,int weight,xxx ooo,...){
	this.id=id;
	this.name=name;
	this.height=height;
	this.weight=weight;
	...
    } 
}

 

 

然而,在我们创建对象的时候,就会这样:

Person Muscle=new Person("helloworld","Muscle",172,55,........);

如果有很多属性呢?我参与的实际项目中,遇到过超过150个属性的。使用构造器设参方法,难道我需要写上百个?再慢慢选?慢慢填数据?再save?oh my god!

方法二:javaBean setter 模式

package new_and_destory_object;
/*
 * @author 莫仕豪  
 * moshihao@gmail.com  muscle1990.com  
 * @version:2013-7-31 下午9:53:36
 *
 */
public class Person {
    private String id;//必选
    private String name;//必选

    private int height;//可选
    private int weight;//可选
    //private xxx ooo;//可选
    //and so on...//可选
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getHeight() {
        return height;
    }
    public void setHeight(int height) {
        this.height = height;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }    
    //...    
}

这种方法深信大家也不会陌生,然而,同样的情况,属性多呢。。。

Person Muscle = new Person();
Muscle.setId("helloworld");
...

除此之外,因为构造过程被分到几次的调用,所以javaBean会产生不一致的可能状态。

方法三:builder模式

幸运的是,还有第三种替代方法,既能够保持像重叠构造器那样安全,又能像javaBean那样易读灵活。

package new_and_destory_object;

/*
 * @author 莫仕豪  
 * moshihao@gmail.com  muscle1990.com  
 * @version:2013-7-31 下午9:53:36
 *
 */
public class Person {
    private String id;// 必选
    private String name;// 必选

    private int height;// 可选
    private int weight;// 可选

    // and so on...

    public static class Builder {
	// 必选属性
	private final String id;
	private final String name;
	// 可选
	private int height   =0;
	private int weight   =0;
	// and so on... 

	public Builder(String id,String name){
	    this.id=id;
	    this.name=name;
	}
	public Builder height(int height){
	    this.height=height;
	    return this;
	}
	public Builder weight(int weight){
	    this.weight=weight;
	    return this;
	}
	// and so on... 

	public Person build(){
	    return new Person(this);
	}
    }

    public Person(Builder builder) {
	id     =builder.id;
	name   =builder.name;
	height =builder.height;
	weight =builder.weight;
    }
}

注意Person是不可变的。下面的创建对象的实现:

Person Muscle = new Person.Builder("helloworld","Muscle").height(173).weight(55).build();

这样的代码,就会变得非常容易编写,并且简洁易懂。

 

  • 第三条:用私有构造器或者枚举类型强化单例属性。
    public class Singleton{
    	private static final Singleton INSTANCE =new Singleton();
    	private Singleton(){...}
    
    	public static Singleton getInstance(){return INSTANCE;}
        }

     

这部分暂时没多大感觉,单例用得比较少,不过也学习了。

 

  • 第四条:通过私有构造器,强化不可实例化的能力。

这个讲得不错,在HSBC Gltc的Interview的时候有问到。

 

  • 第五条:避免创建不必要的对象

这部分写得特别受用,不多说,来码:

String s=new String("String");//永远不要这样写代码,求你了

赋值的String已经为对象实例,这样就是等于多了一个实例,如果是在比较频繁的String操作,这样会产生成千上万无用的实例。

String s="String";//这样就可以了,简洁明了不浪费

还需要将可重用的重用,正如同书中例子一样,起点时间,终点时间设为static final,这样就可以了,完全没有必要每一次调用都实例化。

 

装箱拆箱问题:

public static void main(String args[]){
	//需要拆箱时间:16s
	//基本类型时间:2s
	Long sum =0L;
	for(long i=0;i<Integer.MAX_VALUE;i++){
	    sum +=1;
	}
	System.out.println(sum);
    }

倘若你还不知道Long和long的区别的话,简单点说就是Long是对象,long是基本类型,他们转换需要装箱拆箱。而效率从上可见一般,优先使用基本类型,不要手滑写错类型。。。

  • 第六条:

消除过期对象引用

package new_and_destory_object;

import java.util.Arrays;
import java.util.EmptyStackException;
/*
 * @author 莫仕豪  
 * moshihao@gmail.com  muscle1990.com  
 * @version:2013-7-31 下午11:13:52
 *
 */

public class Stack {
    public Object[] elements;
    private int size =0;
    private static final int DEFAULT_ININAL_CAPACITY =16;

    public Stack(){
	elements=new Object[DEFAULT_ININAL_CAPACITY];
    }
    public void push(Object e){
	ensureCapacity();
	elements[size++]=e;
    }
    public Object pop(){
	if(0==size){
	    throw new EmptyStackException();}
	    return elements[--size];
	}

    private void ensureCapacity() {
	if(elements.length == size){
	    elements =Arrays.copyOf(elements, 2 *size+1);
	}
    }
}

因为栈会维护对象的过期引用,了解JVM内存管理的应该明白,有引用的对象,就算明知道是垃圾,一样不会回收。所以这段代码就算运行没有一点问题,但是会有导致内存溢出的风险。

我们需要修改弹出栈的操作:

public Object pop(){
	if(0==size){
	    throw new EmptyStackException();
	    }
	Object result =elements[--size];
	elements[size] =null;
	return result;
	}

一般而言,只要是类自己管理内存,我们就需要警惕内存泄漏的发生。

除此之外,我们还需要注意,内存泄漏的另一个常见的来源是缓存。一旦你把对象放到缓存中,就很容易被淡忘。

 

  • 第七条:避免使用终结方法:

老实说,没用过,但是了解一下也是好的

 

  • 结言:

effective java读书小记(一)创建和销毁对象,这篇读书笔记连想带码,用了差不多两个小时的时间,很久没有这样写过读书笔记了,学海无涯,书山有路,加油!

Muscle Mo 2013.7.31.晚写于大学城宿舍

posted @ 2013-08-01 20:18  坚固66  阅读(159)  评论(0编辑  收藏  举报