作者: 康凯森
日期: 2016-11-05
分类: Java
多个服务提供者实现一个服务,系统为多个服务提供者的客户端提供多个实现,并把他们从多个实现中解耦出来。
服务提供者框架有3个重要组件:
对于JDBC 来说:
静态工厂相比构造器的优势1 :有名称
静态工厂相比构造器的优势2 :不必每次调用时都创建一个新对象
静态工厂相比构造器的优势3 :可以返回原返回类型的任何子类型的对象
静态工厂相比构造器的优势4 : 在创建参数化类型实例的时候,可以使代码更加简洁
静态工厂方法与其他的静态方法实际上没有任何区别
静态工厂和构造器的共同缺陷:不能很好的扩展到大量的可选参数
重叠构造器模式 不适合参数很多的情景
JavaBeans模式 线程安全问题
Builder模式 线程安全,易读,适合大量参数的场景,生成的对象是不可变的
如果对象是不可变的,它就始终可以被重用
优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱
小对象的创建和回收动作是十分廉价的
真正正确使用对象池的典型对象示例就是数据库连接池
所谓的过期引用,是指永远也不会再被解除的引用。
如果一个对象引用被无意识地保留起来了, 那么,垃圾回收机制不仅不会处理这个对象,而且也不会处理被这个对象所引用的所有其他对象。
一旦对象引用已经过期,只需清空引用即可。即赋值为null。
只要类是自己管理内存,就应该警惕内存泄露问题。
内存泄露的另一个常见来源是缓存。
内存泄露的第三个来源是监听器和回调。
终结方法的缺点是不能保证会被及时执行。
自反性 对称性 传递性 一致性 如果俩个对象相等,它们必须始终保证相等
instanceof
操作符 检查 参数是否为正确的类型;public boolean equals(Object obj){
//判断obj是否和this相等,保证自反性
if (obj == this) return true;
//判断obj是否为null,保证最后一条准则
if (obj == null) return false;
//判断两个对象的实际类型是否相等,
//如果不相等,则说明比较的是两个不同种类的对象,应当返回false
if (obj.getClass() != this.getClass()) return false;
//强制类型转换
//由于之前已经使用getClass判断过实际类型,因此这里强转是安全的
Student stu = (Student) obj;
//判断每个属性是否相等
// 对于基本类型的属性用“ == ”比较,对象类型的属性用 equals 比较
if (this.age == stu.age && this.name.equals(stu.name) )
return true;
else return false;
}
相等的对象必须具有相等的 hashcode
永远不要让客户去做任何类库可以替客户完成的事情。
设计良好的模块会隐藏所有的实现细节,把它的API和实现清晰地隔离开来。然后,模块之间只通过API进行通信。
包含共有可变域的类并不是线程安全的。
不可变类只是实例不能被修改的类。
1 不要提供任何修改对象的方法。
2 保证类不会被扩展。
3 使得所有域都是 final 和私有的。
4 确保对于任何可变组件的互斥访问。
不可变对象本质上是线程安全的,不要求同步,可以自由共享。
不可变类的真正唯一缺点,对于每个不同的值都需要一个单独的对象。
好的API文档应该描述了一个给定的方法做了什么工作,而不是如何做到的。
抽象类允许包含某些方法的实现,但是接口不可以。
为了实现由抽象类定义的类型,类必须成为抽象类的一个子类。
接口的优点:
抽象类的演变比接口的演变要容易得多。
一般不定义常量。
常量的定义可以放在本身类,枚举类,工具类中。
代码
这类对象的方法执行其他对象上的操作,如果一个类仅仅导出这样一个方法,它的实例实际上就等同于一个指向该方法的指针。这样的实例被称为函数对象。
函数指针的主要用途就是实现策略模式,在Java中实现这种模式,要声明一个接口来表示该策略,并且为每个具体策略声明一个实现了该借口的类。
代码
具体的策略类只使用一次时,往往使用匿名类实现。
重复使用时 使用私有的静态成员类。
嵌套类存在的目的只是为了它的外围类服务。
静态成员类 作为共有类的辅助类,仅当与它的外部类一起使用时才有意义。
非静态成员类 每个实例都与一个外部类的外部实例相关联 。
匿名类:动态创建函数对象 创建过程对象 静态工厂方法的内部
局部类 :在任何“可以声明局部变量的地方” 都可以声明局部类
后3个称为内部类。
无限制的通配符类型
list<type1> 既不是 list<type2>的子类型,也不是它的超类型
有限的通配符类型
List<? extend E>
Collection<? super E>
PECS producer-extends consumer-super
保护性拷贝是在检查参数的有效性之前进行的,并且有效性检查是针对拷贝后的对象,而不是针对原始的对象。
谨慎地选择方法的名称。
避免过长的参数列表:
对于参数类型, 优先使用接口而不是类。
覆盖方法 代码例子
可变参数的每次调用都会导致进行一次数组分配和初始化。
Collections.EMPTY_SET;
Collections.EMPTY_LIST;
Collections.EMPTY_MAP;
Javadoc
方法的文档注释应该简洁地描述出它和客户端之间的约定
说明方法做了什么
列举出方法的前置条件 @throws @param
后置条件
类的线程安全性和可序列化性
第一次使用的地方声明
try 语句
无法使用的情况:
可靠的算法库
性能不断提升
代码融入主流
Random.nextInt(int)
java.long java.util java.io
集合相关的API
java.util.concurrent
BigDecimal int long 进行货币计算
基本类型只有值
装箱基本类型有null值
基本类型更节省空间和时间
何时使用装箱基本类型:
使用StringBuilder
要努力编写好的程序而不是快的程序
要考虑API设计决策的性能后果
在每次试图优化之前和之后,要对性能进行测量
设计API
线路层协议
永久数据格式
异常永远不应该用于正常的控制流
设计良好的API不应该强迫它的客户端为了正常的控制流而使用异常
如果类有状态相关的方法,则也应该有状态测试的方法或者可识别的返回值: next()方法 和 hasNext() 方法
受检异常 运行时异常 错误
非受检异常是不需要也不应该被捕获的可抛出结构
实现的所有未受检的抛出结构都应该是RuntimException的子类
IllegalArgumentException
IllegalStateException
更高层的实现应该捕获低层的异常,同时抛出可以按照高层抽象进行解释的异常
javadoc 的 throws 标记
失败的方法调用应该使对象保持在被调用之前的状态
不可变对象
检查参数有效性
恢复代码
对象拷贝
long 或者 double 类型变量的读,写不是原子的
如果读和写操作没有都被同步,同步就不会起作用
安全发布对象:
分拆锁 分离锁 非阻塞并发控制
不要在同步区域调用外来方法
同步区做尽可能少的工作
executor framework 并发集合 同步器
线程安全性有多种级别
静态域的延迟初始化
code
实例域的延迟初始化
code
Thread.yield 没有可预测的语义
一个类可以使用不包含在任何方法体中的静态代码块,当类被载入时,静态代码块被执行,且只被执行一次,静态块常用来执行类属性的初始化。
对象的初始化顺序:首先执行父类静态的内容,父类静态的内容执行完毕后,接着去执行子类的静态的内容,当子类的静态内容执行完毕之后,再去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,父类的非静态代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。子类的非静态代码块执行完毕再去执行子类的构造方法。总之一句话,静态代码块内容先执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。
本文是《Effective Java》读书笔记,代码会持续完善补充。