This will be a series of posts based on the book Effective Java by Joshua Bloch. The posts will be split into the following parts based on the chapters in the book.
This post will cover Creating and Destroying Objects
Benefits
Disadvantages
Examples
Examples
Eliminate obsolete object references
- Creating and Destroying Objects
- Methods Common to All Objects
- Classes and Interfaces
- Substitutes for C Constructs
- Methods
- General Programming
- Exceptions
- Threads
- Serialization
This post will cover Creating and Destroying Objects
Providing static factory method
Consider providing static factory methods in the object to
create instances of the class.
Benefits
1.
Methods can be named to indicate the type of
object that is being returned. This will avoid the problem where it is found
necessary to have two constructors having the same signatures, but different
parameters.
2.
Factory methods do not need to construct an
instance each time they are invoked. This can be useful in limiting the number
of object instances of a class created in the JVM. This can be used to create
immutable classes. E.g. factory methods the Boolean class can be used to ensure
that only two objects of type Boolean ever exist in the JVM. Additional
advantage is if a == b also means a.equalsEvery method expects a (b) and the
performance of == can be significantly better than ==.
3.
Factory methods need to return the same class,
but can return any object which is a subtype of the return type. This can be used to hide classes in
libraries. Only the interface will be exposed to the outside world.
Disadvantages
- 1. Classes without public or protected constructors cannot be subclassed.
- 2. The methods used to create instances of class are not distinguishable from other methods of the class. It can be difficult to identify such methods easily in a class.
Examples
The collections class java.util.Collections can be used to
instantiate a variety of Collection classes. The Java Cryptographic Extension
uses this method to allow instantiation of the multiple implementation of the
APIs.
Enforce Singleton Pattern with a private Constructor
A singleton is a class which has only one instance in the
JVM.
There are two ways to create a Singleton
public class FirstSingleton {
public static final
FirstSingleton INSTANCE = new FirstSingleton();
private FirstSingleton
() {
}
}
}
}
public class SecondSingleton {
private static final
SecondSingleton INSTANCE = new SecondSingleton ();
private SecondSingleton
() {
}
}
public static
SecondSingleton getInstance() {
return INSTANCE;
}
}
}
The advantage of the second method is that if in a future
date one wishes to convert the object from a Singleton to Multiton it will be
possible without changing any references to this object. This will be near
impossible if one adopts the first pattern.
If we wish to make a Singleton class Serializable then just
implementing the interface will not be sufficient, one will need to implement
the readResolve method too so that multiple instances of the class are not
created.
Enforce noninstantiability with a private constructor
Sometimes there is a requirement for a class which has only
static methods and constants. Typically utility classes will fall under this category.
For such classes one should define a private constructor so
that this can never be instantiated. If this private constructor is not defined
then it will be possible to instantiate this class which does not make sense.
This will also prevent the class from being subclassed.
Examples
1.
java.util.Arrays
2.
java.util.Math
3.
java.util.Collections
Avoid creating duplicate objects
When objects are immutable it makes sense to use the same
instance of the object rather than creating a new instance.
E.g.
String s = new String(“silly”); //Don’t do this.
This unnecessarily creates another instance of the String
whereas the following would have sufficed.
String s = “silly”;
Any object that needs to contain a constant value should be
defined once and reused instead of being instantiated again and again. E.g.
fixed dates against which some other dates must be compared.
Note: Creating
objects is not very expensive but they should be created only when required or
when it makes sense to create objects to keep the code simple and clear.
Maintaining an object pool is a bad idea unless the objects
are real heavy weights, i.e. creation of these objects are expensive. One
example of such an object is the Database Connection.
Be careful and do not reuse an object which should not be.
Typically objects that can change should not be reused, instead a new instance
or a copy should be made.
Eliminate obsolete object references
Objects that are no longer required, but may not be Garbage
Collected by virtue of it going out of scope should be nulled so that they can
be garbage collected.
Consider the following example which is an implementation of
Stack:
public class Stack {
private Object[]
elements;
private int size = 0;
public Stack (int
initialCapacity) {
this.elements =
new Object[initialCapacity];
}
}
public void push
(Object e) {
ensureCapacity();
elements[size++] =
e;
}
public void Object
pop() {
if (size == 0)
throw new EmptyStackException();
return
elements[--size];
}
/**
* Ensure space for at
least one more element, roughly
* doubling capacity
each time the array needs to grow.
*/
private void
ensureCapacity() {
if
(elements.length == size) {
Object[]
oldElements = elements;
Elements = new
Object[2 * elements.length + 1];
System.arrayCopy(oldElement, 0, elements, 0, size);
}
}
}
}
On the face of it the object is fine and there are no
problems, but there is a hidden memory leak. This happens in the pop method. As
the objects are popped out of the stack they are no longer required in the
array, but since there is a reference to these objects in the array the garbage
collector does not know that these can be collected. This means that even though
the Stack has shrunk in size the memory is not getting released and this can
cause problems depending on how the object is used.
The way to fix is to nullify the references that are not
required as below
public void Object
pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[size];
elements[--size] = null;
return result;
}
This does not mean that one should nullify every object that
is not required. This mechanism should be used only under condition as
described above, i.e. in scenarios where the Class is managing its own memory
without the knowledge of Garbage Collector.
Cache is another source of such leaks. If object in the
cache are no longer required, then references to them should be removed. If the
reference to the objects needs to cease based on reference to the object
outside the scope of the cache then using a WeakHashMap will automatically
remove the object from the Map once the reference to the object outside the
cache is removed.
If it is not known as to when the object out of scope but it
is desired that it be possible to remove objects based on the oldest object
then we can use the LinkedHashMap object. This allows for removeEldestEntry
which will remove the earliest entry.
Avoid Finalizers
Finalizers are invoked when the object is Garbage collected.
It is not known as to when the Garbage Collection will kick off. Given this one
should not depend on the finalzer to free resources. Resources should be freed
typically in the finally block. If one depends on finalizer to free resources
it maybe too late. Expect the clients of the class to invoke the method for
freeing of the resources
The next post is about the best practices in writing methods which are common to all the classes.
The next post is about the best practices in writing methods which are common to all the classes.
No comments:
Post a Comment