My "pensive" - where i simply siphon the excess thoughts from my mind, pour them into the basin (of cloud storage), and examine them at leisure.
Sunday, March 23, 2014
Effective Java Notes
Creating and Destroying Objects
Item 1: Consider static factory methods instead of constructors.
Advantages:
i. Can have descriptive names, unlike constructors.
ii. Not required to create new object, each time they are called.
iii. Can return any subtype of their return type.
iv. Reduce the verbosity of creating parameterized types. (Obviated by diamond in Java 7).
Disadvantages:
i. Classes with only static factory methods without non-private constructors cannot be sub-classed.
ii. Not readily distinguishable from other static methods. Follow conventional names like valueOf, of, getInstance, newInstance, getType, and newType.
Item 2: Consider a builder when faced with many constructor parameters.
Consider creation of a class with a number of optional parameters. There are two traditional approaches for this task. The first involves telescoping constructors. They are succinct, but hard to read and follow as the number of parameters increase. The second approach is to use the Javabeans pattern of a nullary constructor followed by a series of setter methods being called. This has three disadvantages, namely:
i. Excessively verbose
ii. Since construction is now spread across several calls, construction is in a inconsistent state in between.
iii. Object cannot be made immutable anymore.
To overcome the disadvantages of both the approaches above, we should use the Builder pattern with the following three steps.
i. The client calls a factory to get a builder object first.
ii. Then all setter methods are called on this builder object to set each optional parameter in a fluent fashion.
iii. Finally the client calls the parameter-less build() method on the builder to get an immutable instance.
Note that this simulates the optional parameter setting available in Python.
Item 3: Enforce the singleton property with a private constructor or an enum type.
Making a class a singleton can make it hard to test its clients as it is not possible to substitute a mock implementation for a singleton unless it implements an interface to serve as its type.
There have two traditional approaches for declaring a singleton. In both, we rely on a private constructor and exporting a public static member.
i. In the first approach, the member is a final field accessed directly as className.fieldName.
ii. In the second approach, the member is obtained through a static factory.
Given that modern JVM would inline the call to the static factory, the two approaches are equally efficient. The second approach however is more amenable to change to say threadlocal behavior rather than singleton.
The traditional approaches above suffer from two deficiencies.
i. A priviledged client can reflectively invoke the private constructor with the aid of setAccessible method. So you must ensure that the constructor throws an exception if a second instance is created.
ii. To make the class serializable, it is not sufficient to merely state “implements Serializable”. Declare all instance fields transient and provide a readResolve() method returning the same instance each time.
The “best way to implement a singleton” property is use a single-element enum. This solves the serialization problem for free.
Item 4: Enforce non-instantiability with a private constructor
For utility classes, it is desirable to enforce non-instantiability. Marking it as abstract allows the creation of a subclass that can be instantiated. So it is preferable to put in a private constructor. To prevent a method within the class from calling the constructor, throw an assertion error inside the private constructor.
Item 5: Avoid creating unnecessary objects.
Since you can always re-use immutable objects, prefer String s = “dummy” to String s = new String(“dummy”);
Boolean.valueOf(String) is preferable to Boolean(String) constructor. Prefer static factories to enable re-use.
Use static initializers to set mutable objects that would not be changed later.
Item 6: Eliminate obsolete object references.
Memory leaks in garbage-collected languages caused by unintentional object retentions are insidious.
Null-ing out object references should be the exception rather than the norm.
Whenever a class manages its own memory, be alert for memory leaks.
If the desired lifetime of a cache is determined by external references to the key, consider using a WeakHashMap.
Another common source of memory leaks is listeners and other callbacks. If you have an API where clients can register themselves and do not de-register themselves, then you have a potential for a memory leak.
Use a heap profiler to catch memory leaks.
Item 7: Avoid finalizers
Finalizers are unpredicatable, dangerous and generally unnecessary.
Do not do anything time critical in a finalizer as the JVM does not guarantee when a finalizer would be called.
Do not depend on a finalizer to update critical persistent state as the JVM does not guarantee that a finalizer would be called.
There is a severe performance penalty for using finalizers.
Provide an explicit termination method and ask clients to invoke it. Use this in conjunction with try-finally blocks.
The finalizer can act as a safety net in case the owner forgets to call the explicit termination method. In this case, the finalizer must log a warning if it finds that the resource has not yet been terminated.
If you need to associate a finalizer with a public, non-final class, then use a finalizer guardian, so that finalization can take place even if a subclass finalizer fails to invoke super.finalize().
Methods Common to All Objects
Item 8: Obey the general contract when overriding equals.
Do not override equals if any of the following conditions apply:
i. Each instance of the class in inherently unique.
ii. You don’t care whether the class provides a logical equality test.
iii. A superclass has overridden equals and the super class behavior is appropriate for this class as well.
iv. The class is private or package-private and you are certain that its equals method will never be invoked.
The equals method must be reflexive, symmetric, transitive, consistent. It should return false on being passed a null argument.
There is no way to extend an instantiable class and add a value component while preserving the equals contract.
Do not write an equals method that depends on unreliable resources.
A simple recipe for equals method is as follows
i. Use the == operator to check if the argument is a reference to this object.
ii. Use the instanceof operator to check if the argument has the correct type.
iii. Cast the argument to the correct type.
iv. For each significant field in the class, check if that field of the argument matches the corresponding field of the object.
Don’t substitute another type for Object in the equals declaration.
Item 9: Always override hashCode when you override equals.
Equal objects must have equal hashcodes. Hence you must override hashcode in every class that overrides equals.
The text also provides a rudimentary approach to writing a hashCode method; but within RiskFocus code, we have been using Apache library’s HashCodeBuilder.
Item 10: Always override toString.
Providing a good toString implementation makes your class pleasant to use.
When practical, the toString method should return all of the interesting information contained in the object.
Document the format of your toString representation or mention that clients should not rely on the format remaining unchanged.
Provide programmatic access to all the information contained in the value returned by toString.
Item 11: Override clone judiciously.
Cloneable represents a highly atypical use of an interface. If a class implements Cloneable, we are modifying the behavior of a protected method (ie clone) on a superclass (ie Object).
If you override the clone method in a non-final class, then you should return an object obtained by invoking super.clone.
A class that implements Cloneable is expected to provide a public clone method.
In order to make a class cloneable, it may be necessary to remove final modifiers from some fields.
Make sure you do a deep copy of your mutable fields.
A final approach to object copying is to provide a copy constructor or copy factory.
Item 12: Consider implementing Comparable.
The Comparable interface has a sole method named compareTo.
Implementing Comparable allows operating your class within the ambit of many Collections in a useful manner.
Try to make your compareTo consistent with equals.
There is no way to extend an instantiable class with a value component while preserving the compareTo contract.
Use Double.compare and Float.compare instead of the signs < and >.
Remember if A is a large positive int and B is a large negative int, then A-B would overflow and return a negative integer.
Classes and Interfaces
Item 13: Minimize the accessibility of classes and interfaces.
Make each class or member as inaccessible as possible.
Instance fields should never be public.
Classes with public mutable fields are not thread-safe.
Never have a static final array as public since non-empty arrays are always mutable. Either clone the array or use Collections.unmodifiableList(Arrays.asList()) method.
Item 14: In public classes, use accessor methods not public fields.
Provide accessor(getter) and mutator(setter) methods for all public classes.
For private or package-private, you may or may not provide accessors/mutators.
Item 15: Minimize mutability.
To make a class immutable, follow the following five rules
i. Do not provide any methods to change the object’s state.
ii. Ensure that the class cannot be extended – consider putting a static factory instead of a public constructor.
iii. Make all fields final.
iv. Make all fields private.
v. Ensure exclusive access to any mutable components received in constructor through defensive copying.
In the imperative or procedural approach, one provides methods to alter the state of the object. In the functional approach, methods return the result of applying a function to their operand without modifying it.
Immutable objects are simple and thread-safe. Share freely and cache frequently used instances.
Immutable objects are great building blocks for other objects. They are good for keys in maps.
The disadvantage lies in the creation of a new object for each distinct value.
Consider providing a method covering complicated sequences on operations on your immutable object. Then internally you can convert to a mutable object, make the transformations and finally create the new immutable object just once.
Lazy initialization should be used in hashCode computation here.
Limit the mutability of classes as far as possible. Make every field final unless there is a reason to make it non-final.
Item 16: Favor composition over inheritance.
Inheritance violates encapsulation.
Have a wrapper class if need be that holds the class intended earlier as base class and forwards requests to it. Compare this with the decorator pattern.
Since the wrapped object does not know of its wrapper, a reference to itself would elude the wrapper. This is called the SELF problem and precludes the use of the wrapper mechanism in many callback scenarios.
Class B should extend class A iff B is an A.
Item 17: Design and document for inheritance or else prohibit it.
The class must document its self-use of overridable methods. Preferably, write private helper methods and completely eliminate the use of public/protected overridable methods.
You must provide hooks into the class’s internal workings in the form of judiciously chosen protected methods.
The only way to test a class designed for inheritance is to write at least three subclasses and test.
Constructors (as well as clone and readObject) methods must not invoke overridable methods.
Implementing Cloneable and Serializable places considerable burden on developers subclassing the given class.
Prohibit subclassing in classes not designed for inheritance.
Item 18: Prefer interfaces to abstract classes.
Interfaces are usually the best way to implement a type. Their advantages are as follows:
i. Existing classes can easily be retrofitted to implement a new interface.
ii. Interfaces are ideal for defining mixins. Mixin is a type that a class can implement in addition to its primary type to show that it can provide additional behaviour.
iii. Interfaces allow the construction of non-hierarchial type frameworks. If you use abstract classes, you risk a combinatorial explosion of classes to take care of each choice.
iv. Interfaces enable safe, powerful functionality enhancements via the wrapper class idiom.
Provide a skeletal implementation class to go with each non-trivial interface that you export.
Check the Adapter pattern in GoF book using an anonymous inner class to convert an int array to an Integer list.
The class implementing an interface can forward invocations of interface methods to a contained instance of a private inner class that extends the skeletal implementation. This technique is called simulated multiple inheritance.
Abstract classes have one advantage over interfaces in that they are easier to evolve. In general, once an interface is published, it is not possible to change it any more.
Item 19: Use interfaces only to define types.
To avoid writing out the constants, some programmers put in their constants in interfaces. This is the so-called constant interface anti-pattern. Instead place them in a utility class and use the static import feature.
Item 20: Prefer class hierarchies to tagged classes.
One sometimes sees a class whose instances come in two or more flavors. These are handled by multiple switch statements and by having data corresponding to each flavor within the same class.
Such classes are called tagged classes. These are verbose, error-prone and inefficient.
Replace with an abstract class with the two flavors constituting separate subclasses.
Item 21: Use function objects to represent strategies.
A state-less class with only one method can represent a function object.
The functionality of function-pointers is replicated via the Strategy interface in Java.
Declare an interface to represent a strategy and a class that implements this interface for each concrete strategy.
If the strategy is used once, use an anonymous inner class. Else put it as a private static member in a class and export via a static final field whose type is the Strategy interface.
Item 22: Favor static member classes over non-static.
A nested class (a class defined inside another class) is classified into static member, non-static member, anonymous and local classes.
Static member classes are used as public helper classes.
If you declare a member class that does not require access to an enclosing instance, always put the static modifier.
Anonymous classes are used in function objects, process objects, and in static factory methods.
Local classes are used in place of anonymous classes, if the class instantiation is done from multiple locations.
Generics
Item 23: Don't use raw types in new code.
For details on generics, see the online tutorial by Langer and read the book by Naftalin & Wadler.
When you write just "List", you have opted out of generic type checking. In contrast, a List
Suppose the element is unknown and does not matter. Even then do not use raw types. Instead use the wildcard ? as in List.
You cannot put any element other than null into a Collection.
Since generic type information is erased at run-time, there are two places where raw types must be used.
i. Use raw types in class literals, such as List.class.
ii. Use raw types while using the instanceOf operator.
To reiterate, Set is a parametrized type representing a set that can contain objects of any type. Set is a wildcard type representing a set that can contain only objects of unknown type. Set is a raw type, which opts out of the generic type system. The first two are safe; the third is not.
Item 24: Eliminate unchecked warnings.
Eliminate every unchecked warning you can.
If you cannot eliminate a warning and are convinced that your code is actually type-safe, then suppress the warning and add a comment proving your assumption.
Always use the SuppressWarnings annotation on the smallest scope possible.
It is illegal to put a SuppressWarnings annotation on a return statement. Create a local variable to hold the return value and annotate that with the SuppressWarning annotation.
Item 25: Prefer lists to arrays.
Arrays are covariant, i.e. if Sub is a subtype of Super, then Sub[] is also a subtype of Super[]. This leads to run-time errors if one is not careful.
Object[] arr = new Long[1] ;
arr[0] = "dummyString";
This code above throws an ArrayStoreException.
Generics are invariant; so problems like the one above are caught at compile-time itself.
Arrays are reified; ie they know and enforce their element types at runtime. Generics in contrast are implemented by erasure for reasons of Java code migration compatibility. An implication of this is that arrays and generics do not mix well.
As an example, new List[], new E[] are illegal expressions. Technically, we say that List, List and E are non-reifiable since their run-time representation carries less information than their compile-time representation. The only parametrized types that are reifiable are unbounded wildcard types, such as List and Map.
There are two mistakes with the code given at the bottom of Page 121. In the for-loop, you should have snapshot and not list. It is then that you would get the internal locking. Secondly, prior to Java 1.5, there was no for-each loop. So the code claimed to be written in pre-Java 1.5 code should have the old-style iterator.
To remove errors/warnings coming about by mixing arrays and generics, consider replacing arrays with lists.
Item 26: Favor generic types.
You cannot create a Stack, but can create a Stack.
Java does not support lists natively; so some generic types such as ArrayList must be implemented atop arrays.
Item 27: Favor generic methods.
Remember that the order in which the set's elements are printed out is implementation-dependent.
To ease writing of parametrized type instance creation code, the author recommends a static factory. We get the same benefit by invoking Lists or other appropriate class from org.apache.commons. or org.google.commons. library.
Recursive type bound refers to code like "T extends Comparable". This refers to all types that can be compared to another instance of their own type.
Item 28: Use bounded wildcards to increase API flexibility.
A type does not extend itself; yet it is a subtype of itself.
A common mnemonic is PECS or producer-extends, consumer-super.
Do not use wildcard types as return types. It would force clients also to use wildcard code.
If a user of a class has to think about wildcard types, there is probably something wrong with the class's API.
When invoking a method, you might have to something explicitly mention the type parameter. Example, Union.union(integers, doubles).
Comparables and comparators are always consumers in the PECS scheme. Hence, you should always use Comparable over Comparable. The same holds true for Comparator as well.
If a type parameter appears only once in a method declaration, replace it with a wildcard.
Consider the case of swapping two elements in a list. If the list element is declared as a List, then you can get the element out of the list; but you cannot put it back. To circumvent this problem, have a private helper method for wildcard capture. This private helper is a generic method that would deduce the type of the generics being passed in. See Pg 140 for an example.
Given Class type as an input argument to a method, the method can return type.cast(someMap.get(type)). The someMap is defined as a Map, Object>. Such a map is called a type-safe heterogenous container.
When a class literal is passed among methods to communicate both compile-time and runtime type information, it is called a type token.
The use of type.cast above is called dynamic casting.
If you are using either third-party code or legacy code that you suspect of not using generics, consider wrapping your collections with Collection.checked methods. Example would be
Now even if someone copied a reference of setOfInts to a plain Set-instance, they would not be able to insert non-integer items into it.
Enums and Annotations
Item 30: Use enums instead of int constants.
Using int/string to simulate enums is called the int/string enum pattern. It is always a bad idea.
int enums are compile-time constants; and it is not very helpful to have numbers printed out while debugging.
In other languages like C/C++, enums are essentially int values. But in Java, they are full-fledged classes.
Look at Langer's FAQ to understand Enum>.
Enum types provide compile-time type safety. Each type has its own namespace.
To associate data with enum constants, declare instance fields and write a constructor that takes the data and stores it in the fields.
If you want to associate a different behavior with each instance, then declare an abstract method in the enum type, and then override it with a conreate method for each constant in a constant-specific class body. Such methods are called constant-specific method implementation.
Consider including a from-string method on your enum types.
Enums should not switch on their values to share code. Instead use the strategy enum method.
Switches on enums are good for augmenting external enums with constant-specific behaviour.
Item 31: Use instance fields instead of ordinals.
Never derive a value associated with an enum from its ordinal; store it in an instance field.
Ordinal would change if you re-order your instances or include additional ones in the middle.
Item 32: Use EnumSet instead of bit fields.
The EnumSet class can efficiently represent sets of values drawn from a single enum type. Use EnumSet.of(YourEnumName.A, YourEnumName.B).
Just because an enumerated type is used in sets, there is no reason to represent it in bit fields.
If you wrap it in Collections.unmodifiableSet, it would become immutable but would entail a discernible performance cost.
Item 33: Use EnumMap instead of ordinal indexing.
Instead of using ordinal(), use an EnumMap or a nested EnumMap.
Item 34: Emulate extensible enums with interfaces.
Have your enum type implement an interface. You can emulate an extensible enum by making the new enum also implement the same interface.
& InterfaceName> refers to a type that is both an enum-type as well as implementing an interface. To access the enumConstants from a class-name, use the getEnumConstants() method.
Item 35: Prefer annotations to naming patterns.
Annotations often need meta-annotations like @Retention and @Target.
A marker annotation has no parameters.
Item 36: Consistently use the override annotation.
You should use the Override annotation on every method declaration you believe to override a superclass declaration.
Item 37: Use marker interfaces to define types.
If you want to define a type that that does not have any new methods associated with it, then use a marker interface.
If you want to mark program elements other than classes/interfaces, or allow for the possibility of adding more information to the marker in the future, or fit the marker into a framework making heavy use of annotations, then use a marker annotation.
Methods
Item 38: Check parameters for validity.
At the start of methods, check parameters for validity.
For public methods, document the exceptions thrown using the @throws javadoc tag. The exception would typically be an IllegalArgumentException, IndexOutOfBoundsException or NullPointerException.
For non-public methods, check parameters using a series of assertions. Unlike normal validity checks, asserts throw an AssertionError if they fail. Further, they have no cost and no effect unless you enable them by passing -ea to the java interpreter.
In constructors in particular, one must always check the validity of the parameters passed.
Item 39: Make defensive copies when needed.
You must program defensively, with the assumption that the clients of your class will do their best to destroy its invariants.
If you want to create an immutable class, make sure to make a defensive coy of each mutable parameter in the constructor and set it then only to the field declared as final.
Defensive copies are made before checking the validity of parameters and further, the validity check is performed on the copies rather than the originals. This protects the class against changes to the parameters from another thread during the window of vulnerability between the time of parameters being checked and parameters being copied. Violating this principle leads to the so-called TOCTOU or time-of-check/time-of-use attack.
Do not use the clone method to make a defensive copy of a parameter whose type is subclassable by untrusted parties.
In your accessors, return defensive copies of mutable internal fields. You can use clone here however.
To avoid thinking about defensive copying, consider using immutable objects whereever possible.
Item 40: Design method signatures carefully.
Choose method names carefully. Follow naming convention of package over general naming convention picked up elsewhere.
Don't go overboard in providing convenience methods.
Avoid long parameter lists, especially if they are identically typed. To ameliorate the problem, there are three ways:
i. Break up method into multiple methods.
ii. Create helper classes to hold groups of parameters.
iii. Adapt the Builder pattern from object construction to method invocation.
For parameter types, favor interfaces over classes.
Prefer two-element enum types to boolean parameters.
Item 41: 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.
Never export two overloadings with the same number of parameters. Do not overload a method with varargs.
For constructors, the option of not overloading is often not present. There, consider using static factories.
In the List implementation, there are two overloading of the remove method. remove(int i) removes the element from position i. remove(E e) removes the element e of type E with which the generic List was instantiated. Now when E is Integer (ie boxed version of int), there is possibility of confusion. remove((Integer) i) and remove(i) behave differently for int i.
In the String class, valueOf(char[]) and valueOf(Object) do completely different things. This is a mistake in design.
Item 42: Use varargs judiciously.
Varargs or variable arity methods were added to Java in release 1.5.
If your method requires a minimum of one argument, use "int arg1, int.. args" instead of "int.. args".
Dont retrofit every method that has a final array parameter;use varargs only when a call really operates on a variable-length sequence of values.
Note that varargs has a performance penalty as it causes internally an array allocation and initialization.
Item 43: Return empty arrays or collections, not nulls.
To return an array, use "return someList.toArray(new Type[])". To return an empty list, use Collections.emptyList().
There is no reason ever to return null from an array or collection-valued method instead of returning an empty array or collection.
Returning null forces the client of the code to separately check for the null.
Item 44: Write doc comments for all exposed API elements.
To document your API properly, you must precede every exported class, interface, constructor, method and field declaration with a doc comment.
The doc comment for a method must state what a method does, not how it does it. Mention pre-conditions, post-conditions, and side-effects. Describe thread-safety of the class as well.
Use @param, @return and @throws tags. You can use HTML tags in your comment.
Use @code instead of because it eliminates the need to escape HTML metacharacters.
The first sentence of each doc comment becomes the summary description of the element. No two members or constructors should have the same summary description.
When documenting a generic type or method, be sure to document all type parameters.
When documenting an enum type, be sure to document the constants as well as the type and all public methods.
When documenting an annotation type, be sure to document any members as well as the type itself.
General Programming
Item 45: 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 initializer should contain an initializer.
Prefer for loop to whie loops.
Keep methods small and focussed.
Item 46: Prefer for-each loops to traditional for loops.
The traditional for loop using an iterator should be used only for filtering (remove list elements), transforming (replacing list elements) and parallel iteration (traversing multiple collections).
For any group of elements, consider implementing Iterable.
Item 47: Know and use the libraries.
For example, if you want a random number, use Random.nextInt() rather than trying to cook up a pseudo-random generator.
Every programmer must know the contents of three packages - java.lang, java.util and java.io.
Within java.util, the Collections framework and concurrent subpackage are of particular importance.
Item 48: Avoid float and double if exact answers are required.
The float and double types are particularly ill-suited for monetary calculations. Use BigDecimal, int or long instead.
float cannot represent any negative power of 10 correctly.
Item 49: Prefer primitive types to boxed primitives.
There are three differences between the primitives and the boxed primitives.
i. Primitives only have their values, boxed primitives have their identities distinct from values.
ii. Boxed primitive have only non-functional value - null.
iii. Primitives are generally time and space efficient as compared to boxed primitives.
Applying the == operator to boxed primitives is almost always wrong.
When you mix primitives and boxed primitives in a single operation, the boxed primitives is auto-unboxed. Beware of throwing an accidental NullPointerException in this auto-unboxing.
Item 50: Avoid strings where other types are more appropriate.
Strings are more cumbersome, less flexible, slower and error-prone than other types, if they are used inappropriately. Do not use strings in place of primitive types, enums and aggregate types.
Capability refers to an unforgeable key, such as the key used internally in ThreadLocal to store your object against each thread. Strings should not be used for capabilities.
Item 51: Beware the performance of string concatenation.
Since strings are immutable, concatenating two strings requires the contents of both strings to be copied. This implies that using the string concatenation operator repeatedly to concatenate n strings requires time quadratic in n.
Use the unsynchronized StringBuilder instead. StringBuffer is now deprecated.
Use character arrays, if need be.
Item 52: Refer to objects by their interfaces.
If appropriate interface types exist, then parameters, return values, variables, and fields should all be declared using interface types.
There are three scenarios in which you would not use an interface.
i. For Value classes like String, Random, there is no interface.
ii. For Class-based framework, use the base class as there may be no interface available.
iii. If the class (for eg LinkedHashMap) provides methods not found in the interface, then use the class instead of the interface.
Item 53: Prefer interfaces to reflection.
There are three disadvantages of using reflection.
i. You lose the benefits of compile-time type checking.
ii. Code required for reflection is clumsy and verbose.
iii. Performance suffers.
As a rule, objects should not be accessed reflectively in normal applications in runtime. Exceptions to the rule include class browsers, object inspectors, code analysis tools, interpretive embedded systems, and remote procedure call systems.
For many programs that must use a class that is unavailable at compile-time, there exists at compile time an appropriate interface or superclass by which to refer to the class. If so, you can create instances reflectively, and access them normally via their interface or superclass.
Item 54: Use native methods judiciously.
The Java Native Interface (JNI) allows Java applications to call native methods, which are special methods written in native programming languages like C or C++.
It is rarely advisable to use native methods for performance reasons.
The use of native methods has the following disadvantages:
i. Native languages are not safe from memory corruption.
ii. Applications are now more difficult to debug.
iii. Performance cost is present in going into and out of native code.
iv. Writing the glue code is tedious to write and read.
Item 55: Optimize judiciously.
Strive to write good programs rather than fast ones.
Strive to avoid design decisions that would limit performance.
Consider the performance consequences of your API design decisions. For example
i. Making a public type mutable entails a lot of defensive copying.
ii. Using inheritance in a public class where composition should have been used places artificial limits on the performance of the subclass.
iii. Using concrete implementations instead of interfaces can prevent you from taking advantages of future improvements to the library.
It is a very bad idea to warp an API to achieve good performance.
Measure performance before and after each optimization.
It is extremely important to run a profiler to understand which part of the code is slow.
Java does not have a strong performance model. Performance varies greatly across JVMs.
Item 56: Adhere to generally accepted naming conventions
See Java Language Specification Section 6.8 or see Pg 237 of the text.
They are important to follow but too numerous to list here.
Exceptions
Item 57: Use exceptions only for exceptional conditions.
If you have a state-dependent method, then use either a distinguished return value (such as null) or a state-testing method instead of choosing to use exceptions for control flow.
Item 58: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors.
Besides checked and unchecked exceptions, there is a category of Error that can be thrown. There is a strong convention against using it outside of the JVM. So do not create any subclasses of Error. Instead sub-class RuntimeException.
While it is possible to define other categories of throwable objects, do not do so.
Item 59: Avoid unnecessary use of checked exceptions.
Instead of invocation with a checked exception, try having an invocation with a state-testing method followed by an unchecked exception.
Item 60: Favor the use of standard exceptions.
There are three reasons for favoring standard exceptions, namely
i. It makes the API easier to learn and use.
ii. It makes the API easier to read later.
iii. Fewer exception classes imply a smaller memory footprint and less time spent loading classes.
Use IllegalArgumentException when you are passed an argument whose value is inappropriate. Use IllegalStateException when you the caller attempted to use an object before it was properly initialized.
Use a NullPointerException when a caller passes null for a parameter for which null values are prohibited. Use IndexOutOfBoundsException on receiving an out-of-range value for a parameter representing an index into a sequence.
Use ConcurrentModificationException if an object designed for use b a single thread (or with external synchronization) is being concurrently modified. Use UnSupportedOperationException if an object does not support an attempted operation.
Item 61 Throw exceptions appropriate to the abstraction.
Higher layers should catch lower-level exceptions and, in their place, throw exceptions that can be explained in terms of the higher-level abstraction. This is called exception translation.
Exception Chaining involving catching a lower-level exception and then encapsulate it inside a higher-level exception that is then thrown. Most exceptions have chaining-aware constructor; but if they don't, then use the Throwable interface's initCause method.
Item 62 Document all exceptions thrown by each method.
Always declare checked exceptions individually and document precisely the conditions under which each is thrown using the @throws Javadoc tag.
Do not take the shortcut out by marking your method as "throws Exception". Specify the particular exceptions that you expect.
A well-documented list of unchecked exceptions a method can throw acts as a list of preconditions for its successful execution. It is important that you do this for all interfaces; this specifies their general contract.
Do not use the 'throws' keyword to include unchecked exception in the method declaration. Use only the @throws Javadoc for them.
Item 63 Include failure-capture information in detail messages.
Printing out the stack trace of an exception prints out its toString representation which in turn consists of the exception-class name along with its detail message. So ensure that the detail message of an exception contains the values of all the parameters and fields that contributed to the exception in the first place.
Do not include unnecessary prose in exceptions.
Instead of a string constructor, it would have been better if Java had constructor for exceptions which required users to fill in all the required information for its detail message.
It is rare that the programmer would want programmatic access to the details of an unchecked exception.
Item 64 Strive for failure atomicity.
Failure Atomicity refers to a property of a method that ensures that a failed invocation of it would always leave objects in the state they were prior to the method invocation.
Failure atomicity is not achievable (eg in some ConcurrentModificationException cases) or desirable in a very small minority of cases. In all other scenarios, strive for it.
Failure atomicity is guaranteed if the object you send is immutable.
For mutable arguments passed to a method, failure atomcity is ensured by four approaches:
i. Check parameters for validity at start of method itself.
ii. Check parameters for validity before making any change to the object.
iii. Write recovery code to return object to original state on encountering an error.
iv. Make a temporary copy of the object received; operate on it and copy back to the original object only if you are successful.
Item 65 Don't ignore exceptions.
At the least, put in a comment in the catch block that explains your reason for ignoring the exception thrown.
Concurrency
Item 66: Synchronize access to shared mutable data.
Synchronized keyword combines the functionality of mutual exclusion and the volatile keyword. Not only does synchronization prevent a thread from observing an object in an inconsistent state, it also ensures that each thread entering the block sees the effects of all previous modifications.
You cannot avoid synchronization just because you are dealing with atomic data. Atomic merely guarantees that a thread would not see an arbitrary value when reading the field; it does not guarantee that a value written by one thread would be visible to another.
Do not use Thread.stop() method.
If you are using a Boolean variable to signal to another thread to stop, then mark it as volatile. Else the hoisting optimization by some VMs would result in a liveness failure.
You must synchronize both read and write methods.
Confine mutable data to a single thread as far as possible.
Item 67: Avoid excessive synchronization.
To avoid deadlock and safety failures, never cede control to the client within a synchronized method or block.
If your array is traversed frequently, but modified rarely, then consider using a CopyOnWriteArrayList.
Do as little work as possible inside a synchronized block.
Item 68: Prefer executors and tasks to threads.
Be familiar with the java.util.Concurrent framework.
The Executor framework is a flexible interface-based task execution facility. See java.util.concurrent.Executors.
For a lightly loaded server, use CachedThreadPool. For a heavily loaded server, use FixedThreadPool.
The notion of thread combines the unit of work with the mechanism for executing it. Now the unit of work is called a task. The mechanism for executing it is in the executor service.
The tasks are either Runnable or Callable (which is Runnable with a return value).
Read Goetz’s Java Concurrency in Practice.
Item 69: Prefer concurrency utilities to wait and notify.
Given the difficulty of using wait and notify correctly, you should always use the higher-level concurrency utilities instead.
It is impossible to exclude concurrent activity from a concurrent collection; and hence locking it would merely serve to slow down the program.
Use ConcurrentHashMap in preference to Collections.synchronizedMap or HashTable.
BlockingQueue has a method called take which removes and returns the head element from a queue and blocks if the queue is empty. This features enables it to be used in producer-consumer (or work) queues.
Synchronizers enable threads to wait for another another. Examples include CountDownLatch, Semaphore, CyclicBarrier, and Exchanger.
For interval timing, always use System.nanoTime in preference to System.currentTImeMillis.
Always use the wait loop idiom to invoke the wait method; never invoke it outside of a loop.
Item 70: Document thread safety.
The presence of synchronized modifier in a method declaration is an implementation detail, not a part of its exported API. One cannot conclude on its basis that the method is thread-safe.
There are 5 levels of thread-safety.
i. immutable like String, Long and BigInteger.
ii. unconditionally thread-safe like Random and ConcurrentHashMap. They have internally enough synchronization that they can be used without external synchronization.
iii. conditionally thread-safe wherein certain methods need synchronization. Example is collection returned by Collections.synchronized wrappers whose iterators need synchronization.
iv. not thread-safe like ArrayList. Clients here must surround each method invocation with synchronization.
v. thread-hostile. If a class modifies internally its static members without synchronization, then do not use the class in multithreaded applications.
If an object represents a view on another object, you must synchronize on the backing object to prevent its direct modification.
If you have a public lock, you expose yourself to a denial of service attack by a client who holds on to the lock for long. To prevent this, have a private final lock object and synchronize on it. This private lock idiom is to used only on unconditionally thread-safe classes.
The private lock idiom is useful for classes designed for inheritance.
Item 71: Use lazy initialization judiciously.
Under most circumstances, normal initialization is preferable to lazy initialization.
If you use lazy initialization to break an initialization circularity, use a synchronized accessor. Here, put the synchronized modifier before the accessor. Inside the method, check if the field is null, and if so, call the initializing method.
If you need to use lazy initialization for performance reasons on a static field, then use the so-called lazy initialization holder class idiom. In the definition of the static final variable, equate it to the initializing method.
If you need to use lazy initialization for performance reasons on an instance field, then use the so-called double check idiom. First check if the field is null. If not, return the value you have. If it is null, synchronize on object; again check for null and if found to be null, initialize. The instance field should be declared as volatile.
If you can tolerate repeated initialization, then remove the second check from the double-check idiom. This is called the single-check idiom.
If you further do not care if each thread recalculates the value, then remove the volatile modifier from the single-check idiom. This is then called the racy single-check idiom. This is used internally by the String class to cache their hash codes.
Item 72: Don't depend on the thread scheduler.
Programs relying on the thread scheduler would be non-portable.
Threads should not run if they are not doing useful work. Threads should not busy-wait.
Do not fix bugs by calling Thread.yield. It has no testable semantics.
Do not fix bugs by changing thread priorities.
Item 73: Avoid thread groups.
They are a deprecated feature of the language.
Use thread pool executors instead.
Serialization
Item 74: Implement Serializable judiciously.
Implementing Serializable decreases the flexibility to change the class's implementation after release.
Have a custom serialized form. Have your own stream-unique identifiers, also referrred to as serial version UIDs.
Implementing Serializable enables an extra-linguistic mechanism for creating objects - creating a security hole.
Implementing Serializable increaes the testing burden since on every new release, you must try to deserialize with older versions of the class and then test its semantics.
Think long and hard before implementing Serializable.
Classes designed for inheritance must rarely implement Serializable.
On a non-serializable class designed for inheritance, provide a nullary constructor.
Inner classes should not implement Serializable; static classes can.
Item 75: Consider using a custom serialized form.
Accept the default serialized form only if the actual representation of object contains only logical state.
Even if you accept the default form, provide a readObject method.
Using the default serialized form has four disadvantages:
i. It ties the exported API to the internal representation permanently.
ii. It can consume excessive space.
iii. It can consume excessive time.
iv. It can cause stack overflows.
Even if all fields are transient, do not dispense with the defaultWriteObject and defaultReadObject invocation.
Declare a field as non-transient iff it is part of the logical state of the object.
You must impose any sychronization on object serialization that you apply on other methods reading the entire state of the object.
Declare an explicit serial version UID.
Item 76: Write readObject methods defensively.
Defensively copy mutable components of immutable classes and check for invariants in your readObject method.
Think of readObject like a public constructor; do not invoke any overridable methods in the class.
Do not use the writeUnshared and readUnshared methods.
Item 77: For instance control, prefer enum types to readResolve.
For a singleton class implementing Serializable, declare all fields as transient and put in a readResolve method to return the one true reference.
Prefer the enum singleton to the class-based approach for security reasons.
Item 78: Consider serialization proxies instead of serialized instances.
If performance is not a big concern, use the the serialization proxy pattern.
Make both your class and its proxy class implement Serializable. In the writeReplace method of your main class, return a proxy object constructed from itself. In the readObject method of the proxy class, return the main class object. In both cases, use the public constructors of the classes involved. Further block the readObject method of the proxy class by making it throw a exception whenever invoked.
No comments:
Post a Comment