
Effective Java 第三版翻译
coloured_glaze
这个作者很懒,什么都没留下…
展开
-
Effective Java 第三版 中文翻译
effective-java-third-edition介绍Effective Java 第三版全文翻译,纯属个人业余翻译,不合理的地方,望指正,感激不尽!目录推荐序前言致谢第一章 引言第二章 创建和销毁对象 本章涉及创建和销毁对象,包括何时以及如何创建它们,何时以及如何避免创建它们,如何确保它们被及时销毁,以及如何管理在销毁之前必须进行的清理操作。第1项:考虑静态工...原创 2019-05-25 08:46:28 · 3652 阅读 · 1 评论 -
第1项:考虑静态工厂方法而不是构造函数
类允许客户端获取实例的传统方法是提供公共构造器。还有一种技术应该是每个程序员的工具箱的一部分。一个类可以提供一个公共静态工厂方法,它仅仅是一个返回类实例的静态方法。下面是布尔(布尔型的盒装原语类)的一个简单示例。这个方法将一个布尔原始值转换成布尔对象引用:public static Boolean valueOf(boolean b) { return b ? Boolean.TRU...原创 2019-03-21 21:27:10 · 365 阅读 · 0 评论 -
第2项:当面临多个参数的构造器时考虑使用构建器
静态工厂和构造器有个共同的局限性:他们都不能很好地扩展到大量的可选参数。考虑用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个域是必需的:每份的含量、每罐的含量以及每份的卡路里,还有超过20个可选域:总脂肪、饱和脂肪量、转化脂肪、胆固醇、钠等等。大多数产品在某几个可选域中都会有非零的值。 对于这样的类,应该采用哪种构造器或者静态方法来编写呢?程序猿一向习惯采用重叠构造器(tele...原创 2019-03-23 10:10:13 · 220 阅读 · 0 评论 -
第3项:用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类 [Gamma95]。Singleton通常代表无状态的对象,例如函数(第24项)或者本质上唯一的系统组件。使类称为Singleton会使它的客户端测试变得十分困难,因为除非它实现了作为其类型的接口,否则不可能将模拟实现替换为单例。 实现单例的方法有两种。 两者都基于保持构造函数私有并导出公共静态成员以提供对唯一实例的访问。 在一种方法中,该成员是f...原创 2019-03-24 08:28:40 · 270 阅读 · 0 评论 -
第4项:通过私有构造器强化不可实例化的能力
有时候你会想要编写一个只包含一组静态方法和静态字段的类。这种类名声很不好,因为有些人为了避免使用面向对象的思维方式而滥用这样的类(some people abuse them to avoid thinking in terms of objects),但是他们确实有它们特有的用处。我们可以使用这种类,以java.lang.Math或者 java.util.Arrays的方式对原始值或数组的相...原创 2019-03-26 20:23:13 · 218 阅读 · 0 评论 -
第5项:固定资源首选使用依赖注入
许多类依赖于一个或多个底层资源。 例如,拼写检查器依赖于字典。常见的做法是将这些类实现为静态实用程序类(第4项):// Inappropriate use of static utility - inflexible & untestable!public class SpellChecker { private static final Lexicon dictionar...原创 2019-03-26 20:25:43 · 399 阅读 · 0 评论 -
第6项:避免创建不需要的对象
一般来说,最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。重用的方式既快速,有流行。如果对象是不可变(immutable)的(第17项),那么就能重复使用它。 作为一个极端的反面例子,考虑下面的语句:String s = new String("bikini"); // DON'T DO THIS! 该语句每次被执行的时候都创建一个新的String实例,但是这些对象...原创 2019-04-06 15:02:22 · 194 阅读 · 0 评论 -
第7项:清除过期对象的引用
当你从手工管理内存的语言(比如C或者C++)转换到具有垃圾回收功能的语言的时候,程序猿的工作就会变得更加容易,因为当你用完了对象之后,他们就会被自动回收。当你第一次经历对象回收功能的时候,会觉得这简直有点不可思议。这很容易给你留下这样的印象,认为自己不再需要考虑内存管理的事情了,其实不然。 考虑下面这个简单的栈实现的例子:// Can you spot the "memory leak"...原创 2019-04-06 15:05:10 · 231 阅读 · 0 评论 -
第8项:避免使用终结方法和清空方法
终结方法是不可预测的,通常很危险,一般情况下是不必要的(Finalizers are unpredictable, often dangerous, and generally unnecessary.)。使用 终结方法会导致行为不稳定,降低性能,以及可移植性问题。当然,终结方法也有可用之处,我们将在本项的最后再做介绍;但是,作为一项规则,我们应该避免使用它们。在Java 9 中,终结方法已经...原创 2019-04-06 15:06:34 · 617 阅读 · 0 评论 -
第9项:尽量使用try-with-resources而不是try-finally(Prefer try-with-resources to try-finally)
Java库包含许多必须通过调用close方法手动关闭的资源。 示例包括InputStream,OutputStream和java.sql.Connection。 关闭资源经常被客户忽视,可预见的可怕性能后果。 虽然其中许多资源使用终结方法作为安全网,但终结方法不能很好地工作(第8项)。 在之前的做法中(Historically),try-finally语句是保证资源正确关闭的最佳方式,即使...原创 2019-04-06 15:07:46 · 1028 阅读 · 0 评论 -
第10项:重写equals时请遵守通用约定
重写equals方法看起来似乎很简单,但是有许多重写方式会导致错误,而且后果非常严重。最容易避免这类问题的办法就是不覆盖equals方法,在这种情况下,类的每个实例都只能与它自身相等。如果满足了以下任何一个条件,那就是正确的做法:类的每个实例都是唯一的。 对于代表活动实体而不是值(value)的类来说确实如此,例如Thread。Object提供的equals实现对这些类具有完全正确的行为...原创 2019-07-07 12:25:24 · 360 阅读 · 0 评论 -
第11项:当重写equals方法时总要重写hashCode方法
在每个重写了equals方法的类中,也必须重写hashCode方法。 如果你没有这么做的话,就会违反Object.hashCode的通用约定,这将让它在HashMap和HashSet等集合中无法正常运作。这里是约定的内容,摘自Onject规范:在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一地...原创 2019-07-07 12:25:57 · 246 阅读 · 0 评论 -
第12项:始终重写toString方法
虽然java.lang.Object提供了toString方法的一个实现,但它返回的字符串通常并不是使用类的用户所期望看到的。它由类名后跟“at”符号(@)和散列码的无符号十六进制表示组成,例如PhoneNumber@163b91。 toString的通用约定中约定了返回的字符串应该是“一个简洁且信息丰富的表示,人们可以很容易阅读。”虽然可以说PhoneNumber@163b91简洁易读,但是...原创 2019-07-07 12:26:24 · 536 阅读 · 0 评论 -
第13项:谨慎地重写clone方法
Cloneable接口的目的是作为对象的一个mixin接口(mixin interface)(第20项),表明这样的类【的对象】是允许克隆的。遗憾的是,它没有达到这个目的。它的主要缺陷是缺少克隆方法,而Object的clone方法【的访问权限】是受保护【protect】的, 如果不采用反射(第65项),就不能仅仅因为它实现了Cloneable而在对象上调用clone方法。即使是反射调用也可能失...原创 2019-07-07 12:26:59 · 1921 阅读 · 0 评论 -
第14项:考虑实现Comparable接口
本章中讨论的其他方法不同,compareTo方法并没有在Object中声明。相反,它是Comparable接口中唯一的方法,它的特征与Object的equals方法类似,只是出了简单的equals比较之外,它还允许进行顺序比较,并且它是通用的。 通过实现Comparable,类表明它的实例具有自然顺序。实现Comparable的对象数组就像这样简单:Arrays.sort(a); 它同...原创 2019-07-07 12:27:33 · 375 阅读 · 0 评论 -
第15项:使类和成员的可访问性最小化
要区分精心设计的组件和设计不好的组件的最重要的因素在于,这个组件对于其他组件而言,是否隐藏了其内部数据和其他市县细节。设计良好的组件会隐藏其所有实现细节,把它的API与它的实现完全分离。组件之间只能通过它们的API进行通信,并且不知道彼此之间内部的工作细节。这种被称为信息隐藏(information hiding )或封装(encapsulation)的概念是软件设计的基本原则[Parnas7...原创 2019-07-07 12:28:06 · 432 阅读 · 0 评论 -
第16项: 在公有类中使用访问方法而非公有域
有时候,可能会编写一些退化类(degenerate classes),没什么用,只是用来集中实例域:// Degenerate classes like this should not be public!class Point { public double x; public double y;} 由于这种类的数据域是可以被直接访问的,这些类没有提供封装(enc...原创 2019-07-07 12:28:40 · 324 阅读 · 0 评论 -
第17项:使可变性最小化
不可变类只是其实例不能被修改的类。每个实例中包含的所有信息在对象的生命周期内都是固定的,因此无法观察到任何更改。 Java平台库包含许多不可变类,包括String,基本类型的包装类以及BigInteger和BigDecimal。这么做有很多好的理由:不可变类更容易设计、实现和使用。它们不容易出错并且更安全。 为了使类称为不可变类,要遵循一下五条原则:不要提供任何修改对象状态的方法(也...原创 2019-07-07 12:29:10 · 445 阅读 · 0 评论 -
第18项:复合优先于继承
继承(inheritance)是实现代码重用的有力手段,但它并非永远是完成这项工作的最佳工具。使用不当会导致软件变得很脆弱。在包的内部使用继承是非常安全的,在那里,子类和超类的实现都处在同一个程序猿的控制之下。对于专门为了继承而设计、并且具有很好的文档说明的类来说(第19项),使用继承也是非常安全的。然而,跨越包边界,从普通的具体类(concrete class)继承,则是非常危险的(Inhe...原创 2019-07-07 12:47:13 · 298 阅读 · 0 评论 -
第19项:要么为继承而设计,并提供文档,要么就禁止继承
第18项提醒我们,对于不是为了继承而设计、并且没有文档说明的“外来”类进行子类化是多么危险。那么对于专门为了继承而设计并且具有良好文档说明的类而言,这又意味着什么呢? 首先,该类的文档必须精确地描述覆盖每个方法所带来的影响。换句话说,该类必须有文档说明它可覆盖(overridable)的方法的自用性(self-use)。 对于每个公有的或受保护的方法或者构造器,它的文档必须指出方法调用哪些...原创 2019-07-07 12:49:50 · 312 阅读 · 0 评论 -
第20项:接口优先于抽象类
Java程序设计语言提供了两种机制,可以用来定义允许多个实现的类型:接口和抽象类。自从Java 8[JLS 9.4.3]中引入接口的默认方法依赖,这两种机制都允许您为某些实例方法提供实现。一个更为重要的区别在于,为了实现由抽象类定义的类型,类必须称为抽象类的一个子类。任何一个类,只要它定义了所有必要的方法,并且遵守通用约定,它就被允许实现一个接口,而不管这个类是处于类层次(class hier...原创 2019-07-07 12:50:22 · 363 阅读 · 0 评论 -
第21项:为“后代”设计接口(design interface for posterity)
在Java 8之前,不可能在不破坏现有实现的情况下向接口添加方法。如果你向接口添加了新方法,现有的【接口实现类】通常会缺少该方法【的实现】,从而导致编译时错误。在Java 8中,添加了默认方法构造[JLS 9.4],目的是允许向现有接口添加方法。但是在现有接口中添加新的方法充满了风险。 默认方法的声明包括一个默认实现,该实现由实现该接口但未实现默认方法的所有类使用。 虽然向Java添加默认...原创 2019-07-08 20:34:44 · 382 阅读 · 0 评论 -
第22项:接口只用于定义类型
当类实现接口时,接口就可以充当引用这个类的实例的类型(type)。因此,类实现了接口,就应该说明客户端可以对这个类的实例做什么。为了任何其他目的而定义接口是不恰当的。 有一种借口被称为常量接口(constant interface),它不满足上面的条件。这种接口没有包含任何方法,它只包含静态的final字段,每个字段都导出一个常量。使用这些常量的类实现这个接口,以避免类名来修饰常量名。下面...原创 2019-07-08 20:34:58 · 292 阅读 · 0 评论 -
第23项:类层次优于标签类
有时候,可能会遇到带有两种甚至更多种风格的实例的类,并包含表示实例风格的标签(tag)字段。例如,考虑下面的这个类,它能够表示圆形或者矩形:// Tagged class - vastly inferior to a class hierarchy!class Figure { enum Shape { RECTANGLE, CIRCLE }; // Tag field -...原创 2019-07-08 20:35:14 · 278 阅读 · 0 评论 -
第24项:静态成员类优于非静态成员类(Favor static member classes over nonstatic)
嵌套类(nested class)是指在另一个类中定义的内部类。嵌套类应该只存在于为其外围类(enclosing class)提供服务。如果嵌套类在某些其他上下文中有用,那么它应该是顶层类(top-level class)。有四种嵌套类:静态成员类,非静态成员类,匿名类和局部类。除第一种之外的其他类型都被称为内部类。在该项中将告诉你何时使用哪种嵌套类及其原因。 静态成员类是最简单的嵌套类。...原创 2019-07-08 21:16:35 · 401 阅读 · 0 评论 -
第25项:限制源文件只有一个顶级类
虽然Java编译器允许您在单个源文件中定义多个顶级类,但这样做没有任何好处,并且存在重大风险。风险源于这样的事实:在源文件中定义多个顶级类可以为类提供多种定义。使用哪种定义受源文件传递给编译器的顺序的影响。 为了使这个事实更加具体点,请参考下面这个源文件,它只包含一个Main类,它引用另外两个顶级类(Utensil和Dessert)的成员:public class Main { ...原创 2019-07-08 21:17:08 · 603 阅读 · 0 评论 -
第26项:不要使用原生态类型
先来介绍一些术语。声明中具有一个或者多个类型参数(type parameter)的类或者接口,就是泛型(generic)类或者接口[JLS, 8.1.2, 9.1.2]。例如,List接口就只有单个类型参数E,表示列表的元素类型。接口的全名是List(读作“E的列表”),但人们通常简称为List。泛型类和接口统称为泛型类型(generic type)。 每种泛型定义一组参数化的类型(par...原创 2019-07-08 21:19:42 · 502 阅读 · 0 评论 -
第27项:消除非受检警告
用泛型编程时,会遇到许多编译器警告:非受检强制转换警告(unchecked cast warnings)、非受检方法调用警告、非受检普通数组创建警告,以及非受检转换警告(unchecked conversion warnings)。当你越来越熟悉泛型之后,遇到的警告也会越来越少,但是不要期待从一开始用泛型编写代码就可以正确地进行编译。 有许多非受检警告很容易消除。例如,假设意外地编写了这样...原创 2019-07-08 21:20:11 · 321 阅读 · 0 评论 -
第28项:列表优先于数组
数组与泛型相比,有两个重要的不同点。首先,数组是协变的(covariant)。这个词听起来有点吓人,其实只是表示如果Sub为Super的子类型,那么数组类型Sub[]就是Super[]的子类型。相反,泛型则是不可变的(invariant):对于任意两个不同的类型Type1和Type2,List既不是List的子类型,也不是List的超类型[JLS, 4.10; Naftalin07, 2.5]...原创 2019-07-08 21:20:41 · 316 阅读 · 0 评论 -
第29项:优先考虑泛型
一般来说参数化声明并使用JDK提供的泛型和方法通常并不困难。编写自己的泛型会比较困难一些,但是值得花些时间去学习如何编写。 考虑到第7项中这个简单(玩具)堆栈的实现:// Object-based collection - a prime candidate for genericspublic class Stack { private Object[] elements;...原创 2019-07-08 21:21:17 · 379 阅读 · 0 评论 -
第30项:优先考虑泛型方法
就如类可以从泛型中受益一般,方法也一样。静态工具方法尤其合适于泛型化。Collections中的所有“算法”方法(例如binarySearch和sort)都泛型化了。 编写泛型方法与编写泛型相类似。例如下面这个方法,它返回两个集合的联合:// Uses raw types - unacceptable! (Item 26) public static Set union(Set s1,...原创 2019-07-08 21:21:49 · 572 阅读 · 0 评论 -
第31项:利用有限制通配符来提升API的灵活性
如28项所述,参数化类型是不可变的(invariant)。换句话说,对于任何两个截然不同的类型Type1和Type2而言,List既不是List的子类型,也不是它的超类型。虽然List不是List的子类型,这与直觉相悖,但是实际上很有意义。你可以将任何对象放进一个List中,却只能将字符串放进List中。因为List不能完成List所能做的所有事情,因此它不是子类型(由Liskov替换主体,第...原创 2019-07-08 21:22:21 · 385 阅读 · 0 评论 -
第32项:谨慎地结合泛型和可变参数(Combine generics and varargs judiciously)
可变参数方法(第53项)和泛型都在Java 5时添加到了平台中,所以你可能会期望它们会优雅地相互作用;可悲的是,它们不能相互作用。可变的目的是允许客户端将数量可变的参数传递给方法,但它是一个漏洞抽象( leaky abstraction):当你调用可变参数方法时,会创建一个数组来保存可变参数;该数组应该是一个实现细节,是可见的。因此,当可变参数具有泛型或者参数化类型时,会出现令人困惑的编译器警...原创 2019-07-08 21:22:52 · 2813 阅读 · 2 评论 -
第33项:优先考虑类型安全的异构容器
泛型最常用于集合,如Set和Map<K, V>,以及单元素容器,例如ThreadLocal 和AtomicReference 。在这些用法中,它都充当被参数化了的容器。这样就限制你没个容器只能有固定数目的类型参数。一般来说,这种情况正是你想要的。一个Set只有一个类型参数,表示它的元素类型;一个Map有两个类型参数,表示它的键和值类型;诸如此类。 但是,有时候你会需要更多的灵活...原创 2019-07-08 21:23:26 · 406 阅读 · 0 评论 -
第34项:用enum代替int常量
枚举类型(enum type)是指由一组固定的常量组成合法值的类型,例如一年中的季节、太阳系中的行星或者一副扑克牌中的花色。在编程语言中还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组具名的int常量,没个类型成员一个常量: // The int enum pattern - severely deficient! public static final int APP...原创 2019-07-08 21:24:14 · 473 阅读 · 0 评论 -
第35项:用实例域代替序数
许多枚举天生就与一个单独的int值相关联。所有的枚举都有一个ordinal方法,它返回每个枚举常量在类型中的数字位置。你可以试着从序数中得到关联的int值:// Abuse of ordinal to derive an associated value - DON'T DO THISpublic enum Ensemble { SOLO, DUET, TRIO, QUARTET,...原创 2019-07-08 21:24:49 · 312 阅读 · 0 评论 -
第36项:用EnumSet代替位域
用EnumSet代替位域 如果一个枚举的元素主要用在集合中,一般就使用int枚举模式(第34项),将2的不同倍数赋予每个常量:// Bit field enumeration constants - OBSOLETE!public class Text { public static final int STYLE_BOLD = 1 << 0; // 1 pub...原创 2019-07-08 21:26:47 · 295 阅读 · 0 评论 -
第37项:用EnumMap代替序数索引(建议结合第二版和原书一起看)
有时候,你可能会见到利用ordinal方法(第35项)索引到数组或列表的代码。例如用下面这个简单的类来表示植物:class Plant { enum LifeCycle { ANNUAL, PERENNIAL, BIENNIAL } final String name; final LifeCycle lifeCycle; Plant(String name...原创 2019-07-09 20:42:23 · 561 阅读 · 0 评论 -
第38项:用接口模拟可扩展的枚举
在几乎所有方面,枚举类型都优于【完爆!】本书第一版[Bloch01]中描述的类型安全枚举模式。 从表面上看,有一个异常与可伸缩性有关,这个异常可能处在原来的模式中,却没有得到语言构造的支持。换句话说,使用这种模式,就有可能让一个枚举类型去扩展另一个枚举类型;利用这种语言特性,则不可能这么做。这绝非偶然。枚举的可伸缩性最后证明基本上不是什么好点子。扩展类型的元素为基本类型的实例,基本类型的实例却...原创 2019-07-09 20:44:01 · 405 阅读 · 0 评论 -
第39项:注解优于命名模式
在之前的做法中(Historically),一般使用命名模式(naming pattern) 表明有些程序元素需要通过某种工具或者框架进行特殊处理。例如,在第4版之前,JUnit测试框架要求其用户通过使用字符test作为测试方法名称的开头[Beck04]。这种方法可行,但它有几个很大的缺点。首先,文字拼写错误会导致失败,并且没有任何提示。例如,假设你不小心将测试方法命名为tsetSafetyO...原创 2019-07-09 20:44:31 · 543 阅读 · 0 评论