Joshua Bloch's much awaited (second) Effective Java book is scheduled to arrive in few weeks. Joshua has done favors to millions of programmers by distilling his knowledge of Java programming language (and programming in general) in his first Effective Java Book (published by Addison Wesley).
I just thought of providing a cheat-sheet for this book so that programmers can refer to it (link to it) while programming, reviewing code, thinking etc. This text is neither a replacement of the actual book nor is it a mere copy of contents in it (which I have read and referred to, several times). It is more of my understanding and reflection of the entire book, although I have heavily depended on the actual text for it is impossible to come up with alternate explanation of any concept explained in this book. Joshua's deep understanding of the language, excellent choice of words, tenacious following of the Java Language Specification, clear and concise manner of explanation makes this book a great reference. Thanks Joshua, for your insights. It is also obvious that Joshua rates API design and clarity in programming higher than performance considerations. It is striking that great programmers think alike:
Premature optimization is the root of all evil.
Although this book was written with J2SE 1.4 SDK in mind (Boy! it is already years old), the material applies well even to Java SE 6. I created this e-reference so that it would make a good read while we wait for Joshua's second book (and hopefully thereafter).
Learning the art of programming, like most other disciplines, consists of first learning the rules and then learning when to violate them. - Joshua Bloch.
I initially thought of creating it as a Wiki topic but then decided against it. A companion Wiki is created here and this document is highly anchored. I haven't gone through several editorial and correctness iterations of the text here, and rely on programmers at large to enhance and possibly correct the contents. Interested readers can comment on the Wiki by linking to the anchors from here. The anchors in this document are listed here.
| ID (Linked to discussion) | Item | Related Items (From this book, JLS etc.) |
| 1. | Consider providing static factory methods instead of constructors. | 13 02 21 34 14 |
| 2. | Enforce the singleton property with a private constructor. | 10 57 21 03 |
| 3. | Enforce noninstantiability with a private constructor. | 02 15 |
| 4. | Avoid creating duplicate objects. | |
| 5. | Eliminate obsolete object references. | 29 |
| 6. | Avoid finalizers. | JLS-12.6 |
| 12. | Minimize the accessibility of classes and members. | JLS-6.6 18 19 |
| 13. | Favor Immutability. | 04 10 24 |
| 14. | Favor composition over inheritance. | 15 |
| 15. | Design and document inheritance or else prohibit it. |
| ID (Linked to list) | Discussion | // DON'T DO THIS! |
| 1. | Consider providing static factory methods instead of constructors. This technique has both advantages and disadvantages. To favor composition over inheritance though, having static factory methods is advisable. Advantages:
Disadvantages:
|
|
| 2. | Enforce the singleton property with a private constructor. The topic of Singletons is under scrutiny by developer community. It's clearly an overused pattern. Developers should think hard to justify the need for Singletons. It has somehow become a deplorable norm to create Singletons without justification and at times implement them incorrectly. But first, the following discusses two ways of implementing Singletons correctly (under normal circumstances) should the need for them be justified: First: This is correct, straightforward and should generally be employed. public class MySingleton { private static final MySingleton INSTANCE = new MySingleton(); /*essential to make this private*/ private MySingleton() { // this can possibly delay class loading, but that's OK } public static getInstance() { return INSTANCE; } // implement readResolve() method if this class needs to be Serializable } Second: public class MySingleton { private static MySingleton INSTANCE = null; /*essential to make this private*/ private MySingleton() { // this can possibly delay class loading, but that's OK } public synchronized static getInstance() { // this results in lock contention, but it is correct if (INSTANCE == null) { INSTANCE = new MySingleton(); } return INSTANCE; } // implement readResolve() method if this class needs to be Serializable } There are other techniques like lazy initialization, but you have to be extremely careful with them. The first technique above is by far the best way of defining Singletons. Few good references on this topic: |
Never be over-smart with implementing something as trivial as correct Singleton pattern. This results in insidious issues with thread safety of your program because of the so-called broken Double-checked locking idiom: //Don't Do This public class OverSmart { |
| 3. | Enforce noninstantiability with a private constructor. A class can be made noninstantiable only by providing a single explicit private constructor. Note that noninstantiability of a class can't be guaranteed by making it Another technique I have seen is to create a parameterless public constructor that throws a RuntimeException. But this is not an effective code pattern because it does not make the intent of the programmer explicit. For API or library designers, it is a good idea to make a class noninstantiable if it is a mere collection of utility static methods. |
//Don't Do This public class YouCantCreateMe { public YouCantCreateMe() { throw new RuntimeException("Sorry"); } } |
| 4. | Avoid creating duplicate objects. Quoting Joshua: "Reuse can be both faster and more stylish". Following the intent of this item requires sound judgment. There are quite a few considerations and counter-arguments that a programmer has to contemplate while following what has been said here. Note that reuse here refers to functionally equivalent objects. In other words, there is a potential to reuse existing objects in lieu of new ones, if the new ones are functionally equivalent to existing ones. In order to take benefits of object reuse, one should carefully study the immutable classes and use them whenever possible as they always promote reuse through functional equivalence. Making a class immutable however, needs thorough analysis. More subtle cases involve reusing the objects when they are mutable. These are more complex cases to analyze. For example, a profiling session tells you that you are calling a method hundreds of times and that this method unnecessarily creates invariant objects every time it is called. In that case, again, after thorough analysis, you can optimize the use of existing objects by facilitating reuse. |
//Don't Do This String s = new String("silly"); //The argument to the String constructor ("silly") is itself a |
| 5. | Eliminate obsolete object references. Myth for novice programmers: Java (programs written in Java) has no memory leaks. Although Java manages the memory required for our programs, it can not understand what we want to do. If we are holding onto an object by maintaining a reference to it, it is obvious that Java thinks that we want to do that. This means that as programmers, it is our prime responsibility to Nulling out a reference to remove obsolete references to an object is good, but one must not overdo it. The best way to eliminate an obsolete reference is to reuse the variable in which it was contained or to let it fall out of scope.
|
|
| 6. |
"Finalizers are unpredictable, often dangerous, and generally unnecessary. Their use can cause erratic behavior, poor performance, and portability problems." Frankly, I have never used finalizers and hence I don't have much to add to this discussion. It is worth noting that depending on execution of finalizers is a bad decision nevertheless. Providing and making clients of your code call a termination or resource reclamation method leads to much cleaner API. Examples are: close() method on several java.io classes. A legitimate use of finalizers arises when you feel the need of a safety net because the clients may not do the due diligence and resources are not reclaimed. Obviously, finalizers are not the best solution for this (owing to previous discussion), but that is the only way. Another reason for their use is in case of classes that have native peers. Remember however that native peers must not hold critical resources to be reclaimed in a finalizer.
Finally, Joshua suggests a finalizer guardian idiom for a nonfinal public class that guards against a malicious subclass that fails to call super.finalize() failing to give your class a chance to finalize. This idiom is used in java.util.Timer class. A finalizer guardian's (which is an instance of anonymous inner class) sole purpose (at the cost of its existence) is to call the enclosing instance's finalize() method. A quick check on GlassFish codebase reveals that there are several finalizers and I am not quite sure if all of them are called for. |
|
| 12. | Minimize the accessibility of classes and members. Benefits of information hiding (encapsulation) and separating interfaces from implementation are obvious. Java provides several facilities to hide the internals of a module. This results in clean design and modules can be tested, changed without affecting the correctness of the entire system since modules rely on each other's API. Don't confuse information hiding with the subtle topic of Method Overloading-Method Overriding-Field Hiding. Access Control Mechanism in Java should be carefully studied. From this standpoint, (top-level) classes and members are the two broad categories of interest. As of Java SE 5.0 and Java SE 6, packages are the only way to sand-box code organized into classes and interfaces. The natural way to design large bodies of code is to first define the API and then define packages, then interfaces and classes and then implement the methods and fields considering access specification at every step. To achieve this, it is best to start with the most protected access and then relax it, till you achieve required accessibility.
The default access is implied by the absence of any specifier on a given entity. Thus, a top-level class (and interface) is either public which means it is seen by the universe or it is package-private which means by default, only other classes and interface in the same package can see it. This is the first entry point as far as access is concerned.
A member can have one of private (accessible only from within the class) , package-private (default, accessible from within any class in the same package), protected (mostly accessible from any subclass anywhere and from within the package) and public (accessible from anywhere) access specifiers. An important conclusion is that the protected access specifier is less protected than the default access, don't place a protected access specifier in an attempt to protect a member of a class. Thus, if you are unsure of accessibility, start with private, followed by package-private. Public variables should be avoided as they result in exposing the internal representation of the data and are hard to replace by something else. An exception here is the definition of constants. Constants should always be instances of either primitive types or immutable classes. "A final field containing a reference to a mutable object has all the disadvantages of
To summarize, optimizing access specification is like tightening the screws leaving tolerance that is just enough, eliminating extraneous words in a sentence, paragraphs in a chapter and so on. |
//Don't Do This class Base { public static final String NAME = "Base"; // Hiding/Shadowing is not advisable } class Derived extends Base { public static final String NAME = "Derived"; }
//Potential security hole!
public static final Type[] VALUES = { ... };
|
| 13. |
Favor Immutability.
Follow this recipe to impart immutability to classes. Immutable objects are simple and inherently thread-safe; they require no synchronization since there are no state changes that they go through. It is appropriate to share the internals of immutable objects with other objects if there is no way to modify them. Generally speaking, the internals of an immutable object are comprised of immutable objects. Ways to make an object immutable in addition to following the recipe:
It is an oversight that BigInteger and BigDecimal classes are not final. You should always make value classes immutable. To reduce the performance overhead, you can think of a package-private mutable companion class that an immutable class uses to speed up multistep operations. If predicting what can be done with an immutable class is not possible, make this companion class public (for example, java.lang.String and java.lang.StringBuffer). |
|
| 14. | Favor composition over inheritance. Implementation Inheritance is dangerous with classes not designed for that. This is mainly because the superclass implementation of overridden methods may change from release to release and break the subclasses even if their (of subclasses) implementation has not changed. To quote Joshua:
Surprising things may result if you rely on the implementation of behavior in superclasses. This is especially so for classes that are not designed for extension. The effect is pronounced when when you use overriding. An alternate technique uses composition and employs the decorator or forwarding design pattern. The idea is to make an implementation a private member of your class and forward requests of common interface to that member. This is likely to make your implementation more robust. <personal-problem> Inheritance is appropriate only when a genuine subtype relationship exists between the subclass and the superclass. According to Joshua:
|
|
| 15. | Design and document (for) inheritance or else prohibit it. (Editorial comment: As the perhaps only case, I thought for is not required in this item's synopsis). Perhaps this item tries to minimize the dilemma about when to choose inheritance. Documentation of how a class uses its overridable methods in the /**doc comments */ of a method is a must. This helps users of the API to understand the implications of overriding methods. This is called documenting self-use of the class. This is where (I feel) the art part of programming comes into picture (again), since there is no recipe to follow.
Note that if you violate the above, a final field can be observed in two different states by a program! It's generally not a good idea for classes designed for extension to implement Cloneable or Serializable interfaces. If they do, they should follow certain restrictions:
This is where the implementation of a class becomes part of public interface of a class. This is because if you have designed a Serializable class for extension, you have to make readresolve() or writeReplace() methods protected or else they are silently ignored by the subclasses while (de)serializing. Since this is rather confusing, I am going to quote Joshua, as I can't do any better --
This item concludes by providing a technique of moving the self-use of a class's overridable methods to its private helper method. |
|
Overloading and overriding (of methods) on the other hand, are a usual practice and there are scenarios when these techniques are appropriate. Even then, overloading should be used more cautiously. At first, it may seem that naming a behavior of a class by a single verb is appropriate, but experience shows that naming methods explicitly (or, uniquely) is more expressive and maintainable than overloaded methods (with same names and slightly differing parameter lists). Overloading is a compile-time phenomenon and compiler generates the code to bind a call to the given overloaded method with a given method. The run-time type of method's parameters does not affect the choice of overloading. As a rule, don't overload methods if they take same number of parameters. It's also messy (impossible?) when the parameters are of same type, but convey something completely different.
Overriding is a more widely used procedure. It is the language mechanism to support the so-called polymorphism, where a subclass modifies a (super) class's behavior identified by a method with a given name. A subtle characteristic of Java's method overriding is that this decision is made dynamically (based on the run-time type of the object on which the method is invoked) and that it is impossible to call super class's implementation of an overridden method on an instance of a subclass (except from within subclass using the keyword super). Another consequence of using overriding is that an overridden method should have access specification that is no less restrictive than what is specified in the nearest super class that provides its implementation. This leads us into Liskov's Substitution Principle which states that anything that can be done with an instance of a class can also be done with an instance of any of its subclasses. (This is true with interface-based programming as well. In other words, since all the methods in a Java interface are public by default, all the classes implementing the interface must have their public implementations).
Java 5 introduces an annotation, @Override to denote a method definition that overrides it in a subclass. Compiler ensures that use of @Override is correct (a compiler . Perhaps it was a good idea to provide @Overload annotation as well.
1. Don't provide any methods that modify the object (known as mutators).
2. Ensure that no methods may be overridden. This prevents careless or malicious subclasses from compromising the immutable behavior of the class. Preventing method overrides is generally done by making the class final.
3. Make all fields final. This clearly expresses your intentions in a manner that is enforced by the system. Also, it may be necessary to ensure correct behavior if a reference to a newly created instance is passed from one thread to another without synchronization, depending on the results of ongoing efforts to rework the memory model.
4. Make all fields private. This prevents clients from modifying fields 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 (Item 12).
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. Neither initialize such a field to a client-provided object reference nor return the object reference from an accessor. Make defensive copies (Item 24) in constructors, accessors, and readObject methods (Item 56).
0.1 04 September 2007 km@dev.java.net (केदार Mhaswade) Created document, described
Chapter 2.
0.2 11 September 2007 km@dev.java.net (केदार Mhaswade) Added notes, item #12, #13.
0.3 11 November 2007, km@dev.java.net (केदार Mhaswade) Added notes, item #14, #15.
Notes