Fork me on GitHub

Effective Java



I read this beautifully written article a few days ago - “I will not do your tech interview”. I can’t agree more with the author. Every single time I have had to give/take a technical interview, more than the sense of being inadequately prepared I feel like carrying an inexplicable psychological burden. And I have met no one who does not fear what Ellis beautifully calls as - “bear-trap of a stupid brainteaser” :-).

In the years to come, internet is definitely going to give more and more relief to competent engineers. Having a GitHub repository with a dump of one’s pet technology prototypes, having a StackOverflow point score, well articulated tweets and maybe even well-written technology blog (read this by Nathan Marz) will pay dividends to engineers continuously at work to sharpen their axe…

But then, my current reality is a reality. And I have to take technical interviews as part of my job. And hiring the right people is so much more important for a small company - many times it is the only differentiator between success and failure of the company itself. So with the job’s being dished out being so important, technical interviews are not supposed to be easy. Both for the interviewee and the interviewer. Pressed into the interviewing job, I felt the need to brush-up my fundamentals. This post is from my re-read of Joshua Bloch’s classic - “Effective Java” - from a interviewer’s perspective… trying to quickly refresh the elementary concepts to myself. It aint coherent or complete… will keep adding stuff to this post over time as I realise what questions really make the cut. There are plenty of interview-questions blogs and books out there - but I felt, instead of quizzing a candidate on some corner case of the JVM or language (which many times the interviewer himself might have realised just hours before the interview), it would be more honest/ethical on my part to quiz in what are well-known and real-world areas of programming for an aspiring engineer - and ‘Effective Java’ is precisely the guide for such a setting…

Now planning to write few more blogs like these in the days to come… one surely on Design Patterns by GoF. Maybe one on JavaScript’s good parts per Doughlas Crockford. And time permitting, few more…

Creating and Destorying Objects

Consider static factory methods instead of constructors
Similar to flyweight. valueof/of/getInstance/newInstance/getType/newType
Consider a builder when faced with many constructor parameters
Telescoping constructors are hard to read and write. Inconsistent state partway through the construction
Enforce the singleton property with a private constructor or an enum type
All instance fields should be transient. Provide a readResolve() method else serialization/deserialization can lead to new objects
Enforce non-instantiability with a private constructor
Avoid creating unnecessary objects
A statement like this in a for loop can lead to huge number of unnecessary objects getting created - String s = new String("stringette"); The improved version is simply the following: String s = "stringette"; This version uses a single String instance, rather than creating a new one each time it is executed. Furthermore, it is guaranteed that the object will be reused by any other code running in the same virtual machine that happens to con- tain the same string literal The static factory method Boolean.valueOf(String) is almost always preferable to the constructor Boolean(String)
Eliminate obsolete object references
Spot the memory leak in this program?
            public class Stack {
                   private Object[] elements;
                   private int size = 0;
                   private static final int DEFAULT_INITIAL_CAPACITY = 16;
                   public Stack() {
                       elements = new Object[DEFAULT_INITIAL_CAPACITY];
                   }
                   public void push(Object e) {
                       ensureCapacity();
                       elements[size++] = e;
                   }
                   public Object pop() {
                       if (size == 0)
                           throw new EmptyStackException();
                       return elements[--size];
                   }
                   /**
                    * Ensure space for at least one more element, roughly
                    * doubling the capacity each time the array needs to grow.
                    */
                   private void ensureCapacity() {
                       if (elements.length == size)
                           elements = Arrays.copyOf(elements, 2 * size + 1);
                   }
            }
Avoid finalizers
What is a finalizer? Is it always called by the GC? Is there a performance penalty to using finalizer? Why?

The Java methods common to all objects

Obey the general contract when overriding equals()
1. When do you override equals()?
When a class has a notion of logical equality that differs from mere object identity, and a superclass has not already overridden equals to implement the desired behavior.
2. What are the main rules that you would follow to implement equals()?
  • Use == to check for same reference
  • Use instanceof to check if the agrument is of the correct type
  • Match all significant fields of the two objects
  • Symmetric? Transivitve? Consistent?
  • override hashCode()
Always override hashCode() when you override equals()
1. If two objects are equal according to the equals (Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
2. It is not required that if two objects are unequal according to the equals (Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
3. How will you compute the hashCode()? Do not be tempted to exclude significant parts of an object from the hash code computation to improve performance
Always override toString()
Override clone() judiciously
1. Does Cloneable interface have a clone() method? Why not?
Because the Java Object's clone() method (which is protected) is supposed to be used
2. How does Java Object's clone() method work?
If a class implements Cloneable, Object’s clone method returns a field-by-field copy of the object; otherwise it throws CloneNotSupportedException
3. What are the 3 rules for implementing Cloneable?
a. x.clone() != x b. x.clone().getClass() == x.getClass() c. x.clone().equals(x)
4. How to clone properly?
All classes that implement Cloneable should override clone with a public method whose return type is the class itself. This method should first call super.clone and then fix any fields that need to be fixed. Typically, this means copying any mutable objects that comprise the internal “deep structure” of the object being cloned, and replacing the clone’s references to these objects with ref- erences to the copies. While these internal copies can generally be made by call- ing clone recursively, this is not always the best approach. If the class contains only primitive fields or references to immutable objects, then it is probably the case that no fields need to be fixed.
5. How come interfaces like Cloneable and Serializable have no methods? Why do they exist at all then? How does JVM use them?
The UID and custom readers/writers are accessed via reflection. Serializable serves as a marker to the JRE/JVM, which may take action(s) based on its presence. Refer to http://en.wikipedia.org/wiki/Marker_interface_pattern. An example of the application of marker interfaces from the Java programming language is the Serializable interface. A class implements this interface to indicate that its non-transient data members can be written to an ObjectOutputStream. The ObjectOutputStream private method writeObject() contains a series of instanceof tests to determine writeability, one of which looks for the Serializable interface. If any of these tests fails, the method throws a NotSerializableException.
Consider implementing Comparable
1. What is the use of the Comparable interface?
Helps in sorting when there is a natural order among the objects
2. Whats the difference between interfaces like Comparable and those like Cloneable/Serializable?

Classes and Interfaces

Minimize the accessibility of classes and members
What is package-private? How do you implement? The member is accessible from any class in the package where it is declared. Technically known as default access, this is the access lev- el you get if no access modifier is specified.
In public classes, use public classes not public fields
Minimize mutability
1. Is it good or bad to minimize mutability? why?
If objects are immutable they are automatically thread-safe and no synchronization or locking is required
2. How would you make an object immutable?
  • No mutators - no setters
  • Class cant be extended - class should be marked final
  • Make all fields final
  • Make all fields private
  • Ensure exclusive access to any mutable components
  • getters should return a new instance of the object
Favor composition over inheritance
Design and document for inheritance or else prohibit it
Prefer interfaces to abstract classes
Why are interfaces better than 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.
  • Interfaces enable safe, powerful functionality enhancements
  • combine the virtues of interfaces and abstract classes by providing an abstract skeletal implementation class to go with each nontrivial interface that you export
Use interfaces only to define types
Is 'constants' in an interface a good programming pattern?
No.
Prefer class hierarchies to tagged classes
Use function objects to represent strategies
Favor static member classes over nonstatic
1. What are the 4 kinds of nested classes?
  • a. static member classes
  • b. nonstatic member classes
  • c. anonymous classes
  • d. local classes
2. When will you make a nested class static?
If an instance of a nested class can exist in isolation from an instance of its enclosing class, then the nested class must be a static member class: it is impossible to create an instance of a nonstatic member class without an enclosing instance. If you declare a member class that does not require access to an enclosing instance, always put the static modifier in its declaration
3. Why would one prefer static classes?
The association between a nonstatic member class instance and its enclosing instance is established when the former is created; it cannot be modified thereafter. Storing this reference costs time and space, and can result in the enclosing instance being retained when it would otherwise be eligible for garbage collection

Generics

Dont use raw types in new code
1. What is the problem with doing private final Collection stamps = ... ;
Loss of compile time type safety
2. Is List.class legal? What will it give me?
It is not legal
Eliminate unchecked warnings
How do you eliminate a unchecked warning?
Suppress the warning with an @SuppressWarnings("unchecked") annotation. Always use the Suppress- Warnings annotation on the smallest scope possible.
Prefer lists to arrays
1. If Sub is a subtype of Super, then is the array Sub[] a subtype of Super[]?
Yes. Arrays are covariant. Lists are invariant
2. So which one is better? And why?
Lists are better. Arrays are reified. This means that arrays know and enforce their element types at runtime. Generics, by contrast, are implemented by erasure. This means that they enforce their type constraints only at compile time and discard (or erase) their element type information at runtime.
3. Test question -
This code fragment is legal but fails at runtime! -
                  Object[] objectArray = new Long[1];
                  objectArray[0] = "I don't fit in"; // Throws ArrayStoreException
But this one wont compile at all! -
List<Object> ol = new ArrayList<Long>(); // Incompatible types ol.add("I don't fit in");
4. Are these legal?
new List<E>[]
new List<String>[]
new E[]
No. It is illegal to create an array of a generic type, a parameterized type, or a type parameter. Types such as E, List<E>, and List are technically known as non-reifiable types. Intuitively speaking, a non-reifiable type is one whose runtime representation contains less information than its compile-time representation.
Favor generic types
Which of these is better and why?
        public class Stack {
            private Object[] elements;
            public void push(Object e) {
            }
            public Object pop() {
            }
        }
or
        public class Stack {
            private E[] elements;
            public void push(E e) {
            }
            public E pop() {
            }
        }
Favor generic methods
Which of these is better and why? public static Set union(Set s1, Set s2) or public static <E> Set<E> union(Set<E> s1, Set<E> s2)
Use bounded wildcards to increase API flexibility
What is the PECS rule or Get-and-Put principle? Bounded wildcards can be of two types - X<? extends E> or Y<? super E> PECS stands for producer-extends, consumer-super. In other words, if a parameterized type represents a T producer, use ; if it represents a T consumer, use .
Consider typesafe heterogenous containers

Enums and Annotations

Use enums instead of int constants
1. Does enum extend Java Object?
They provide high-quality implementations of all the Object methods
2. Which interfaces do enum implement?
they implement Comparable and Serializable, and their serialized form is designed to withstand most changes to the enum type.
3. How would you associate data with enums?
To associate data with enum constants, declare instance fields and write a constructor that takes the data and stores it in the fields. Enums are by their nature immutable, so all fields should be final
4. How would you associate a different behavior with every enum constant?
using apply()
Use instance fields instead of ordinals
Is using ordinals a bad idea? If so, what is the option?
Use instance fields
Use EnumSet instead of bit fields
Whats the usecase for EnumSets? Instead of bit fields which look ugly like this text.applyStyles(STYLE_BOLD | STYLE_ITALIC); one can do this - text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
Use EnumMap instead of ordinal indexing
It is rarely appropriate to use ordinals to index arrays: use EnumMap instead
Emulate extensible enums with interfaces
Prefer annotations to naming patterns
1. Any usecase you can think of for custom annotations?
JUnit testing framework originally required its users to designate test methods by beginning their names with the characters test
2. Which annotation do you use most?
@Override, @Deprecated, @SuppressWarnings
Consistently use the Override annotation
What @Override for?
it indicates that the annotated method declaration overrides a declaration in a supertype
Use marker interfaces to define types

Methods

Check parameters for validity
Make defensive copies when needed
Design method signatures carefully
Is Map as a method parameter better or HashMap - why?
Map is. This is super basic.
Use overloading judiciously
Use varargs judiciously
Return empty arrays or collections, not nulls
What is better - returning null or empty collections?
Empty Collections
Write doc comments for all exposed API comments

General Programming

Minimize the scope of local variables
Prefer foreach loops to traditional for loops
Know and use the libraries
Avoid float and double if exact answers are required
Prefer primitives to boxed primitives
What makes the performance of this program bad?
            public static void main(String[] args) {
                Long sum = 0L;
                for (long i = 0; i < Integer.MAX_VALUE; i++) {
                    sum += i;
                }
                System.out.println(sum);
            }
Avoid strings when other types are more appropriate
Beware the performance of string concatenation
1. Before 1.5, for string concatenation StringBuffer was preferred - what is it now?
StringBuilder
2. What is the difference between StringBuilder and StringBuffer?
StringBuider is unsynchronized - this makes it much faster. But should be used with care in concurrent programs
Refer to objects by their interfaces
Which one is better and why?
        List subscribers = new ArrayList();
        ArrayList subscribers = new ArrayList();
Prefer interfaces to reflection
Reflection allows one class to use another, even if the latter class did not exist when the former was compiled. So what are the problems using it?
  • You lose all the benefits of compile-time type checking, including exception checking
  • The code required to perform reflective access is clumsy and verbose
  • Performance suffers
Use native methods judiciously
Optimize judiciously
Adhere to generally accepted naming conventions

Exceptions

Use exceptions only for exceptional conditions
Use checked exceptions for recoverable conditions and runtime exceptions for programming errors
1. What are the different types of exceptions?
* Checked exceptions * Unchecked exceptions - runtime exceptions and errors
2. When would you code for checked exceptions?
when the caller is can reasonably expected to recover
3. When would you throw a runtime exception?
When the program is as good as dead
4. When would you throw a error?
there is a strong convention that errors are reserved for use by the JVM to indicate resource defi- ciencies, invariant failures, or other conditions that make it impossible to continue execution. Given the almost universal acceptance of this convention, it’s best not to implement any new Error subclasses. Therefore, all of the unchecked throw- ables you implement should subclass RuntimeException
Avoid unnecessary use of checked exceptions
Tell me the exceptions you know and when you would use them
  • IllegalArgumentException - argument aint right
  • IllegalStateException - calling a method on an object before it is properly initialized
  • NullPointerException - someone invokes a method on a null object
  • ConcurrentModificationException - if a object designed to be used by a single thread is being concurrently modified
  • IndexOutOfBoundException - accessing array beyond its data length
  • UnsupportedOperationException - object does not support a method
Favor the use of standard exceptions
Throw exceptions appropriate to the abstraction
Document all exceptions thrown by each method
Include failure-capture information in detail messages
Strive for failure atomicity
Dont ignore exceptions

Concurrency

Synchronize access to shared mutable data
1. Is writing of all primitive data types atomic in Java?
reading or writing a variable is atomic unless the variable is of type long or double
2. How long would you expect this program to run?
        public class StopThread {
               private static boolean stopRequested;
               public static void main(String[] args)
                       throws InterruptedException {
                   Thread backgroundThread = new Thread(new Runnable() {
                       public void run() {
                           int i = 0;
                           while (!stopRequested)
                                i++;
                       }
                   });
                   backgroundThread.start();
                   TimeUnit.SECONDS.sleep(1);
                   stopRequested = true;
               }
        }
Probably permanently. The VM might do what is called hoisting, the virtual machine might transform this code:
        while (!done)
            i++;
into this code:
        if (!done)
            while (true)
                i++;
How would you correct his?
3. Is this program thread safe? Can generateSerialNumber() be called from multiple threads safely?
        private static volatile int nextSerialNumber = 0;
           public static int generateSerialNumber() {
               return nextSerialNumber++;
        }
4. What are the 4 factors that need trade-off when writing multi-threaded concurrent programs?
Safety, Liveness, Efficiency, Reusability
5. Whats the tradeoff between Safety and Liveness?
safety: nothing bad happens liveness: something good eventually happens
6. What is reentracy? Is Java reentrant?
Yes
7. Whats the difference between ArrayList and CopyOnWriteArrayList?
It is a variant of ArrayList in which all write operations are implemented by making a fresh copy of the entire underlying array. Because the internal array is never modified, iteration requires no locking and is very fast. For most uses, the performance of CopyOnWriteArrayList would be atrocious, but it’s perfect for observer lists, which are rarely modified and often traversed.
Avoid excessive synchronization
Prefer executors and tasks to threads
1. In the post Java 1.5 world, the use of 'Thread' is probably not a good idea due to the availability of new functionality in java.util.concurrent - what are they?
Executors and tasks
2. There are some data structures designed in Java collections specifically for concurrent usage - what are they and how do they work?
ConcurrentHashMap etc
3. Why is it a bad idea to rely on Thread.yield or Java's thread priorities API?
Not portable
Prefer concurrency utilities to wait and notify
Document thread safety
Use lazy initialization judiciously
Dont depend on the thread scheduler
Avoid thread groups

Serialization

Implement serializable judiciously
1. What is serialVersionUID?
Every serializable class has a unique identification number associated with it. If you do not specify this number explicitly by declaring a static final long field named serialVersionUID, the system automatically generates it at runtime by applying a complex procedure to the class. The automatically generated value is affected by the class’s name, the names of the interfaces it implements, and all of its public and protected members. If you change any of these things in any way, for example, by adding a trivial convenience method, the automatically generated serial version UID changes. If you fail to declare an explicit serial version UID, compatibility will be broken, resulting in an InvalidClassException at runtime. If no serial version UID is provided, an expensive computation is required to generate one at runtime. If you ever want to make a new version of a class that is incompatible with existing versions, merely change the value in the serial version UID declaration.
2. Why should a class be made to implement Serilizable with caution?
A major cost of implementing Serializable is that it decreases the flexibility to change a class’s implementation once it has been released.
Consider using a custom serialized form
How good is Java's ObjectStream based Serialization? When would you implement your own custom serialized form?
The default serialized form of an object is a reasonably efficient encoding. The default serialized form is likely to be appropriate if an object’s phys- ical representation is identical to its logical content. Drawbacks - can be excessive in space consumption, not very fast, it permanently ties the exported API to the current internal representation
Write readObject methods defensively
For instance control prefer enum types than readResolve
Consider serialization proxies instead of serialized instances

End Node


comments powered by Disqus