- Static factory methods vs. constructors.
- Advantages
- They have descriptive names.
- Example: BigInteger.probablePrime
- They can reuse existing objects.
- Example: Boolean.valueOf(boolean)
- They can return a subtype of the declared type - good for hiding impl classes.
- Example: java.util.Collections methods
- Example: service provider frameworks like Java Cryptography Extension
- Disadvantages
- If class has no public or protected ctors, it can't be subclassed.
- Static factory methods are not distinguished in javadoc from other static methods.
- Can compensate by using standard naming conventions (still evolving), e.g. valueOf, getInstance, etc.
- Use private ctor to enforce singleton
- More future flexibility if singleton obtained through static method. E.g. lets you return a unique instance per thread in the future without changing API.
- If singleton is Serializable, must also implement readResolve().
- Use private ctor to enforce noninstantiability.
- Making class abstract for this purpose does not work.
- Avoid duplicate objects
- Especially if object is immutable.
- Relates to using static factory methods instead of ctors (Item 1).
- Can make it clear when objects are used as if they were constants (e.g. Calendar).
- Relates to lazy initialization (Item 48).
- Case: adapters that delegate to a backing object.
- But, object pools are generally bad, except when object is extremely expensive to create (e.g. database connection).
- Clean out obsolete objects
- Mainly to prevent memory leaks (termed unintentional object retentions).
- Essential when a class manages its own memory.
- Common issue in caches. Possible solution strategies:
- WeakHashMap
- Timer-based expiration of old objects.
- java.util.LinkedHashMap - has a removeEldestEntry method, which can expire entries based on insertion-order or access-order, depending on how the LinkedHashMap is constructed.
- Diagnosis of memory leaks usually requires using a heap profiler.
- Finalizer issues
- Two legitimate uses: safety net and cleaning up noncritical native resources.
- Finalizer guardian idiom - consider using for every nonfinal public class with a finalizer.
- Contract of equals
- When not overriding is ok:
- Each instance of the class is inherently unique (e.g. Thread).
- A logical equality test isn't really needed (e.g. java.util.Random).
- A superclass alread implements equals adequately (e.g. AbstractSet, AbstractList, AbstractMap).
- Class is private or package-private, and you're certain equals will never be called. But, could implement anyway to at least throw UnsupportedOperationException, just to make sure.
- When overriding is recommended:
- When there's a concept of logical equality that's different from object identity.
- Override hashCode when overriding equals
- Required if object used as key in HashMap/Hashtable or value in HashSet.
- Equal objects must have equal hash codes.
- Override toString
- clone
- clone() is like another constructor.
- Callers of clone() must cope with catching an exception.
- clone() is incompatible with the normal use of final fields.
- You are probably better off providing an alternative means of object copying.
- One alternative is the copy constructor.
- Comparable
- Minimize accessibility
- For information hiding and encapsulation
- Purpose is to hide design decisions, so that they may be changed in the future with no impact to the API.
- Immutable objects
- Inherently thread-safe; no synchronization required.
- Can be shared freely.
- Even internals can be shared
- But they require a new object for each distinct value (e.g. BigInteger).
- Classes should be immutable unless there's a very good reason to make them mutable.
- If a class cannot be made immutable, limit its mutability as much as possible.
- Constructors should create fully initialized objects with all their invariants established.
- Composition vs. inheritance
- Inheritance breaks encapsulation.
- Inheritance must document self-use (evidence of broken encapsulation).
- Prohibit inheritance if not designed for inheritance
- Must document precisely the effects of overriding any method.
- May have to provide hooks into internal workings for subclass to use.
- Constructors must not invoke overridable methods.
- Neither clone nor readObject may invoke an overridable method, directly or indirectly.
- Interfaces vs. abstract classes
- Existing classes can be easily retrofitted to implement a new interface.
- Interfaces are ideal for defining mixins.
- Interfaces allow the construction of nonhierarchical type frameworks.
- Intefaces enable safe, powerful functionality enhancements via the wrapper class idiom.
- It is far easier to evolve an abstract class than it is to evolve an interface.
- Proper use of interfaces
- Don't use interfaces as a means of exporting constants.
- Static vs. nonstatic member classes
- If you declare a member class that doesn't require access to an enclosing instance, put the static modifier in the declaration.
- C structures => classes
- C unions => class hierarchies
- C enum => classes (in JDK 1.5, enum)
- C function pointers => classes and interfaces
- Validate method parameters
- Document restrictions and enforce them with checks that throw runtime exceptions.
- Nonpublic methods should generaly check their parameters using assertions rather than normal checks.
- Defensive copies
- You must program defensively with the assumption that clients of your class will do their best to destroy its invariants.
- It's essential to make a defensive copy of each mutable parameter to the constructor.
- Defensive copies are made before checking the validity of the parameters, and the validity check is performed on the copies rather than the originals -- prevents possible race with another thread.
- Do not use the clone method to make a defensive copy of a parameter whose type is subclassable by untrusted parties.
- Method signature design
- Choose method names carefully, following standard naming conventions (item 38).
- 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.
- Break up method into multiple methods, each with fewer parameters.
- Create helper classes (aka parameter object) to hold aggregates of parameters.
- For parameter types, favor interfaces over classes.
- Avoid function objects unless there is good reason to use them.
- Method overloading
- The choice of which method overload to invoke is made at compile time.
- Note: selection among overloaded methods is static, but selection among overridden methods is dynamic.
- Avoid confusing uses of overloading. A safe, conservative policy is nevr to export two overloadings with the same number of parameters.
- Use differently named methods instead - e.g. ObjectOutputStream's write* and read* methods.
- Returning zero-length array vs. null
- Null causes special-case code to be in both the method and the client.
- Zero-length arrays are immutable and thus can be shared freely.
- There is no reason ever to return null from an array-valued method instead of returning a zero-length array.
- Document all exported APIs
- Every exported class, interface, and member should be preceded with a doc comment.
- Method doc should succinctly describe the contract between the method and the client.
- The contract should say what the method does rather than how it does its job.
- Sometimes necessary to document overall architecture of a complex API in a separate document, linked from the related class or package doc comments.
- Minimize local variable scope
- Know the libraries
- float/double and exact answers
- String vs. non-string
- Perf of string concatenation
- Refer to objects by their interfaces
- Interfaces vs. reflection
- Avoid native methods
- Optimizing
- Naming conventions
- Proper use of Exceptions
- Checked vs. runtime exceptions
- Proper use of checked exceptions
- Benefits of standard exceptions
- Exceptions and abstraction
- Document all thrown exceptions
- Exception message detail
- Failure atomicity
- Don't ignore exceptions
- 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
- They are basically obsolete.
- Only useful bit is ThreadGroup.uncaughtException for handling an uncaught exception thrown from one of the threads in the group.
- Implementing Serializable
- Custom serialized form
- Defensive readObject impl
- When to provide readResolve
Saturday, February 10, 2007
Effective Java Summary
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment