《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; }
通过调用该方法,就可以非常方便地利用工厂得到实例。
静态工厂和构造器不同的优势在于:
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 莫仕豪 * [email protected] 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 莫仕豪 * [email protected] 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 莫仕豪 * [email protected] 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 莫仕豪 * [email protected] 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.晚写于大学城宿舍