随笔分类 - Effective Java 第三版读书笔记
摘要:在 Java 8 中添加了 Stream API,以简化顺序或并行执行批量操作的任务。该 API 提供了两个关键的抽象:流(Stream),表示有限或无限的数据元素序列,以及流管道(stream pipeline),表示对这些元素的多步计算。Stream 中的元素可以来自任何地方。常见的源包括集合,
阅读全文
摘要:现在 Java 已经有了 lambda 表达式,编写 API 的最佳实践已经发生了很大的变化。例如模板方法模式——其中一个子类重写原始方法以专门化其父类的行为——变得没有那么吸引人。现代替代的选择是提供一个静态工厂或构造方法来接受函数对象以达到相同的效果。通常地说,可以编写更多以函数对象为参数的构造
阅读全文
摘要:lambda 优于匿名类的主要优点是它更简洁。Java 提供了一种生成函数对象的方法,比 lambda 还要简洁,那就是方法引用( method references)。下面是一段代码片段,它维护一个从任意键到 的 map。如果将该值解释为键的实例个数,则该程序是一个多重集合的实现。该代码的功能是,
阅读全文
摘要:以往,具有单一抽象方法的接口(或者很少使用的抽象类)被用作函数类型。它们的实例(称为函数对象)表示函数(functions)或行动(actions)。自从 JDK 1.1 于 1997 年发布以来,创建函数对象的主要手段就是匿名类(条款 24)。下面是一段代码片段,按照字符串长度顺序对列表进行排序,
阅读全文
摘要:虽然 Java 编译器允许在单个源文件中定义多个顶级类,但这样做没有任何好处,并且存在重大风险。风险源于在源文件中定义多个顶级类可能会为一个类提供多个定义。使用哪个定义会依赖于源文件传递给编译器的顺序。 为了具体说明,请考虑下面的源文件,其中只包含一个引用其他两个顶级类( 和 类)的成员的 类: 现
阅读全文
摘要:嵌套类(nested class)是在另一个类中定义的类。嵌套类应该只存在于其外部类(enclosing class)中。如果一个嵌套类在其他一些情况下是有用的,那么它应该是一个顶级类。有四种嵌套类:静态成员类,非静态成员类,匿名类和局部类。除了第一种以外,剩下的三种都被称为内部类(inner cl
阅读全文
摘要:有时你可能会碰到一个类,它的实例有两个或更多的风格(flavor),并且包含一个标签属性(tag field),表示实例的风格。例如下面这个类,它可以表示一个圆形或矩形: 这样的标签类有许多缺点。它们有杂乱无章的样板代码,包括枚举声明、标签属性和 switch 语句。可读性差,因为多个实现在一个类中
阅读全文
摘要:当类实现接口时,该接口会作为一种类型(type),可以被用来引用类的实例。因此,一个类实现了一个接口,就是表明客户端可以对这个类的实例做些什么。为其他目的定义接口是不合适的。 一种失败的接口就是所谓的常量接口(constant interface)。这样的接口不包含任何方法;它只包含静态 final
阅读全文
摘要:在 Java 8 之前,不可能在不破坏现有实现的情况下为接口添加方法。如果向接口添加了一个新方法,现有的实现通常会缺少该方法,从而导致编译时错误。在 Java 8 中加入了默认方法( default method)构造,目的是允许将方法添加到现有的接口。但是增加新的方法到现有的接口是充满风险的。 默
阅读全文
摘要:Java 有两种机制定义多实现类型:接口和抽象类。由于在 Java 8 中引入了接口的默认方法,因此这两种机制都允许为某些实例方法提供实现。一个主要的区别是要实现由抽象类定义的类型,该类必须是抽象类的子类。因为 Java 只允许单一继承,所以抽象类的这种局限严格限制了它作为类型定义的使用。任何定义所
阅读全文
摘要:条款 18 提醒你注意没有设计和文档说明(针对继承)的“外来”类的子类化的危险。 那么为了继承而设计和文档说明一个类是什么意思呢? 首先,这个类必须准确地描述重写这个方法带来的影响。换句话说,该类必须文档说明可重写方法的自用性(self use)。对于每个公共或受保护的方法,文档必须指明方法调用哪些
阅读全文
摘要:继承是实现代码重用的有效方式,但并不总是最好的工具。使用不当,会导致脆弱的软件。在一个包的内部使用继承是安全的,因为子类和父类的实现都在同一个程序员的控制之下。然而,从普通的具体类跨越包级边界继承是危险的。提醒一下,本书使用“继承”一词来表示实现继承(当一个类继承另一个类时)。在这个条款中讨论的问题
阅读全文
摘要:不可变类是一个实例不能被修改的类。包含在每个实例中的所有信息在对象的生命周期中是固定的,因此不会观察到任何变化。Java 平台类库包含许多不可变的类,包括 String 类,基本类型包装类以及 BigInteger 类和 BigDecimal 类。使用不可变类有很多理由:不可变类比可变类更容易设计,
阅读全文
摘要:有时候,你可能会试图写一些退化的类(degenerate classes),除了集中实例属性之外别无用处: 因为这些类的数据属性可以直接被访问,因此这些类不提供封装的好处(条目 15)。在不更改 API 的情况下你无法更改其表示形式,无法强制执行不变量,并且在访问属性时无法执行辅助操作。坚持面向对象
阅读全文
摘要:设计良好的组件与设计不好的组件最重要的区别就是它们隐藏内部数据和其他实现细节的程度。一个设计良好的组件隐藏了它的所有实现细节,干净地将它的 API 与实现分离开来。然后组件只通过它们的 API 进行交流,并且对彼此的内部工作一无所知。这一概念,被称为信息隐藏或封装,是软件设计的基本原则。 信息隐藏如
阅读全文
摘要:与本章讨论的其他方法不同, 方法并没有在 类中声明。相反,它是 接口中的唯一方法。 通过实现 接口,一个类表明它的实例有一个自然序( natural ordering )。对实现 接口的对象所组成的数组排序非常简单,如下所示: 通过实现 接口,可以让你的类与所有依赖此接口的泛型算法和集合实现进行交互
阅读全文
摘要:Cloneable 接口不包含任何方法,它只是决定了 Object 中受保护的 clone 方法的行为:如果一个类实现了 Cloneable 接口,那么 Object 的 clone 方法将返回该对象的逐个属性(field by field)拷贝;否则会抛出 异常。这是一个非常反常的接口使用,不应该
阅读全文
摘要:虽然 Object 类提供了 toString 方法的实现,但它返回的字符串通常不是类的用户想要看到的。它由类名后跟一个 “ at ” 符号(@)和哈希码的无符号十六进制表示组成,例如 。toString 的通用约定要求,返回的字符串应该是“一个简洁但内容丰富的表示,对人们来说是很容易阅读的”。提供
阅读全文
摘要:在每一个重写 方法的类中,都要重写 方法 。如果不这样做,你的类会违反 hashCode 的通用约定,这会阻止它在 HashMap 和 HashSet 这样的集合中正常工作。下面是根据 Object 源码改编的约定: 1. 在一个应用程序执行过程中,如果在 方法比较中没有修改任何信息,在一个对象上重
阅读全文
摘要:重写 方法看起来很简单,但是有很多方法会导致重写出错。避免此问题的最简单方法是不去重写 方法,在这种情况下,类的每个实例只与自身相等。如果满足以下任一条件,则说明不去重写是正确的做法: 每个类的实例都是固有唯一的。例如像 Thread 这样代表活动实体而不是值的类。Object 提供的 实现对这些类
阅读全文