
Effective Java
文章平均质量分 81
Effective Java。一共包含90个条目,每个条目讨论一条规则。这些规则反映了最有经验的优秀程序员在实践中常用的一些有益的做法。
曾梦想仗剑闯天涯
永远相信美好的事情即将发生!
展开
-
Effective Java(第三版)目录
本书的目标是帮助读者更加有效地使用Java编程语言及其基本类库java.lang、java.util和java.io,以及子包java.util.concurrent和java.util.function等。本书也会时不时地讨论到其他的类库。本书一共包含90个条目,每个条目讨论一条规则。这些规则反映了最有经验的优秀程序员在实践中常用的一些有益的做法。全书以一种比较松散的方式将这些条目组织成12章,每一章都涉及软件设计的一个主要方面。原创 2023-07-11 14:58:59 · 556 阅读 · 0 评论 -
Effective Java笔记(33)优先考虑类型安全的异构容器
总而言之,集合 API 说明了泛型的一般用法,限制 每个容器只 能有固定数目的类型参数 。 你可以通过将类型参数放在键上而不是容器上来避开这一限制 。 对于这种类型安全的异构容器,可以用 Class 对象作为键 。 以这种方式使用的 Class 对象称作类型令牌 。 你也可以使用定制的键类型 。 例如,用一个 DatabaseRow 类型表示一个数据库行(容器),用泛型 Column原创 2023-08-11 09:18:59 · 287 阅读 · 0 评论 -
Effective Java笔记(32)谨慎并用泛型和可变参数
总而言之,可变参数和泛型不能良好地合作,这是因为可变参数设施是构建在顶级数组之上的一个技术露底,泛型数组有不同的类型规则 。 虽然泛型可变参数不是类型安全的,但它们是合法的 。 如果选择编写带有泛型(或者参数化)可变参数的方法,首先要确保该方法是类型安全的,然后用@SafeVarargs 对它进行注解,这样使用起来就不会出现不愉快的情况了 。原创 2023-08-10 09:24:52 · 282 阅读 · 0 评论 -
Effective Java笔记(31)利用有限制通配符来提升 API 的灵活性
总而言之,在 API 中使用通配符类型虽然比较需要技巧,但是会使 API 变得灵活得多 。 如果编写 的是将被广泛使用的类库, 则一定要适当地利用通配符类型 。 记住基本的原则:producer-extends,consumer-super(PECS ) 。 还要记住所有的 comparable 和comparator 都是消费者 。原创 2023-08-10 09:11:55 · 793 阅读 · 0 评论 -
Effective Java笔记(30)优先考虑泛型方法
总而言之,泛型方法就像泛型一样,使用起来比要求客户端转换输入参数并返回值的方法来得更加安全,也更加容易 。 就像类型一样 ,你应该确保方法不用转换就能使用 ,这通常意味着要将它们泛型化 。 并且就像类型一样,还应该将现有的方法泛型化,使新用户使用起来更加轻松 ,且不会破坏现有 的客户端。原创 2023-08-09 09:07:50 · 468 阅读 · 0 评论 -
Effective Java笔记(29)优先考虑泛型
总而言之,使用泛型比使用需要在客户端代码中进行转换的类型来得更加安全,也更加容易。 在设计新类型的时候,要确保它们不需要这种转换就可以使用 。 这通常意味着要把类做成是泛型的。 只要时间允许,就把现有的类型都泛型化 。 这对于这些类型的新用户来说会变得更加轻松,又不会破坏现有的客户端 。原创 2023-08-08 10:13:23 · 252 阅读 · 0 评论 -
Effective Java笔记(28)列表优于数组
总而言之,数组和泛型有着截然不同的类型规则 。 数组是协变且可以具体化的;泛型是不可变的且可以被擦除的 。 因此,数组提供了运行时-的类型安全,但是没有编译时的类型安全,反之,对于泛型也一样 。 一般来说,数组和泛型不能很好地混合使用 。 如果你发现自己将它们混合起来使用,并且得到了编译时错误或者警告,你的第一反应就应该是用列表代替数组 。原创 2023-08-08 09:58:35 · 899 阅读 · 0 评论 -
Effective Java笔记(27)消除非受检的警告
总而言之,非受检警告很重要 ,不要忽略它们。每一条警告都表示可能在运行时抛出ClassCastException 异常 。 要尽最大的努力消除这些警告。如果无法消除非受检警告,同时可以证明引起警告的代码是类型安全的就可以在尽可能小的范围内使用@SuppressWarnings("unchecked")注解禁止该警告 。 要用注释把禁止该警告的原因记录下来。原创 2023-08-07 17:02:18 · 311 阅读 · 0 评论 -
Effective Java笔记(26)请不要使用原生态类型
总而言之,使用原生态类型会在运行时导致异常,因此不要使用 。 原生态类型只是为了与引人泛型之前的遗留代码进行兼容和互用而提供的 。 让我们做个快速的回顾:Set原创 2023-08-07 09:56:26 · 837 阅读 · 0 评论 -
Effective Java笔记(25)限制源文件为单个顶级类
永远不要把多个顶级类或者接口放在一个源文件中 。 遵循这个规则可以确保编译时一个类不会有多个定义 。 这么做反过来也能确保编译产生的类文件,以及程序结果的行为,都不会受到源文件被传给编译器时的顺序的影响 。原创 2023-08-04 09:03:10 · 229 阅读 · 0 评论 -
Effective Java笔记(24)静态成员类优于非静态成员类
共有四种不同的嵌套类,每一种都有自己的用途 。 如果一个嵌套类需要在单个方法之外仍然是可见的,或者它太长了,不适合放在方法内部,就应该使用成员类 。 如果成员类的每个实例都需要一个指向其外围实例的引用,就要把成员类做成非静态的;否则 ,就做成静态的 。 假设这个嵌套类属于一个方法的内部,如果你只需要在一个地方创建实例, 并且已经有了一个预置的类型可以说明这个类的特征,就要把它做成匿名类 ; 否则, 就做成局部类 。原创 2023-08-04 08:55:43 · 182 阅读 · 0 评论 -
Effective Java笔记(23)类层次优于标签类
简而言之,标签类很少有适用的时候 。 当你想要编写一个包含显式标签域的类时,应该考虑一下,这个标签是否可以取消,这个类是否可以用类层次来代替 。当你遇到一个包含标签域的现有类时,就要考虑将它重构到一个层次结构中去 。原创 2023-08-04 08:41:24 · 214 阅读 · 0 评论 -
Effective Java笔记(22)接口只用于定义类型
接口应该只被用来定义类型,它们不应该被用来导出常量。原创 2023-08-03 09:25:47 · 179 阅读 · 0 评论 -
Effective Java笔记(21)为后代设计接口
在发布程序之前,测试每一个新的接口就显得尤其重要。程序员应该以不同的方法实现每一个接口。最起码不应少于三种实现。编写多个客户端程序,利用每个新接口的实例来执行不同的任务,这一点也同样重要。这些步骤对确保每个接口都能满足其既定的所有用途起到了很大的帮助。它还有助于在接口发布之前及时发现其中的缺陷,使你依然能够轻松地把它们纠正过来。或许接口程序发布之后也能纠正,但是千万别指望它啦!原创 2023-08-03 09:19:44 · 173 阅读 · 0 评论 -
Effective Java笔记(20)接口优于抽象类
接口通常是定义允许多个实现的类型的最佳途径。如果你导出了一个重要的接口,就应该坚决考虑同时提供骨架实现类。而且,还应该尽可能地通过缺省方法在接口中提供骨架实现,以便接口的所有实现类都能使用。也就是说,对于接口的限制,通常也限制了骨架实现会采用的抽象类的形式。原创 2023-08-03 09:12:37 · 290 阅读 · 0 评论 -
Effective Java笔记(19)要么设计继承并提供文档说明,要么禁止继承
专门为了继承而设计类是一件很辛苦的工作。你必须建立文档说明其所有的自用模式,并且一旦建立了文档,在这个类的整个生命周期中都必须遵守。如果没有做到,子类就会依赖超类的实现细节,如果超类的实现发生了变化,它就有可能遭到破坏。为了允许其他人能编写出高效的子类,还你必须导出一个或者多个受保护的方法。除非知道真正需要子类,否则最好通过将类声明为final,或者确保没有可访问的构造器来禁止类被继承。原创 2023-08-03 08:52:34 · 247 阅读 · 0 评论 -
Effective Java笔记(18)复合优先于继承
继承的功能非常强大,但是也存在诸多问题,因为它违背了封装原则。只有当子类和超类之间确实存在子类型关系时,使用继承才是恰当的 。 即使如此,如果子类和超类处在不同的包中,并且超类并不是为了继承而设计的,那么继承将会导致脆弱性( fragility )。为了避免这种脆弱性,可以用复合和转发机制来代替继承,尤其是当存在适当的接口可以实现包装类的时候。 包装类不仅比子类更加健壮,而且功能也更加强大。原创 2023-08-02 08:50:00 · 304 阅读 · 0 评论 -
Effective Java笔记(17)使可变性最小化
与之相对应的更常见的是过程的(procedural)或者命令式的( imperative )方法,使用这些方法时,将一个过程作用在它们的操作数上, 会导致它的状态发生改变。不可变的类变成 final 的另 一种办法就是,让类的所有构造器都变成私有的或者包级私有的,并添加公有的静态工厂( static factory )来代替公有的构造器(详见第 1 条)。对于处在包外部的客户端而言,不可变的类实际上是 final 的,因为不可能对来自另一个包的类 、 缺少公有的或受保护的构造器的类进行扩展。原创 2023-07-31 09:09:47 · 194 阅读 · 0 评论 -
Effective Java笔记(16)要在公有类而非公有域中使用访问方法
无论是在类定义中,还是在使用该类的客户端代码中,这种方法比访问方法的做法更不容易产生视觉混乱。让公有类直接暴露域虽然从来都不是种好办法,但是如果域是不可变的,这种做法的危害就比较小一些。如果不改变类的 API ,就无法改变这种类的表示法,当域被读取的时候,你也无法采取辅助的行动,但是可以强加约束条件。但有时候会需要用包级私有的或者私有的嵌套类来暴露域,无论这个类是可变的还是不可变的。如果公有类暴露了它的数据域 ,要想在将来改变其内部表示法是不可能的,因为公有类的客户端代码已经遍布各处了。原创 2023-07-18 09:15:19 · 223 阅读 · 0 评论 -
Effective Java笔记(15)使类和成员的可访问性最小化
Java 提供了许多机制( facility )来协助信息隐藏 。 访问控制( access control )机制决定了类、接口和成员的可访问性( accessibility ) 。 实体的可访问性是由该实体声明所在的位置,以及该实体声明中所出现的访问修饰符( private 、protected 和 public)共同决定的 。 正确地使用这些修饰符对于实现信息隐藏是非常关键的 。原创 2023-07-18 09:06:33 · 206 阅读 · 0 评论 -
Effective Java笔记(14)考虑实现 Comparable 接口
与 equals 方法不同的是,它对所有的对象强行施加了一种通用的等同关系,compareTo 不能跨越不同类型的对象进行比较:在比较不同类型的对象时,compareTo 可以抛出 ClassCastException 异常。每当在 compareTo 方法的实现中比较域值时,都要避免使用 < 和>操作符,而应该在装箱基本类型的类中使用静态的 compare 方法,或者在 Comparator 接口中使用比较器构造方法。如果最关键的域是相等的,则进一步比较次关键的域,以此类推。原创 2023-07-17 09:21:37 · 193 阅读 · 0 评论 -
Effective Java笔记(13)谨慎地覆盖 clone
Cloneable 接口的目的是作为对象 的 一个 mixin 接口( mixin interface ) ,表明这样的对象允克隆( clone ) 。 遗憾的是,它并没有成功地达到这个目的 。 它的主要缺陷在于缺少一个 clone 方法, 而 Object 的 clone 方法是受保护的 。 如果不借助于反射( reflection) ,就不能仅仅因为一个对象实现了 Cloneable ,就调用 clone方法 。 即使是反射调用也可能会失 败 ,因为不能保证该对象一定具有可访问的 clone 方法 。原创 2023-07-17 09:07:50 · 204 阅读 · 0 评论 -
Effective Java笔记(12)始终要覆盖 toString
toString 的通用约定指出,被返回的字符串应该是一个“简洁的但信息丰富,并且易于阅读的表达形式”。除了降低了程序的性能,使得程序员 们去做这些不必要的工作之外,这个解析过程也很容易出错,会导致系统不稳定,如果格式发生变化,还会导致系统崩溃。如果没有提供这些访问方法,即使你已经指明了字符串的格式是会变化的,这个字符串格式也成了事实上 的 API。例如,带有对象引用的一个组件,在它记录的错误消息中,可能包含该对象的字符串表示法。无论是否决定指定格式,都应该在文档中明确地表明你的意图。原创 2023-07-14 09:26:19 · 318 阅读 · 0 评论 -
Effective Java笔记(11)覆盖 equals 时总要覆盖 hashCode
如果没有规定散列函数的细节,那么当你发现了它的内部缺陷时,或者发现了更好的散列函数时·,就可以在后面的发行版本中修正它。特别是在实践中,散列函数可能面临大量的实例,在你选择忽略的区域之中,这些实例仍然区别非常大。根据类的 equals 方法,两个截然不同的实例在逻辑上有可能是相等的,但是根据 Object 类的 hashCode 方法,它们仅仅是两个没有任何共同之处的对象。因此,对象的 hashCode 方法返回两个看起来是随机的整数,而不是根据第二个约定所要求的那样,返回两个相等的整数。原创 2023-07-14 09:16:40 · 259 阅读 · 0 评论 -
Effective Java笔记(10)覆盖 equals 时请遵守通用约定
如果类实现的接口改进了 equals 约定,允许在实现了该接口的类之间进行比较,那么就使用接口。如果覆盖 equals ,一定要比较这个类的所有关键域,并且查看它们是否遵守 equals 合约的所有五个条款。例如,大多数的Set 实现都从Abstract Set 继承 equals 实现,List 实现从 AbstractList 继承 equals 实现,Map 实现从 AbstractMap 继承 equals 实现。如果该类型是个类,也许就能够直接访问参数中的域,这要取决于它们 的可访问性。原创 2023-07-14 09:07:57 · 383 阅读 · 0 评论 -
Effective Java笔记(9)try-with-resources 优先于 try -finally
例如在 firstLineOfFile方法中,如果底层的物理设备异常,那么调用 readLine 就会抛出异常,基于同样的原因,调用 close 也会出现异常。在这种情况下,第二个异常完全抹除了第一个异常。在异常堆枝轨迹中,完全没有关于第一个异常的记录,这在现实的系统中会导致调试变得非常复杂,因为通常需要看到第一个异常才能诊断出问题何在。以 firstLineOfFile 方法为例,如果调用 readLine 和(不可见的) close 方法都抛出异常,后一个异常就会被禁止,以保留第一个异常。原创 2023-07-13 09:56:51 · 219 阅读 · 0 评论 -
Effective Java笔记(8)避免使用终结方法和清除方法
如果用清除方法来清除类的所有实例 ,它的速度 比终结方法会稍微快一些(在我的机器上大约是每个实例花 500ns ),但如果只是把清除方法作为一道安全网( safety net ),下面将会介绍,那么清除方法的速度还会更快一些。第一种用途是,当资源的所有者忘记调用它的 close 方法时,终结方法或者清除方法可以充当样做井不能保证终结方法或者清除方法会被及时地运行,但是在客户端无法正常结束操作的情况下,迟一点释放资源总比永远不释放要好。清除方法没有这个问题,因为使用清除方法的一个类库在控制它的线程。原创 2023-07-13 09:43:57 · 222 阅读 · 0 评论 -
Effective Java笔记(7)消除过期的对象引用
数组活动区域(同前面的定义)中的元素是已分配的( allocated ),而数组其余部分的元素则是自由的( free )。如果一个栈先是增长 ,然后再收缩 , 那么,从栈中弹出来的对象将不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,它们也不会被回收。更为常见的情形则是,“缓存项的生命周期是否有意义”并不是很容易确定,随着时间的推移,其中的项会变得越来越没有价值。不严格地讲,这段程序有一个“内存泄漏”,随着垃圾回收器活动的增加,或者由于内存占用的不断增加,程序性能的降低会逐渐表现出来。原创 2023-07-13 09:25:29 · 299 阅读 · 0 评论 -
Effective Java笔记(6)避免创建不必要的对象
乍看之下 ,好像每次调用 keySet 都应该创建一个新 的 Set 实例,但是,对于一个给定的 Map 对象,实际上每次调用 keySet 都返回同样的 Set 实例。虽然被返回的 Set 实例一般是可改变的,但是所有返回的对象在功能上是等同的 :当其中一个返回对象发生变化的时候,所有其他的返回对象也要发生变化,因为它们是由 同一个 Map 实例支撑的。相反,由于小对象的构造器只做很少量的显式工作,所以小对象的创建和回收动作是非常廉价的,特别是在现代的 JVM 实现上更是如此。原创 2023-07-13 09:13:40 · 596 阅读 · 0 评论 -
Effective Java笔记(5)优先考虑依赖注入来引用资源
这是依赖注入( dependency injection )的一种形式:词典(dictionary )是拼写检查器的一个依赖( depend ency ),在创建拼写检查器时就将词典注入( injected )其中。建议尝试用 SpellChecker 来支持多词典,即在现有的拼写检查器中,设 dictionary域为 nonfinal ,并添加一个方法用它来修改词典,但是这样的设置会显得很笨拙、容易出错,并且无法并行工作。实际上,每一种语言都有自己的词典,特殊同汇还要使用特殊的词典。原创 2023-07-12 14:35:18 · 551 阅读 · 0 评论 -
Effective Java笔记(3)用私有构造器或者枚举类型强化 Singleton 属性
工厂方法返回该类的唯一实例,但是,它很容易被修改,比如改成为每个调用该方法的线程返回一个唯一的实例。否则,每次反序列化一个序列化的实例时,都会创建一个新的实例,比如,在我们的例子中,会导致“假冒的 Elvis”。公有域方法的主要优势在于,API 很清楚地表明了这个类是一个 Singleton:公有的静态域是 final 的,所以该域总是包含相同的对象引用。这种方法在功能上与公有域方法相似,但更加简洁,无偿地提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。原创 2023-07-12 14:22:03 · 1084 阅读 · 0 评论 -
Effective Java笔记(2)遇到多个构造器参数时要考虑使用构建器
幸运的是,还有第三种替代方法,它既能保证像重叠构造器模式那样的安全性,也能保证像 JavaB eans 模式那么好的可读性。如果一开始就使用构造器或者静态工厂,等到类需要多个参数时才添加构造器,就会无法控制,那些过时的构造器或者静态工厂显得十分不协调。程序员一向习惯采用重叠构造器( telescoping cons tructor )模式,在这种模式下,提供的第一个构造器只有必要的参数,第二个构造器有一个可选参数,第三个构造器有两个可选参数,依此类推,最后一个构造器包含所有可选的参数。原创 2023-07-12 14:06:21 · 184 阅读 · 0 评论 -
Effective Java笔记(4)通过私有构造器强化不可实例化的功能
这些类的名声很不好,因为有些人在面向对象的语言中滥用这样的类来编写过程化的程序,但它们也确实有特别的用处。我们也可以通过 java .util .Collections的方式,把实现特定接口的对象上的静态方法,包括工厂方法组织起来。然而 ,在缺少显式构造器的情况下,编译器会自动提供一个公有的 、无参的缺省构造器( default constructor )。所有的构造器都必须显式或隐式地调用超类( superclass )构造器,在这种情形下,子类就没有可访问的超类构造器可调用了。原创 2023-07-11 15:42:02 · 348 阅读 · 0 评论 -
Effective Java笔记(1)用静态工厂方法代替构造器
本条目中所指的静态工厂方法并不直接对应于设计模式( Design Pattern )中的工厂方法。下面是一个来自 Boolean (基本类型 boolean 的装箱类)的简单示例。对于类而言,为了让客户端获取它自身的一个实例,最传统的方法就是提供一个公有的构造器。还有一种方法,类可以提供一个公有的静态 工厂 方法( static factory method ),它只是一个返回类的实例的静态方法。如果不通过公有的构造器,或者说除了公有的构造器之外,类还可以给它的客户端提供静态工厂方法。原创 2023-07-11 15:24:51 · 422 阅读 · 0 评论