一、引言
- 清晰性和简洁性最为重要:模块的用户永远也不应该被模块的行为所迷惑(那样就不清晰了);模块要竟可能的小,但又不能太小(模块是指任何可重用的软件组件,从单个方法,到包含多个包的复杂系统,都可以是一个模块)。代码应该被重用,而不是被拷贝。模块之间的依赖性应该可能地降到最小。错误应该尽早被检测出来,最好是在编译时刻。
- 始终关心如何编写出清晰、正确、可用、健壮、灵活和可维护的程序来。
- Java语言支持4中类型:接口(interface)、类(class)、数组(array)和基本类型(primitive)。前三种类型通常被称为引用类型(reference type),类实例和数组是对象(object),而基本类型的值不是对象。类成员(member)由它的域(field)、方法(method)、成员类(member class)和成员接口(member interface)组成。方法的签名(signature)由它的名称和所有参数类型组成;签名不包括它的返回类型。
二、创建和销毁对象
考虑用静态工厂方法代替构造器
1). 静态工厂方法与构造器不同的第一大优势在于,有名称,可以让代码更易于阅读和使用,当一个雷需要多个带有相同签名的构造器是,就用静态工厂方法代替构造器,并且慎重地选择名称以便突出他们之间的区别。
2). 不必在每次调用它们的时候都创建一个新对象。这使得不可变类可以使用预先构建好的实例或者将构建好的实例缓存起来重用,避免创建不必要的重复对象。
3). 可以返回原返回类型的任何子类型的对象。
服务提供者框架模式// Serice interface public interface Service{ ...// Service-specific methods go here } // Service provider interface public interface Provider{ Service newService(); } //Noninstantiable class for service registration and access public class Services{ private Services(){} private static final Map<String, Provider>provides = new ConcurrentHashMap<String,Provider>(); public static final String DEFAULT_PROVIDER_NAME="<def>"; public static void registerDefaultProvider(Provider p){ registerProvider(DEFAULT_PROVIDER_NAME,p); } publid static void registerProvider(String name, Provider p){ providers.put(name, p); } public static Serivce newInstance(){ return newInstance(DEFAULT_PROVIDER_NAME); } public static Service newInstance(String name){ Provider p = providers.get(name); if(p == null) throw new IllegalArgumentexception("No provider registered with name:"+name); return p.newService(); } }
4). 在创建参数化类型实例的时候,它们使代码变得更加简洁
静态工厂通常更加合适,因此切忌第一反应就是提供公有的构造器,而不先考虑静态工厂.- 遇到多个构造器参数时要考虑用构建器
Builder模式还比重叠构造器模式更加冗长,因此它只在有很多参数的时候才使用, 如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是一种选择, 特别是当大多数参数是可选的时候 - 使用私有构造器或者枚举类型强化Singleton属性
Singleton 指仅仅被实例化一次的类
实现Singleton简单的两种方式,这个两种方式都要把构造器保持为私有的,并导出公有的静态成员,以便客户端能够访问该类的唯一实例, 但这种方式在Singleton类变成是可序列化时仅仅在声明中加上”implement Serializable”是不够的,必须声明所有的实例域都是transient的,并提供一个readResolve方法,否则每次反序列化时都会创建一个新的实例
或者使用一个包含单个元素的枚举类型
public enum SS{
INSTANCE;
…
}
这种方式提供了序列化机制,绝对防止多次实例化 - 通过私有构造器强化不可实例化的能力
- 避免创建不必要的对象
要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱
不要错误地认为 创建对象的代价非常昂贵,我们应该要尽可能地避免创建对象. 相反小对象的构造器只做很少量的显示工作, 所以, 小对象的创建和回收动作是非常廉价的, 通过创建附加的对象,提升程序的清晰性/简洁性和功能性 - 消除过期的对象引用
一般而言:只要类是自己管理内存,程序员就应该警惕内存泄漏问题, 内存泄漏的另一个常见来源是缓存. 第三个常见来源是监听器和其他回调. - 避免使用终结方法
3. 对于所有对象都通用的方法
- 覆盖equals时请遵守通用约定
equals方法实现了等价关系
1).自反性(reflexive). 对于任何非null的引用值x, x.equals(x)必须返回true;
2). 对称性(symmetric).对于任何非null的引用值x和y, 当且仅当y.equals(x)返回true时,x.equals(y)必须返回true;
3).传递性(transitive). 对于任何非null的引用值x, y 和z, 如果x.equals(y)返回true, 并且y.equals(z)也返回true, 那么x.equals(z)也必须返回true;
4).一致性(consistent). 对于任何非null的引用值x和y, 只要equals的比较操作在对象中所用的信息没有被修改, 多次调用x.equals(y)就会一致地返回true, 或者一致地返回false
5).对于任何非null的引用值x, x.equals(null)必须返回false - 高质量equals方法诀窍:
1). 使用==操作符检查”参数是否为这个对象的引用”,如果是,则返回true
2).使用instanceof操作符检查”参数是否为正确的类型”,如果不是,则返回false
3).把参数转换成正确的类型,因为转换之前进行过instanceof测试,所以确保会成功
4).对于该类中的每个”关键(significant)”域,检查参数中的域是否与该对象中对应的域相匹配,如果这些测试都成功,则返回true,否则返回false
5). 当你编写完成了equals方法后,应该问自己三个问题:它是否是对称的,传递的,一致的?
最后的一些告诫:
1).覆盖equals时总是要覆盖hashCode
2).不要企图让equals方法过于智能
3).不要将equals声明中的Object对象替换为其他的类型 - 覆盖equals时总是要覆盖hashCode
如果不这样做的话,就会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMap, HashSet 和Hashtable - 始终要覆盖toString
是否在文档中指定返回值的格式 - 谨慎地覆盖clone
可以提供一个拷贝构造器(copy constructor)或拷贝工厂(copy factory).如:
public Yun(Yum yum);
public static Yum newInstance(Yum yum);
其他的接口都不应该扩展Cloneable接口 - 考虑实现Comparable接口
类实现了这个接口, 就表明它的实例具有内在的排序关系(natural ordering)
compareTo方法的通用约定与equals方法的相似:
将这个对象与指定的对象进行比较, 当该对象小于,等于或大于指定对象的时候,分别返回一个负整数,零或者正整数,如果由于指定对象的类型而无法与该对象进行比较, 则跑出ClassCastException异常.