EffectiveJava 读书笔记 -01- 对象创建与销毁
一、静态工厂方法替代构造器
静态工厂方法是一个返回类的实例的静态方法。
优势:
静态工厂方法有名称,构造器的参数本身没有确切的描述被返回对象。
不必每次调用的时候都创建一个对象。
可以返回原返回类型的任何子类。
返回的对象的类可以随着每次调用而发生变化,这取决于静态工厂方法的参数值。
返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在。(如jdbc的ConnectionManager.getConnection,通过Class.forName(
"com.mysql.jdbc.Driver"
)
来加载jdbc驱动,在编写getConnection方法时是无法感知数据库类型的)
缺点:
类如果只有私有化构造器,就不能通过构造器被实例化。
静态工厂方法很难被程序员发现。通常通过命名规则来避免这种劣势。
规则:from-----类型转换方法,只有单个参数,返回该类型的一个实例。
Date.from(instant)
of--------聚合方法,带有多个参数,返回该类型的一个实例。
valueOf--比from和of更繁琐的一种替代方法。
create/newInstance--确保每次调用都返回一个新的实例
getType--在工厂方法处于不同的类中的时候使用。type表示工厂方法所返回的对象的类型。
newType--像newInstance一样,在工厂方法处于不同的类中的时候使用。
二、遇到多个构造方法参数时要考虑使用构造器模式
静态工厂和构造方法有个共同的局限性,它们都不能很好地扩展到大量的可选参数。
例子:
该类有如下构造方法:
当我们构建一个可口可乐包装营养成分标签时:
你必须要设置标签列表中所有属性的值,如果由更多的参数,使得构建类变得非常复杂。此时,可能会考虑到JavaBean的getter和setter方法
看上去构造类更直观的,遗憾的是,JavaBean模式很难保证对象的一致性。可能cocaCola类未初始化完就被其他类调用。此时,用构造器模式可以很好的解决这个问题。
客户端调用代码:
这样客户端代码易于阅读,可以在builder的构造器中检查参数的有效性。
三、用私有构造器或者枚举类型强化Singleton属性
Singleton类是指仅仅被实例化一次的类。要实现这个目的有两种方式。
1、保持构造器为私有,共有静态成员是一个final域:
虽然构造方法时私有的,但是,仍然可以通过反射setAccessible(true)来访问构造方法。如果要抵御这种攻击,可以修改构造器,在返回第二个实例时抛出异常。
2、保持构造器私有,共有的成员是个静态工厂方法:
共有域方法的主要优势在于,API很清楚的表明了这个类是一个Singleton。
四、通过私有构造器强化不可实例化的能力
有时候可能需要编写只包含静态方法和静态域的类,如java.lang.Math、java.util.Arrays等,这样的工具类不希望被实例化,因为实例化他们没有任何意义。让这个类包含一个私有构造器,它就不能被实例化。
五、优先考虑依赖注入来引用资源
有许多类会依赖一个或多个底层的资源。如拼写检查器需要依赖字典。
在实例化拼写检查器时,我们必须实例化一个字典,而不同的语言有不同的字典,如果我们在编译期间无法确定字典,程序将无法正常运行。
当实例化一个拼写检查器实例时,就将字典通过构造器注入到拼写检查器中了。依赖注入同样适用于构造器模式、静态工厂。虽然依赖注入提升了项目的灵活性和可测试性,但是在大型项目中,会导致类的组织混乱,因为一个项目通常包含上千个依赖,这些类之间的相互依赖使得项目变得凌乱不堪。引入依赖注入框架,可以很好的解决这个问题。长见的依赖注入框架:Dagger、Guice、Spring。
六、避免创建不必要的对象
一般来说,最好能重用对象,而不是每次都需要的时候就创建一个功能相同的新对象。
因为“hello”本来就是一个String实例,new String()又多构建了一个新的实例,这样做是没必要的。正确的方式应该是:
有些对象的创建成本比其他对象要高得多,比如Person对象创建时要从数据库(数据量亿级规模)获取属性值,建议缓存下来重用。
版权声明: 本文为 InfoQ 作者【wander】的原创文章。
原文链接:【http://xie.infoq.cn/article/5566638fcddac8f8fa62018bf】。文章转载请联系作者。
评论