zoukankan      html  css  js  c++  java
  • 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.晚写于大学城宿舍

  • 相关阅读:
    javascript模拟枚举
    javascript实现命名空间效果
    js数组去重
    文件上传插件
    script标签的defer属性
    js数组复制
    更改visual studio2010的主题
    关于json格式在.net前后台传值的详细示例
    where T : class泛型类型约束
    JavaScript模仿鼠标拖动选择功能
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3231147.html
Copyright © 2011-2022 走看看