学长写的,转来自勉

原文链接 http://fengshenwu.com/blog/2011/10/28/new_learning_java_introduction/

笔者在java的圈子里面已经有4年多了。积累了一些经验,此文后面是笔者对未来的猜测,前面是自己经历过的。

干我们程序员这行,就是要吃苦,加班写代码,加班看书。不过,反过来想,我们在扮演上帝的角色,还很开心的。我们的世界可能也是某个外星程序员写代控制的呢。

干我们程序员这行,要有兴趣、激情、越爱这份事业,不然,很难做出成绩。兴趣、激情这东西,也是要与工作结合起来,相辅相成。笔者不做过多评论。 笔者讲java什么呢?在次我不评论java与c谁优谁劣,也不讨论谁的技术含量高,可以说各有千秋,笔者只讲选择了java的同学以后怎么一路走过来。

笔者大致把java进阶分为几个阶段。一些阶段与java没有太多的关系的。此文最适合大学生了,可以认为是进入java世界的一个导论吧。不过你要了解基础的算法,数据结构,体系结构哦。首先你要明白,你想走技术路线还是业务路线。以下是讲技术路线的。

第一个阶段

初级工程师。就是要了解清楚java的语法及面向对象的知识。推荐的书籍是《java编程思想》或者一些java入门的书籍。如果想快速的了解,可以看视频,推荐孙鑫的《java入门视频教程》。在此阶段,你要至少写上几万行java的代码,对java的基础知识有所了解,如:java的语法、java的集合、注解、泛型、IO等。此阶段一般要持续半年到一年左右。此阶段是java的基础,可以自己写写记事本,计算器,小工具等。

第二个阶段

中级工程师。此阶段我们一般在公司就业了。 提高编写代码的能力。此阶段看的书籍比较多。在此阶段,你会发现第一阶段写的代码很糟糕,很复杂,自己看以前的代码也会很费解,反正,就认为以前写的代码就是烂。推荐的书籍有《设计模式–可复用面向对象软件的基础》、《Head First 设计模式》、《重构:改善既有代码的设计》、《Effective Java中文版》等 在此阶段,你可能要做很多的项目,这些项目要使你接触很多的技术。可以说要对J2EE规范有一个大体的了解。如:JMS、POJO、序列化、MVC等。对spring、apache MQ、ibatis、apache、jetty、maven、oracle、缓存机制、分布式、负载均衡、监控、网站架构、开发平台、云、事件模型 有一些接触与了解。此阶段一般在一年到2年左右。

第三个阶段

高级工程师。这个时候你就要考虑选择方向了,在这个时候你基本是一个合格的程序员了。一般的方向有:性能优化、项目管理、网站架构师、某个领域的技术专家等 此阶段有多久呢。。。不好说啊。可能很久吧。。。

  • 对于性能优化,看的东西大致就比较深入了,一般看的书有《JAVA并发编程实践》、《linux内核》系列,《深入理解JAVA虚拟机》《分布式JAVA应用》《构建高性能Web站点》当然对于一些常见的工具你得熟悉JMETER、APACHE、JETTY、JBOSS等。
  • 对于网站架构师,一般涉及到架构层面的东西了,要多与其他公司交流。知识点:一般是SOA、EDA等、书籍推荐《SOA实践指南——分布式系统设计的艺术》等。当然你还要研究许多的开源框架,如:APACHE、JETTY、JBOSS、MAVEN、SPRING等等等。。。。
  • 对于项目管理,也就是说你从工程师变成项目经理了,你要带项目了,有时候也顺便带人。其实跟JAVA没有太多的关系,这个阶段看书基本很难有突破的,一般要实地带人,带项目。推荐的书是:《敏捷软件开发:原则、模式与实践》因为你是开发出身,看这本身有好处。另外就是《项目修炼之道了》
  • 技术专家。此点就是专注专注自己的一块,可以是某个产品、可以是某种技术。例如:java JVM研究、LINUX内核定制、OSGI、静态代码检测、CORE JAVA、工具开发、JETTY定制等

第四个阶段

咨询师、专家。到了这个阶段,估计你就有大量的从业经验了。这个基本是从网站架构师转化过来的。这个阶段第一要会忽悠。呵呵。。。这个也是本事啊。一些软能力开始发挥较大的作用。如:交流能力、管理能力、英语能力。。。

第五个阶段

高级专家、骨灰级别技术专家等了。他们已经在一些技术领域有较大的成就了。可以说在业界有一定的名气了。君不见Qcon演讲的嘉宾,君不见白发的老头程序员。呵呵 。。。他们是怎么来的。坚持啊。。。。

计算机技能都是在做项目的时候进步最快了,都不能闭门造车,要多与人交流。
尽量找自己喜欢的领域,再做自己喜欢的事情。不要在第二个阶段太久,要到第三个阶段,选择自己的方向。
不管你做什么,尽量多地写技术博客、看技术资讯、多逛技术论坛。多研究研究开源的产品。
最后,祝福你在java的世界里面航程原来原远,祝福你成为高级专家。

第3条:用私有构造器或者枚举类型强化Singleton属性

Item 3:Enforce the singleton property with a private constructor or an enum type

Singleton指仅仅被实例化一次的类[Gamma95, p. 127]。Singleton通常被用来代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统。使类成为Singleton会使它的客户端测试变得十分困难,因为无法给Singleton替换模拟实现,除非它实现一个充当其类型的接口。

A singleton is simply a class that is instantiated exactly once [Gamma95, p. 127]. Singletons typically represent a system component that is intrinsically unique, such as the window manager or file system. Making a class a singleton can make it difficult to test its clients, as it’s impossible to substitute a mock implementation for a singleton unless it implements an interface that serves as its type.

在Java1.5发行版本之前,实现Singleton有两种方法。这两种方法都要把构造器保持为私有的,并导出公有的静态成员,以便允许客户端能够访问该类的唯一实例。在第一种方法中, 公有静态成员是个final域:

Before release 1.5, there were two ways to implement singletons. Both are based on keeping the constructor private and exporting a public static member to provide access to the sole instance. In one approach, the member is a final field:

// Singleton with public final field
public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() {... }
	
    public void leaveTheBuilding() { ... }
}

私有构造器仅被调用一次,用来实例化公有的静态final域Elvis.INSTANCE。由于缺少公有的或者受保护的构造器,所以保证了的全局唯一性:一旦Elvis类被实例化,只会存在一个Elvis实例,不多也不少。客户端的任何行为都不会改变这一点,但要提醒一点:享有特权的客户端可以借助AccessibleObject.setAccessible方法,通过反射机制(见第53条)调用私有构造器。如果需要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候 抛出异常。

The private constructor is called only once, to initialize the public static final field Elvis.INSTANCE. The lack of a public or protected constructor guarantees a “monoelvistic” universe: exactly one Elvis instance will exist once the Elvis class is initialized—no more, no less. Nothing that a client does can change this, with one caveat: a privileged client can invoke the private constructor reflectively (Item 53) with the aid of the AccessibleObject.setAccessible method. If you need to defend against this attack, modify the constructor to make it throw an exception if it’s asked to create a second instance.

在实现Singleton的第二种方法中,公有的成员是个静态工厂方法:

In the second approach to implementing singletons, the public member is a static factory method:

// Singleton with static factory
public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() {...}
    public static Elvis getInstance() {return INSTANCE;}
	
    public void leaveTheBuilding() { ... }
}

对于静态方法Elvis.getInstance的所有调用,都会返回同一个对象引用,所以,永远不会创建其他的Elvis实例(上述提醒依然适用)。

All calls to Elvis.getInstance return the same object reference, and no other Elvis instance will ever be created (with the same caveat mentioned above).

公有域方法的主要好处在于,组成类的成员的声明很清楚地表明了这个类是一个Singleton:公有的静态域是final的,所以该域将总是包含相同的对象引用。公有域方法在性能上不再有任何优势:现代的JVM(Java虚拟机,Java Virtual Machine)实现几乎都能够将静态工厂方法的调用内联化。

The main advantage of the public field approach is that the declarations make it clear that the class is a singleton: the public static field is final, so it will always contain the same object reference. There is no longer any performance advantage to the public field approach: modern Java virtual machine (JVM) implementations are almost certain to inline the call to the static factory method.

工厂方法的优势之一在于,它提供了灵活性:在不改变其API的前提下,我们可以改变该类是否应该为Singleton的想法。工厂方法返回该类的唯一实例,但是,它可以很容易被修改, 比如改成为毎个调用该方法的线程返回一个唯一的实例。第二个优势与泛型(见第27条)有关。这些优势之间通常都不相关,public域(public-field)的方法比较简单。

One advantage of the factory-method approach is that it gives you the flexibility to change your mind about whether the class should be a singleton without changing its API. The factory method returns the sole instance but could easily be modified to return, say, a unique instance for each thread that invokes it. A second advantage, concerning generic types, is discussed in Item 27. Often neither of these advantages is relevant, and the final-field approach is simpler.

为了使利用这其中一种方法实现的Singleton类变成是可序列化的serializable(见第11章)仅仅在声明中加上“implements Serializable”是不够的。为了维护并保证Singleton,必须声明所有实例域都是瞬时(transient)的,并提供一个readResolve方法(见第77条)。否则,毎次反序列化一个序列化的实例时,都会创建一个新的实例,比如说,在我们的例子中,会导致“假冒的Elvis”。为了防止这种情况,要在Elvis类中加入下面这个readResolve方法:

To make a singleton class that is implemented using either of the previous approaches serializable (Chapter 11), it is not sufficient merely to add implementsSerializable to its declaration. To maintain the singleton guarantee, you have to declare all instance fields transient and provide a readResolve method (Item 77). Otherwise, each time a serialized instance is deserialized, a new instance will be created, leading, in the case of our example, to spurious Elvis sightings. To prevent this, add this readResolve method to the Elvis class:

// readResolve method to preserve singleton property
private Object readResolve() {
    // Return the one true Elvis and let the garbage collector
    // take care of the Elvis impersonator.
    return INSTANCE;
}

从Java1.5发行版本起,实现Singleton还有第三种方法。只需编写一个包含单个元素的枚举类型:

As of release 1.5, there is a third approach to implementing singletons. Simply make an enum type with one element:

// Enum singleton - the preferred approach
public enum Elvis {
    INSTANCE;
	
    public void leaveTheBuilding() { ... }
}

这种方法在功能上与公有域方法相近,但是它更加简洁,无偿地提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。虽然这种方法还没有广 泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。

This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton.

第2条:遇到多个构造器参数时要考虑用构建器

Item 2:Consider a builder when faced with many constructor parameters

静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。考虑用一个类表示包装食品外面显示的营养成份标签。这些标签中有几个域是必需的:每份的含量. 每罐的含量以及毎份的卡路里,还有超过20个可选域:总脂肪量.饱和脂肪量、转化脂肪、 胆固醇、钠等等。大多数产品在某几个可选域中都会有非零的值。

Static factories and constructors share a limitation: they do not scale well to large numbers of optional parameters. Consider the case of a class representing the Nutrition Facts label that appears on packaged foods. These labels have a few required fields—serving size, servings per container, and calories per serving—and over twenty optional fields—total fat, saturated fat, trans fat, cholesterol, sodium, and so on. Most products have nonzero values for only a few of these optional fields.

对于这样的类,应该用哪种构造器或者静态方法来编写呢?程序员一向习惯采用重叠构造器(telescoping constructor)模式,在这种模式下,你提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,依此类推,最后一个构造器包含所有可选参数。下面有个示例,为了简单起见,它只显示四个可选域:

What sort of constructors or static factories should you write for such a class? Traditionally, programmers have used the telescoping constructor pattern, in which you provide a constructor with only the required parameters, another with a single optional parameter, a third with two optional parameters, and so on, culminating in a constructor with all the optional parameters. Here’s how it looks in practice. For brevity’s sake, only four optional fields are shown:

// Telescoping constructor pattern - does not scale well!
public class NutritionFacts {
    private final int servingSize;   // (mL)            required
    private final int servings;      // (per container) required
    private final int calories;      //                 optional
    private final int fat;           // (g)             optional
    private final int sodium;        // (mg)            optional
    private final int carbohydrate;  // (g)             optional

    public NutritionFacts(int servingSize, int servings) {
        this(servingSize, servings, 0);
    }

    public NutritionFacts(int servingSize, int servings,
            int calories) {
        this(servingSize, servings, calories, 0);
    }

    public NutritionFacts(int servingSize, int servings,
            int calories, int fat) {
        this(servingSize, servings, calories, fat, 0);
    }

    public NutritionFacts(int servingSize, int servings,
            int calories, int fat, int sodium) {
        this(servingSize, servings, calories, fat, sodium, 0);
    }

    public NutritionFacts(int servingSize, int servings,
           int calories, int fat, int sodium, int carbohydrate) {
        this.servingSize  = servingSize;
        this.servings     = servings;
        this.calories     = calories;
        this.fat          = fat;
        this.sodium       = sodium;
        this.carbohydrate = carbohydrate;
    }
}

当你想要创建实例的时候,就利用参数列表最短的构造器,但该列表中包含了要设置的所有参数:

When you want to create an instance, you use the constructor with the shortest parameter list containing all the parameters you want to set:

NutritionFacts cocaCola =
    new NutritionFacts(240, 8, 100, 0, 35, 27);

这个构造器调用通常需要许多你本不想设置的参数,但还是不得不为它们传递值。在这个例子中,我们给fat传递了一个值为0。如果“仅仅”是这6个参数,看起来还不算太糟,问题是随着参数数目的增加,它很快就失去了控制。

Typically this constructor invocation will require many parameters that you don’t want to set, but you’re forced to pass a value for them anyway. In this case, we passed a value of 0 for fat. With “only” six parameters this may not seem so bad, but it quickly gets out of hand as the number of parameters increases.

一句话:重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然较难以阅读。如果读者想知道那些值是什么意思,必须很仔细地数着这些参数来探个究竟。一长串类型相同的参数会导致一些微妙的错误。如果客户端不小心颠倒了其中两个参数的顺序,编译器也不会出错,但是程序在运行时会出现错误的行为。

In short, the telescoping constructor pattern works, but it is hard to write client code when there are many parameters, and harder still to read it. The reader is left wondering what all those values mean and must carefully count parameters to find out. Long sequences of identically typed parameters can cause subtle bugs. If the client accidentally reverses two such parameters, the compiler won’t complain, but the program will misbehave at runtime (Item 40).

遇到许多构造器参数的时候,还有第二种代替办法,即JavaBeans模式,在这种模式下,调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及毎个相关的可选参数:

A second alternative when you are faced with many constructor parameters is the JavaBeans pattern, in which you call a parameterless constructor to create the object and then call setter methods to set each required parameter and each optional parameter of interest:

// JavaBeans Pattern - allows inconsistency, mandates mutability
public class NutritionFacts {
    // Parameters initialized to default values (if any)
    private int servingSize  = -1;  // Required; no default value
    private int servings     = -1;  //     "     "     "      "
    private int calories     = 0;
    private int fat          = 0;
    private int sodium       = 0;
    private int carbohydrate = 0;

    public NutritionFacts() { }

    // Setters
    public void setServingSize(int val)  { servingSize = val; }
    public void setServings(int val)     { servings = val; }
    public void setCalories(int val)     { calories = val; }
    public void setFat(int val)          { fat = val; }
    public void setSodium(int val)       { sodium = val; }
    public void setCarbohydrate(int val) { carbohydrate = val; }
}

这种模式弥补了重叠构造器模式的不足。说得明白一点,就是创建实例很容易,这样产生的代码读起来也很容易:

This pattern has none of the disadvantages of the telescoping constructor pattern. It is easy, if a bit wordy, to create instances, and easy to read the resulting code:

NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);

遗憾的是,JavaBeans模式自身有着很严重的缺点。因为构造过程被分到了几个调用中,在构造过程中JavaBeans可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。试图使用处于不一致状态的对象,将会导致失败,这种失败与包含错误的代码大相径庭,因此它调试起来十分困难。与此相关的另一点不足在于,JavaBeans模式阻止了把类做成不可变的可能(见第15条),这就需要程序员付出额外的努力来确保它的线程安全。

Unfortunately, the JavaBeans pattern has serious disadvantages of its own. Because construction is split across multiple calls, a JavaBean may be in an inconsistent state partway through its construction. The class does not have the option of enforcing consistency merely by checking the validity of the constructor parameters. Attempting to use an object when it’s in an inconsistent state may cause failures that are far removed from the code containing the bug, hence difficult to debug. A related disadvantage is that the JavaBeans pattern precludes the possibility of making a class immutable (Item 15), and requires added effort on the part of the programmer to ensure thread safety.

当对象的构造完成,并且不允许在解冻之前使用时,通过手工“冻结”对象,可以弥补这些不足,但是这种方式十分笨拙,在实践中很少使用。此外,它甚至会在运行时导致错误, 因为编译器无法确保程序员会在使用之前先在对象上调用freeze方法。

It is possible to reduce these disadvantages by manually “freezing” the object when its construction is complete and not allowing it to be used until frozen, but this variant is unwieldy and rarely used in practice. Moreover, it can cause errors at runtime, as the compiler cannot ensure that the programmer calls the freeze method on an object before using it.

幸运的是,还有第三种替代方法,既能保证像重叠构造器模式那样的安全性,也能保证像JavaBeans模式那么好的的可读性。这就是Builder模式[Gamma95, p. 97]的一种形式。不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成不可变的对象。这个builder是它构建的类的静态成员类(见第22条)。下面就是它的示例:

Luckily, there is a third alternative that combines the safety of the telescoping constructor pattern with the readability of the JavaBeans pattern. It is a form of the Builder pattern [Gamma95, p. 97]. Instead of making the desired object directly, the client calls a constructor (or static factory) with all of the required parameters and gets a builder object. Then the client calls setter-like methods on the builder object to set each optional parameter of interest. Finally, the client calls a parameterless build method to generate the object, which is immutable. The builder is a static member class (Item 22) of the class it builds. Here’s how it looks in practice:

// Builder Pattern
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values
        private int calories      = 0;
        private int fat           = 0;
        private int carbohydrate  = 0;
        private int sodium        = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val)
            { calories = val;      return this; }
        public Builder fat(int val)
            { fat = val;           return this; }
        public Builder carbohydrate(int val)
            { carbohydrate = val;  return this; }
        public Builder sodium(int val)
            { sodium = val;        return this; }

        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

注意NutritionFacts是不可变的,所有的默认参数值都单独放在一个地方。builder的setter方法返回builder本身,以便可以把调用链接起来。下面就是客户端代码:

Note that NutritionFacts is immutable, and that all parameter default values are in a single location. The builder’s setter methods return the builder itself so that invocations can be chained. Here’s how the client code looks:

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
   calories(100).sodium(35).carbohydrate(27).build();

这样的客户端代码很容易编写,更为重要的是,易于阅读。builder模式模拟了具名的可选参数,就像Ada和Python中的一样。

This client code is easy to write and, more importantly, to read. The Builder pattern simulates named optional parameters as found in Ada and Python.

builder像个构造器一样,可以对其参数强加约束条件。build方法可以检验这些约束条件。将参数从builder拷贝到对象中之后,并在对象域而不是builder域(见第39条)中对它们进行检验,这一点很重要。如果违反了任何约束条件,build方法就应该抛出IllegalStateException(见第60条)。异常的详细信息应该显示出违反了哪个约束条件(见第63条)

Like a constructor, a builder can impose invariants on its parameters. The build method can check these invariants. It is critical that they be checked after copying the parameters from the builder to the object, and that they be checked on the object fields rather than the builder fields (Item 39). If any invariants are violated, the build method should throw an IllegalStateException (Item 60). The exception’s detail method should indicate which invariant is violated (Item 63).

对多个参数强加约束条件的另一种方法是,用多个setter方法对某个约束条件必须持有的所有参数进行检查。如果该约束条件没有得到满足,setter方法就会抛出IllegalArgumentException。这有个好处,就是一且传递了无效的参数,立即就会发现约束条件失败,而不是等着调用build方法。

Another way to impose invariants involving multiple parameters is to have setter methods take entire groups of parameters on which some invariant must hold. If the invariant isn’t satisfied, the setter method throws an IllegalArgumentException. This has the advantage of detecting the invariant failure as soon as the invalid parameters are passed, instead of waiting for build to be invoked.

与构造器相比,builder的微略优势在于,builder可以有多个可变(varargs)参数。构造器就像方法一样,只能有一个可变参数。因为builder利用单独的方法来设置每个参数,你想要多少个可变参数,它们就可以有多少个,直到毎个setter方法都有一个可变参数。

A minor advantage of builders over constructors is that builders can have multiple varargs parameters. Constructors, like methods, can have only one varargs parameter. Because builders use separate methods to set each parameter, they can have as many varargs parameters as you like, up to one per setter method.

Builder模式十分灵活,可以利用单个builder构建多个对象。builder的参数可以在创建对象期间进行调整,也可以随着不同的对象而改变。builder可以自动填充某些域,例如毎次创建对象时自动增加序列号。

The Builder pattern is flexible. A single builder can be used to build multiple objects. The parameters of the builder can be tweaked between object creations to vary the objects. The builder can fill in some fields automatically, such as a serial number that automatically increases each time an object is created.

设置了参数的builder生成了一个很好的抽象工厂(Abstract Factory) [Gamma95, p. 87]。换句话说,客户端可以将这样一个builder传给方法,使该方法能够为客户端创建一个或者多个对象。要使用这种用法,需要有个类型来表示builder。如果使用的是发行版本1.5或者更新的版本,只要一个泛型(见第26条)就能满足所有的builder,无论它们在构建哪种类塱的对象:

A builder whose parameters have been set makes a fine Abstract Factory [Gamma95, p. 87]. In other words, a client can pass such a builder to a method to enable the method to create one or more objects for the client. To enable this usage, you need a type to represent the builder. If you are using release 1.5 or a later release, a single generic type (Item 26) suffices for all builders, no matter what type of object they’re building:

// A builder for objects of type T
public interface Builder<T> {
    public T build();
}

注意,可以声明NutritionFacts.Builder类来实现Builder

Note that our NutritionFacts.Builder class could be declared to implement Builder.

带有Builder实例的方法通常利用有限制的通配符类型(bounded wildcard type,见第28条)来约束构建器的类型参数。例如,下面就是构建每个节点的方法,它利用一个客户端提供的Builder实例来构建树:

Methods that take a Builder instance would typically constrain the builder’s type parameter using a bounded wildcard type (Item 28). For example, here is a method that builds a tree using a client-provided Builder instance to build each node:

Tree buildTree(Builder<? extends Node> nodeBuilder) { ... }

Java中传统的抽象工厂实现是Class对象,用newInstance方法充当build方法的一部分。这种用法隐含着许多问题。newInstance方法总是企图调用类的无参构造器,这个构造器甚至可能根本不存在。如果类没有可以访问的无参构造器,你也不会收到编译时错误。相反,客户端代码必须在运行时处理InstantiationException或者IllegalAccessException,这样既不雅观也不方便。newInstance方法还会传播由无参构造器抛出的任何异常,即使newInstance缺乏相应的throws子句。换句话说,Class.newInstance破坏了编译时的异常检查。上面讲过的Builder接口弥补了这些不足。

The traditional Abstract Factory implementation in Java has been the Class object, with the newInstance method playing the part of the build method. This usage is fraught with problems. The newInstance method always attempts to invoke the class’s parameterless constructor, which may not even exist. You don’t get a compile-time error if the class has no accessible parameterless constructor. Instead, the client code must cope with InstantiationException or IllegalAccessException at runtime, which is ugly and inconvenient. Also, the newInstance method propagates any exceptions thrown by the parameterless constructor, even though newInstance lacks the corresponding throws clauses. In other words, Class.newInstance breaks compile-time exception checking. The Builder interface, shown above, corrects these deficiencies.

Builder模式的确也有它自身的不足。为了创建对象,必须先创建它的构建器。虽然创建构建器的开销在实践中可能不那么明显,但是在某些十分注重性能的情况下,可能就成问题了。Builder模式还比重叠构造器模式更加冗长,因此它只在有很多参数的时候才使用,比如4个或者更多个参数。但是记住,将来你可能需要添加参数。如果一开始就使用构造器或者静态工厂,等到类需要多个参数时才添加构建器,就会无法控制,那些过时的构造器或者静态 工厂显得十分不协调。因此,通常最好一开始就使用构建器。

The Builder pattern does have disadvantages of its own. In order to create an object, you must first create its builder. While the cost of creating the builder is unlikely to be noticeable in practice, it could be a problem in some performancecritical situations. Also, the Builder pattern is more verbose than the telescoping constructor pattern, so it should be used only if there are enough parameters, say, four or more. But keep in mind that you may want to add parameters in the future. If you start out with constructors or static factories, and add a builder when the class evolves to the point where the number of parameters starts to get out of hand, the obsolete constructors or static factories will stick out like a sore thumb. Therefore, it’s often better to start with a builder in the first place.

简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是种不错的选择,特别是当大多数参数都是可选的时候。与使用传统的重叠构造器模式相比, 使用Builder模式的客户端代码将更易于阅读和编写,构建器也比JavaBeans更加安全。

In summary, the Builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters, especially if most of those parameters are optional. Client code is much easier to read and write with builders than with the traditional telescoping constructor pattern, and builders are much safer than JavaBeans.

第1条:考虑用静态工厂方法代替构造器

Item 1: Consider static factory methods instead of constructors

对于类而言,为了让客户端获取它自身的一个实例,最常用的方法就是提供一个公有的构造器。还有一种方法,也应该在毎个程序员的工具箱中占有一席之地。类可以提供一个公有的静态工厂方法(static factory method),它只是一个返回类的实例的静态方法。下面是一个来自Boolean(基本类型boolean的包装类)的简单示例。这个方法将boolean基本类型值转换成了一个Boolean对象引用:

The normal way for a class to allow a client to obtain an instance of itself is to provide a public constructor. There is another technique that should be a part of every programmer’s toolkit. A class can provide a public static factory method, which is simply a static method that returns an instance of the class. Here’s a simple example from Boolean (the boxed primitive class for the primitive type boolean). This method translates a boolean primitive value into a Boolean object reference:

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

注意,静态工厂方法与设计模式[Gamma95, p. 107]中的工厂方法模式不同。本条目中所指的静态工厂方法并不直接对应于设计模式中的工厂方法。

Note that a static factory method is not the same as the Factory Method pattern from Design Patterns [Gamma95, p. 107]. The static factory method described in this item has no direct equivalent in Design Patterns.

类可以通过静态工厂方法来提供它的客户端,而不是通过构造器。提供静态工厂方法而不是公有的构造器,这样做具有几大优势。

A class can provide its clients with static factory methods instead of, or in addition to, constructors. Providing a static factory method instead of a public constructor has both advantages and disadvantages.

静态工厂方法与构造器不同的第一大优势在于,它们有名称。如果构造器的参数本身没有确切地描述正被返回的对象,那么具有适当名称的静态工厂会更容易使用,产生的客户端代码也更易干阅读。例如,构造器BigInteger(int,int,Random)返回的BigInteger可能为素数,如果用名为BigInteger.probablePrime的静态工厂方法來表示,显然更为清楚。(1.4的发行版本中最终增加了这个方法。)

One advantage of static factory methods is that, unlike constructors, they have names.If the parameters to a constructor do not, in and of themselves,describe the object being returned, a static factory with a well-chosen name is easier to use and the resulting client code easier to read. For example, the constructor BigInteger(int,int,Random), which returns a BigInteger that is probably prime, would have been better expressed as a static factory method named BigInteger.probablePrime. (This method was eventually added in the 1.4 release.)

一个类只能有一个带有指定签名的构造器。编程人员通常知道如何避开这一限制:通过提供两个构造器,它们的参数列表只在参数类型的顺序上有所不同。实际上这并不是个好主意。 面对这样的API,用户永远也记不住该用哪个构造器,结果常常会调用错误的构造器。并且, 读到使用了这些构造器的代码时,如果没有参考类的文档,往往不知所云。

A class can have only a single constructor with a given signature. Programmers have been known to get around this restriction by providing two constructors whose parameter lists differ only in the order of their parameter types. This is a really bad idea. The user of such an API will never be able to remember which constructor is which and will end up calling the wrong one by mistake. People reading code that uses these constructors will not know what the code does without referring to the class documentation.

由于静态工厂方法有名称,所以它们不受上述的限制。当一个类需要多个带有相同签名的构造器时,就用静态工厂方法代替构造器,并且慎重地选择名称以便突出它们之间的区别。

Because they have names, static factory methods don’t share the restriction discussed in the previous paragraph. In cases where a class seems to require multiple constructors with the same signature, replace the constructors with static factory methods and carefully chosen names to highlight their differences.

静态工厂方法与构造器不同的第二大优势在于,不必在每次调用它们的时候都创建一个新对象。这使得不可变类(见第15条)可以使用预先构建好的实例,或者将构建好的实例缓存起来,进行重复利用,从而避免创建不必要的重复对象。Boolean.valueOf(boolean)方法说明了这项技术:它从来不创建对象。这种方法类似于Flyweight模式[Gamma95, p. 195]。如果程序经常请求创建相同的对象,并且创建对象的代价很髙,则这项技术可以极大地提升性能。

A second advantage of static factory methods is that, unlike constructors,they are not required to create a new object each time they’re invoked. This allows immutable classes (Item 15) to use preconstructed instances, or to cache instances as they’re constructed, and dispense them repeatedly to avoid creating unnecessary duplicate objects. The Boolean.valueOf(boolean) method illustrates this technique: it never creates an object. This technique is similar to the Flyweight pattern [Gamma95, p. 195]. It can greatly improve performance if equivalent objects are requested often, especially if they are expensive to create.

静态工厂方法能够为重复的调用返回相同对象,这样有助于类总能严格控制在某个时刻哪些实例应该存在。这种类被称作实例受控的类(instance-controlled)。编写实例受控的类有几个原因。实例受控使得类可以确保它是一个Singleton(见第3条)或者是不可实例化的(见第4条)。它还使得不可变的类(见第15条)可以确保不会存在两个相等的实例,即当且仅当a==b的时候才有a.equals(b)为true。如果类保证了这一点,它的客户端就可以使用==操作符来代替equals(Object)方法,这样可以提升性能。枚举(enum)类型(见第30条)保证了这一点。

The ability of static factory methods to return the same object from repeated invocations allows classes to maintain strict control over what instances exist at any time. Classes that do this are said to be instance-controlled. There are several reasons to write instance-controlled classes. Instance control allows a class to guarantee that it is a singleton (Item 3) or noninstantiable (Item 4). Also, it allows an immutable class (Item 15) to make the guarantee that no two equal instances exist: a.equals(b) if and only if a==b. If a class makes this guarantee, then its clients can use the == operator instead of the equals(Object) method, which may result in improved performance. Enum types (Item 30) provide this guarantee.

静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象。这样我们在选择返回对象的类时就有了更大的灵活性。

A third advantage of static factory methods is that, unlike constructors,they can return an object of any subtype of their return type.This gives you great flexibility in choosing the class of the returned object.

这种灵活性的一种应用是,API可以返回对象,同时又不会使对象的类变成公有的。以这种方式隐藏实现类会使API变得非常简洁。这项技术适用于基于接口的框架(interface-based frameworks,见第18条),因为在这种框架中,接口为静态工厂方法提供了自然返回类型。接口不能有静态方法,因此按照惯例,接口Type的静态工厂方法被放在一个名为Types的不可实例化的类(见第4条)中。

One application of this flexibility is that an API can return objects without making their classes public. Hiding implementation classes in this fashion leads to a very compact API. This technique lends itself to interface-based frameworks(Item 18), where interfaces provide natural return types for static factory methods.Interfaces can’t have static methods, so by convention, static factory methods for an interface named Type are put in a noninstantiable class (Item 4) named Types.

例如, Java Collections Framework的集合接口有32个便利实现,分别提供了不可修改的集合、同步集合等等。几乎所有这些实现都通过静态工厂方法在一个不可实例化的类(java.util.Collections)中导出。所有返回对象的类都是非公有的。

For example, the Java Collections Framework has thirty-two convenience implementations of its collection interfaces, providing unmodifiable collections, synchronized collections, and the like. Nearly all of these implementations are exported via static factory methods in one noninstantiable class(java.util.Collections). The classes of the returned objects are all nonpublic.

现在的Collections Framework API比导出32个独立公有类的那种实现方式要小得多,每种便利实现都对应一个类。这不仅仅是指API数量上的减少,也是概念意义上的减少。用户知道,被返回的对象是由相关的接口精确指定的,所以他们不需要阅读有关的类文档。使用这种静态工厂方法时,甚至要求客户端通过接口来引用被返回的对象,而不是通过它的实现类来引用被返回的对象,这是一种良好的习惯(见第52条)。

The Collections Framework API is much smaller than it would have been had it exported thirty-two separate public classes, one for each convenience implementation. It is not just the bulk of the API that is reduced, but the conceptual weight. The user knows that the returned object has precisely the API specified by its interface, so there is no need to read additional class documentation for the implementation classes. Furthermore, using such a static factory method requires the client to refer to the returned object by its interface rather than its implementation class, which is generally good practice (Item 52).

公有的静态工厂方法所返回的对象的类不仅可以是非公有的,而且该类还可以随着每次调用而发生变化,这取决于静态工厂方法的参数值。只要是已声明的返回类型的子类型,都是 允许的。为了提升软件的可维护性和性能,返回对象的类也可能随着发行版本的不同而不同。

Not only can the class of an object returned by a public static factory method be nonpublic, but the class can vary from invocation to invocation depending on the values of the parameters to the static factory. Any class that is a subtype of the declared return type is permissible. The class of the returned object can also vary from release to release for enhanced software maintainability and performance.

发行版本1.5中引入的类java.util.EnumSet(见第32条)没有公有构造器,只有静态工厂方法。它们返回两种实现类之一,具体则取决于底层枚举类型的大小:如果它的元素有64个或者更少,就像大多数枚举类型一样,静态工厂方法就会返回一个RegularEnumSet实例,用单个long进行支持;如果枚举类型有65个或者更多元素,工厂就返回JumboEnumSet实例,用long数组进行支持。

The class java.util.EnumSet (Item 32), introduced in release 1.5, has no public constructors, only static factories. They return one of two implementations,depending on the size of the underlying enum type: if it has sixty-four or fewer elements, as most enum types do, the static factories return a RegularEnumSet instance, which is backed by a single long; if the enum type has sixty-five or more elements, the factories return a JumboEnumSet instance, backed by a long array.

这两个实现类的存在对于客户端来说是不可见的。如果RegularEnumSet不能再给小的枚举类型提供性能优势,就可能从未来的发行版本中将它删除,不会造成不良的影响。同样地,如果事实证明对性能有好处,也可能在未来的发行版本中添加第三甚至第四个EnumSet实现。客户端永远不知道也不关心他们从工厂方法中得到的对象的类;他们只关心它是EnumSet的某个子类即可。

The existence of these two implementation classes is invisible to clients. If RegularEnumSet ceased to offer performance advantages for small enum types, it could be eliminated from a future release with no ill effects. Similarly, a future release could add a third or fourth implementation of EnumSet if it proved beneficial for performance. Clients neither know nor care about the class of the object they get back from the factory; they care only that it is some subclass of EnumSet.

静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不必存在。这种灵活的静态工厂方法构成了服务提供者框架(Service Provider Frameworks)的基础,例如JDBC (Java数据库连接,Java Database Connectivity)API。服务提供者框架是指这样一个系统:多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把他们从多个实现中解耦出来。

The class of the object returned by a static factory method need not even exist at the time the class containing the method is written. Such flexible static factory methods form the basis of service provider frameworks, such as the Java Database Connectivity API (JDBC). A service provider framework is a system in which multiple service providers implement a service, and the system makes the implementations available to its clients, decoupling them from the implementations.

服务提供者框架中有三个重要的组件:服务接口(Service Interface),这是提供者实现 的,提供者注册API(Provider Registration API),这是系统用来注册实现,让客户端访问它们的;服务访问API(Service Access API),是客户端用来获取服务的实例的。服务访问API一般允许但是不要求客户端指定某种选择提供者的条件。如果没有这样的规定,API就会返回默认实现的一个实例。服务访问API是“灵活的静态工厂”,它构成了服务提供者框架的基础。

There are three essential components of a service provider framework: a service interface, which providers implement; a provider registration API, which the system uses to register implementations, giving clients access to them; and a service access API, which clients use to obtain an instance of the service. The service access API typically allows but does not require the client to specify some criteria for choosing a provider. In the absence of such a specification, the API returns an instance of a default implementation. The service access API is the “flexible static factory” that forms the basis of the service provider framework.

服务提供者框架的第四个组件是可选的:服务提供者接口(Service Provider Interface),这些提供者负责创建其服务实现的实例。如果没有服务提供者接口,实现就按照类名称注册,并通过反射方式进行实例化(见第53条)。对于JDBC来说,Connection就是它的服务接口, DriverManager.registerDriver是提供者注册API,DriverManager.getConnection是服务访问API,Driver就是服务提供者接口。

An optional fourth component of a service provider framework is a service provider interface, which providers implement to create instances of their service implementation. In the absence of a service provider interface, implementations are registered by class name and instantiated reflectively (Item 53). In the case of JDBC, Connection plays the part of the service interface, DriverManager.registerDriver is the provider registration API, DriverManager.getConnection is the service access API, and Driver is the service provider interface.

服务提供者框架模式有着无数种变体。例如,服务访问API可以利用适配器(Adapter)模式[Gamma95, p. 139],返回比提供者需要的更丰富的服务接口。下面是一个简单的实现, 包含一个服务提供者接口和一个默认提供者:

There are numerous variants of the service provider framework pattern. For example, the service access API can return a richer service interface than the one required of the provider, using the Adapter pattern [Gamma95, p. 139]. Here is a simple implementation with a service provider interface and a default provider:

// Service provider framework sketch

// Service interface
public interface Service {
   ... // Service-specific methods go here
}

// Service provider interface
public interface Provider {
   Service newService();
}

// Noninstantiable class for service registration and access
public class Services {
   private Services() { }// Prevents instantiation (Item 4)
   
   // Maps service names to services
   private static final Map<String, Provider> providers =
      new ConcurrentHashMap<String, Provider>();
   public static final String DEFAULT_PROVIDER_NAME = "<def>";
   
   // Provider registration API
   public static void registerDefaultProvider(Provider p) {
      registerProvider(DEFAULT_PROVIDER_NAME, p);
   }
   public static void registerProvider(String name,Providerp){
      providers.put(name, p);
   }
   
   // Service access API
   public static Service newInstance() {
      return newInstance(DEFAULT_PROVIDER_NAME);
   }
   public static Service newInstance(String name) {
      Provider p = providers.get(name);
      if (p == null)
         throw new IllegalArgumentException(
            "No provider registered with name: " + name);
      return p.newService();
    }
}

静态工厂方法的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁。遗憾的是,在调用参数化类的构造器时,即使类型参数很明显,也必须指明。这通常要 求你接连两次提供类型参数:

A fourth advantage of static factory methods is that they reduce the verbosity of creating parameterized type instances.Unfortunately, you must specify the type parameters when you invoke the constructor of a parameterized class even if they’re obvious from context. This typically requires you to provide the type parameters twice in quick succession:

Map<String, List<String>> m = 
   new HashMap<String, List<String>>();

随着类型参数变得越来越长,越来越复杂,这一冗长的说明也很快变得痛苦起来。但是有了静态工厂方法,编译器就可以替你找到类型参数。这被称作类型推导(type inference)。例如,假设让似叩提供了这个静态工厂:

This redundant specification quickly becomes painful as the length and complexity of the type parameters increase. With static factories, however, the compiler can figure out the type parameters for you. This is known as type inference. For example, suppose that HashMap provided this static factory:

public static <K, V> HashMap<K, V> newInstance() {
   return new HashMap<K, V>();
}

你就可以用下面这句简洁的代码代替上面这段繁琐的声明: Then you could replace the wordy declaration above with this succinct alternative:

Map<String, List<String>> m = HashMap.newInstance();

总有一天,Java将能够在构造器调用以及方法调用中执行这种类型推导,但到发行版本1.6为止暂时还无法这么做。

Someday the language may perform this sort of type inference on constructor invocations as well as method invocations, but as of release 1.6, it does not.

遗憾的是,到发行版本1.6为止,标准的集合实现如HashMap并没有工厂方法,但是可以把这些方法放在你自己的工具类中。更重要的是,可以把这样的静态工厂放在你自己的参数 化的类中。

Unfortunately, the standard collection implementations such as HashMap do not have factory methods as of release 1.6, but you can put these methods in your own utility class. More importantly, you can provide such static factories in your own parameterized classes.

静态工厂方法的主要缺点在于,类如果不含公有的或者受保护的构造器,就不能被子类化。对于公有的静态工厂所返回的非公有类,也同样如此。例如,要想将Collections Framework中的任何方便的实现类子类化,这是不可能的。但是这样也许会因祸得福,因为它鼓励程序员使用复合(composition),而不是继承(见第16条)。

The main disadvantage of providing only static factory methods is that classes without public or protected constructors cannot be subclassed. The same is true for nonpublic classes returned by public static factories. For example,it is impossible to subclass any of the convenience implementation classes in the Collections Framework. Arguably this can be a blessing in disguise, as it encourages programmers to use composition instead of inheritance (Item 16).

静态工厂方法的第二个缺点在于,它们与其他的静态方法实际上没有任何区别。在API文档中,它们没有像构造器那样在API文档中明确标识出来,因此,对于提供了静态工厂方法而不是构造器的类来说,要想査明如何实例化一个类,这是非常困难的。Javadoc工具总有一天会注意到静态工厂方法。同时,你通过在类或者接口注释中关注静态工厂,并遵守标准的命名习惯,也可以弥补这一劣势。下面是静态工厂方法的一些惯用名称:

A second disadvantage of static factory methods is that they are not readily distinguishable from other static methods.They do not stand out in API documentation in the way that constructors do, so it can be difficult to figure out how to instantiate a class that provides static factory methods instead of constructors. The Javadoc tool may someday draw attention to static factory methods. In the meantime, you can reduce this disadvantage by drawing attention to static factories in class or interface comments, and by adhering to common naming conventions. Here are some common names for static factory methods:

  • valueOf—不太严格地讲,该方法返回的实例与它的参数具有相同的值。这样的静态工厂方法实际上是类型转换方法。
  • valueOf—Returns an instance that has, loosely speaking, the same value as its parameters. Such static factories are effectively type-conversion methods.

  • of—是valueOf一种更为简洁的替代,在EnumSet (见第32条)中使用并流行起来。
  • of—A concise alternative to valueOf, popularized by EnumSet (Item 32).

  • getInstance—返回的实例是通过方法的参数来描述的,但是不能够说与参数具有同样的值。对于Singleton来说,该方法没有参数,并返回唯一的实例。
  • getInstance—Returns an instance that is described by the parameters but cannot be said to have the same value. In the case of a singleton, getInstance takes no parameters and returns the sole instance.

  • newInstance—像getInstance一样,但newInstance能够确保返回的每个实例都与所有其他实例不同。
  • newInstance—Like getInstance, except that newInstance guarantees that each instance returned is distinct from all others.

  • getType—像getInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。
  • getType—Like getInstance, but used when the factory method is in a different class. Type indicates the type of object returned by the factory method.

  • newType—像newInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。
  • newType—Like newInstance, but used when the factory method is in a different class. Type indicates the type of object returned by the factory method.

简而言之,静态工厂方法和公有构造器都各有用处,我们需要理解它们各自的长处。静态 工厂通常更加合适,因此切忌第一反应就是提供公有的构造器,而不先考虑静态工厂。

In summary, static factory methods and public constructors both have their uses, and it pays to understand their relative merits. Often static factories are preferable, so avoid the reflex to provide public constructors without first considering static factories.

第1章 引言

Chapter 1 Introduction

本书的目标是帮助读者最有效地使用Java程序设计语言及其基本类库:java.lang、java.util,在某种程度上还包括java.uitl.concurrent和java.io。本书也会不时地讨论到其他的类库,但是没有涉及图形用户界面编程、企业级API以及移动设备相关的类库。

THIS book is designed to help you make the most effective use of the Java™ programming language and its fundamental libraries, java.lang, java.util,and, to a lesser extent, java.util.concurrent and java.io. The book discusses other libraries from time to time, but it does not cover graphical user interface programming, enterprise APIs, or mobile devices.

本书共包含78个条目,毎个条目讨论一条规则。这些规则反映了最有经验的优秀程序员在实践中常用的一些有益做法。本书以一种比较自由的方式将这些条目组织成10章,每一章都涉及软件设计的一个主要方面。本书并不一定要按部就班地从头读到尾,因为毎个条目都有一定程度的独立性。这些条目相互之间交叉引用,因此你可以很容易地在书中找到自己需要的内容。

This book consists of seventy-eight items, each of which conveys one rule.The rules capture practices generally held to be beneficial by the best and most experienced programmers. The items are loosely grouped into ten chapters, each concerning one broad aspect of software design. The book is not intended to be read from cover to cover: each item stands on its own, more or less. The items are heavily cross-referenced so you can easily plot your own course through the book.

第2章 创建和销毁对象

Chapter 2 Creating and Destroying Objects

本章的主题是创建和销毁对象:何时以及如何创建对象,何时以及如何避免创建对象, 如何确保它们能够适时地销毁,以及如何管理对象销毁之前必须进行的各种清理动作。

THIS chapter concerns creating and destroying objects: when and how to create them, when and how to avoid creating them, how to ensure they are destroyed in a timely manner, and how to manage any cleanup actions that must precede their destruction.

第1条:考虑用静态工厂方法代替构造器

第2条:遇到多个构造器参数时要考虑用构建器

第3条:用私有构造器或者枚举类型强化Singleton属性

第4条:通过私有构造器强化不可实例化的能力

第5条:避免创建不必要的对象

第6条:消除过期的对象引用

第7条:避免使用终结方法

第3章 对于所有对象都通用的方法

Chapter 3 Classes and Interfaces

第8条:覆盖equals时请遵守通用约定

第9条:覆盖equals时总要覆盖hashCode

第10条:始终要覆盖toString

第11条:谨慎地覆盖clone

第12条:考虑实现Comparable接口

第13条:使类和成员的可访问性最小化

第14条:在公有类中使用访问方法而非公有域

第15条:使可变性最小化

第16条:复合优先于继承

第17条:要么为继承而设计,并提供文档说明,要么就禁止继承

第4章 类和接口

Chapter 4 Classes and Interfaces

第18条:接口优于抽象类

第19条:接口只用于定义类型

第20条:类层次优于标签类

第21条:用函数对象表示策略

第22条:优先考虑静态成员类

第5章泛型

Chapter 5 Generics

第23条:请不要在新代码中使用原生态类型

第24条:消除非受检警告

第25条:列表优先于数组

第26条:优先考虑泛型

第27条:优先考虑泛型方法

第28条:利用有限制通配符来提升API的灵活性

第29条:优先考虑类型安全的异构容器

第6章 枚举和注解

Chapter 6 Enums and Annotations

第30条:用enum代替int常量

第31条:用实例域代替序数

第32条:用EnumSet代替位域

第33条:用EnumMap代替序数索引

第34条:用接口模拟可伸缩的枚举

第35条:注解优先于命名模式

第36条:坚持使用Override注解

第37条:用标记接口定义类型

第7章 方法

Chapter 7 Methods

第38条:检查参数的有效性

第39条:必要时进行保护性拷贝

第40条:谨慎设计方法签名

第41条:慎用重载

第42条:慎用可变参数

第43条:返回零长度的数组或者集合,而不是null

第44条:为所有导出的API元素编写文档注释

第8章 通用程序设计

Chapter 8 General Programming

第45条:将局部变量的作用域最小化

第46条:for-each循环优先于传统的for循环

第47条:了解和使用类库

第48条:如果需要精确的答案,请避免使用float和double

第49条:基本类型优先于装箱基本类型

第50条:如果其他类型更适合,则尽量避免使用字符串

第51条:当心字符串连接的性能

第52条:通过接口引用对象

第53条:接口优先于反射机制

第54条:谨慎地使用本地方法

第55条:谨慎地进行优化

第56条:遵守普遍接受的命名惯例

第9章 异常

Chapter 9 Exceptions

第57条:只针对异常的情况才使用异常

第58条:对可恢复的情况使用受检异常,对编程错误使用运行时异常

第59条:避免不必要地使用受检的异常

第60条:优先使用标准的异常

第61条:抛出与抽象相对应的异常

第62条:每个方法抛出的异常都要有文档

第63条:在细节消息中包含能捕获失败的信息

第64条:努力使失败保持原子性

第65条:不要忽略异常

第10章 并发

Chapter 10 Concurrency

第66条:同步访问共享的可变数据

第67条:避免过度同步

第68条:executor和task优先于线程

第69条:并发工具优先于wait和notify

第70条:线程安全性的文档化

第71条:慎用延迟初始化

第72条:不要依赖于线程调度器

第73条:避免使用线程组

第11章 序列化

Chapter 11 Serialization

第74条:谨慎地实现Serializable接口

第75条:考虑使用自定义的序列化形式

第76条:保护性地编写readObject方法

第77条:对于实例控制,枚举类型优先于readResolve

第78条:考虑用序列化代理代替序列化实例