Item 1;Consider static factoriy moehtods instead of constructors
考虑使用静态工厂方法代替构造函数
Item 2:Consider a builder when faced with many constructor parameters
当构造函数参数过多时,是否可以使用Builder
一个builder的例子:
医生需要开一个只包含维生素(Vitamin)的处方(Prescription),处方一定包括A,可能包括B1、B2、C、D、E等,可能这个列表还会变化。这个例子用builder pattern实现就是:
Prescription prescriotion = new Prescription.builder(100/**mg*/).vitaminB1(20/**mg*).vitaminE(1000/**mg*/).build(); //开了一个包含100mg维生素A、20mg维生素B1、1000mg维生素E的处方(可能这个患者比较需要维生素E)。vitaminB1(20/**mg*).vitaminE(1000/**mg*/) 这种串联的方式来创建对象是一个特色,让我想起了设计模式里的decorator模式,不过decorator是把一种类型的对象不断地包装,直到得到所需要的对象。
Sandwich sanwich = new MeatSandwich(new VegetableSandwich(new CreamSandwich()));最后我们得到了一个有肉、蔬菜和奶油的三明治。O(∩_∩)O哈哈~
Item 3:Enforce the singleon property with a private constructor or an enum type
实现单例的两种方式——private的构造函数或者使用枚举(enum)? 枚举实现单例倒还没用过- -
Item 4:Enforce noninstantiability with a private constructor
将构造函数设为private来防止实例化,而不是简单讲类声明为abstract,后者允许被继承,子类同样可以被实例化
Item 5:Avoid creating unnecessary objects
避免创建多余的对象,比如 new String("A"); new Integer(1) // insteading using Integer.valueOf(1);
Item 6:Eliminate obsolete object references
释放对象引用
Item 7:Avoid finalizers
避免使用finailizer方法,原因很简单,这个方法一定会执行,但你不知道何时这个方法到底何时执行。
Item 8:Obey the general contact when overriding equals
重写euqals方法时,记得遵守它的每一条规则,具体见http://salever.iteye.com/blog/733705
Item 9:Always override hashCode when you override equals
永远记得在你重写equals时也重写hashCode
Item 10:Always override toString
总是重写toString方法,这会使用的类更人性化
Item 11:Override clone judiciously
在适当的时候重写clone方法
Item 12:Consider implementing Comparable
考虑实现Comparable接口,它提供了一个排序方法,使得对象具有一定的顺序。在对象作为集合类元素的时候,实现这个接口是一个不错的选择。
Item 13:Minimize the accessibility of classes and members
最大程度的控制类和成员的访问权限,能用private的就不用protected,能用protected的就不用public
Item 14:In public classes, use accessor menthods, not public fields
在public的类中,采用通过访问器来访问成员变量
Item 15:Minimize mutablility
最大程度地减小可变性,让对象一旦创建就不可更改,比如String,这样的类更简单,而且天生就是线程安全的。在某些场合下,使用静态工厂方法返回经常使用的常量对象,可以避免重复创建。
总之尽可能的减小类的成员的可变性,能用final的就用final。
Item 16:Favor composition over inheritance
组合优先于继承,如果A、B之间不是is-a的关系,那么不要使用继承,组合往往更加合适。另外,分布在不同的package下的class之间最好也能避免继承。要知道,继承实际上破坏了封装性。
Item 17:Design and document for inheritance or else prohibit it
要么好好的设计继承关系并书写良好的说明文档,要么禁用继承。很多情况下,继承方式成为扩展现有类的首要选择,关于继承,有几点需要注意和提高警惕:
A:构造函数不要调用可能会重写的方法,否则将出现难以预料的逻辑错误;
B:在类中不要轻易调用可能会重写的方法,因为这样会加大子类重写时的复杂度,使用private的帮助方法来代替它们;
C:在遇见Cloneable和Serializable时请绕行,非要实现它们的话,请遵守A、B规则,不要在相关方法内调用可能会重写的方法;
D:好好的制定说明文档吧
其实Item16说的已经说的很明白了,不要万不得已,不要轻易使用继承。
Item 18:Prefer interfaces to abstract classes
优先使用interface,这个item主要讲了interface与抽象类在使用中的优缺点,并非某些参考书上一成不变的什么这个可以有变量,那个只能是有常量。
接口提供多种实现的可能,但是相对抽象类来说,它更“死板”,也更稳定,公共接口一般不允许修改,而且实现接口不会破坏类之间的结构关系,它们之间没有继承关系。当你选择使用抽象类的时候,你就要面临it 17中讲得继承问题了。另外,给接口一个基本实现(Skeletal implementation)是一个不错的选择。
Item 19:Use interfaces only to define types
不要用接口干其他的事,其中最常见的就是用它来定义常量,多方便,可以省略public static 几个关键字。但是作者不建议这么做,因为实现了这个接口的类将会用到这些常量,在未来的版本中,如果这些常量被废弃了,它让然要继续实现它,以保证程序的语法正确,这显然不合适。
选用utility class 或者枚举来代替它吧,接口还是用来定义允许多种实现的类型吧。
Item 20:Prefer class hierarchies to tagged classes
限定一个类只能创建一种实例对象,不要尝试通过添加tag来标记不同类型的实例,如果真的需要在类中使用tag 来区分不同类型的实例,那就使用继承结构。
Tagged class,顾名思义,就是在class中使用一个或多个tag,然后
switch(tag){ case TAG_1: instance = new TaggedClass(TAG_1): case TAG_2: instance = new TaggedClass(TAG_2): ... }
Item 21:Use function objects to represent strategies
使用对象引用来实现策略模式,看了一遍无甚感想
Item 22:Favor satic member classes over nonstatic
优先使用静态内部类作为成员,这一节讲得非常好,将四种内部类分析的很透彻。
public class NestedClass { //static member class private static class StaticNested { } //nonstatic member class private class NonstaticNested { } protected void doSth() { StaticNested sn = new StaticNested(); NonstaticNested nn = new NonstaticNested(); //anonymous class 匿名类 Runnable runable = new Runnable(){ @Override public void run() { } }; // local class 局部类 class LocalClass implements Runnable, Comparable{ @Override public void run() { } @Override public int compareTo(Object o) { return 0; } } LocalClass lc = new LocalClass(); } /** * @param args */ public static void main(String[] args) { StaticNested nsn = new NestedClass.StaticNested(); StaticNested sn = new StaticNested(); NonstaticNested nn = new NestedClass().new NonstaticNested(); } }
上述四种内部类,从使用上说:
A,如果内部类需要从外部访问并且不要求与包含类的实例挂上关系,那么选择静态内部类,它与包含类的实例对象没有关联,可以看成一个稍微复杂一些的静态成员;
B,如果内部类的实例与包含类的示例息息相关,那么使用非静态内部类,
new NestedClass().new NonstaticNested(); //outside NonstaticNested nn = new NonstaticNested();//inside
注意非静态内部类因为与其包含类实例之间的关系,在资源和时间消耗上都远远超过静态类,请慎用;
C,在类不会与外界打交道,很简短而且可以确定不会拿类的名字干点啥的 ,可以选择匿名类,注意一定不要影响了代码可读性,而且匿名类不能有静态成员,只能同时实现一个接口或者继承一个基类;
D,如果匿名类很长,而且可能类名有潜在的作用,那么可以考虑使用局部类,它避免了匿名类的的一些短项,比如可以同时实现二个接口。
Item 23:Don't use raw types in new code
不要使用原始类型参数,比如List,建议使用List<Object> List<?> List<String>
Item 24:Eliminate unchecked warnings
尽可能的减少unchecked 警告信息,对于raw 类型的使用上,如果确信是类型安全的,可以使用@SuppressWarnings("unchecked")来避免警告。
Item 25:Prefer lists to arrays
优先使用list。这一节对于list和array分析的很不错,主要包括以下几个方面:
A,行为上的不同:array在运行时才确定元素的类型,而list(参数化的)在编译的时候就需要检查类型,以保证类型的安全。
B,协同变化性(covariant),指的是array之间因存储元素的不同也会存在父子类型的关系,比如int[]是Object[]的一个子类,那么可以将int[]赋给Object[],而list<Integer>与list<Object>则是两个完全不同的集合。
因此,无法创建泛型l的list类型的数组。
示例:
Object[] objs = new String[1]; String[] strings = new String[1]; int [] ints = new int[1]; objs = strings; objs[0] = 1; //java.lang.ArrayStoreException List<String>[] strs = new List<String>[1]; //Error:Cannot create a generic array of List<String> List<Long> longs = new ArrayList<Long>(); longs.add(10L); Object[] objects = strs; objects[0] = longs; String element = strs[0].get(0);
第一段代码就是针对数组的协同变化性和运行时类型检查,编译完全正确,运行时抛出java.lang.ArrayStoreException,第二段代码针对泛型list的数组,编译即出错。若不出错,想想后面的代码,strs[0].get(0)返回的是Long,而不是String,一个潜在的问题就出现了,ClassCastException。
这里面还讲到一个概念——non-reifiable types:简单的讲一个non-reifiable type就是在运行时包含的信息要少于编译时,比如泛型的list,运行时它不会进行类型检查,而编译时则需要,当然一些特殊的list除外——List<?>。non-reifiable types都不能用来创建数组。
Item 26:Favor generic types
优先考虑泛型的类和接口
Item 27:Favor generic methods
优先考虑泛型的方法
Item28:Use bounded wildcards to increase API flexibility
使用绑定的通配符提升API的灵活性
读完这三个已经内牛满面了,作者Joshua Bloch不愧是Java Collection Framework的创始人,也只有他才能把泛型这个问题分析的如此透彻,很多概念和用法都还不是很了解,哎。。。。
Item 29:Consider typesagfe heterogeneous containers
考虑使用类型安全的混杂容器
Item 30:Use enums to instead of int constants
使用枚举代替整形常量
Item 31:Use instance fields instead of ordinals
使用成员变量来代替使用自然顺序的枚举,特别是ordinal()方法的使用上,尤其要注意。
Item 32:Use EnumSet instead of bit fields
使用EnumSet 代替bit类型的成员变量,就像SWT.NONE,SWT.READ_ONLY。我猜为什么SWT的实现很多都是用bit类型的成员组合,比如new Text(composite, SWT.BORDER | SWT.READ_ONLY),也许是SWT的出现比EnumSet要早缘故,先在我们就可以使用EnumSet.of(Style.BORDER, Style.READ_ONLY)来代替了。
Item 33:Use EnumMap instead of ordinal indexing
EnumMap代替ordinal()方式的索引,他举了一个例子,就是液固气之间的转换,先用二维数组实现,然后用EnumMap<State, EnumMap<State, Transition>>实现,后者的确简洁很多,不过这个例子突然是我想起State模式,好像它用EnumMap实现真实绝配。
Item 34:Emulate extensiable enums with interfaces
看了这一节,唯一的收获是发现enum也可以实现接口,不知道它能不能继承类呢?好像不能,只能实现接口。。。
Item 35:Prefer annotations to naming patterns
优先使用注解(annotation),jdk1.5里添加了annotation这个功能,它的使用和实现与反射(reflect)关系紧密,Junit就是用annotation实现的。至于naming patterns就是说使用命名规则来区分某些特殊功能,比如test开头的函数为测试函数等。
说道反射,突然想起一个问题,Class.forName("XX"),经常发现找不到class,为什么(联系Java Class loader机制)
Item 36:Consistently use the Override annotation
随时给重写的方法加上“Override”
Item 37:User Marker interfaces to define types
Marker interface 指的是没有扩展新方法的新类型,仅仅声明一下它是一个新的类型,但没有扩展行为,比如Cloneable、Serializable等,书中还提到了使用annotation添加类型说明,但是与Marker interface相比,后者的优势是,它显示定义了一种新的类型。
Item 38:Check parameters for validity
检查参数的合法性
Item 39:Make defensive copies when needed
使用defensive copy保护对象的不变性。对于Java来说,引用类型的常量仅限定了其不能被重新赋值,但是仍然有可能修改其属性,调用修改状态的方法,因此对于这种常量,注意使用防御性质的复制来保护其不变性。
注意defensive copy 与 clone()方法的区别
Item 40:Design method signatures carefully
函数签名设置的原则:
1,取名,按照公认的规则取名
2,不要在一个类中放置太多的方法
3,避免太多的参数, 可以通过几种方法来减少参数的个数:子方法,辅助子类,build模式
4,优先使用interface类型的参数,比如对于HashMap,使用Map就行了
5,对于boolean类型的参数,考虑用枚举代替
Item 41:Use overloading judiciously
适当的使用方法重载
Item 42:Use varargs judiciously
明智的使用变长参数
Item 43:Return empty arrays or collections, not nulls
使用空集合代替null返回值,以避免额外的null检查或错误
Item 44:Write doc comments for all exposed API elements
这个是关于API注释的,其中{@code} 和 {@literal} 标签比较有用,其他的暂时用不着呢。。。
Item 45:Minimize the scope of local variables
缩小局部变量的作用域
Item 46:Prefer for-each loops to traditional for loops
使用for-each循环代替传统的for循环
Item 47:Know and use the libraries
理解和使用库,这个作者建议开发者至少熟悉java.lang.*、java.utl.*以及部分的java.io.*下的内容
(待续)