《Effective Java》读书笔记

2. Creating and Destroying Objects.

ü         Consider Providing Static Factory Methods Instead of Constructors. Enforce the Singleton

ü         Property with a Private Constructor.

ü         Enforce Noninstantiability with a Private Constructor.

ü         Avoid Creating Duplicate Objects.

ü         Eliminate Obsolete Object References.

ü         Avoid Finalizers.

3. Methods Common to All Objects.

ü         Obey the General Contract when Overriding Equals.

ü         Always Override HashCode When You Override Equals.

ü         Always Override toString. Override Clone Judiciously.

ü         Consider Implementing Comparable.

4. Classes and Interfaces.

ü         Minimize the Accessibility of Classes and Members.

ü         Favor Immutability. Favor Composition Over Inheritance.

ü         Design and Document for Inheritance or Else Prohibit It.

ü         Prefer Interfaces to Abstract Classes. Use Interfaces Only to Define Types.

ü         Favor Static Member Classes Over Non-Static.

 

5. Substitutes for C Constructs.

ü         Replace Structures with Classes.

ü         Replace Unions with Class Hierarchies.

ü         Replace Enums with Classes.

ü         Replace Function Pointers with Classes and Interfaces.


6. Methods.

ü         Check Parameters for Validity.

ü         Make Defensive Copies when Needed.

ü         Design Method Signatures Carefully.

ü         Use Overloading Judiciously.

ü         Return Zero-Length Arrays, Not Nulls.

ü         Write Doc Comments for All Exposed API Elements.


7. General Programming.

ü         Minimize the Scope of Local Variables.

ü         Know and Use the Libraries.

ü         Avoid Float and Double if Exact Answers are Required.

ü         Avoid Strings where Other Types are More Appropriate.

ü         Beware the Performance of String Concatenation.

ü         Refer to Objects by their Interfaces.

ü         Prefer Interfaces to Reflection.

ü         Use Native Methods Judiciously.

ü         Optimize Judiciously.

ü         Adhere to Generally Accepted Naming Conventions.

 

8. Exceptions.

ü         Use Exceptions Only for Exceptional Conditions.

ü         Use Checked Exceptions for Recoverable Conditions.

ü         Runtime Exceptions for Programming Errors.

ü         Avoid Unnecessary Use of Checked Exceptions.

ü         Favor the Use of Standard Exceptions.

ü         Throw Exceptions Appropriate to the Abstraction.

ü         Document All Exceptions Thrown by Each Method.

ü         Include Failure-Capture Information in Detail Messages.

ü         Strive for Failure Atomicity.

ü         Don't Ignore Exceptions.


9. Threads.

ü         Synchronize Access to Shared Mutable Data.

ü         Avoid Excessive Synchronization.

ü         Never Invoke Wait Outside a Loop .

ü         Don't Depend on the Thread Scheduler.

ü         Document Thread-Safety.

ü         Avoid Thread Groups.


10. Serialization.

ü         Implement Serializable Judiciously.

ü         Consider Using a Custom Serialized Form.

ü         Write ReadObject Methods Defensively.

ü         Provide a ReadResolve Method when Necessary.

 

 

1,  考虑以 static factory method 取代构造函数

优点:

和构造函数不同,有自己名称,命名良好,避免署名相同的构造函数只有一个的限制

和构造函数不同,不需要每次被呼叫都创建新对象,条款13(immutable classes)

可以传回一个“隶属于函数返回值类型的任何子类型”对象,条款34

缺点:

无法被subclass

无法和其他static函数有所区分,一般valueOf , getInstance

 

 

 

2,  private构造函数厉行singleton性质

使singleton class成为serializable,在其加上 implements Serializable 不够,还需提供readResolve方法,保证singleton

 

 

3,  private构造函数厉行不可实体化性质(noninstantiability)

 

4,  避免创建“重复对象”(duplicate objects)

使用static initializer避免低效率,static{},条款48lazily initializing

Map interface keySet()

条款24,“保护性拷贝”defensive copying

 

5,  消除老旧的object reference

把老旧的reference设置成null

WeakHapsMap,垃圾回收,会把map中的,没有外部对象引用集合中的对象回收。即没有被外部指向对象的会被回收

SoftReference?

LinkHashMap?

 

6,  避免使用finalizer

finalizer作用

安全网,“明确的终结函数”未被调用时起补救作用。但不保证被及时执行,也不保证终将被执行

与物件的native peers有关

 

子类如果覆盖了父类的finalizer,子类应该手工调用父类的finalizer,应该放在finally里,以确保子类finalize时出异常时,父类finalizar仍被调用

利用匿名类,终结外部实体,外围实体在一个private instance中存储一个reference,指向finalizer guardian,于是,finalizer guardian在外部实体存在时也存在,当guardian被终结时,它执行外围实体所期望的终结动作。


public class Foo{

private final object finalizerGuardian = new Object(){

       protected void finalize() throws Throwable{

              //终结外围实体

}

}

}

 

7,  覆写equals时,请遵守通用契约(general contract) P25

 

没有办法既继承class,又可以维持equals契约。用组合代替继承。

高质量equals要点:

==运算符检查“参数是否是为对象自身的引用”

instanceof检查参数是否为正确类别,不是,返回false,按JLS规定,如果参数为nullinstanceof会返回false,因此,不需要单独一行判断null,作为有效性检查

将参数转换为正确型别

class内每一位有意义的属性,检查参数中的该属性是否与对象内的对应属性吻合。

       避免莽撞抛出NullPointerException

field == null ? o.field == null : field.equals(o.field)

将最可能不一致的属性先进行比较

注意点:

hashcode()总是和equals() 一起被覆写

不要卖弄小聪明。不要考虑别名相等,或者指向同一个文档的link相等。

不要写出依赖“不可依赖资源”的equals()。原因:例如,java.net.URLequals依赖URL中的主机IP地址进行比较。

不要在equals声明中,以其他型别代替Object

 

8,  覆写equals时,请总是一并覆写hashCode()     P36

Object符合以下契约:

同一应用程序执行期间,对同一对象调用hashCode(),必须传回相同的整数结果。——前提是equals所比较的信息不曾被改动过,至于同一应用程序在不同执行期所得的结果,不需一致。

如果两对象equals视为相等,则两对象的hashCode必须获得相同整数。

如两个对象被equals视为不相等,则两对象的hashCode不必产生不同整数。但对不同对象产生不同整数的结果,有可能提高hash table的效率。

 

要点:

将一个非0常数,例如17,存储于int result变量中。

对每一个有意义的属性f,即被equals所考虑的每一属性,进行如下处理:

       对这个属性计算出型别为inthashc

              如果是boolean,计算(f ? 0:1 )

              byte,char,short,int 计算( int )f

              long, (int)(f^(f>>>32))  ??

              float, Float.floatToIntBits(f)

              double, Double.doubleToLongBits,然后将结果按long处理

              如果是object reference,而且classequals通过“传递调用equals”的方式比较这一属性,那就应该传递调用hashCode()

 

如果属性是array,将每个元素视为独立属性,即对每一个有意义的元素执行上述规则,计算出hash码,然后按步骤2.B将这些数值组合起来。

 

问题:怎么组合?

对每个属性,都是用步骤B组合,不断使用乘加方法。

 

将步骤A计算出的hashc按下列组合到变量中:result = 37 * result +c

传回result

完成hashCode后,思考:是否相等的实体具有相等的hash码?

 

对于immutable class考虑在第一次hashCode调用时,进行惰性初始化,条款48

private volatile int hashCode = 0;

public int hashCode(){

if(hashCode == 0){

//计算

}

return hashCode;

}

 

 

9,总是覆写toString() P42

 

10,谨慎的覆写clone()

参考java.util. collection classes源码

特殊interface用法,不应效仿,Cloneable不含任何函数,它用来决定Objectprotected clone()的实作码行为,如果某个class实现Cloneable Object clone(),便应传回“属性逐一拷贝”的对象副本;否则抛出CloneNotSupportedException

实现了Cloneable接口,却是改变了superclassprotected函数行为,与Serializable异曲同工。

一般而言,对对象x,以下为true:

x.clone() != x

x.clone().getClass() == x.getClass()

但这些并非绝对必要。通常情况下,以下为true:

x.clone.equals(x)

 

拷贝一对象,典型是创建“该对象所属的class”的一个实体,并同时要求拷贝内部数据结构,但不得唤起构造函数。一般调用super.clone

类似于“全自动构造链”

 

基本问题:clone架构于“mutable objects”的final属性常规用法不相容,除非此mutable object可以在对象及其可克隆件之间被安全共享。因此,为了让class成为cloneable,或许必须去除class之中某些属性的final属性。

其实,对于链表对象,应该考虑先初始化成新的链表,然后根据每个keyvalue去重新插入,这通常可以产生简单优雅的clone(),但其执行效率与“直接操纵对象及其克隆件的内部成员”相比,稍逊一筹。

 

和构造式一样,clone不应该在构造过程中呼叫任何克隆件的任何nonfinal函数(条款15)

原因是,如果clone调用被覆写的函数,那在subclass修正其新对象的状态之前,该函数就被调用,可能导致克隆件和原对象的不一致。因此,如果不是final,应该就是private.

 

Objectclone声明抛出CloneNotSupportedException异常,但覆写后的clone可能省略此声明,对于final classesclone应该省略此声明,因为人们更愿意使用不含check exceptions的函数。但一个extendable class可扩展类,覆写了clone,这个clone应宣告此异常抛出,这样做,子类可以通过抛出 throw new CloneNotSupportedException(),来解除clone功能。

 

“对象复制”的一个好方法就是提供所谓的copy构造函数。或者static 工厂方法构造。它们不依赖一个带有风险的、超脱于语言之外的对象创建机制;不要求用户遵循未形成良好文件的规矩(Java 5解决了吗??)不会与final正常使用冲突,不要求用户捕捉非必要的checked异常。

 

结论:有把握的说,任何其他interface都不应该extend Cloneable,为继承而是设计的class也不应该实现它。老练程序员拿定主意,绝不覆写和调用clone(),也许只用它来复制array。至少要为“为继承设计的class”提供一个行为良好的protected clone(),否则,subclass就不可能实现Cloneable.

 

11,考虑实现Comparable

Set,没有重复元素,添加相同元素时,不会有提示,但始终只有一个相同元素。

Comparable规格书:

比较某对象和另一指定对象顺序,当对象小于、等于、大于指定对象时,传回负整数、0、正整数。如果两对象类型不允许比较,抛出ClassCastException异常。

保证,所有x,y都满足 sgn(x.compareTo(y)) == -sgn(y.compareTo(y)),如果x.compareTo(y)抛异常,那么y.compareTo(x)也抛。

保证:

l         传递性

l         x.compareTo(y)==0,对任意z,保证x.compareTo(z)==y.compareTo(z)永远成立

l         强烈建议,但不要求,满足(x.compareTo(y)==0) == (x.equals(y))

 

对于一个可实例化的类,不存在一个“既增加新aspect已扩展class,又能维持compareTo契约”的办法,因此,equals的迂回解法同样有效,用组合代替继承P74

 

compareToequals的不同,无需在类型转换前检查参数类型,如参数类型不适当,compareTo应该抛出ClassCastException。如果参数为null,应该抛NullPointerExceptoin

 

用相减方法,判断两个数大小时,要以防溢出的情况。不是理论上的问题,而是这种问题很难排除,需要在一开始就防止发生这样的错误。

 

12,classes和其他成员的可存取性最小化

private

package-private      这种成员可被声明式所在的package内的任何classes访问。即default

protected               除了package-private的访问外,还可以让子类访问

public

 

privatepackage-private成员一般不会影响其exported API,但如果class实现Serializable(条款5455),这些属性有可能被泄漏到exported API中。

一个 exported classprotected成员对于class内部代码而言,相当于作了等同于public的承诺(条款15)。protected成员的需求应该是相当少的。

 

如果一个函数重载了superclass的函数,其访问级别不得低于其在superclass中的级别。特殊情况是,class实现了interface,那么class相应于interface的函数都必须为public,因为interface的所有函数都暗自为public (??可以为protected吗?编译会提示?)

 

注意:长度不为0array总是可变的,因此,class之中如果出现public static final array属性,几乎总是错误的。这样的public array应该换成private arraypublic immutable List

private static final Type[] PRIVATE_VALUES={…}

public static final List VALUES = Collentions.unmodifiableList(Arrays.asList(PRIVATE_VALUES));

另外,如果你需要编译期类型安全性,并且愿意损失一些效率的话,可以用public函数传回private array副本。

public static final Type[] values{

       return (Type[]) PRIVATE_VALUES.clone();

}

除了前述的public static final属性外,不应该拥有public属性,请确保public static final 属性所引用的对象是不可变的。

 

13,不变性(immutability)

实例不能被修改的class。包含在这种class的实例内的信息在创建之初提供,并在对象生存期间固定不变。

 

创建一个immutable class 有以下5原则:

不要提供任何“可改变对象内容”的函数(mutator

保证没有任何函数可以被覆写。防止subclass危害对象的不可变性。可使class final

令所有的属性都为final

令所有的属性都是private

确保对任何克变组件的互斥存取(exclusive access)。不要向用户提供object reference来初始化这样的属性,也不要从存取式中传回object reference请在构造函数、存取式和readObject()(条款56)中使用保护性拷贝(条款24)。(说明不拷贝的话,传回的是实际引用的对象?可以通过这样改变对象的值。)

 

immutable objects本质上是线程安全的,不需要同步机制辅助。

 

immutable classes应该鼓励客户尽可能复用其既有对象,可以为频繁使用的数值提供对应的public static final常数。

如果对immutable class有复杂操作,应该考虑提供public mutable companion class,如StringStringBuffer.

 

其他使函数不得覆写的方法:(除了final class

第一个方法是,使得所有函数都有final,这做法与“在个别的、不可实例化的utlity class上提供新的静态函数”效果相同,因此不推荐使用。

第二个方法是,是其所有构造函数为privatepackage-private,然后添加public static factories,代替public构造函数。

 

14,优先考虑复合,然后才是继承

15,除非专用于继承而设计并提供文件,否则不要使用继承

要为覆盖的函数提供说明

无论间接或直接,构造函数绝对不能调用可被覆写的函数。

CloneableSerializable可能会给继承设计带来特别的困难,所以实现这两个接口,可能会给日后扩展带来困难。可运用特殊手法,让subclasses来实现这些接口。(条款1054

如果要实现接口,那么clone readObject都不应该调用可被覆写的函数,理由同构造函数。

 

如果concrete class并未实现任何标准的interface,那“禁止它被继承”也许会给某些程序员带来不便,如果你认为必须同意这样的class被继承,一个合理做法是保证这个class永远不要调用其任何可被覆写的函数,并以文件清楚说明这个事实。即消除其对于覆盖函数的自用性。

机械似的消除被覆写函数的自用性,而不改变class的行为,做法是把可被覆盖的函数的函数本题搬移到private辅助函数中,然后直接唤起被覆盖函数式的private辅助函数,用以代替被覆盖函数的每一个自用动作。

 

16,尽量以interface代替abstract classes

“骨干absract类”扩展interface,有两者都优点,让abstract实现一下不常用的较固定方法。

interface可实现类似于多重继承,但必须一次正确设计,否则日后想修改,必然会影响到其他实现的子类。

 

interface通常是定义“允许多份实现同时存在”的类型的最佳办法,但如果认为“变化的容易性”比“程序的弹性和功能”重要时,上句话就不成立。这时候,应该提供一个abstract class来定义型别,前提是你理解并接受它带来的束缚。如果你export一个有所作为的interface,你应该强烈考虑提供“骨干实现类别”。最后一点,应该极为谨慎的设计public interface,并通过多做实现代码为它们做全面测试。

 

17interface只应当被用来定义型别(types)

Java标准库中有一些constant interfaces,应该视为反常,不要模范它们。

如果常数与既有的class或者interface紧密关联,那应该把它们加入那个classinterface中。

如果常数适合于烈举型,应该以一个typesafe enum class(条款21export它们,否则应该以一个“不可实例化的utiltiy class”(条款3)加以export

 

18,优先考虑static member classes,然后才是nonstatic

四种nest class:

static member classes

nonstatic member classes

anonymous classes

local class

 

static member class,利用public static final定义type enumP113

 

Item 19: Replace structures with classes

Ref: Item 37,

 

Item 20: Replace unions with class hierarchies

 

Item 21: Replace enum constructs with classes

public class Suit {

private final String name;

private Suit(String name) { this.name = name; }

public String toString() { return name; }

public static final Suit CLUBS = new Suit("clubs");

public static final Suit DIAMONDS = new Suit("diamonds");

public static final Suit HEARTS = new Suit("hearts");

public static final Suit SPADES = new Suit("spades");

}

Item 57.

 

// Typesafe enum with behaviors attached to constants

public abstract class Operation {

private final String name;

Operation(String name) { this.name = name; }

public String toString() { return this.name; }

// Perform arithmetic operation represented by this constant

abstract double eval(double x, double y);

public static final Operation PLUS = new Operation("+") {

double eval(double x, double y) { return x + y; }

};

public static final Operation MINUS = new Operation("-") {

double eval(double x, double y) { return x - y; }

};

public static final Operation TIMES = new Operation("*") {

double eval(double x, double y) { return x * y; }

};

public static final Operation DIVIDED_BY =

new Operation("/") {

double eval(double x, double y) { return x / y; }

};

}

 

It is a good idea for extensible typesafe enum classes to override the equals and hashCode

methods with final methods that invoke the Object methods. This ensures that no subclass

accidentally overrides these methods, maintaining the guarantee that all equal objects of the enumerated type are also identical (a.equals(b) if and only if a==b)

 

the readResolve methods in the classes just shown are package-private rather than

private. This is necessary because the instances of Operation and ExtendedOperation are,

in fact, instances of anonymous subclasses, so private readResolve methods would have no

effect (Item 57).

 

// Serializable, extensible typesafe enum

public abstract class Operation implements Serializable {

private final transient String name;

protected Operation(String name) { this.name = name; }

public static Operation PLUS = new Operation("+") {

protected double eval(double x, double y) { return x+y; }

};

public static Operation MINUS = new Operation("-") {

protected double eval(double x, double y) { return x-y; }

};

public static Operation TIMES = new Operation("*") {

protected double eval(double x, double y) { return x*y; }

};

public static Operation DIVIDE = new Operation("/") {

protected double eval(double x, double y) { return x/y; }

};

// Perform arithmetic operation represented by this constant

protected abstract double eval(double x, double y);

public String toString() { return this.name; }

// Prevent subclasses from overriding Object.equals

public final boolean equals(Object that) {

return super.equals(that);

}

public final int hashCode() {

return super.hashCode();

}

// The 4 declarations below are necessary for serialization

private static int nextOrdinal = 0;

private final int ordinal = nextOrdinal++;

private static final Operation[] VALUES =

{ PLUS, MINUS, TIMES, DIVIDE };

Object readResolve() throws ObjectStreamException {

return VALUES[ordinal]; // Canonicalize

}

}

 

 

// Subclass of extensible, serializable typesafe enum

abstract class ExtendedOperation extends Operation {

ExtendedOperation(String name) { super(name); }

public static Operation LOG = new ExtendedOperation("log") {

protected double eval(double x, double y) {

return Math.log(y) / Math.log(x);

}

};

public static Operation EXP = new ExtendedOperation("exp") {

protected double eval(double x, double y) {

return Math.pow(x, y);

}

};

// The 4 declarations below are necessary for serialization

private static int nextOrdinal = 0;

private final int ordinal = nextOrdinal++;

private static final Operation[] VALUES = { LOG, EXP };

Object readResolve() throws ObjectStreamException {

return VALUES[ordinal]; // Canonicalize

}

}

 

疑问:readResolve只要自己的类就可以了嘛?父类怎么办?

 

Item 22: Replace function pointers with classes and interfaces

 

Because the strategy interface serves as a type for all of its concrete strategy instances, a

concrete strategy class needn't be made public to export a concrete strategy. Instead, a “host class” can export a public static field (or static factory method) whose type is the strategy interface, and the concrete strategy class can be a private nested class of the host.

private和工厂方法,隐藏具体实现,这样就可以防止被子类改写。

 

To summarize, the primary use of C's function pointers is to implement the Strategy pattern.To implement this pattern in the Java programming language, declare an interface to represent the strategy and a class that implements this interface for each concrete strategy.

 

Item 23: Check parameters for validity

Therefore there would be little point in checking ahead of time that the elements in the list were mutually comparable. Note, however, that indiscriminate application of this technique can result in a loss of failure atomicity(Item 46).

 

you should use the exception translation idiom described in Item 43 to translate the natural exception into the correct one.

 

To summarize, each time you write a method or constructor, you should think about what restrictions exist on its parameters. You should document these restrictions and enforce them with explicit checks at the beginning of the method body. It is important to get into the habit of doing this; the modest work that it entails will be paid back with interest the first time a validity check fails.

 

Item 24: Make defensive copies when needed

You must program defensively with the assumption that clients of your class will do

their best to destroy its invariants.

原因:JAVA是引用传递,因此,可以对引用进行修改,从而改变类属性。例如:

// Attack the internals of a Period instance

Date start = new Date();

Date end = new Date();

Period p = new Period(start, end);

end.setYear(78); // Modifies internals of p!

 

it is essential to make a defensive copy of each mutable parameter to the constructor and to use the copies as components of the Period instance in place of the originals:

其实就是把引用传递,改为值传递。

 

Note that defensive copies are made before checking the validity of the parameters (Item 23), and the validity check is performed on the copies rather than on the originals.

原因:防止在这两个操作之间的时间中的弱点,做手脚。因此,先拷贝再检验合法性。

 

To prevent this sort of attack, do not use the clone method to make a defensive copy of a parameter whose type is subclassable by untrusted parties.

原因:clone不是final,可以通过子类继承,从而改变拷贝的操作。

 

// Second attack on the internals of a Period instance

Date start = new Date();

Date end = new Date();

Period p = new Period(start, end);

p.end().setYear(78); // Modifies internals of p!

原因:返回值也是引用类型。指向对象内部属性。

解决方法:return defensive copies of mutable internal fields:

// Repaired accessors - make defensive copies of internal fields

public Date start() {

return (Date) start.clone();

}

public Date end() {

return (Date) end.clone();

}

能够用clone的原因,应该是构造函数已经检查合法性,且构造了Date,因此,statrt/end不会是子类型。

This is acceptable (although not required), as we know with certainty that the class of Period's internal Date objects is java.util.Date rather than some potentially untrusted subclass.

 

Also, it is critical to remember that nonzero-length arrays are always mutable. Therefore you should always make a defensive copy of an internal array before returning it to a client.

数据返回值的可变性。与12对应,很重要,不要使用final array,而应该用list

 

有些程序员就喜欢用不变类型,例如Period,他们会用immutablelong代替Date类型

 

Item 25: Design method signatures carefully

Choose method names carefully.

Don't go overboard in providing convenience methods. When in doubt, leave it out.

Avoid long parameter lists. Long sequences of identically typed parameters are especially harmful.

For parameter types, favor interfaces over classes.

Use function objects (Item 22) judiciously.

 

Item 26: Use overloading judiciously

the choice of which overloading to invoke is made at compile time.

selection among overloaded methods is static, while selection among overridden methods is dynamic.

avoid confusing uses of overloading.

A safe, conservative policy is never to export two overloadings with the same number of parameters.

例如:ObjectOutputStreamwrite方法

 

Exporting multiple overloadings with the same number of parameters is unlikely to confuse programmers if it is always clear which overloading will apply to any given set of actual parameters. This is the case when at least one corresponding formal parameter in each pair of overloadings has a “radically different” type in the two overloadings.

不会隐性转型的两种类型,作为传入参数。[JLS, 5.1.7 ], [JLS, 15.12.1-3].

 

If such a situation cannot be avoided, for example because you are retrofitting an existing class to implement a new interface, you should ensure that all overloadings behave identically when passed the same parameters.

Boolean equals(String s) boolean squals(Object o) 的情况。

 

Item 27: Return zero-length arrays, not nulls

private List cheesesInStock = ...;

private final static Cheese[] NULL_CHEESE_ARRAY = new Cheese[0];

/**

* @return an array containing all of the cheeses in the shop.

*/

public Cheese[] getCheeses() {

return (Cheese[]) cheesesInStock.toArray(NULL_CHEESE_ARRAY);

}

注意:toArray(Object [])toArray()方法的区别。

说明:In this idiom, a zero-length array constant is passed to the toArray method to indicate the desired return type.

 

it is possible to return the same zero-length array from every invocation that returns no items because zero-length arrays are immutable and immutable objects may be shared freely(Item 13).

 

there is no reason ever to return null from an array-valued method instead of returning a zero-length array.

 

Item 28: Write doc comments for all exposed API elements

The doc comment for a method should describe succinctly the contract between the method and its client.

 

Item 29: Minimize the scope of local variables

The most powerful technique for minimizing the scope of a local variable is to declare it where it is first used.

Nearly every local variable declaration should contain an initializer.

 

Item 30: Know and use the libraries

 

The third flaw in the random method is that it can, on rare occasion, fail catastrophically,

returning a number outside the specified range. This is so because the method attempts to map the value returned by rnd.nextInt() into a nonnegative integer with Math.abs. If

nextInt() returns Integer.MIN_VALUE, Math.abs will also return Integer.MIN_VALUE, and

the remainder operator (%) will return a negative number, assuming n is not a power of two.This will almost certainly cause your program to fail, and the failure may be difficult to reproduce.(为什么??)

 

By using a standard library, you take advantage of the knowledge of the experts who wrote it and the experience of those who used it before you.

 

Numerous features are added to the libraries in every major release, and it pays to keep abreast of these additions.

 

every programmer should be familiar with the contents of java.lang, java.util, and, to a lesser extent, java.io. Knowledge of other libraries can be acquired on an as-needed basis.

 

Item 31: Avoid float and double if exact answers are required

 

The float and double types are particularly ill-suited for monetary calculations because it is impossible to represent 0.1 (or any other negative power of ten) as a float or double exactly.

 

Here's a straightforward transformation of the previous program to use the BigDecimal type in place of double

 

An alternative to using BigDecimal is to use int or long, depending on the amounts involved, and to keep track of the decimal point yourself.

 

Item 32: Avoid strings where other types are more appropriate

Strings are poor substitutes for other value types.

Strings are poor substitutes for enumerated types.

Strings are poor substitutes for aggregate types.

Strings are poor substitutes for capabilities.

 

Item 33: Beware the performance of string concatenation

Using the string concatenation operator repeatedly to concatenate n strings requires time quadratic in n.

To achieve acceptable performance, use a StringBuffer in place of a String to store the statement under construction

Note that the second method preallocates a StringBuffer large enough to hold the result. Even if it is detuned to use a default-sized StringBuffer, it is still forty-five times faster than the first.

 

Item 34: Refer to objects by their interfaces

If appropriate interface types exist, parameters, return values, variables, and fields should all be declared using interface types.

It is entirely appropriate to refer to an object by a class rather than an interface if no appropriate interface exists.

A final case in which there is no appropriate interface type is that of classes that implement an interface but provide extra methods not found in the interface—for example, LinkedList. Such a class should be used only to refer to its instances if the program relies on the extra methods: it should never be used as a parameter type (Item 25).

 

Item 35: Prefer interfaces to reflection

You lose all the benefits of compile-time type checking, including exception checking. If a program attempts to invoke a nonexistent or inaccessible method reflectively, it will fail at run time unless you've taken special precautions.

The code required to perform reflective access is clumsy and verbose. It is tedious to write and difficult to read.

Performance suffers. As of release 1.3, reflective method invocation was forty times slower on my machine than normal method invocation. Reflection was rearchitected in release 1.4 for greatly improved performance, but it is still twice as slow as normal access, and the gap is unlikely to narrow.

 

The reflection facility was originally designed for component-based application builder tools. Such tools generally load classes on demand and use reflection to find out what methods and constructors they support.

As a rule, objects should not be accessed reflectively in normal applications at run time.

 

You can obtain many of the benefits of reflection while incurring few of its costs by using it only in a very limited form. For many programs that must use a class unavailable at compile time, there exists at compile time an appropriate interface or superclass by which to refer to the class (Item 34). If this is the case, you can create instances reflectively and access them normally via their interface or superclass. If the appropriate constructor has no parameters, as is usually the case, then you don't even need to use the java.lang.reflect package; the Class.newInstance method provides the required functionality.

 

 

Item 36: Use native methods judiciously

As of release 1.3, it is rarely advisable to use native methods for improved performance.

 

Item 37: Optimize judiciously

Strive to write good programs rather than fast ones.

Strive to avoid design decisions that limit performance.

Consider the performance consequences of your API design decisions.

It is a very bad idea to warp an API to achieve good performance.

Measure performance before and after each attempted optimization.

 

Item 38: Adhere to generally accepted naming conventions

 

Item 39:Use exceptions only for exceptional conditions

Exceptions are, as their name implies, to be used only for exceptional conditions; they should never be used for ordinary control flow.

A well-designed API must not force its client to use exceptions for ordinary control flow.

 

Item 40:Use checked exceptions for recoverable conditions and run-time exceptions for programming errors

Use checked exceptions for conditions from which the caller can reasonably be expected to recover.

Use run-time exceptions to indicate programming errors.

All of the unchecked throwables you implement should subclass RuntimeException

To summarize, use checked exceptions for recoverable conditions and run-time exceptions for programming errors.

 

Item 41:Avoid unnecessary use of checked exceptions

 

Item 42:Favor the use of standard exceptions

 

Item 43: Throw exceptions appropriate to the abstraction

higher layers should catch lower-level exceptions and, in their place, throw exceptions that are explainable in terms of the higher-level abstraction.

 

// Exception chaining prior to release 1.4

private Throwable cause;

HigherLevelException(Throwable t) {

cause = t;

}

public Throwable getCause() {

return cause;

}

 

While exception translation is superior to mindless propagation of exceptions from lower layers, it should not be overused.

If it is impossible to prevent exceptions from lower layers, the next best thing is to have the higher layer silently work around these exceptions, insulating the caller of the higher-level method from the lower-level problem.

logging them.

 

Item 44:Document all exceptions thrown by each method

Always declare checked exceptions individually, and document precisely the conditions under which each one is thrown using the Javadoc @throws tag.

Use the Javadoc @throws tag to document each unchecked exception that a method can throw, but do not use the throws keyword to include unchecked exceptions in the

method declaration.

If an exception is thrown by many methods in a class for the same reason, it is acceptable to document the exception in the class's documentation comment rather than documenting it individually for each method.

 

Item 45:Include failure-capture information in detail messages

To capture the failure, the string representation of an exception should contain the values of all parameters and fields that “contributed to the exception.”

 

Item 46:Strive for </vetbfailure atomicity

Generally speaking, a failed method invocation should leave the object in the state that it was in prior to the invocation.

There are several ways to achieve this effect. The simplest is to design immutable objects (Item 13).

For methods that operate on mutable objects, the most common way to achieve failure atomicity is to check parameters for validity before performing the operation (Item 23).

 

A closely related approach to achieving failure atomicity is to order the computation so that any part that may fail takes place before any part that modifies the object.

 

A third and far less common approach to achieving failure atomicity is to write recovery code that intercepts a failure occurring in the midst of an operation and causes the object to roll back its state to the point before the operation began.

 

A final approach to achieving failure atomicity is to perform the operation on a temporary copy of the object and replace the contents of the object with the temporary copy once the operation is complete.

 

Item 47:Don't ignore exceptions

An empty catch block defeats the purpose of exceptions, which is to force you to handle exceptional conditions.

 

At the very least, the catch block should contain a comment explaining why it is appropriate to ignore the exception.

 

Item 48: Synchronize access to shared mutable data

The language guarantees that reading or writing a single variable is atomic unless the variable is of type long or double. In other words, reading a variable other than a long or double is guaranteed to return a value that was stored into that variable by some thread, even if multiple threads modify the variable concurrently without synchronization.

语言保证基本类型读写的同步,除了longdouble.

 

You may hear it said that to improve performance, you should avoid the use of synchronization when reading or writing atomic data. This advice is dangerously wrong.

 

Synchronization is required for reliable communication between threads as well as for mutual exclusion.

 

The problem with this code is that in the absence of synchronization, there is no guarantee as to when, if ever, the stoppable thread will “see” a change in the the value of stopRequested that was made by another thread.

主要原因:不保证一个现程能看到,另一线程所作的改动。

另一原因:类成员变量是线程共享的,在堆里;而局部变量,是不共享的,在栈里。

 

The volatile modifier guarantees that any thread that reads a field will see the most recently written value. ??

 

the doublecheck idiom does not work, although it does work if the shared variable contains a primitive value rather than an object reference

原因:If a thread reads the reference without synchronization and then invokes a method on the referenced object, the method may observe the object in a partially initialized state and fail catastrophically.

 

// The initialize-on-demand holder class idiom

private static class FooHolder {

static final Foo foo = new Foo();

}

public static Foo getFoo() { return FooHolder.foo; }

 

The idiom takes advantage of the guarantee that a class will not be initialized until it is used [JLS, 12.4.1 ].

In summary, whenever multiple threads share mutable data, each thread that reads or writes the data must obtain a lock.

 

Without synchronization, there is no guarantee as to which, if any, of a thread's changes will be observed by another thread.

 

Item 49: Avoid excessive synchronization

To avoid the risk of deadlock, never cede control to the client within a synchronized method or block.

In other words, inside a synchronized region, do not invoke a public or protected method that is designed to be overridden.

 

As a rule, you should do as little work as possible inside synchronized regions.

 

Item 50: Never invoke wait outside a loop

The Object.wait method is used to make a thread wait for some condition. It must be invoked inside a synchronized region that locks the object on which it is invoked. This is the standard idiom for using the wait method:

synchronized (obj) {

while (<condition does not hold>)

obj.wait();

... // Perform action appropriate to condition

}

Always use the wait loop idiom to invoke the wait method. Never invoke it outside of a loop. The loop serves to test the condition before and after waiting.

原因:notify或者notifyall之前已经被调用过,放到while防止不再被唤醒。

Testing the condition after waiting and waiting again if the condition does not hold are necessary to ensure safety.

原因:

1,   另一个线程在调用notify和等待现程被唤醒的时间间隔中,获得锁,并且修改了保护状态

2,   另外一个线程意外地、或者敌意地调用notify,在条件不满足时

3,   notifying的线程过度唤醒等待现程。例如,只有一些条件满足,但调用了notifyAll.

4,   等待线程有可能在没有notify的情况下被唤醒,称为spurious wakeup。虽然JLS没有提及,但许多JVM的实现却是有这种情况发生,虽然机会不多。

 

notify wakes a single waiting thread, assuming such a thread exists, and notifyAll wakes all waiting threads.)

It is often said that you should always use notifyAll. This is reasonable, conservative advice, assuming that all wait invocations are inside while loops.

原因:wait在循环中,所以可以用notifyAll保证所有线程都会接收到。

 

As an optimization, you may choose to invoke notify instead of notifyAll if all threads that could be in the wait-set are waiting for the same condition and only one thread at a time can benefit from the condition becoming true. Both of these conditions are trivially satisfied if only a single thread waits on a particular object (as in the WorkQueue example, Item 49).

除非刚好只有一个线程或者你只需要唤醒一个线程,例如一个等待工作队列,就只需要唤醒一个在等待的对象。

 

If all of the threads vying for special status are logically equivalent, then all you have to do is carefully use notify instead of notifyAll.

In summary, always invoke wait from within a while loop, using the standard idiom. There is simply no reason to do otherwise. Usually, you should use notifyAll in preference to notify. There are, however, situations where doing so will impose a substantial performance penalty. If notify is used, great care must be taken to ensure liveness.

 

Item 51: Don't depend on the thread scheduler

Any program that relies on the thread scheduler for its correctness or performance is likely to be nonportable.

When faced with a program that barely works because some threads aren't getting enough CPU time relative to others, resist the temptation to “fix” the program by putting in calls to Thread.yield.

 

Thread priorities are among the least portable features of the Java platform.

 

The only use that most programmers will ever have for Thread.yield is to artificially increase the concurrency of a program during testing.

 

Item 52: Document thread safety

The presence of the synchronized modifier in a method declaration is an implementation detail, not a part of the exported API.

 

To enable safe multithreaded use, a class must clearly document in prose the level of thread safety that it supports.

 

immutable— Instances of this class appear constant to their clients. No external

synchronization is necessary. Examples include String, Integer, and BigInteger

(Item 13).

thread-safe— Instances of this class are mutable, but all methods contain sufficient internal synchronization that instances may be used concurrently without the need for external synchronization. Concurrent invocations will appear to execute serially in some globally consistent order. Examples include Random and java.util.Timer.

conditionally thread-safe— Like thread-safe, except that the class (or an associated class) contains methods that must be invoked in sequence without interference from other threads. To eliminate the possibility of interference, the client must obtain an appropriate lock for the duration of the sequence. Examples include Hashtable and Vector, whose iterators require external synchronization.

thread-compatible— Instances of this class can safely be used concurrently by surrounding each method invocation (and in some cases, each sequence of method invocations) by external synchronization. Examples include the general purpose collection implementations, such as ArrayList and HashMap.

thread-hostile— This class is not safe for concurrent use by multiple threads, even if all method invocations are surrounded by external synchronization. Typically, thread hostility stems from the fact that methods modify static data that affect other threads. Luckily, there are very few thread-hostile classes or methods in the platform libraries.

 

Item 53: Avoid thread groups

thread groups are largely obsolete.

 

 

条款54:审慎实现Serializable

A major cost of implementing Serializable is that it decreases the flexibility to change

a class's implementation once it has been released.

Therefore you should carefully design a high-quality serialized form that you

are willing to live with for the long haul (Item 55).

A simple example of the constraints on evolution that accompany serializability concerns

stream unique identifiers, more commonly known as serial version UIDs

原因:UID的值由类名、实现接口名、publicprotected成员、函数决定,如果加了成员或者函数,就会影响到对原来类的兼容性。

 

A second cost of implementing Serializable is that it increases the likelihood of bugs

and security holes.

A third cost of implementing Serializable is that it increases the testing burden

associated with releasing a new version of a class.

Implementing the Serializable interface is not a decision to be undertaken lightly.

Classes designed for inheritance (Item 15) should rarely implement Serializable, and

interfaces should rarely extend it

you should consider providing a parameterless constructor on nonserializable classes designed for inheritance.

原因:If a class that is designed for inheritance is not serializable, it may be impossible to write a serializable subclass. Specifically, it will be impossible if the superclass does not provide an accessible parameterless constructor.

解决方法:The following transformation adds a protected parameterless constructor and an initialization method. The initialization method has the same parameters as the normal constructor and establishes the same invariants:

子类在readObject时,调用initialization方法

 

Inner classes (Item 18) should rarely, if ever, implement Serializable. Therefore, the default serialized form of an inner class is ill-defined. A static member class can, however,implement Serializable.

原因:内部类用编译器产生的语义属性去引用外围的实例,而且用在外部范围内保存局部变量。这些属性与类定义的对应是不确定的,正如匿名类和内部类的名字。

 

Item 55:Consider using a custom serialized form

Do not accept the default serialized form without first considering whether it is appropriate.

The default serialized form is likely to be appropriate if an object's physical representation is identical to its logical content.

 

Even if you decide that the default serialized form is appropriate, you often must provide a readObject method to ensure invariants and security.

 

Using the default serialized form when an object's physical representation differs substantially from its logical data content has four disadvantages:

l         It permanently ties the exported API to the internal representation.

l         In the above example, the serialized form unnecessarily

l         It can consume excessive time

l         It can cause stack overflows.

If all instance fields are transient, it is technically permissible to dispense with invoking defaultWriteObject and defaultReadObject, but it is not recommended.

 

Before deciding to make a field nontransient, convince yourself that its value is part of the logical state of the object.

 

Regardless of what serialized form you choose, declare an explicit serial version UID in every serializable class you write

 

Item 56:Write readObject methods defensively

Just as a constructor must check its arguments for validity (Item 23) and make defensive copies of parameters where appropriate(Item 24), so must a readObject method.

 

Loosely speaking, readObject is a constructor that takes a byte stream as its sole parameter.

隐患:人工构造二进制流,传给readObject函数

 

public MutablePeriod() {

try {

ByteArrayOutputStream bos =

new ByteArrayOutputStream();

ObjectOutputStream out =

new ObjectOutputStream(bos);

// Serialize a valid Period instance

out.writeObject(new Period(new Date(), new Date()));

/*

* Append rogue "previous object refs" for internal

* Date fields in Period. For details, see "Java

* Object Serialization Specification," Section 6.4.

*/

byte[] ref = { 0x71, 0, 0x7e, 0, 5 }; // Ref #5

bos.write(ref); // The start field

ref[4] = 4; // Ref # 4

bos.write(ref); // The end field

// Deserialize Period and "stolen" Date references

ObjectInputStream in = new ObjectInputStream(

new ByteArrayInputStream(bos.toByteArray()));

period = (Period) in.readObject();

start = (Date) in.readObject();

end = (Date) in.readObject();

} catch (Exception e) {

throw new RuntimeException(e.toString());

}

}

原因:It is possible to create a mutable Period instance by fabricating a byte stream that begins with a byte stream representing a valid Period instance and then appends extra references to the private Date fields internal to the Period instance.

提供一个指向内部的对象引用

 

解决方法:When an object is deserialized, it is critical to defensively copy any field

containing an object reference that a client must not possess.

 

There is one other similarity between readObject methods and constructors, concerning nonfinal serializable classes. A readObject method must not invoke an overridable method, directly or indirectly (Item 15).

 

Item 57: Provide a readResolve method when necessary

Any readObject method, whether explicit or default, returns a newly created instance, which will not be the same instance that was created at class initialization time. Prior to the 1.2 release, it was impossible to write a serializable singleton class.

 

If the class of an object being deserialized defines a readResolve method

with the proper declaration, this method is invoked on the newly created object after it is

deserialized. The object reference returned by this method is then returned in lieu of the newly created object. In most uses of this feature, no reference to the newly created object is retained; the object is effectively stillborn, immediately becoming eligible for garbage collection.

 

A readResolve method is necessary not only for singletons, but for all other instance-controlled classes,

Another example of an instance-controlled class is a typesafe enum (Item 21)

 

A second use for the readResolve method is as a conservative alternative to the defensive readObject method recommended in Item 56.

In this approach, all validity checks and defensive copying are eliminated from the readObject method in favor of the validity checks and defensive copying provided by a normal constructor.

 

 

While the defensive readResolve idiom is not widely used, it merits serious consideration.

 

The accessibility of the readResolve method is significant.

 

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值