Effective Java 3rd-----Chapter 4

Class and Interfaces

CLASSES and interfaces lie at the heart of the Java programming language. They are its basic units of abstraction. The language provides many powerful elements that you can use to design classes and interfaces. This chapter contains guidelines to help you make the best use of these elements so that your classes and interfaces are usable, robust, and flexible.

类和接口是java的根本(核心),是抽象的基本单元。java本身提供了很多功能强大的元素用来设计类和接口。本章包括一些指导原则帮助你使用这些元素设计更加可用,健壮和灵活的类和接口。

Item 15: Minimize the accessibility of classes and members

条目15:尽量降低类和成员的访问权限(主要基于接口(API)需要维护考虑)

The single most important factor that distinguishes a well-designed component from a poorly designed one is the degree to which the component hides its internal data and other implementation details from other components. A well-designed component hides all its implementation details, cleanly separating its API from its implementation. Components then communicate only through their APIs and are oblivious to each others’ inner workings. This concept, known as information hiding or encapsulation, is a fundamental tenet of software design [Parnas72].

区分好坏设计的一个最重要因素是一个类隐藏了内部数据,而另一个类详尽的披露内部实现。设计好的组件隐藏它的所有实现细节,清晰的区分出它的实现和API。组件间的交互只应该通过它们之间的API进行,而其内部工作是另一个组件没有感知的。这个概念被叫做信息隐藏或封装,是软件设计的一个基本信条。

Information hiding is important for many reasons, most of which stem from the fact that it decouples the components that comprise a system, allowing them to be developed, tested, optimized, used, understood, and modified in isolation. This speeds up system development because components can be developed in parallel. It eases the burden of maintenance because components can be understood more quickly and debugged or replaced with little fear of harming other components. While information hiding does not, in and of itself, cause good performance, it enables effective performance tuning: once a system is complete and profiling has determined which components are causing performance problems (Item 67), those components can be optimized without affecting the correctness of others. Information hiding increases software reuse because components that aren’t tightly coupled often prove useful in other contexts besides the ones for which they were developed. Finally, information hiding decreases the risk in building large systems because individual components may prove successful even if the system does not.

信息隐藏如此重要的原因有很多,其中大部分源于信息隐藏可以将组成系统的组件解耦,使组件之间的开发,测试,调优,使用,理解,修改独立成为可能。由于可以各个组件之间并行开发,加快了系统的开发速度。因为组件可以被快速的理解,调试,或者替换时候不怎么用担心对于其他组件的影响,所以减轻了维护压力。信息隐藏本身并不能提高效率,但是它使高效调优称为了可能,一旦系统完成并且确定哪个模块是低效的,这个模块可以被调优不影响其他模块的正确性。由于低耦合的代码经常可以被用在其他上下文环境中,所以信息隐藏可以提高软件的复用。由于即使系统构建失败,模块的构建也是可能是成功的,所以信息隐藏还可以降低构建大型系统的风险。

Java has many facilities to aid in information hiding. The access control mechanism [JLS, 6.6] specifies the accessibility of classes, interfaces, and members. The accessibility of an entity is determined by the location of its declaration and by which, if any, of the access modifiers (private, protected, and public) is present on the declaration. Proper use of these modifiers is essential to information hiding.

Java为达到信息隐藏的目标,提供了很多机制保证。控制访问机制可以特别的制定类,接口和成员的访问控制。实例的访问性,在其声明的位置就使用访问修饰符声明好了。(如果又的话,没有默认是包访问权限)恰当的使用这些修饰符是实现信息隐藏的保障。

The rule of thumb is simple: make each class or member as inaccessible as possible. In other words, use the lowest possible access level consistent with the proper functioning of the software that you are writing.

第一条规则很简单:使每一个类或者成员的控制权限尽量小。换句话说,在保证你写的软件的基本功能的情况下,使用最低的访问等级。

For top-level (non-nested) classes and interfaces, there are only two possible access levels: package-private and public. If you declare a top-level class or interface with the public modifier, it will be public; otherwise, it will be package-private. If a top-level class or interface can be made package-private, it should be. By making it package-private, you make it part of the implementation rather than the exported API, and you can modify it, replace it, or eliminate it in a subsequent release without fear of harming existing clients. If you make it public, you are obligated to support it forever to maintain compatibility.

对于顶层(非嵌套)类和接口,只有两种可能的访问级别:package-private和public。如果你使用public声明一个顶层类或者接口,它就是public的。如果不是,它就是包访问权限的。如果顶层类或者接口可以是包访问权限的,它就应该是。通过使顶层类或者接口是包访问权限,可以在包范围内使用,而不用导出接口,同时你可以修改,替换,升级版本而不用担心影响已经存在的调用了API的客户端。如果你使它使public的。你有义务永远维护它的兼容性。

If a package-private top-level class or interface is used by only one class, consider making the top-level class a private static nested class of the sole class that uses it (Item 24).

If a method overrides a superclass method, it cannot have a more restrictive access level in the subclass than in the superclass [JLS, 8.4.8.3].

如果子类重新父类的方法,子类方法的访问权限不应小于父类。(为了保证里氏替换原则)

A special case of this rule is that if a class implements an interface, all of the class methods that are in the interface must be declared public in the class.

这个情况的特例是,实现接口时候,接口中的所有方法在实现类中必须声明为public。无论其在接口中是否是public

Instance fields of public classes should rarely be public
public类的实例字段应该避免是public的

classes with public mutable fields are not generally thread-safe.
拥有public修饰符修饰的可变字段(非final或reference)是非线程安全的

The same advice applies to static fields, with one exception. You can expose constants via public static final fields, assuming the constants form an integral part of the abstraction provided by the class. By convention, such fields have names consisting of capital letters, with words separated by underscores (Item 68). It is critical that these fields contain either primitive values or references to immutable objects (Item 17). a field containing a reference to a mutable object has all the disadvantages of a nonfinal field. While the reference cannot be modified, the referenced object can be modified—with disastrous results.

这个建议同样适用于静态字段,public static final字段作为一个类的抽象的必要部分可以不遵循这个规定。为了方便这些字段必须用全大写和下划线组成。这个原则适用于原始类型值或者指向不可变类的引用(String)。如果引用指向一个可变类,并且声明为public,将有所有非final字段的风险。虽然引用不能修改,但是,被指像的对象可以被修改造成毁灭性的结果。

Note that a nonzero-length array is always mutable, so it is wrong for a class to have a public static final array field, or an accessor that returns such a field.
长度非零的数组不适合作为public字段,或者提供访问接口被访问。

you can make the array private and add a public method that returns a copy of a private array:
可以返回一个数组类的副本

Item16: In public classes, use accessor methods, not public fields

Item 16:在public类中,使用访问器方法,不要使用public字段

if a class is package-private or is a private nested class, there is nothing inherently wrong with exposing its data fields

包访问权限的类和私有内部类可以暴露数据字段

In summary, public classes should never expose mutable fields. It is less harmful, though still questionable, for public classes to expose immutable fields. It is, however, sometimes desirable for package-private or private nested classes to expose fields, whether mutable or immutable.

public类不应该暴露可变字段,暴露不可变字段伤害相对小一些但是也是值得质疑的。有时对于包私有或者私有的内部类是可行的,无论字段是可变或者不可变。

Item 17: Minimize mutability

Item 17: 最小化可变性

An immutable class is simply a class whose instances cannot be modified. All of the information contained in each instance is fixed for the lifetime of the object, so no changes can ever be observed.
不可变类是指类的实例不可以被改变。类实例的所有信息在对象的生命周期中是固定的,所以无法观察到任何改变。

The Java platform libraries contain many immutable classes, including String, the boxed primitive classes, and BigInteger and BigDecimal.
JAVA中的不可变类有String, 原始类的装箱类, and BigInteger and BigDecimal.

There are many good reasons for this: Immutable classes are easier to design, implement, and use than mutable classes. They are less prone to error and are more secure.
不可变类的好处有,易于设计,实现,使用,不容易出错并且更加安全

To make a class immutable, follow these five rules:
不可变类的五条法则:

  1. Don’t provide methods that modify the object’s state (known as mutators).
    不提供改变对象状态的方法(Mutator(Java 8)如果一个方法改变了调用它的对象,我们就说这是一个更改器方法。)
  2. Ensure that the class can’t be extended. This prevents careless or malicious subclasses from compromising the immutable behavior of the class by behaving as if the object’s state has changed. Preventing subclassing is generally accomplished by making the class final, but there is an alternative that we’ll discuss later.
    保证类不可继承(final)。这样可以防止由于粗心或者恶意的子类削弱不可变类的行为,好像对象的状态发生改变。可以通过final修饰类达到这个目的,后面将会讨论一个更好的方法。
  3. Make all fields final. This clearly expresses your intent in a manner that is enforced by the system. Also, it is necessary to ensure correct behavior if a reference to a newly created instance is passed from one thread to another without synchronization, as spelled out in the memory model [JLS, 17.5; Goetz06, 16].
    使所有的字段为final。这样可以通过系统的强制的方式清晰的表达你的意图。在不使用synchronization关键字的情况下,需要保证新创建的引用对象的线程安全,像内存模型那样(不太明白)
  4. Make all fields private. This prevents clients from obtaining access to mutable objects referred to by fields and modifying these objects directly. While it is technically permissible for immutable classes to have public final fields containing primitive values or references to immutable objects, it is not recommended because it precludes changing the internal representation in a later release (Items 15 and 16).
    使所有字段为private。可以防止客户端,直接获取可变对象的引用,然后直接修改对象。虽然技术上允许不可变类的public final字段指向原生变量值或者不可变对象的引用,但是不推荐使用,因为这样使在今后的版本中修改改变量的内部表示变的不可能。
  5. Ensure exclusive access to any mutable components. If your class has any fields that refer to mutable objects, ensure that clients of the class cannot obtain references to these objects. Never initialize such a field to a client-provided object reference or return the field from an accessor. Make defensive copies (Item 50) in constructors, accessors, and readObject methods (Item 88).

对于任意的可变组件,要保证访问的唯一性。如果你的类有任意字段指向可变对象,保证客户端不能获取对象的引用。永远不要初始化客户端提供的对象引用或者从访问器获得的字段。在构造器(构造函数),访问器(getter)或者反序列化方法中做防御性copy
Many of the example classes in previous items are immutable. One such class
is PhoneNumber in Item 11, which has accessors for each attribute but no corresponding mutators. Here is a slightly more complex example:
在前面的章节中的类很多都是不可变的。其中第11章的PhoneNumber就是,它有每个属性的访问器,但是没有合适的存取器。这有一个略微复杂的例子。
The functional approach may appear unnatural if you’re not familiar with it, but it enables immutability, which has many advantages.
如果你对函数式方法不是很熟悉,会觉得他很不自然,但是函数式方法有不可变性。函数式方法有很多有点。

  1. Immutable objects are simple.
    不可变对象是简单的(状态和创建时一致)
  2. Immutable objects are inherently thread-safe; they require no synchronization.
    不可变对象是隐式线程安全的,不需要synchronization关键字
  3. immutable objects can be shared freely.
    不可变对象可以自由的分享
  4. Not only can you share immutable objects, but they can share their internals.
    不止不可变对象可以被分享,它的内部变量也可以被分享。由于外部无法修改不可变对象的内部变量(即使内部变量是可变的)。所以内部变了可以在多个不可变实例中共享。BigInteger的取反操作只改变了符号位,数值部分的数组是正负两个实例公用的。
  5. Immutable objects make great building blocks for other objects
    不可变对象为其他对象的构建提供了很好的基础,比如String作为map和set的key。不需要担心对象的改变导致摧毁map和set的不可变性
  6. Immutable objects provide failure atomicity for free (Item 76).
    不可变对象免费提供故障原子。(故障原子failure atomicity指在程序抛出异常时,发生异常的对象本身状态不变,比如String的subString方法如果抛出异常,不会影响到原来的String,最多是获取不到subString)
  7. The major disadvantage of immutable classes is that they require a separate object for each distinct value.
    不可变对象的主要缺点是,每个不同的值都需要一个单独的对象

Immutable classes should therefore encourage clients to reuse existing instances wherever possible.One easy way to do this is to provide public static final constants for commonly used values. For example, the Complex class might provide these constants:

   public static final Complex ZERO = new Complex(0, 0);
   public static final Complex ONE  = new Complex(1, 0);
   public static final Complex I    = new Complex(0, 1);

不可变对象应该尽可能的加强客户端对于已经存在的实例的重用.这种情况的一个简单实现是对于大多数情况,提供static final的常量。

This approach can be taken one step further. An immutable class can provide static factories (Item 1) that cache frequently requested instances to avoid creating new instances when existing ones would do.All the boxed primitive classes and BigInteger do this.
更进一步的做法是,提供缓存常用实例的静态工厂,来避免创建一个已经存在的对象。所有装箱基本类和BigInteger就是这么做的。

Opting for static factories in place of public constructors when designing a new class gives you the flexibility to add caching later, without modifying clients.
在设计类时用静态工厂方法替代公有的构造方法,这样可以在不改变客户端的情况下,给你在以后添加缓存的自由。

you need not and should not provide a clone method or copy constructor (Item 13) on an immutable class.
不应该为不可变类提供clone方法和copy构造函数

The package-private mutable companion class approach works fine if you can accurately predict which complex operations clients will want to perform on your immutable class. If not, then your best bet is to provide a public mutable companion class. The main example of this approach in the Java platform libraries is the String class, whose mutable companion is StringBuilder (and its obsolete predecessor, StringBuffer).
如果你能清楚的预测哪一个是复杂操作对于你的不可变类,你可以通过提供包私有的可变组件方法很好的解决问题。如果不能,最好提供一个可变组件。java平台上最主要的例子就是StringBuilder和String的关系。

Now that you know how to make an immutable class and you understand the pros and cons of immutability, let’s discuss a few design alternatives. Recall that to guarantee immutability, a class must not permit itself to be subclassed. This can be done by making the class final, but there is another, more flexible alternative. Instead of making an immutable class final, you can make all of its constructors private or package-private and add public static factories in place of the public constructors (Item 1). To make this concrete, here’s how Complex would look if you took this approach:

用将构造函数私有化或者包私有化并提供静态工厂方法的方式,使类不可被继承。而不使用final class。这样的好处是可以提供更多的设计灵活性,在包内可以对类进行更加灵活的设计。还可以在性能调优的时候提供一定的缓存能力。

The list of rules for immutable classes at the beginning of this item says that no methods may modify the object and that all its fields must be final. In fact these rules are a bit stronger than necessary and can be relaxed to improve performance. In truth, no method may produce an externally visible change in the object’s state.

本章开始说的关于不可变类的规定:没有任何方法可以改变对象的状态,类的字段必须是final的;实际上这个规则过于严格相比实际需求,可以适当放宽要求以提升性能。实际上是,没有任何方法可能会在对象的状态中产生外部可见的变化。可以通过可变对象进行大开销对象的缓存

One caveat should be added concerning serializability. If you choose to have your immutable class implement Serializable and it contains one or more fields that refer to mutable objects, you must provide an explicit readObject or readResolve method, or use the ObjectOutputStream.writeUnshared and ObjectInputStream.readUnshared methods, even if the default serialized form is acceptable.

需要注意类的序列化,如果实现了Serializable接口,并且包含一个或多个指向可变对象的字段,必须显示的提供readObject方法和readResolve方法或者使用 ObjectOutputStream.writeUnshared and ObjectInputStream.readUnshared 方法,尽管默认的序列化方法也可接受。详细会在Item 88中讨论。

Summarize

  1. resist the urge to write a setter for every getter. Classes should be immutable unless there’s a very good reason to make them mutable.
    克制住为每一个getter写setter的欲望。除非有好的理由,否则类应该是不可变的。不可变类的唯一缺点是在特定条件下的性能问题。为小的对象设计不可变类,为大的对象也设计不可变类,但要设计对应的public的可变的组件(String 的StringBuilder)。除非有性能要求,可以破例。
  2. There are some classes for which immutability is impractical. If a class cannot be made immutable, limit its mutability as much as possible.
    对于一些类,设计成不可变类是不现实的。如果类不能设计为不可变的,要尽可能的限制其可变性。
  3. Constructors should create fully initialized objects with all of their invariants established.
    构造方法应该在创建时初始化类的所有内部变量。

Item 18: Favor composition over inheritance

Item 18:用聚合取代继承

Unlike method invocation, inheritance violates encapsulation.
不像方法调用,继承会破坏封装。

为什么不用继承

  1. 继承在调用super方法时,依赖于父类的实现细节,而父类在后续的版本迭代中也许会对实现细节进行更改。
  2. 只添加新的方法,而不是修改原来方法的情况也不太乐观,可能父类在以后的版本迭代中增加了一个和你在子类中添加的一样的方法。会导致,不可预料的错误。

The disadvantages of wrapper classes are few. One caveat is that wrapper classes are not suited for use in callback frameworks, wherein objects pass self-references to other objects for subsequent invocations (“callbacks”). Because a wrapped object doesn’t know of its wrapper, it passes a reference to itself (this) and callbacks elude the wrapper.
包装类的缺点非常少,一个需要注意的点是包装类不适合被用在毁掉框架中,或者说不适合用在将自己的引用传递出去被调用的方法中。因为包装对象不知道被包装对象的是什么,他只是传递一个引用然后调用被包装对象。

Inheritance is appropriate only in circumstances where the subclass really is a subtype of the superclass.
In other words, a class B should extend a class A only if an “is-a” relationship exists between the two classes.
继承只适合子类是父类的子类型的情况。换句话说,B继承A应该只发生在B是A这种关系存在的前提下。

继承前需要明确

  1. 是否是is-a关系
    2 . 继承会继承父类API的缺陷。是否能够接受

summarize

To summarize, inheritance is powerful, but it is problematic because it violates encapsulation. It is appropriate only when a genuine subtype relationship exists between the subclass and the superclass. Even then, inheritance may lead to fragility if the subclass is in a different package from the superclass and the superclass is not designed for inheritance. To avoid this fragility, use composition and forwarding instead of inheritance, especially if an appropriate interface to implement a wrapper class exists. Not only are wrapper classes more robust than subclasses, they are also more powerful.
继承是很强大的方法,但是由于他破坏封装性,会带来一些问题。只有子类和父类存在子类是父类的子类型时,才应该考虑继承。子类和父类不在同一个包下或者父类的设计初衷不是被继承,会导致子类的健壮性很差。为了避免这种弱健壮性,用组合和转发请求到被继承实例,特别是有一个被包装类实现的接口,也可以被实现的时候。不止因为被包装类比子类健壮性强,还因为他们更加功能强大。

Item 19:Design and document for inheritance or else prohibit it

Item 19:设计和文档化继承,或者禁止继承

Item 18 alerted you to the dangers of subclassing a “foreign” class that was not designed and documented for inheritance. So what does it mean for a class to be designed and documented for inheritance?

item 18告诉我们继承一个没有文档和设计的外来类的风险。所以被设计用于继承、文档化的类是什么含义呢。
本节主要介绍内容

  1. the class must document precisely the effects of overriding any method. In other words, the class must document its self-use of overridable methods.
    类必须对任何方法提供精确的重写会产生的影响的文档。换句话说,可继承类必须为自己使用的可充写的方法提供文档。
  2. The only way to test a class designed for inheritance is to write subclasses.
    检测一个被设计用于继承的类的唯一办法就是写子类
  3. you must test your class by writing subclasses before you release it.
    在发布被设计用于继承的类之前,一定要写子类测试

For each public or protected method, the documentation must indicate which overridable methods the method invokes, in what sequence, and how the results of each invocation affect subsequent processing.
对于每一个public或者protected方法,都需要文档纪录这个方法调用了哪些重写方法,用什么顺序调用的,每个方法的结果对接下来的子流程有什么影响。

More generally, a class must document any circumstances under which it might invoke an overridable method. For example, invocations might come from background threads or static initializers.

一般的,一个类必须为任何情况下的可能调用重写方法的地方提供文档。例如,可能被后台线程调用或者被静态初始化方法调用。

A method that invokes overridable methods contains a description of these invocations at the end of its documentation comment. The description is in a special section of the specification, labeled “Implementation Requirements,” which is generated by the Javadoc tag @implSpec. This section describes the inner workings of the method. Here’s an example, copied from the specification for java.util.AbstractCollection:

The @implSpec tag was added in Java 8 and used heavily in Java 9. This tag should be enabled by default, but as of Java 9, the Javadoc utility still ignores it unless you pass the command line switch -tag "apiNote:a:API Note:".
关于注解的使用

a class may have to provide hooks into its internal workings in the form of judiciously chosen protected methods
一个类可能必须提供钩子,在内部工作的时候,比较理智的办法是选择protected的方法

Ps.注释的写法是写作用,调用了哪些重写方法,应该如何实现

设计被继承类的时候的规范:

  1. Constructors must not invoke overridable methods
    构造方法不能调用重写方法。直接或者间接都不行
    If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will get invoked before the subclass constructor has run.
    违反这个规则,程序会导致失败的结果。由于父类的构造方法在子类的构造方法之前调用,所以子类的状态是未初始化的,会导致子类的重写方法发生不可预期的错误。
  2. The Cloneable and Serializable interfaces present special difficulties when designing for inheritance. It is generally not a good idea for a class designed for inheritance to implement either of these interfaces because they place a substantial burden on programmers who extend the class.
    被设计用于被继承的类实现Cloneable and Serializable接口是非常困难的。对于被设计用于继承的类,实现这两个方法中的任何一个都不是一个好主意,因为这对于打算继承这个类的开发人员来说是一个明显的重担。
  3. neither clone nor readObject may invoke an overridable method, directly or indirectly.In the case of readObject, the overriding method will run before the subclass’s state has been deserialized. In the case of clone, the overriding method will run before the subclass’s clone method has a chance to fix the clone’s state.In either case, a program failure is likely to follow. In the case of clone, the failure can damage the original object as well as the clone.
    如果实在需要实现以上两个接口,无论是clone方法还是readObject方法都不要调用重写方法,无论直接或者间接。在readObject方法的情况中,子类重写的方法会在子类初始化完成前运行。对于clone方法的情况中,子类的重写方法会在子类clone方法修复clone状态前被调用。可能clone失败会摧毁被克隆对象的原始副本。
  4. Finally, if you decide to implement Serializable in a class designed for inheritance and the class has a readResolve or writeReplace method, you must make the readResolve or writeReplace method protected rather than private.
    如果决定实现Serializable要使readResolve or writeReplace 为protect的,不要是private。

There are some situations where it is clearly the right thing to do, such as abstract classes, including skeletal implementations of interfaces (Item 20). There are other situations where it is clearly the wrong thing to do, such as immutable classes (Item 17)
设计被继承类正确的事:用抽象类,包含实现的框架。不推荐的事:比如不可变类。

The best solution to this problem is to prohibit subclassing in classes that are not designed and documented to be safely subclassed.
对于既不是不可变类又不是被设计用于继承的类A,如果A被继承会产生,A的状态改变导致子类不可用。对于A这种类,最好的办法是禁止继承。

禁止继承的两种方法

  1. 声明类是final的。
  2. 使构造函数private或者package-private,然后添加静态public工厂方法。

If a concrete class does not implement a standard interface, then you may inconvenience some programmers by prohibiting inheritance. If you feel that you must allow inheritance from such a class, one reasonable approach is to ensure that the class never invokes any of its overridable methods and to document this fact.
如果一个实体类没有实现任何标准接口,你需要禁止继承来。如果觉得必须允许继承,一个有理由的方法是确保这个类从未调用过任何可重写的方法,然后文档化这个事实。

You can eliminate a class’s self-use of overridable methods mechanically, without changing its behavior. Move the body of each overridable method to a private “helper method” and have each overridable method invoke its private helper method. Then replace each self-use of an overridable method with a direct invocation of the overridable method’s private helper method.
可以通过提供私有帮助方法的形式,规避self-use问题。

Summary

In summary, designing a class for inheritance is hard work. You must document all of its self-use patterns, and once you’ve documented them, you must commit to them for the life of the class. If you fail to do this, subclasses may become dependent on implementation details of the superclass and may break if the implementation of the superclass changes. To allow others to write efficient subclasses, you may also have to export one or more protected methods. Unless you know there is a real need for subclasses, you are probably better off prohibiting inheritance by declaring your class final or ensuring that there are no accessible constructors.
总的来说,设计一个被用于继承的类是一个艰难的工作。必须文档化所有self-use模式,并且一旦文档化必须终身保证。如果不能保证,会导致子类依赖父类的实现细节,并且会导致问题如果父类发生了变化。为了给子类提供性能优化的空间,需要将一个或多个方法protected。应该通过final关键字或者私有化构造方法,禁止继承,除非你能确定这个类确实需要继承。

Item 20: Prefer interfaces to abstract class

Item 20: 多用接口少用抽象类

classes win over interfaces, derived interfaces win over superinterfaces, and any other conflicts are resolved by the implementing class.
方法覆盖的顺序,类方法覆盖接口方法,衍生接口方法覆盖父接口方法,其他冲突需要实现类取解决。

接口的限制
There are limits on how much implementation assistance you can provide with default methods. Although many interfaces specify the behavior of Object methods such as equals and hashCode, you are not permitted to provide default methods for them. Also, interfaces are not permitted to contain instance fields or nonpublic static members (with the exception of private static methods). Finally, you can’t add default methods to an interface that you don’t control.
对于默认接口方法提供的帮助有一下限制。默认接口方法不可以重写Object 类的方法(语法检查限制)。(设计这个限制有两个原因,一是重写接口时,继承的优先级高,所有类继承自Object类,重写在接口中的方法,会被Objcet方法覆盖。二是从设计角度讲toString,hashCode,equals方法都是和实例的状态相关的,而接口是一个无状态的type,所以不应该在接口中提供默认方法)接口也不能包含实例字段,非public的静态成员(静态私有方法除外,java9提供新特性)最后,你不能添加一个默认方法到一个你不能控制的接口中。

Writing a skeletal implementation is a relatively simple, if somewhat tedious, process. First, study the interface and decide which methods are the primitives in terms of which the others can be implemented. These primitives will be the abstract methods in your skeletal implementation. Next, provide default methods in the interface for all of the methods that can be implemented directly atop the primitives, but recall that you may not provide default methods for Object methods such as equals and hashCode. If the primitives and default methods cover the interface, you’re done, and have no need for a skeletal implementation class. Otherwise, write a class declared to implement the interface, with implementations of all of the remaining interface methods. The class may contain any nonpublic fields ands methods appropriate to the task.
写一个骨架实现是很简单的:

  1. 看看哪些基本方法需要被重写,然后将该方法变为abstract的。(基本方法是其他方法调用的方法)
  2. 在接口中为那些可以使用基本方法实现的方法,提供默认实现。但是不要覆盖Object的方法。
  3. 如果以上两种方法覆盖了接口。就完成了。这个时候就不需要骨架类了。
  4. 如果不是,需要声明一个类实现接口,然后完成剩下的方法。(剩下的方法应该是和状态相关的或者是toString,equals,hashCode等不允许被接口提供给默认实现的)别忘了提供文档
    ps.类包含很多非公有字段和方法用于实现任务。

Summary

To summarize, an interface is generally the best way to define a type that permits multiple implementations. If you export a nontrivial interface, you should strongly consider providing a skeletal implementation to go with it. To the extent possible, you should provide the skeletal implementation via default methods on the interface so that all implementors of the interface can make use of it. That said, restrictions on interfaces typically mandate that a skeletal implementation take the form of an abstract class.

总的来说,接口是多实现的最理想实现方式。如果提供了一个普通接口,应该提供一个健壮的骨架实现类。为了提升可扩展性,应该通过在接口中增加default方法,来使所有实现类使用这个方法。也就是说,对于接口授权的限制(),可以通过使用抽象类作为骨架方法实现。

Item 21:Design interfaces for posterity

Item 21:设计向后兼容的接口

Prior to Java 8, it was impossible to add methods to interfaces without breaking existing implementations. If you added a new method to an interface, existing implementations would, in general, lack the method, resulting in a compile-time error. In Java 8, the default method construct was added [JLS 9.4], with the intent of allowing the addition of methods to existing interfaces. But adding new meth- ods to existing interfaces is fraught with risk.
java8之后,可以向interface中增加default方法,使的为已经设计好的接口中添加方法成为了可能,(原来会报编译期错误)提供一个default实现,不过还是充满风险的。

But it is not always possible to write a default method that maintains all invariants of every conceivable implementation.
在添加默认方法时,维护所有实现类的不变是很困难的。java8之后也不建议通过在接口中增加默认方法的形式对接口进行扩展。因为,接口设计之初是默认接口是不会被扩展新方法的。(org.apache.commons.collections4.collection.SynchronizedCollection该类包装传进来的集合类,并为每一个方法提供同步包装,使每个方法变成同步方法。如果该类和java8一起用,由于java8在Collection接口中增加了removeIf方法,如果该类不重写这个方法,或者说没重写这个方法的版本和java8一起工作,就会破坏该类的设计初衷:为集合类提供所有同步方法)

Summary

Therefore, it is critically important to test each new interface before you release it. Multiple programmers should implement each interface in different ways. At a minimum, you should aim for three diverse implementations. Equally important is to write multiple client programs that use instances of each new interface to perform various tasks. This will go a long way toward ensuring that each interface satisfies all of its intended uses. These steps will allow you to discover flaws in interfaces before they are released, when you can still correct them easily. While it may be possible to correct some interface flaws after an interface is released, you cannot count on it.

在发布接口之前测试,是很重要的。一千个程序员对同一个接口会有一千种实现。你最少要对准三种实现。同样重要的是,要写多个客户端使用每个新接口的实例取完成不同任务。这将很大程度上保证每个接口满足所有的预期使用方法。使用多个客户端使用接口的实例测试会使你在接口发布前,发现它的一些瑕疵,在你能很轻易修复他们的时候。虽然在发布接口后仍然可以修正它的一些缺陷,但不应该寄希望于此。

Item 22:Use interface only to define types

Item 22:只使用接口定义类型

When a class implements an interface, the interface serves as a type that can be used to refer to instances of the class. That a class implements an interface should therefore say something about what a client can do with instances of the class. It is inappropriate to define an interface for any other purpose.

当一个类实现一个接口,这个接口可以用于指向这个类的实例。一个类实现一个接口,是再说一个客户端可以用这个实例做什么。定义一个接口用于除了声明这个类的能做什么的功能外,其他都是不合适的。

The constant interface pattern is a poor use of interfaces.
常量接口模式是一个接口的滥用

If the constants are strongly tied to an existing class or interface, you should add them to the class or interface. For example, all of the boxed numerical primitive classes, such as Integer and Double, export MIN_VALUE and MAX_VALUE constants. If the constants are best viewed as members of an enumerated type, you should export them with an enum type (Item 34). Otherwise, you should export the constants with a non-instantiable utility class (Item 4).

如果必须存在常量可以使用枚举或使用不可实例化的工具类。

If you make heavy use of the constants exported by a utility class, you can avoid the need for qualifying the constants with the class name by making use of the static import facility:
如果尽量使用工具类导出常量,可以通过使用静态倒入工厂来避免使用类限定名使用类常量

// Use of static import to avoid qualifying constants
import static com.effectivejava.science.PhysicalConstants.*;
public class Test {
    double atoms(double mols) {
        return AVOGADROS_NUMBER * mols;
    }
    ... 
    // Many more uses of PhysicalConstants justify static import 
} 

Summary

In summary, interfaces should be used only to define types. They should not be used merely to export constants.
总的来说,接口只应该被用来定义类型,不应该仅仅用于导出常量。

Item 23: Prefer class hierarchies to tagged classes

Item 23: 使用继承来标记类型(不要使用标记字段)

// Tagged class - vastly inferior to a class hierarchy!
class Figure {
    enum Shape { RECTANGLE, CIRCLE };
    // Tag field - the shape of this figure
    final Shape shape;
    // These fields are used only if shape is RECTANGLE
    double length;
    double width;
    // This field is used only if shape is CIRCLE
    double radius;
    // Constructor for circle
    Figure(double radius) {
        shape = Shape.CIRCLE;
        this.radius = radius;
    }
    // Constructor for rectangle
    Figure(double length, double width) {
        shape = Shape.RECTANGLE;
        this.length = length;
        this.width = width;
    } 
    double area() {
        switch(shape) {
          case RECTANGLE:
            return length * width;
          case CIRCLE:
            return Math.PI * (radius * radius);
          default:
            throw new AssertionError(shape);
        } 
    } 
} 

上面的类使用final字段shape来标示类是圆形或者长方形

Such tagged classes have numerous shortcomings. They are cluttered with boilerplate, including enum declarations, tag fields, and switch statements. Read- ability is further harmed because multiple implementations are jumbled together in a single class.

这种标记类有许多缺点。 它们杂乱无章,包括枚举声明,标记字段和开关语句。 可读性受到进一步损害,因为多个实现混合在一个类中。

Item 24: Favor static member classes over nonstatic

Item 24: 多使用静态成员类

A nested class is a class defined within another class. A nested class should exist only to serve its enclosing class. If a nested class would be useful in some other context, then it should be a top-level class. There are four kinds of nested classes: static member classes, nonstatic member classes, anonymous classes, and local classes. All but the first kind are known as inner classes. This item tells you when to use which kind of nested class and why.

嵌套类是一种在其他类内部定义的类。嵌套类应该只服务于他的外层类。如果一个嵌套类在其他地方被使用,应该将他变成顶层类。有四种嵌套类:静态成员类,非静态成员类,匿名类,本地类。第一种呗叫做内部类。本章告诉你什么时候使用什么内部类,以及为什么。

A static member class is the simplest kind of nested class. It is best thought of as an ordinary class that happens to be declared inside another class and has access to all of the enclosing class’s members, even those declared private. A static member class is a static member of its enclosing class and obeys the same accessibility rules as other static members. If it is declared private, it is accessible only within the enclosing class, and so forth.

静态成员类是最简单的一种嵌套类。最好将它想象为可以在类内部声明的,可以访问外部类所有成员(甚至是private 的)的普通类。静态成员类,是外部类的一个静态成员,和其他静态成员遵守一样的访问规则。如果静态成员类被定义为private的,它只可以被内部访问。

One common use of a static member class is as a public helper class, useful only in conjunction with its outer class.
静态成员类的最普通应用就是作为外部类的辅助类,比如Calculator的Operation类。

Despite the syntactic similarity, these two kinds of nested classes are very different. Each instance of a nonstatic member class is implicitly associated with an enclosing instance of its containing class. Within instance methods of a nonstatic member class, you can invoke methods on the enclosing instance or obtain a reference to the enclosing instance using the qualified this construct [JLS, 15.8.4]. If an instance of a nested class can exist in isolation from an instance of its enclosing class, then the nested class must be a static member class: it is impossible to create an instance of a nonstatic member class without an enclosing instance.

尽管静态成员类和非静态成员类在语法上是相似的,他们之间仍有许多的不同。非静态成员类的实例和其外层类的实例是关联的。非静态成员类的实例方法可以访问外层类或者通过this获得外层类的引用。嵌套类的一个实例如果可以脱离其外层类实例单独存在,该嵌套类应该声明为静态成员类:不通过外层类实例创建非静态成员类的实例是不可能的。

The association between a nonstatic member class instance and its enclosing instance is established when the member class instance is created and cannot be modified thereafter. Normally, the association is established automatically by invoking a nonstatic member class constructor from within an instance method of the enclosing class. It is possible, though rare, to establish the association manually using the expression enclosingInstance.new MemberClass(args). As you would expect, the association takes up space in the nonstatic member class instance and adds time to its construction.

非静态成员实例和外层实例的关系在成员类创建的时候建立,并且不可改变。关系在外层类的方法调用非静态成员类的构造函数的时候建立。使用enclosingInstance.new MemberClass(args)这种方法创建非静态成员类是不太常见的。像你想到的一样,这种关联会消耗内存并且增加构造时间。

One common use of a nonstatic member class is to define an Adapter [Gamma95] that allows an instance of the outer class to be viewed as an instance of some unrelated class.

非静态成员类的一个常见用法是作为适配器,可以让外层类的实例被看作是一个毫不相关的类。比如,集合类和其Iterator

// Typical use of a nonstatic member class
public class MySet<E> extends AbstractSet<E> {
    ... // Bulk of the class omitted
    @Override
    public Iterator<E> iterator() {
        return new MyIterator();
    }
    private class MyIterator implements Iterator<E> { 
          ... 
    }
}

If you declare a member class that does not require access to an enclosing instance, always put the static modifier in its declaration, making it a static rather than a nonstatic member class. If you omit this modifier, each instance will have a hidden extraneous reference to its enclosing instance. As previously mentioned, storing this reference takes time and space. More seriously, it can result in the enclosing instance being retained when it would otherwise be eligible for garbage collection (Item 7). The resulting memory leak can be catastrophic. It is often difficult to detect because the reference is invisible.

成员类不需要访问外层类的实例,应该将其声明为静态的,静态的比非静态的好。如果忽略了static修饰符,每个实例会隐藏的包含一个没用的指向外层类的引用,这样浪费时间和空间。更严重的,这样会使外层实例在本应该垃圾回收时无法被回收,造成内存泄漏,并且很难排查,因为引用是不可见的。

A common use of private static member classes is to represent components of the object represented by their enclosing class. For example, consider a Map instance, which associates keys with values.
静态私有成员类的典型应用是作为外围类表示的对象的组件。比如Map和Entry

As you would expect, an anonymous class has no name. It is not a member of its enclosing class. Rather than being declared along with other members, it is simultaneously declared and instantiated at the point of use.

像你知道的一样,匿名类是没有名字的。它不是外围类的一个成。相比于被声明和其他成员声名,他只是简单的声明为了接下来的使用。

There are many limitations on the applicability of anonymous classes. You can’t instantiate them except at the point they’re declared. You can’t perform instanceof tests or do anything else that requires you to name the class. You can’t declare an anonymous class to implement multiple interfaces or to extend a class and implement an interface at the same time. Clients of an anonymous class can’t invoke any members except those it inherits from its supertype. Because anonymous classes occur in the midst of expressions, they must be kept short— about ten lines or fewer—or readability will suffer.

匿名类在使用的时候有很多限制。你只能在声明他的地方实例化。不能使用instanceof语法,或者其他任意需要使用到类名的地方。不能声明一个匿名类去实现多个接口,或者继承一个类的同时实现接口。匿名类只能调用从其继承结构中继承下来的方法。因为匿名类生命在程序之间,所以要尽量的短,否则会降低程序的可读性。

Before lambdas were added to Java (Chapter 6), anonymous classes were the preferred means of creating small function objects and process objects on the fly, but lambdas are now preferred (Item 42). Another common use of anonymous classes is in the implementation of static factory methods (see intArrayAsList in Item 20).

在lambdas加入到JAVA之前,匿名类作为一个小的函数对象存在,使的处理更加轻快。现在更加推崇lambdas表达式。另一个匿名类的用法是在一个静态工厂中作为实现使用。

static List<Integer> intArrayAsList(int[] a) {
    Objects.requireNonNull(a);
    // The diamond operator is only legal here in Java 9 and later // If you're using an earlier release, specify <Integer> return new AbstractList<>() {
    return new AbstractList<Integer>() {
        @Override
        public Integer get(int i) {
            return a[i];  // Autoboxing (Item 6)
        }
        @Override
        public Integer set(int i, Integer val) {
            int oldVal = a[i];
            a[i] = val;     // Auto-unboxing
            return oldVal;  // Autoboxing
        }
        @Override
        public int size() {
            return a.length;
        }
    };
}

Local classes are the least frequently used of the four kinds of nested classes. A local class can be declared practically anywhere a local variable can be declared and obeys the same scoping rules. Local classes have attributes in common with each of the other kinds of nested classes. Like member classes, they have names and can be used repeatedly. Like anonymous classes, they have enclosing instances only if they are defined in a nonstatic context, and they cannot contain static members. And like anonymous classes, they should be kept short so as not to harm readability.

本地类是使用频率最低的嵌套类。本地类几乎可以声明在任意变量可以声明的地方,并且遵守变量的作用域规则。本地类和其他三种嵌套类的每一种都有一些相同的地方。和成员类(静态成员类和非静态成员类)一样,他有名字,可以被重复使用。和匿名类一样,它拥有外围类的实例,在他被声明在非静态上下文中的时候,如果被声明在静态上下文,他不可以拥有外围类的实例。和匿名类一样他应该尽可能短来保证程序的可读性。

Summary

To recap, there are four different kinds of nested classes, and each has its place. If a nested class needs to be visible outside of a single method or is too long to fit comfortably inside a method, use a member class. If each instance of a member class needs a reference to its enclosing instance, make it nonstatic; otherwise, make it static. Assuming the class belongs inside a method, if you need to create instances from only one location and there is a preexisting type that characterizes the class, make it an anonymous class; otherwise, make it a local class.

总的来说,有四种嵌套类,每种的定位都不同。如果一个类需要在单个方法外仍然是可见的,或者太长不适合放在方法内部,就使用成员类。并且如果每个实例有需要有外围类的引用,就使用费静态,否则就使用静态的。(只有成员类需要每个实例都拥有外部实例的时候,才使用非静态)方法内部的嵌套类,你只需要在一个地方创建他,并且已经预先存在一个类型可以说明这个类的特征,使用匿名类,否则,用本地类。

Item 25:Limit source files to a single top-level class

Item 25:一个源文件只有一个顶级类

If you’re lucky enough to compile the program with the command javac Main.java Dessert.java, the compilation will fail, and the compiler will tell you that you’ve multiply defined the classes Utensil and Dessert. This is so because the compiler will first compile Main.java, and when it sees the reference to Utensil (which precedes the reference to Dessert), it will look in Utensil.java for this class and find both Utensil and Dessert. When the compiler encounters Dessert.java on the command line, it will pull in that file too, causing it to encounter both definitions of Utensil and Dessert.

用命令行编译时候,如果先编译Main.java,会在遇到Utensil的引用时候编译Utensil文件,然后再编译Utensil文件的时候,会报重复定义类Utensil and Dessert.如果先编译Utensil文件则不会出问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,096评论 0 10
  • **2014真题Directions:Read the following text. Choose the be...
    又是夜半惊坐起阅读 8,534评论 0 23
  • 正文之前 在之前写的JavaWeb项目中使用了JDBC,在此来回顾一下,并做个demo看看,先来看看JDBC的概念...
    胖若两人_阅读 2,314评论 0 1
  • 今天在工作上犯下了弥天大错 虽然结局没有太坏 但是心理上也该吓坏吓傻了 我到底该怎么办 这个工作 怎么干
    C小宁willbe女王阅读 181评论 0 0
  • 2018.2.17 农历戊戌狗年甲寅月庾晨日 星期六 16°C-9°C 题画梅 【明】徐渭 出来不见梅花谱, 信手...
    王译婕0阅读 717评论 0 0