This page contains my notes from Effective Java book by Joshua Bloch

Index:
Chapter 2: Creating and Destroying Objects
Chapter 3: Methods common to all objects
Chapter 8: Methods

Chapter 0: Useful Links

- Revisiting Effective Java in 2019 by Edson Yanaga on YouTube.
- Revisiting Effective Java in 2018 by Edson Yanaga on Youtube. 2hr version.

Chapter 2: Creating and Destroying Objects

Item 1: Consider static factory methods instead of Constructors
- A class can provide its clients with static factory methods instead of, or in addition to, public constructors. Providing a static factory method instead of a public constructor has both advantages and disadvantages.
- One advantage of static factory methods is that, unlike constructors, they have names. A class can have only a single constructor with a given signature. Programmers have been known to get around this restriction by providing two constructors whose parameter lists differ only in the order of their parameter types. This is a bad idea. Because they have names, static factory methods don't share the same restriction.
- A second advantage of static factory methods is that, unlike constructors, they are not required to create a new object each time they're invoked. This allows immutable classes (Item 17) to use preconstructed instances, or to cache instances as they're constructed, and dispense them repeatedly to avoid creating unnecessary duplicate objects.
- The ability of static factory methods to return the same object from repeated invocations allows classes to maintain strict control over what instances exist at any time. Classes that do this are said to be instance-controlled. Instance control allows a class to guarantee that it is a singleton (Item 3) or noninstantiable (Item 4). Also, it allows an immutable value class (Item 17) to make the guarantee that no two equal instances exist: a.equals(b) if and only if a == b. This is the basis of the Flyweight pattern.
- For instance consider the following code. When a new instance of the CreatorClass is being created, the first thing that will be done is that the static variable, baseClass will be initialized. Now everytime that the getBaseClassInstance method is called to return an instance of the BaseClass object, it will the same instance of BaseClass that will be returned. This is what we are validating by using the == check in the code as well.
Expand Gist Expand Code Snippet
- A third advantage of static factory methods is that, unlike constructors, they can return an object of any subtype of their return type. This gives you great flexibility in choosing the class of the returned object. Look at the Pizza example from Item#2 to see an example of this in action.
- A fourth advantage of static factories is that the class of the returned object can vary from call to call as a function of the input parameters. Any subtype of the declared return type is permissible.
- A fifth advantage of static factories is that the class of the returned object need not exist when the class containing the method is written. Such flexible static factory methods form the basis of service provider frameworks, like the Java Database Connectivity API (JDBC). A service provider framework is a system in which providers implement a service, and the system makes the implementations available to clients, decoupling the clients from the implementations.
- The main limitation of providing only static factory methods is that classes without public or protected constructors cannot be subclassed.
- A second shortcoming of static factory methods is that they are hard for programmers to find. They do not stand out in API documentation in the way that constructors do, so it can be difficult to figure out how to instantiate a class that provides static factory methods instead of constructors.

Some common names for static methods

:
a) from: A type-conversion method that takes a single parameter and returns a corresponding instance of this type, for example: Date d = Date.from(instant);
b) of: An aggregation method that takes multiple parameters and returns an instance of this type, for example: Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
c) valueOf: A more verbose version alternative to from and of, for example: BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
d) instance or getInstance: Returns an instance that is described by its parameters (if any) but cannot be said to have the same value, for example: StackWalker luke = StackWalker.getInstance(options);
e) create or newInstance: Like instance or getInstance, except that the method guarantees that each call returns a new instance, for example: Object newArray = Array.newInstance(classObject, arrayLen);
f) getType: Like getInstance, but used if the factory method is in a different class. Type is the type of object returned by the factory method, for example:
FileStore fs = Files.getFileStore(path);
g) newType: Like newInstance but used if the factory method is in a different class. Type is the type of object returned by the factory method, for example:
BufferedReader br = Files.newBufferedReader(path);
h) type: A concise alternative to getType and newType, for example: List<Complaint> litany = Collections.list(legacyLitany);

How to use the Builder pattern where inheritance is involved?


- This seems to be quite the struggle in itself. Google: "use inheritance in builder pattern java". // TODO: Some links:
a) Builder Pattern and Inheritance
b) Subclassing a Java Builder class
Item 2: Consider a Builder when faced with many constructor parameters
- Static factories and constructors share a limitation: they do not scale well to large numbers of optional parameters. Consider the case of a class representing the Nutrition Facts label that appears on packaged foods. These labels have a few required fields - serving size, servings per container, and calories per serving - and more than twenty optional fields - total fat, saturated fat, trans fat, cholesterol, sodium, and so on.
- Traditionally, programmers have used the telescoping constructor pattern, in which you provide a constructor with only the required parameters, another with a single optional parameter, a third with two optional parameters, and so on, culminating in a constructor with all the optional parameters. The telescoping constructor pattern works, but it is hard to write client code when there are many parameters, and harder still to read it.
- A second alternative when you're faced with many optional parameters in a constructor is the JavaBeans pattern, in which you call a parameterless constructor to create the object and then call setter methods to set each required parameter and each optional parameter of interest.
- Unfortunately, the JavaBeans pattern has serious disadvantages of its own. Because construction is split across multiple calls, a JavaBean may be in an inconsistent state partway through its construction. The class does not have the option of enforcing consistency merely by checking the validity of the constructor parameters. Attempting to use an object when it's in an inconsistent state may cause failures that are far removed from the code containing the bug and hence difficult to debug. A related disadvantage is that the JavaBeans pattern precludes (prevent from happening; make impossible) the possibility of making a class immutable (Item 17) and requires added effort on the part of the programmer to ensure thread safety.
- It is possible to reduce these disadvantages by manually "freezing" the object when its construction is complete and not allowing it to be used until frozen, but this variant is unwieldy and rarely used in practice. What does "freezing" an object in Java mean? Read here on SE. This is a messy pattern and probably not widely used. A far neater pattern is the builder pattern.

What is the Builder Pattern?


- Instead of making the desired object directly, the client calls a constructor (or static factory) with all of the required parameters and gets a builder object. Then the client calls setter-like methods on the builder object to set each optional parameter of interest. Finally, the client calls a parameterless build method to generate the object, which is typically immutable. - IntelliJ can create Builder Patterns for you as shown here on SO.
Expand Gist Expand Code Snippet
- The Builder pattern is well-suited to class hierarchies. The following example makes use of recursive type parameters. Read more about them here on the other page.
- You can refactor the existing constructor in your class into a Builder or a static method by right-clicking on the constructor - Refactor - Replace Constructor With *
- There is stuff going on over here that I do not understand.
// TODO: Builder pattern for class hierarchies
- The technique, wherein a subclass method is declared to return a subtype of the return type declared in the superclass, is known as covariant return typing. It allows clients to use these builders without the need for casting.
- Note that Pizza.Builder is a generic type with a recursive type parameter (Item 30). This, along with the abstract self method, allows method chaining to work properly in subclasses, without the need for casts.
Expand Gist Expand Code Snippet
Item 3: Enforce the Singleton property with a private constructor or an Enum type
- The first two techniques of creating a Singleton is by creating a class with a private constructor.
- Read this explanation on DZone explaining how to use Enums for creating Singletons. It also goes over the first two techniques of creating singletons along with their drawbacks.
Expand Gist Expand Code Snippet
- Go through all the answers at this SO Link.
- An enum is a java class with the special condition that its definition must start with at least one "enum constant". Apart from that, and that enums can't be extended or used to extend other classes, an enum is a class like any class and you use it by adding methods below the constant definitions:
Expand Gist Expand Code Snippet
- Note that the enum is created only when the INSTANCE field is first accessed. Only at that point will JVM implicitly call the (implicit) constructor of the enum. Hence the instantiation is a lazy instantiation. You can override the constructor of the enum that you create. But note that manually calling the constructor is a compile error. To sum up, by default enums have implicit private constructor and that explicitly adding a private constructor is not needed unless you actually have code that you need to run in that constructor
- Why is it mentioned in the book that a Singleton Class is hard to test. "Making a class a singleton can make it difficult to test its clients because it's impossible to substitute a mock implementation for a singleton unless it implements an interface that serves as its type." Read this SO Link.

What is the difference between a Singleton class and a static class? Should a Java singleton use static fields? When should a Singleton be used, and when should a static static class be used?


- Difference between static class and singleton pattern?
- Should a Java singleton use static variables?
- When to use a Singleton and when to use a static class?
Item 4: Enforce non-instantiability with a private constructor
- You have the concept of Utility Classes in java. The idea is that these classes have static methods that serve some specific purpose, but the class in which the methods are present is NEVER meant to be instantiated. For example, consider the java.lang.Math class. You can call a number of methods from this class like Math.log10(1000), Math.pow(10,2) etc. All of these methods are declared static in the Math class and you are not expected to instantiate the Math class in order to make use of these methods. In fact, you CANNOT instantiate the Math class because the constructor of the Math class is declared private with the following comment on top of it: Don't let anyone instantiate this class..
- You can see the same thing when you look at the java.util.Arrays class as well. Remember this class has useful methods such as Arrays.asList("Alice", "Bob");. Again, just like the Math class, these methods are also declared static. And the Arrays class itself has a private constructor. With the following comment: Suppresses default constructor, ensuring non-instantiability.
- This is important. This idiom is mildly counterintuitive because the private constructor is provided expressly so that it cannot be invoked. Remember that if you did not specify a constructor, then java would have provided a default constructor, which would have allowed others to instantiate the class.
- Attempting to enforce noninstantiability by making a class abstract does not work. The class can be subclassed and the subclass instantiated.
- Note that as a side-effect of using this idiom, this prevents the Utility Class from being subclassed. All constructors must invoke a superclass constructor, explicitly or implicitly, and a subclass would have no accessible superclass constructor to invoke.
Expand Gist Expand Code Snippet
Item 5: Prefer Dependency Injection to hard-wiring resources
- Static utility classes and singletons are inappropriate for classes whose behavior is parameterized by an underlying resource.
- Imagine you wanted to create a spell checker. The spell checker depends on the dictionary that is going to be used for testing the spelling. Thus the dictionary is a dependency for the SpellChecker class. You can create an instance variable of type Dictionary in the SpellChecker class and have that class use that particular dictionary. But now if you want to create a spell checker for a different language, you will have to create a different SpellChecker class with a different Dictionary. This is certainly not ideal.
Expand Gist Expand Code Snippet
- What is required is the ability to support multiple instances of the class (in our example, SpellChecker), each of which uses the resource desired by the client (in our example, the dictionary). A simple pattern that satisfies this requirement is to pass the resource into the constructor when creating a new instance. This is one form of dependency injection: the dictionary is a dependency of the spell checker and is injected into the spell checker when it is created.
Expand Gist Expand Code Snippet
- Read about how DI improves the testability of class here on SO.
Item 6: Avoid creating unnecessary objects
- Never use new String() to create a new String. Use the String literal because in that way you can reuse the same String instance.
- Why does the code say: "Prevents VM from optimizing away everything". Explained here on SO.
Expand Gist Expand Code Snippet
- You can often avoid creating unnecessary objects by using static factory methods (Item 1) in preference to constructors on immutable classes that provide both.
- Use a Pattern when you are trying to match a string with a regex instead of directly calling yourStr.matches("regexExpression"). Note the below example how we are making use of the Pattern class.
- While String.matches is the easiest way to check if a string matches a regular expression, it’s not suitable for repeated use in performance-critical situations. The problem is that it internally creates a Pattern instance for the regular expression and uses it only once, after which it becomes eligible for garbage collection. Creating a Pattern instance is expensive because it requires compiling the regular expression into a finite state machine. To improve the performance, explicitly compile the regular expression into a Pattern instance (which is immutable) as part of class initialization, cache it, and reuse the same instance for every invocation of the isRomanNumeral method.
Expand Gist Expand Code Snippet
- You can use the keySet method of a Map interface to get a view of the keys of the Map in the form of a Set. The keySet is backed by the underlying Map, i.e. any changes to the key set are going to be reflected in the Map. If you create multiple instances of a key set from the same Map, making changes to one key set are going to impact all of the other key sets that you have created as well.
- Prefer primitives to boxed primitives, and be wary of unintentional autoboxing. In the below example, the variable sum is declared as a Long instead of a long, which means that the program constructs about 2^31 unnecessary Long instances (roughly one for each time the long i is added to the Long sum). Changing the declaration of sum from Long to long reduces the runtime.
Expand Gist Expand Code Snippet
Item 7: Eliminate obsolete object references
- You can run into OutOfMemoryError in Java as well if you are not careful with Objects that are no longer in use in the program, but your datastructures are still maintaining a reference to it.
- Consider the below code. As you are adding elements to the array, you are increasing the length value, and once you "remove" the last element, you are simulating that by just reducing the length parameter of the array. This causes a memory leak, because your array is still storing a reference to the object in the array. Imagine this was a reference to a rather large object, which had references of its own. As your program scaled, you would have run into memory issues.
- One way of getting around this problem is by nulling out the references once you are done with them.
Expand Gist Expand Code Snippet
- Nulling out object references should be the exception rather than the norm. The best way to eliminate an obsolete reference is to let the variable that contained the reference fall out of scope. This occurs naturally if you define each variable in the narrowest possible scope.


-

So when should you null out a reference? What aspect of the MyArray class makes it susceptible to memory leaks?


- Simply put, it manages its own memory.
- The storage pool consists of the elements of the myArray array (the object reference cells, not the objects themselves). The elements in the active portion of the array (elements of the array that are at index less than length) are allocated, and those in the remainder of the array are free. The garbage collector has no way of knowing this; to the garbage collector, all of the object references in the elements array are equally valid. Only the programmer knows that the inactive portion of the array is unimportant. The programmer effectively communicates this fact to the garbage collector by manually nulling out array elements as soon as they become part of the inactive portion. Generally speaking, whenever a class manages its own memory, the programmer should be alert for memory leaks. Whenever an element is freed, any object references contained in the element should be nulled out.
Item 8: Avoid finalizers and cleaners
- Finalizers are deprecated as of Java 9 and have been replaced by cleaners. Cleaners are less dangerous than finalizers, but still unpredictable, slow, and generally unnecessary.
- This SO answer goes into details about why the mechanism of finalization is weird?
- This SO answer goes into details about Cleaners vs Finalizers.
- Refer text for complete explanation.
Item 9: Prefer try-with resources to try-finally
- Always use try-with-resources instead of using the old try-catch block.
- The problem with using the older method is: Even the correct code for closing resources with try-finally statements, as illustrated in the first code example, has a subtle deficiency. The code in both the try block and the finally block is capable of throwing exceptions. For example, in the firstLineOfFile method, the call to readLine could throw an exception due to a failure in the underlying physical device, and the call to close could then fail for the same reason.
Expand Gist Expand Code Snippet

Chapter 3: Methods common to all objects

- Although Object is a concrete class, it is designed primarily for extension. All of its non-final methods (equals, hashCode, toString, clone, and finalize) have explicit general contracts because they are designed to be overridden. It is the responsibility of any class overriding these methods to obey their general contracts; failure to do so will prevent other classes that depend on the contracts (such as HashMap and HashSet) from functioning properly in conjunction with the class.
- The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).
- This chapter tells you when and how to override the nonfinal Object methods.
Item 10: Obey the general contract when overriding equals
- You can read about the equals and hashcode here on Baeldung.
- And here are on Thorben Janssen
- SO Link on the topic
- The easiest way to avoid problems when overriding equals is to not override equals. This is the right thing to do if any of the following conditions apply:
a) Each instance of the class is inherently unique: This is the case when a class represents active entities rather than values.
b) There is no need for the class to provide a "logical equality" test: Does the client need the functionality where they will have to compare two instances of this class?
c) A superclass has already overridden equals, and the superclass behavior is appropriate for this class: For example, most Set implementations inherit their equals implementation from AbstractSet, List implementations from AbstractList, and Map implementations from AbstractMap.
d) The class is private or package-private and you are sure that its equals method will never be invoked.

So when is it appropriate to override equals?


- It is when a class has a notion of logical equality that differs from mere object identity and a superclass has not already overridden equals. This is generally the case for value classes. A value class is simply a class that represents a value, such as Integer or String.
- Someone that calls equals on such a class will expect the result to return whether the two classes are logically equivalent, and not just whether they refer to the same object or not. Overriding equals is necessary to satisfy this expectation, as well as enable the instances of these value classes to serve as keys to a map, or allow them to be added to a Set.
- Note that singleton value classes do not need their equals method overridden, since there will always be just a single instance of these classes. Hence in these cases the object equality is the same as the value equality.

What is the contract of the equals method?


- The contract says that the equals method should be an equivalence relation. What this means is that the equals method should satisfy the following conditions:
a)

Reflexive

: Each instance should be equal to itself. For any non-null reference value x, x.equals(x) must return true. If you were to violate it and then add an instance of your class to a collection, the contains method might well say that the collection didn't contain the instance that you just added. Hard to screw this one up.
b)

Symmetry

: For any non-null reference values x and y, x.equals(y) must return true if and only if y.equals(x) returns true.
- Consider this implementation of equals. As expected, cis.equals(s) returns true. The problem is that while the equals method in CaseInsensitiveString knows about ordinary strings, the equals method in String is oblivious to case-insensitive strings (i.e. no relations has been specified between the CaseInsensitiveString class and the String class). Therefore, s.equals(cis) returns false, a clear violation of symmetry.
Expand Gist Expand Code Snippet
- In order to correct this error, we change the equals implementation as follows. Now, we are no longer trying to compare String and CaseInsensitiveString in the equals method. Hence, comparison on both sides now returns false.
Expand Gist Expand Code Snippet
- This is the equals that was auto-generated by IntelliJ. Note that the overridden equals methods is checking whether the two strings being compared are EXACTLY equal or not. And not checking for equalsIgnoreCase, as we want. So do not blindly use the auto-generated equals method. Check what is going on.
Expand Gist Expand Code Snippet
c)

Transitivity

: For any non-null reference values x, y, z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) must return true.
- Consider the case of a subclass that adds a new value component to its superclass. Below is a class Point that is being extended by ColorPoint. ColorPoint adds a new field called color to the Point class.
Expand Gist Expand Code Snippet
- Now, how should the equals of the ColorPoint class be written? If you leave it out entirely, the implementation is inherited from Point and color information is ignored in equals comparisons. While this does not violate the equals contract, it is clearly unacceptable.
- So the first version that we write compares whether the points are the same and the color is the same as well. But this solution breaks the rule of symmetricity. point.equals(colorPoint)) returns true, whereas colorPoint.equals(point) returns false.
- Note that the expectation here is that both of the comparisons should return false.
Expand Gist Expand Code Snippet
- So, in order to get around this issue, say we decide that if the types are different, then we are not going to compare the color values at all. And that kind of makes sense as well. But if we do this, this ends up breaking the rule of transitivity.
Expand Gist Expand Code Snippet

So, what's the solution?


- It turns out that this is a fundamental problem of equivalence relations in object-oriented languages.
- There is no way to extend an instantiable class and add a value component while preserving the equals contract, unless you're willing to forgo the benefits of object-oriented abstraction.
- You may hear it said that you can extend an instantiable class and add a value component while preserving the equals contract by using a getClass test in place of the instanceof test in the equals method. In fact, note that this is what Intellij was auto-generating in the example above. This is an example of using this equals in the Point class. We already saw that this will return false when performing the comparisons from both sides.
- This has the effect of equating objects only if they have the same implementation class. This may not seem so bad, but the consequences are unacceptable: An instance of a subclass of Point is still a Point, and it still needs to function as one, but it fails to do so if you take this approach!
- You can read about the Liskov Substitution Principle here on the other page.
- In the below example, although the ColorPoint class extends the Point class (and hence every instance of a ColorPoint class IS-A Point class), the equals method of the Point class checks for the exact implementation type in getClass. Hence the code treats the two classes as different classes and that is a violation of Liskov Substitution Principle.
Expand Gist Expand Code Snippet

What is a workaround to this problem then?


- Follow the advice of Item 18, "Favor composition over inheritance."
- Instead of having ColorPoint extend Point, give ColorPoint a private Point field and a public view method (Item 6) that returns the point at the same position as this color point:
Expand Gist Expand Code Snippet
- Note that you can add a value component to a subclass of an abstract class without violating the equals contract. (Item 23)
d)

Consistency

: For any non-null reference values x and y, multiple invocations of x.equals(y) must consistently return true or consistently return false, provided no information used in equals comparisons is modified.
- If two objects are equal, they must remain equal for all time unless one (or both) of them is modified. In other words, mutable objects can be equal to different objects at different times while immutable objects can't.
- Do not write an equals method that depends on unreliable resources. equals methods should perform only deterministic computations on memory-resident objects.
e)

Non-Nullity

: For any non-null reference value x, x.equals(null) must return false.
- Note the places above where we have been using the instanceof operator.
- To test its argument for equality, the equals method must first cast its argument to an appropriate type so its accessors can be invoked or its fields accessed. Before doing the cast, the method must use the instanceof operator to check that its argument is of the correct type.
- If this type check were missing and the equals method were passed an argument of the wrong type, the equals method would throw a ClassCastException, which violates the equals contract. But the instanceof operator is specified to return false if its first operand is null, regardless of what type appears in the second operand. Therefore, the type check will return false if null is passed in, so you don't need an explicit null check.
Expand Gist Expand Code Snippet

What are the things to remember when writing a custom equals method

?
Expand Gist Expand Code Snippet
Item 11: Always override hashCode when you override equals
- The contract of the hashCode states the following three rules:
a) When the hashCode method is invoked on an object repeatedly during an execution of an application, it must consistently return the same value, provided no information used in equals comparisons is modified. This value need not remain consistent from one execution of an application to another.
b) If two objects are equal according to the equals(Object) method, then calling hashCode on the two objects must produce the same integer result.
c) If two objects are unequal according to the equals(Object) method, it is not required that calling hashCode on each of the objects must produce distinct results. However, the programmer should be aware that producing distinct results for unequal objects may improve the performance of hash tables.
- The key provision that is violated when you override the equals but do not override the hashCode is point 2: equal objects must have equal hash codes. If you override equals but do not override hashCode, two distinct instances of a class might be logically equal according to the equals method that you overrode. But these two equal objects will end up returning different numbers when you call the hashCode method on these two logically equal objects.
- So why does this matter? Consider the below code.
- The PhoneNumber class's failure to override hashCode causes the two equal instances to have unequal hash codes, in violation of the hashCode contract. Therefore, the get method is likely to look for the phone number in a different hash bucket from the one in which it was stored by the put method. Even if the two instances happen to hash to the same bucket, the get method will almost certainly return null, because HashMap has an optimization that caches the hash code associated with each entry and doesn't bother checking for object equality if the hash codes don't match.
Expand Gist Expand Code Snippet

So how should we write a proper hashCode function?


- Read the complete thing in the text.
- What is a "canonical representation" of a field mean here on SO.
- You may exclude derived fields from the hash code computation. In other words, you may ignore any field whose value can be computed from fields included in the computation.
- You must exclude any fields that are not used in equals comparisons, or you risk violating the second provision of the hashCode contract.
- This is what the auto-generated hashCode from IntelliJ looks like. Note that in this auto-generated method, we are not calculating the hash values of the individual fields for some reason.
Expand Gist Expand Code Snippet
- Alternatively, the Objects class has a static hash method that takes an arbitrary number of objects and returns a hash code for them. This method lets you write one-line hashCode methods whose quality is comparable to those written according to the recipe in this item. Unfortunately, they run more slowly because they entail array creation to pass a variable number of arguments, as well as boxing and unboxing if any of the arguments are of primitive type. This style of hash function is recommended for use only in situations where performance is not critical.
- This is what it looks like.
Expand Gist Expand Code Snippet
- If a class is immutable and the cost of computing the hash code is significant, you might consider caching the hash code in the object rather than recalculating it each time it is requested. If you believe that most objects of this type will be used as hash keys, then you should calculate the hash code when the instance is created. Otherwise, you might choose to lazily initialize the hash code the first time hashCode is invoked. The String class for example lazily-initializes it's hash code. Once the hashCode has been computed, it caches the value.
Expand Gist Expand Code Snippet
- Do not be tempted to exclude significant fields from the hash code computation to improve performance. While the resulting hash function may run faster, its poor quality may degrade hash tables' performance to the point where they become unusable. In particular, the hash function may be confronted with a large collection of instances that differ mainly in regions you've chosen to ignore.
Item 12: Always override toString
- While Object provides an implementation of the toString method, the string that it returns is generally not what the user of your class wants to see. It consists of the class name followed by an "at" sign (@) and the unsigned hexadecimal representation of the hash code.
- Override Object's toString implementation in every instantiable class you write, unless a superclass has already done so. It makes classes much more pleasant to use and aids in debugging. The toString method should return a concise, useful description of the object, in an aesthetically pleasing format.
Item 13: Override clone judiciously
- Read text.
- The idea is to create a deep-copy of the object. But you cannot have your class just implement the Cloneable interface in order to achieve this functionality. Your class needs to, both, implement Cloneable and override the clone method from the Object class in order to achieve a deep copy.
- But there are better ways to create a copy of the object. Either by using a copy constructor, or by using a static method. clone is rarely used to achieve this outcome. This is also the way that is preferred nowadays? See for example SO answer here and here.
- If we can create a deep copy of an object by specifying a method of our own, why do we need to explicitly implement and then override the clone method? Explained here on SO.
Item 14: Consider implementing Comparable
- A lot of stuff going on over here. Read the text for full info.
- If you are writing a value class with an obvious natural ordering, such as alphabetical order, numerical order, or chronological order, you should implement the Comparable interface. Implementing the Comparable interface means overriding the compareTo method.
- The general contract of the compareTo method is similar to that of equals: Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. Throws ClassCastException if the specified object's type prevents it from being compared to this object.

Chapter 8: Methods

- This chapter discusses several aspects of method design: how to treat parameters and return values, how to design method signatures, and how to document methods.
Item 49: Check parameters for validity
- General principle: you should attempt to detect errors as soon as possible after they occur. Failing to do so makes it less likely that an error will be detected and makes it harder to determine the source of an error once it has been detected.