Effective Java2-学习笔记 第11-20条

11.谨慎地覆盖clone

  • 如果可以用拷贝构造器或拷贝工厂代替

12.考虑实现Comparable接口

  • 值类存在非常明显的内在排序关系,就应该坚决考虑实现这个接口
  • 保持和equals()的一致性
  • 当该对象小于,等于或大于指定对象的时候,分别返回一个负整数,零或者正整数.如果由于指定对象的类型而无法与该对象进行比较,则抛出ClassCastException异常
  • 如果非零差值不会溢出,则可以直接return这个插值

13.使类和成员的可访问性最小化

  • 尽可能地使每个类或者成员不被外界访问
  • 如果你把它做成公有的,你就有责任永远支持它,以保持它们的兼容性
  • 受保护的成员应该尽量少用
  • 可以让测试作为被测试的包的一部分来运行,从而能够访问它的包级私有的元素
  • 如果域是非final的,或者是一个指向可变对象的final引用,那么一旦使这个域成为公有的,就放弃了对存储在这个域中的值进行限制的能力,这意味着,你也放弃了域不可变的能力.同时,当这个域被修改的时候,你也失去了对它采取任何行动的能力.因此,包含公有可变域的类并不是线程安全的.
  • 如果final域包含可变对象的引用,它便是final域的所有缺点.虽然引用本身不能被修改,但是它所引用的对象却是可以被修改-这会导致灾难性的后果
  • 类具有公有的静态final数组域,或者返回这种域的访问方法,这几乎总是错误的.如果类具有这样的域或者访问方法,客户端将能够修改数组中的内容.这是安全漏洞的一个常见根源,解决:1.可以使公有数组变成私有的,并增加一个公有的不可变列表  2.使数组变成私有的,并增添一个公有方法,它返回私有数组的一个备份

14.在公有类中使用访问方法而非共有域

  • 如果公有类暴露了它的数据域,要想在将来改变其内部表示法是不可能的,因为公有类的客户端代码已经遍布各处了.
  • 如果类是包级私有的嵌套类,直接暴露它的数据域并没有本质的错误-假设这些数据确实描述了改类所提供的抽象.
  • 在私有嵌套类的情况下,改变的作用域范围被进一步限制在外围类中.
  • 如果不改变类的API,就无法改变这种类的表示法.

15.使可变性最小

不可变的类比可变类更加易于设计,实现和使用.它们不容易出错,且更加安全.

为了使类更为不可变,要遵循下面五条规则:

  1. 不要提供任何会修改对象状态的方法.
  2. 保证类不会被扩展.
  3. 使所有的域都是final的.
  4. 使所有的域都成为私用的.
  5. 确保对于任何可变组件的互斥访问.
    • 如果类具有指向可变对象的域,则必须确保该类的客户端无法获得指向这些对象的引用.
    • 永远不要用客户端提供的对象引用来初始化这样的域,也不要从任何访问方法中返回该对象引用.
    • 在构造器,访问方法和readObject方法中请使用保护性拷贝技术
  • 不可变对象本质上是线程安全的,它们不要求同步.
  • 不可变对象可以被自由地共享,永远也不需要进行保护性拷贝.
  • 不仅可以共享不可变对象,甚至也可以共享它们的内部信息.
  • 不可变对象为其他对象提供大量的构件.
  • 不可变类真正唯一的缺点是,对于每个不同的值都需要一个单独的对象.

如果你执行一个多步骤的操作,并且每个步骤都会产生一个新的对象,除了最后的结果之外其他的对象最终都会被丢弃,此时性能问题就会显露出来.

  1. 解决,先猜测一下会经常用到哪些多步骤的操作,然后将它们作为基本类型提供.如果某个多步骤操作已经作为基本类型提供,不可变的类就可以不必在每个步骤单独创建一个对象.
  2. 解决,如果能精确地预测出客户端将要在不同的类上执行哪些复杂的多阶段操作,这种包级私有的可变配套类的方法就可以工作的很好.如果无法预测,最好的方法是提供一个公有的可变配套类.
  • 让不可变的类编程final的另一种方法就是,让类的所有构造器都变成私有的或者包级私有的,并添加公有的静态工厂来代替公有的构造器
    • 这种方法还使得有可能通过改善静态工厂的对象缓存能力,在后续的发行版本中改进该类的性能.
    • 工厂的名字可以清楚地表明它的功能.
  • 如果你在编写一个类,它的安全性依赖于(来自不可信客户端的)BigInteger或者BigDecimal参数的不可变性,就必须进行检查,以确定这个参数是否为"真正"的BigInteger或者BigDecimal,而不是不可信子类的实例.如果是后者的话,就必须在假设它可能是可变的前提下对它进行保护性拷贝
  • 除非有很好的理由要让类成为可变的类,否则就应该是不可变的.唯一的缺点是在特定的情况下存在潜在的性能问题.
  • 因此,除非有令人信服的理由要使域变成是非final的,否则要使每个域都是final的.
  • 构造器应该创建完全初始化的对象,并建立起所有的约束关系.与所增加的复杂性相比,"重新初始化"方法通常并没有带来太多的性能优势.
posted @ 2018-10-25 22:44  lemon-Xu  阅读(140)  评论(0编辑  收藏  举报