创建销毁对象
Item1: 使用静态工厂代理构造器
优点:
1.静态工厂可自定义名字,见名知意,如BigInteger(int, int, Random)
与BigInteger.probablePrime
.有时多个构造器会让人无从选择,容易出错。
2.静态工厂不需要每次调用都创建一个对象。对于某些对象可以重复利用的场景,这种方式效率更高。
3.静态工厂可返回方法返回对象的任意子类,基于接口编程。
4.静态工厂可根据输入参数来创建不同的返回对象
5.静态工厂可以仅仅是一个占位符,根据注入的不同服务,返回不同的对象,如JDBC
缺点:
1.如果类中不包含public
或protected
的构造函数,将无法被继承
2.难以区分那些是静态工厂方法,那些是普通的静态方法,通常使用方法名称区分,如of
,from
,valueOf
,getInstance
等
Item2: 若构造器需要很多参数的时候,考虑使用建造者模式
当构造器需要多个参数,而且部分参数可选的时候,通常有几种处理方式:
1.使用构造器重载,默认参数在简单的构造器中设置,简化客户端调用。
缺点:在参数很多的时候,组合的情况很多。代码难写也难读。
2.使用JavaBean的方式,初始化设置属性的默认值,使用set方法来设置必要的参数。
缺点: 会导致不一致的问题,参数没设置完整,对象已经是可用的。另外会让对象变得可变,多线程就得考虑线程的安全
3.使用建造者模式,Builder
使用链式调用,每设置一个参数,返回的是本身,设置完所有的参数,调用build
方法的时候,将构造器传入给要新建的对象,通过构造器设置完参数后,返回。
Item3: 使用私有构造器或者枚举类型来强化单例属性
三种方式:
1.public static final INSTANCE= new Singleton()
,私有化构造器,简单直接
2.私有化属性,私有化构造器,静态工厂返回单例,如果不需要对单例对象加工,也不需要使用范型,也不需要使用suppler,如Singleton::INSTANCE
,则使用第一种方式
3.使用枚举来实现,通常作为最佳实践
Item4: 通过私有化构造器来强化不可实例化的能力
一些工具类只包含静态方法,静态变量,不需要被继承和实例化,此时采用私有化构造器来保证其不被实例化和继承。
Iemt5: 采用依赖注入来取代硬编码
资源使用依赖注入比使用静态工厂或静态工具类更灵活更容易测试。
Item6: 避免创建不必要的对象
不可变对象复用,原生类型的装箱拆箱。
Item7: 消除过期对象的引用
不再引用的对象置为null,绝大部分情况不需要设置,若在作用域以外仍然会引用废弃的对象,才需要设置。自己管理内存或者对象的情况下,需要考虑。
Item8: 避免使用终结方法
Finalizers
不可预料,危险通常是不必要的。JAVA9不推荐使用Finalizers
,以Cleaners
取而代之,比Finalizers
安全,同样不可预料,影响性能,没有必要使用。因为不知道终结方法何时执行。Finalizers
会吞噬异常,Cleaners
不会吞噬异常。作为安全网或者是终结非关键的本地资源,否则不要使用终结方法。
Item9: 使用try-with-resources
代替try-finally
对所有对象都通用的方法
Item10: 覆盖equal
的时候遵守通用约定
1.自反性 对于非空对象,x.equal(x)
返回ture
2.对称性 x.equal(y)
则y.equal(x)
x,y为继承关系,容易出现违反对称性,因为调用不同的equal
方法。
3.传递性 x.equal(y)
,y.equal(z)
则x.equal(z)
y为父类,x,z为子类,容易违反传递性,将子对象转换成父对象比较,都相等,子对象里判断了其他属性,导致两个子对象不相等。只要父类不可实例化,就不会违反传递性。
4.一致性 在不修改x,y的情况下,x.euqal(y)
不变,如果依赖了不可靠的资源,比如域名对应ip,就容易违反。
5.任何非空x,x.equal(null)
返回false
我们无法在扩展可实例化的类的同时,即增加新的属性,又保留equal
的约定。
实现高质量的equal
步骤:
1.使用==
判断是否相同的引用
2.使用instanceof
检验参数的类型是否正确
3.将参数转换成正确的类型
4.检查关键的字段是否一致
5.考虑对称性、传递性、一致性
实现equal
要注意equal
的参数是Object
类型,如果改成其他类型的,就不是重写,而是重载。重写时使用override
注解,重写错类,会提示编译错误。
Item11: 覆盖equal
时,一定要覆盖hashcode
相等的对象,hashcode
一定相等,不相等的对象,hashcode
也可能相等。因此,equal
里用到的关键字段,在hashcode
的计算时,都应该用到,在equal
里没有用到的字段,不要用于hashcode
计算。
hashcode
计算规则:
1.初始化一个reuslt值,步骤2循环关键字段
2.根据字段计算c,如果字段是原生类型,则调用Type.hashCode(f)
,如果字段是引用类型,则调用对象的hashCode
,如果是数组,使用Arrays.hashCode
3.result = 31* result + c,重复循环计算关键字段。
Item12: 始终覆盖toString
Item13: 谨慎地覆盖clone
Item14: 考虑实现Comparable
类与接口
Item15: 使类和成员的访问最小化
Item16: 在公有类中使用访问方法,不使用公有域
Item17: 使可变性最小化
实现不可变类:
1.不提供任何会修改对象的方法
2.保证类不会被扩展
3.使所有的域都是final
4.使所有的域都成为私有的
5.如果域是可变的,则不让改域能在外部访问,也不要返回可变的域,也不要使用外部传入的可变对象来初始化域
Item18: 组合优与继承
继承破坏了封装。继承其他人编写的具体类时,要有文档说明可以被继承,否则容易出错。只有在确实子类是父类的子类型,采用继承,否则用组合。
Item19: 为继承做好设计,并文档化,否则就不要用继承
Item20: 接口优于抽象类
Item21: 做好接口设计
虽然默认接口能实现在接口里添加方法,不影响原来的实现。但是不要这么做,因为可能导致运行时的问题。默认方法可用于提取接口实现的公有逻辑,不要用来修改接口。
Item22: 接口只用于定义类型
不要使用常量接口,常量可以使用工具类来实现。
Item23: 类层次优于标签类
Item24: 优先考虑静态成员类
Item25: 限制一个文件定义多个顶级类
泛型
Item26: 不要使用原生类型
Item27: 消除非受检警告
尽量消除,无法消除的使用注解SuppressWarnings("unchecked")
,并注明原因,SuppressWarnings("unchecked")
范围越小越好。
Item28: 列表优先于数组
Item29: 优先考虑泛型
Item30: 优先考虑泛型方法
Item31: 利用有限制通配符来提升API的灵活性
Item32: 明智的组合泛型和可变参数
Item33: 优先考虑类型安全的异构容器
枚举和注解
Item34: 用enum代替int常量
Item35: 用实例域代替序数
Item36: 用EnumSet代替位域
Item37: 用EnumMap代替序数索引
Item38: 用接口模拟可伸缩的枚举
Item39: 注解优先于命名模式
Item40: 坚持使用Override注解
Item41: 用标记接口定义类型
lamda与stream
Item42: lamda优先于匿名类
优先使用lamda,lamda由于没有名字和文档,所以表达式应该清晰明了,代码不要超过3行,。大部分使用匿名类的场景都可以使用lamda替换,除非要使用多个函数。