Panion 2nd Edition Apr 2007
Panion 2nd Edition Apr 2007
Pet Object
Pet Object
id=1
name
=Cat
id=1
name
=Cat
Pet Object
Pet Object
a
b
equals() method
id=1
name
=Cat
Pet Object
Pet Object
id=1
name
=Cat
id=1
name
=Cat
Pet Object
Pet Object
a
b
Note: String assignment with the new operator follow the same rule as == and equals( ) as mentioned above.
St r i ng st r = new St r i ng( ABC) ; / / Wrong. Avoi d t hi s because a new St r i ng i nst ance
/ / i s cr eat ed each t i me i t i s execut ed.
Variation to the above rule:
The literal String assignment is shown below, where if the assignment value is identical to another String assignment
value created then a new String object is not created. A reference to the existing String object is returned.
St r i ng st r = ABC; / / Right because uses a si ngl e i nst ance r at her t han
/ / cr eat i ng a new i nst ance each t i me i t i s execut ed.
Let us look at an example:
Java - Fundamentals
34
publ i c cl ass St r i ngBasi cs {
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
St r i ng s1 = new St r i ng( " A" ) ; / / not r ecommended, use St r i ng s1 = " A"
St r i ng s2 = new St r i ng( " A" ) ; / / not r ecommended, use St r i ng s2 = " A"
//standard: follows the == and equals() rule like plain java objects.
i f ( s1 == s2) { / / shal l ow compar i son
Syst em. out . pr i nt l n( " r ef er ences/ i dent i t i es ar e equal " ) ; //never reaches here
}
i f ( s1. equal s( s2) ) { / / deep compar i son
Syst em. out . pr i nt l n( " val ues ar e equal " ) ; // this line is printed
}
//variation: does not follow the == and equals rule
St r i ng s3 = " A" ; / / goes i nt o a String pool.
St r i ng s4 = " A" ; / / r ef er s t o St r i ng al r eady i n t he pool.
i f ( s3 == s4) { / / shal l ow compar i son
Syst em. out . pr i nt l n( " r ef er ences/ i dent i t i es ar e equal " ) ; //this line is printed
}
i f ( s3. equal s( s4) ) { / / deep compar i son
Syst em. out . pr i nt l n( " val ues ar e equal " ) ; //this line is also printed
}
}
}
Design pattern: String class is designed with Flyweight design pattern. When you create a String constant as shown
above in the variation, (i.e. String s3 = A, s4= A), it will be checked to see if it is already in the String pool. If it is in the
pool, it will be picked up from the pool instead of creating a new one. Flyweights are shared objects and using them can
result in substantial performance gains.
Q. What is an intern() method in the String class?
A pool of Strings is maintained by the String class. When the intern() method is invoked equals() method is invoked to
determine if the String already exist in the pool. If it does then the String from the pool is returned. Otherwise, this String
object is added to the pool and a reference to this object is returned. For any two Strings s1 & s2, s1.intern() ==
s2.intern() only if s1.equals(s2) is true.
Q 19: What are the non-final methods in Java Object class, which are meant primarily for extension? LF CO
A 19: The non-final methods are equals(), hashCode(), toString(), clone(), and finalize(). The other methods like
wait(), notify(), notifyAll(), getClass() etc are final methods and therefore cannot be overridden. Let us look at
these non-final methods, which are meant primarily for extension (i.e. inheritance).
Important: The equals() and hashCode() methods prove to be very important, when objects implementing these two
methods are added to collections. If implemented incorrectly or not implemented at all then your objects stored in a
collection like a Set, List or Map may behave strangely and also is hard to debug.
Method
name
Explanation
equals()
method with
public
access
modifier
This method checks if some other object passed to it as an argument is equal the object in which this method is
invoked. It is easy to implement the equals() method incorrectly, if you do not understand the contract. The contract
can be stated in terms of 6 simple principles as follows:
1. o1.equals(o1) which means an Object (e.g. o1) should be equal to itself. (aka Reflexive).
2. o1.equals(o2) if and only o2.equals(o1) So it will be incorrect to have your own class say MyPet to have a
equals() method that has a comparison with an Object of class java.lang.String class or with any other built-in
Java class. (aka Symmetric) .
3. o1.equals(o2) && o2.equals(o3) implies that o1.equals(o3) as well It means that if the first object o1 equals to
the second object o2 and the second object o2 is equal to the third object o3 then the first object o1 is equal to
the third object o3. For example, imagine that X, Y and Z are 3 different classes. The classes X and Y both
implement the equals() method in such a way that it provides comparison for objects of class X and class Y. Now
if you decide to modify the equals() method of class Y so that it also provides equality comparison with class Z,
then you will be violating this principle because no proper equals comparison exist for class X and class Z
objects. So, if two objects agree that they are equal and follow the above mentioned symmetric principle, then
Java - Fundamentals
35
one of them cannot decide to have a similar contract with another object of different class. (aka Transitive)
4. o1.equals(o2) returns the same as long as o1 and o2 are unmodified if two objects are equal, they must
remain equal as long as they are not modified. Similarly, if they are not equal, they must remain non-equal as long
as they are not modified. (aka Consistent)
5. !o1.equals(null) which means that any instantiable object is not equal to null. So if you pass a null as an
argument to your object o1, then it should return false. (aka null comparison)
6. o1.equals(o2) implies o1.hashCode() == o2.hashCode() This is very important. If you define a equals()
method then you must define a hashCode() method as well. Also it means that if you have two objects that are
equal then they must have the same hashCode, however the reverse is not true (i.e. if two objects have the same
hashCode does not mean that they are equal). So, If a field is not used in equals(), then it must not be used in
hashCode() method. (equals() and hashCode() relationship)
publ i c cl ass Pet {
i nt i d;
St r i ng name;
public boolean equals(Object obj){
i f ( t hi s == obj ) r et ur n t r ue; / / i f bot h ar e r ef er r i ng t o t he same obj ect
i f ( ( obj == nul l ) | | ( obj . get Cl ass( ) ! = t hi s. get Cl ass( ) ) ) {
r et ur n f al se;
}
Pet r hs = ( Pet ) obj ;
r et ur n i d == r hs. i d && ( name == r hs. name | |
( name ! = nul l && name. equal s( r hs. name) ) ) ;
}
/ / hashCode() method must be implemented here.
}
hashCode()
method with
public
access
modifier
This method returns a hashCode() value as an Integer and is supported for the benefit of hashing based
java.util.Collection classes like Hashtable, HashMap, HashSet etc. If a class overrides the equals() method, it
must implement the hashCode() method as well. The general contract of the hashCode() method is that:
1. Whenever hashCode() method is invoked on the same object more than once during an execution of a Java
program, this method must consistently return the same integer result. The integer result need not remain
consistent from one execution of the program to the next execution of the same program.
2. If two objects are equal as per the equals() method, then calling the hashCode() method in each of the two
objects must return the same integer result. So, If a field is not used in equals(), then it must not be used in
hashCode() method.
3. If two objects are unequal as per the equals() method, each of the two objects can return either two different
integer results or same integer results (i.e. if 2 objects have the same hashCode() result does not mean that they
are equal, but if two objects are equal then they must return the same hashCode() result).
publ i c cl ass Pet {
i nt i d;
St r i ng name;
public boolean equals(Object obj){
/ / as shown above.
}
/ / bot h f i el ds i d & name ar e used i n equal s( ) , so bot h f i el ds must be used i n
/ / hashCode( ) as wel l .
public int hashCode() {
i nt hash = 9;
hash = ( 31 * hash) + i d;
hash = ( 31 * hash) + ( nul l == name ? 0 : name. hashCode( ) ) ;
r et ur n hash;
}
}
toString() The toString() method provided by the java.lang.Object returns a string, which consists of the class name
Java - Fundamentals
36
method with
public
access
modifier
followed by an @ sign and then unsigned hexadecimal representation of the hashcode, for example
Pet@162b91. This hexadecimal representation is not what the users of your class want to see.
Providing your toString() method makes your class much more pleasant to use and it is recommended
that all subclasses override this method. The toString() method is invoked automatically when your object
is passed to println(), assert() or the string concatenation operator (+).
publ i c cl ass Pet {
i nt i d;
St r i ng name;
public boolean equals(Object obj){
/ / as shown above.
}
public int hashCode() {
/ / as shown bef or e
}
public String toString() {
St r i ngBuf f er sb = new St r i ngBuf f er ( ) ;
sb. append( i d=) . append( i d) ;
sb. append( , name=) . append( name) ;
r et ur n sb. t oSt r i ng( ) ;
}
}
clone()
method with
protected
access
modifier
You should override the clone() method very judiciously. Implementing a properly functioning clone method is complex
and it is rarely necessary. You are better off providing some alternative means of object copying (refer Q26 in Java
section) or simply not providing the capability. A better approach is to provide a copy constructor or a static factory
method in place of a constructor.
//constructor
publ i c Pet ( Pet pet ToCopy) {
}
//static factory method
publ i c st at i c Pet newI nst ance( Pet pet ToCopy) {
}
The clone() method can be disabled as follows:
publ i c f i nal Obj ect cl one( ) t hr ows Cl oneNot Suppor t edExcept i on {
t hr ow new Cl oneNot Suppor t edExcept i on( ) ;
}
finalize()
method
with
protected
access
modifier
Unlike C++ destructors, the finalize() method in Java is unpredictable, often dangerous and generally unnecessary.
Use try{} finally{} blocks as discussed in Q32 in Java section & Q45 in Enterprise section. The finalize() method should
only be used in rare instances as a safety net or to terminate non-critical native resources. If you do happen to call the
finalize() method in some rare instances then remember to call the super.finalize() as shown below:
protected void finalize() throws Throwable {
t r y{
/ / f i nal i ze subcl ass st at e
}
f i nal l y {
super.finalize();
}
}
Q 20: When providing a user defined key class for storing objects in the HashMaps or Hashtables, what methods do you
have to provide or override (i.e. method overriding)? LF PI CO FAQ
A 20: You should override the equals() and hashCode() methods from the Object class. The default implementation of
the equals() and hashcode(), which are inherited from the java.lang.Object uses an object instances memory
location (e.g. MyObject@6c60f2ea). This can cause problems when two instances of the car objects have the
same color but the inherited equals() will return false because it uses the memory location, which is different for
Java - Fundamentals
37
the two instances. Also the toString() method can be overridden to provide a proper string representation of your
object.
.
345678965
(hash value for
John)
76854676
(hash value for
Sam)
Map myMap = new HashMap();
myMap.put(John, Sydney);
myMap (HashMap)
Key index
array
hasCode()
1
. c
a
ll
2
. s
t
o
r
e
John etc
list of keys which hash to the
same hash value 345678065.
Because often
two or more
keys can hash
to the same
hash value the
HashMap
maintains a
linked list of
keys that were
mapped to the
same hash
value.
storing value:
myMap.get(John);
retrieving value:
3
.
C
a
l
l
t
o
f
in
d
t
h
e
p
o
s
i
t
io
n
4
. f
o
u
n
d
t
h
e
p
o
s
it
io
n
equals()
5. Loop through list of keys at this position, and use
the equals() m
ethod to see if the key is present
u
s
e
s
If the key is not found (i.e. equals() method returns false for all
items in the list), then it assumes that the key is not present in the
HashMap myMap.
Note: It is simplified for clarity. myMap.containsKey(John) also calls hashCode() & equals() methods. If two keys are equal then they must have
the same hashCode() value, But if two keys have the same hashCode() value does not mean that they are equal.
Sydney etc
List of values for the
corresponding list of keys
Sam etc
Melbourne
etc
hashCode() & equals() methods
List of keys
List of values
Q. What are the primary considerations when implementing a user defined key?
If a class overrides equals(), it must override hashCode().
If 2 objects are equal, then their hashCode values must be equal as well.
If a field is not used in equals(), then it must not be used in hashCode().
If it is accessed often, hashCode() is a candidate for caching to enhance performance.
It is a best practice to implement the user defined key class as an immutable (refer Q21) object.
Q. Why it is a best practice to implement the user defined key class as an immutable object?
Problem: As per the code snippet shown below if you use a mutable user defined class UserKey as a HashMap
key and subsequently if you mutate (i.e. modify via setter method e.g. key. set Name( Sam) ) the key after the
object has been added to the HashMap then you will not be able to access the object later on. The original key
object will still be in the HashMap (i.e. you can iterate through your HashMap and print it both prints as Sam as
opposed to John & Sam) but you cannot access it with map. get ( key) or querying it with
map. cont ai nsKey( key) will return false because the key John becomes Sam in the List of keys at the key
index 345678965 if you mutate the key after adding. These types of errors are very hard to trace and fix.
Map myMap = new HashMap( 10) ;
/ / add t he key J ohn
UserKey key = new UserKey( J ohn) ; / / Assume UserKey cl ass i s mut abl e
myMap. put ( key, Sydney) ;
/ / now t o add t he key Sam
key. setName( Sam) ; / / same key obj ect i s mut at ed i nst ead of cr eat i ng a new i nst ance.
/ / Thi s l i ne modi f i es t he key val ue J ohn t o Sam i n t he List of keys
/ / as shown i n t he di agr amabove. Thi s means t hat t he key J ohn cannot be
/ / accessed. Ther e wi l l be t wo keys wi t h Sam i n posi t i ons wi t h hash
/ / val ues 345678965 and 76854676.
myMap. put ( key, Mel bour ne) ;
myMap. get ( new User Key( J ohn) ) ; / / key cannot be accessed. The key hashes to the same position
/ / 345678965 in the Key index array but cannot be found in the List of keys
Solution: Generally you use a java.lang.Integer or a java.lang.String class as the key, which are immutable Java
objects. If you define your own key class then it is a best practice to make the key class an immutable object (i.e.
do not provide any setXXX() methods in your key class. e.g. no set Name( ) method in the UserKey class). If a
programmer wants to insert a new key then he/she will always have to instantiate a new object (i.e. cannot mutate
the existing key because immutable key object class has no setter methods).
Java - Fundamentals
38
Map myMap = new HashMap( 10) ;
/ / add t he key J ohn
User Key key1 = new User Key( J ohn) ; / / Assume UserKey i s i mmut abl e
myMap. put ( key1, Sydney) ;
/ / add t he key Sam
UserKey key2 = new UserKey(Sam); / / Si nce UserKey i s i mmut abl e, new i nst ance i s cr eat ed.
myMap. put ( key2, Mel bour ne) ;
myMap. get ( new User Key( J ohn) ) ; / / Now the key can be accessed
Similar issues are possible with the Set (e.g. HashSet) as well. If you add an object to a Set and subsequently
modify the added object and later on try to query the original object it may not be present.
mySet . cont ai ns( or i gi nal Obj ect ) may return false.
J2SE 5.0 introduces enumerated constants, which improves readability and maintainability of your code. Java
programming language enums are more powerful than their counterparts in other languages. Example: As shown
below a class like Weather can be built on top of simple enum type Season and the class Weather can be
made immutable, and only one instance of each Weather can be created, so that your Weather class does not
have to override equals() and hashCode() methods.
publ i c cl ass Weat her {
public enum Season {WINTER, SPRING, SUMMER, FALL}
pr i vat e f i nal Season season;
private static final List<Weather> listWeather = new ArrayList<Weather> ();
pr i vat e Weat her ( Season season) { t hi s. season = season; }
publ i c Season get Season ( ) { r et ur n season; }
static {
f or ( Season season : Season. val ues( ) ) { / / usi ng J 2SE 5. 0 f or each l oop
l i st Weat her . add( new Weat her ( season) ) ;
}
}
}
publ i c st at i c Ar r ayLi st <Weat her > get Weat her Li st ( ) { r et ur n listWeather; }
publ i c St r i ng t oSt r i ng( ) { r et ur n season; } / / t akes advant age of t oSt r i ng( ) met hod of Season.
}
Q 21: What is the main difference between a String and a StringBuffer class? LF PI CI CO FAQ
A 21:
String StringBuffer / StringBuilder (added in J2SE 5.0)
String is immutable: you cant modify a string
object but can replace it by creating a new
instance. Creating a new instance is rather
expensive.
//Inefficient version using immutable String
String output = Some text
Int count = 100;
for(int i =0; i<count; i++) {
output += i;
}
return output;
The above code would build 99 new String
objects, of which 98 would be thrown away
immediately. Creating new objects is not
efficient.
StringBuffer is mutable: use StringBuffer or StringBuilder when you want
to modify the contents. StringBuilder was added in Java 5 and it is
identical in all respects to StringBuffer except that it is not synchronized,
which makes it slightly faster at the cost of not being thread-safe.
//More efficient version using mutable StringBuffer
StringBuffer output = new StringBuffer(110);// set an initial size of 110
output.append(Some text);
for(int i =0; i<count; i++) {
output.append(i);
}
return output.toString();
The above code creates only two new objects, the StringBuffer and the
final String that is returned. StringBuffer expands as needed, which is
costly however, so it would be better to initialize the StringBuffer with the
correct size from the start as shown.
Another important point is that creation of extra strings is not limited to overloaded mathematical operator + but
there are several methods like concat(), trim(), substring(), and replace() in String classes that generate new
string instances. So use StringBuffer or StringBuilder for computation intensive operations, which offer better
performance.
Q. What is an immutable object? Immutable objects whose state (i.e. the objects data) does not change once it is
instantiated (i.e. it becomes a read-only object after instantiation). Immutable classes are ideal for representing
Java - Fundamentals
39
numbers (e.g. java.lang.Integer, java.lang.Float, java.lang.BigDecimal etc are immutable objects), enumerated
types, colors (e.g. java.awt.Color is an immutable object), short lived objects like events, messages etc.
Q. What are the benefits of immutable objects?
Immutable classes can greatly simplify programming by freely allowing you to cache and share the references to
the immutable objects without having to defensively copy them or without having to worry about their values
becoming stale or corrupted.
Immutable classes are inherently thread-safe and you do not have to synchronize access to them to be used in a
multi-threaded environment. So there is no chance of negative performance consequences.
Eliminates the possibility of data becoming inaccessible when used as keys in HashMaps or as elements in
Sets. These types of errors are hard to debug and fix. Refer Q20 in Java section under Q. Why it is a best
practice to implement the user defined key class as an immutable object?
Q. How will you write an immutable class? CO
Writing an immutable class is generally easy but there can be some tricky situations. Follow the following guidelines:
1. A class is declared final (i.e. final classes cannot be extended).
publ i c f i nal cl ass MyI mmut abl e { }
2. All its fields are final (final fields cannot be mutated once assigned).
pr i vat e f i nal i nt [ ] myAr r ay; //do not declare as private final int[] myArray = null;
3. Do not provide any methods that can change the state of the immutable object in any way not just setXXX
methods, but any methods which can change the state.
4. The this reference is not allowed to escape during construction from the immutable class and the immutable
class should have exclusive access to fields that contain references to mutable objects like arrays, collections
and mutable classes like Date etc by:
Declaring the mutable references as private.
Not returning or exposing the mutable references to the caller (this can be done by defensive copying)
Wrong way to write an immutable class Right way to write an immutable class
Wrong way to write a constructor:
public final class MyImmutable {
private final int[] myArray;
public MyImmutable(int[] anArray) {
this.myArray = anArray; // wrong
}
public String toString() {
StringBuffer sb = new StringBuffer("Numbers are: ");
for (int i = 0; i < myArray.length; i++) {
sb.append(myArray[i] + " ");
}
return sb.toString();
}
}
// the caller could change the array after calling the
constructor.
int[] array = {1,2};
MyImmutable myImmutableRef = new MyImmutable(array) ;
System.out.println("Before constructing " + myImmutableRef);
array[1] = 5; // change (i.e. mutate) the element
System.out.println("After constructing " + myImmutableRef);
Out put:
Bef or e const r uct i ng Number s ar e: 1 2
Right way is to copy the array before assigning in the constructor.
public final class MyImmutable {
private final int[] myArray;
public MyImmutable(int[] anArray) {
this.myArray = anArray.clone(); // defensive copy
}
public String toString() {
StringBuffer sb = new StringBuffer("Numbers are: ");
for (int i = 0; i < myArray.length; i++) {
sb.append(myArray[i] + " ");
}
return sb.toString();
}
}
// the caller cannot change the array after calling the constructor.
int[] array = {1,2};
MyImmutable myImmutableRef = new MyImmutable(array) ;
System.out.println("Before constructing " + myImmutableRef);
array[1] = 5; // change (i.e. mutate) the element
System.out.println("After constructing " + myImmutableRef);
Out put:
Before constructing Numbers are: 1 2
Java - Fundamentals
40
Af t er const r uct i ng Number s ar e: 1 5
As you can see in the output that the MyImmutable object
has been mutated. This is because the object reference gets
copied as discussed in Q22 in Java section.
After constructing Numbers are: 1 2
As you can see in the output that the MyImmutable object has not
been mutated.
Wrong way to write an accessor. A caller could get the array
reference and then change the contents:
publ i c i nt [ ] get Ar r ay( ) {
r et ur n myAr r ay;
}
Right way to write an accessor by cloning.
publ i c i nt [ ] get Ar ay( ) {
r et ur n ( i nt [ ] ) myArray.clone();
}
Important: Beware of using the clone() method on a collection like a Map, List, Set etc because they are not only difficult
to implement correctly refer Q19 in Java section but also the default behavior of an objects clone() method automatically
yields a shallow copy. You have to deep copy the mutable objects referenced by your immutable class. Refer Q26 in Java
section for deep vs. shallow cloning and Q22 in Java section for why you will be modifying the original object if you do not
deep copy.
Q. How would you defensively copy a Date field in your immutable class?
public final class MyDiary {
private Date myDate = null;
public MyDiary(Date aDate){
this.myDate = new Date(aDate.getTime()); // defensive copying by not exposing the myDate reference
}
public Date getDate() {
return new Date(myDate.getTime); // defensive copying by not exposing the myDate reference
}
}
Q 22: What is the main difference between pass-by-reference and pass-by-value? LF PI FAQ
A 22: Other languages use pass-by-reference or pass-by-pointer. But in Java no matter what type of argument you
pass the corresponding parameter (primitive variable or object reference) will get a copy of that data, which is
exactly how pass-by-value (i.e. copy-by-value) works.
In Java, if a calling method passes a reference of an object as an argument to the called method then the passed-
in reference gets copied first and then passed to the called method. Both the original reference that was
passed-in and the copied reference will be pointing to the same object. So no matter which reference you use, you
will be always modifying the same original object, which is how the pass-by-reference works as well.
ref d
Pass-by-value for primitive variables vs Object references
public void first(){
int i= 10;
int x = second(i);
//At this point
//value of i is still 10
//value of x is 11
}
public int second(int k) {
k++;
return k ;
}
i = 10
k = 10
k = 11
Copy of i
stores i
c
o
p
ie
s
i
acts on k
r
e
f
public void first(){
Car c = new Car("red")
//At this point
//color is Red
second(c);
//At this point
//color is Blue
}
public void second(Car d)
{
d.setColor(blue);
//color is blue
}
Car object
String color = red
ref c
c
o
p
y
o
f
c
Primitive variables Object references
modifies the original
object through copied
reference
modifies the copy k
but not the original.
Changes
color = blue
If your method call involves inter-process (e.g. between two JVMs) communication, then the reference of the
calling method has a different address space to the called method sitting in a separate process (i.e. separate
Java - Fundamentals
41
JVM). Hence inter-process communication involves calling method passing objects as arguments to called method
by-value in a serialized form, which can adversely affect performance due to marshaling and unmarshaling cost.
Note: As discussed in Q69 in Enterprise section, EJB 2.x introduced local interfaces, where enterprise beans that can be used
locally within the same JVM using Javas form of pass-by-reference, hence improving performance.
Q 23: What is serialization? How would you exclude a field of a class from serialization or what is a transient variable?
What is the common use? What is a serial version id? LF SI PI FAQ
A 23: Serialization is a process of reading or writing an object. It is a process of saving an objects state to a sequence of
bytes, as well as a process of rebuilding those bytes back into a live object at some future time. An object is
marked serializable by implementing the java.io.Serializable interface, which is only a marker interface -- it simply
allows the serialization mechanism to verify that the class can be persisted, typically to a file.
Transient variables cannot be serialized. The fields marked transient in a serializable object will not be
transmitted in the byte stream. An example would be a file handle, a database connection, a system thread etc.
Such objects are only meaningful locally. So they should be marked as transient in a serializable class.
byte stream
w
r
it
e
t
o
Serialization
File
class Car implements Serializable {
String color = null;
transient File fh = null;
}
Car Object1
Class Car implements
Serializable {
String color =null;
}
Car Object 2
deserialize
serialize
d
e
s
e
r
ia
l
iz
e
Serialization can adversely affect performance since it:
Depends on reflection.
Has an incredibly verbose data format.
Is very easy to send surplus data.
Q. When to use serialization? Do not use serialization if you do not have to. A common use of serialization is to
use it to send an object over the network or if the state of an object needs to be persisted to a flat file or a
database. (Refer Q57 on Enterprise section). Deep cloning or copy can be achieved through serialization. This
may be fast to code but will have performance implications (Refer Q26 in Java section).
To serialize the above Car object to a file (sample for illustration purpose only, should use try {} catch {} block):
Car car = new Car ( ) ; // The Car class implements a java.io.Serializable interface
Fi l eOut put St r eamf os = new Fi l eOut put St r eam( f i l ename) ;
Obj ect Out put St r eamout = new Obj ect Out put St r eam( f os) ;
out.writeObject(car); / / ser i al i zat i on mechani smhappens her e
out . cl ose( ) ;
The objects stored in an HTTP session should be serializable to support in-memory replication of sessions to
achieve scalability (Refer Q20 in Enterprise section). Objects are passed in RMI (Remote Method Invocation)
across network using serialization (Refer Q57 in Enterprise section).
Q. What is Java Serial Version ID? Say you create a Car class, instantiate it, and write it out to an object
stream. The flattened car object sits in the file system for some time. Meanwhile, if the Car class is modified by
adding a new field. Later on, when you try to read (i.e. deserialize) the flattened Car object, you get the
java.io.InvalidClassException because all serializable classes are automatically given a unique identifier. This
exception is thrown when the identifier of the class is not equal to the identifier of the flattened object. If you really
think about it, the exception is thrown because of the addition of the new field. You can avoid this exception being
thrown by controlling the versioning yourself by declaring an explicit serialVersionUID. There is also a small
Java - Fundamentals
42
performance benefit in explicitly declaring your serialVersionUID (because does not have to be calculated). So, it
is best practice to add your own serialVersionUID to your Serializable classes as soon as you create them as
shown below:
publ i c cl ass Car {
static final long serialVersionUID = 1L; / / assi gn a l ong val ue
}
Note: Alternatively you can use the serialver tool comes with Suns JDK. This tool takes a full class name on the
command line and returns the serialVersionUID for that compiled class. For example:
static final long serialVersionUID = 10275439472837494L; / / gener at ed by ser i al ver t ool .
Q 24: Explain the Java I/O streaming concept and the use of the decorator design pattern in Java I/O? LF DP PI SI
A 24: Java input and output is defined in terms of an abstract concept called a stream, which is a sequence of data.
There are 2 kinds of streams.
Byte streams (8 bit bytes) Abstract classes are: InputStream and OutputStream
Character streams (16 bit UNICODE) Abstract classes are: Reader and Writer
Design pattern: java.io.* classes use the decorator design pattern. The decorator design pattern attaches
responsibilities to objects at runtime. Decorators are more flexible than inheritance because the inheritance
attaches responsibility to classes at compile time. The java.io.* classes use the decorator pattern to construct
different combinations of behavior at runtime based on some basic classes.
Attaching responsibilities to classes at
compile time using subclassing.
Attaching responsibilities to objects at runtime using a decorator
design pattern.
Inheritance (aka subclassing) attaches
responsibilities to classes at compile time. When
you extend a class, each individual changes you
make to child class will affect all instances of the
child classes. Defining many classes using
inheritance to have all possible combinations is
problematic and inflexible.
By attaching responsibilities to objects at runtime, you can apply changes
to each individual object you want to change.
Fi l e file = new Fi l e( c: / t emp) ;
Fi l eI nput St r eamfis = new Fi l eI nput St r eam( file) ;
Buf f er edI nput St r eambi s = new Buf f er edI nput St r eam( fis) ;
Decorators decorate an object by enhancing or restricting functionality of
an object it decorates. The decorators add or restrict functionality to
decorated objects either before or after forwarding the request. At runtime
the BufferedInputStream (bis), which is a decorator (aka a wrapper
around decorated object), forwards the method call to its decorated object
FileInputStream (fis). The bis will apply the additional functionality of
buffering around the lower level file (i.e. fis) I/O.
java.io.* class hierarchy
java.lang.Object
java.io.InputStream java.io.OutputStream java.lang.System java.io.Reader java.io.Writer
java.io.FileInputStream java.io.FileOutputStream
java.io.InputStreamReader java.io.OutputStreamWriter
java.io.FileReader java.io.FileWriter java.io.BufferedReader
Note: Only a few subclasses of abstract classes are
shown for clarity.
Q. How does the new I/O (NIO) offer better scalability and better performance?
Java - Fundamentals
43
Java has long been not suited for developing programs that perform a lot of I/O operations. Furthermore,
commonly needed tasks such as file locking, non-blocking and asynchronous I/O operations and ability to map file
to memory were not available. Non-blocking I/O operations were achieved through work around such as
multithreading or using JNI. The New I/O API (aka NIO) in J2SE 1.4 has changed this situation.
A servers ability to handle several client requests effectively depends on how it uses I/O streams. When a server
has to handle hundreds of clients simultaneously, it must be able to use I/O services concurrently. One way to
cater for this scenario in Java is to use threads but having almost one-to-one ratio of threads (100 clients will have
100 threads) is prone to enormous thread overhead and can result in performance and scalability problems
due to consumption of memory stacks (i.e. each thread has its own stack. Refer Q34, Q42 in Java section) and
CPU context switching (i.e. switching between threads as opposed to doing real computation.). To overcome this
problem, a new set of non-blocking I/O classes have been introduced to the Java platform in java.nio package.
The non-blocking I/O mechanism is built around Selectors and Channels. Channels, Buffers and Selectors are
the core of the NIO.
Non-blocking I/O (i.e. New I/O)
Client-1
Client-2
Client-3
S
e
l
e
c
t
o
r
Channel
Channel
Channel
Key-5
client-2
Key-4
client-1
Key-3
client-3
Key-2
client-2
Key-1
client-1
Process
Demultiplexing
Multiplexing
sequentially processed
(unlike threads, no context switching and separate
stack allocations are required )
A Channel class represents a bi-directional communication channel (similar to InputStream and OutputStream)
between datasources such as a socket, a file, or an application component, which is capable of performing one or
more I/O operations such as reading or writing. Channels can be non-blocking, which means, no I/O operation will
wait for data to be read or written to the network. The good thing about NIO channels is that they can be
asynchronously interrupted and closed. So if a thread is blocked in an I/O operation on a channel, another thread
can interrupt that blocked thread.
A Selector class enables multiplexing (combining multiple streams into a single stream) and demultiplexing
(separating a single stream into multiple streams) I/O events and makes it possible for a single thread to efficiently
manage many I/O channels. A Selector monitors selectable channels, which are registered with it for I/O events
like connect, accept, read and write. The keys (i.e. Key1, Key2 etc represented by the SelectionKey class)
encapsulate the relationship between a specific selectable channel and a specific selector.
Buffers hold data. Channels can fill and drain Buffers. Buffers replace the need for you to do your own buffer
management using byte arrays. There are different types of Buffers like ByteBuffer, CharBuffer, DoubleBuffer, etc.
Design pattern: NIO uses a reactor design pattern, which demultiplexes events (separating single stream into
multiple streams) and dispatches them to registered object handlers. The reactor pattern is similar to an observer
pattern (aka publisher and subscriber design pattern), but an observer pattern handles only a single source of
events (i.e. a single publisher with multiple subscribers) where a reactor pattern handles multiple event sources
(i.e. multiple publishers with multiple subscribers). The intent of an observer pattern is to define a one-to-many
dependency so that when one object (i.e. the publisher) changes its state, all its dependents (i.e. all its
subscribers) are notified and updated correspondingly.
Another sought after functionality of NIO is its ability to map a file to memory. There is a specialized form of a
Buffer known as MappedByteBuffer, which represents a buffer of bytes mapped to a file. To map a file to
MappedByteBuffer, you must first get a channel for a file. Once you get a channel then you map it to a buffer and
subsequently you can access it like any other ByteBuffer. Once you map an input file to a CharBuffer, you can
do pattern matching on the file contents. This is similar to running grep on a UNIX file system.
Java - Fundamentals
44
Another feature of NIO is its ability to lock and unlock files. Locks can be exclusive or shared and can be held
on a contiguous portion of a file. But file locks are subject to the control of the underlying operating system.
Q 25: How can you improve Java I/O performance? PI BP
A 25: Java applications that utilize Input/Output are excellent candidates for performance tuning. Profiling of Java
applications that handle significant volumes of data will show significant time spent in I/O operations. This means
substantial gains can be had from I/O performance tuning. Therefore, I/O efficiency should be a high priority for
developers looking to optimally increase performance.
The basic rules for speeding up I/O performance are
Minimize accessing the hard disk.
Minimize accessing the underlying operating system.
Minimize processing bytes and characters individually.
Let us look at some of the techniques to improve I/O performance. CO
Use buffering to minimize disk access and underlying operating system. As shown below, with buffering
large chunks of a file are read from a disk and then accessed a byte or character at a time.
Without buffering : inefficient code
try{
File f = new File("myFile.txt");
FileInputStream fis = new FileInputStream(f);
int count = 0;
int b = 0;
while((b = fis.read()) != -1){
if(b== '\n') {
count++;
}
}
// fis should be closed in a finally block.
fis.close() ;
}
catch(IOException io){}
Note: fis.read() is a native method call to the
underlying operating system.
With Buffering: yields better performance
try{
File f = new File("myFile.txt");
FileInputStream fis = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(fis);
int count = 0;
int b = 0 ;
while((b = bis.read()) != -1){
if(b== '\n') {
count++;
}
}
//bis should be closed in a finally block.
bis.close() ;
}
catch(IOException io){}
Note: bis.read() takes the next byte from the input buffer and only
rarely access the underlying operating system.
Instead of reading a character or a byte at a time, the above code with buffering can be improved further by
reading one line at a time as shown below:
Fi l eReader f r = new Fi l eReader ( f ) ;
Buf f er edReader br = new Buf f er edReader ( f r ) ;
whi l e ( br . r eadLi ne( ) ! = nul l ) count ++;
By default the System.out is line buffered, which means that the output buffer is flushed when a new line
character (i.e. \n) is encountered. This is required for any interactivity between an input prompt and display
of output. The line buffering can be disabled for faster I/O operation as follows:
Fi l eOut put St r eamf os = new Fi l eOut put St r eam( f i l e) ;
Buf f er edOut put St r eambos = new Buf f er edOut put St r eam( f os, 1024) ;
Pr i nt St r eamps = new Pr i nt St r eam( bos, f al se) ;
// To redirect standard output to a file instead of the System console which is the default for both System.out (i.e.
// standard output) and System.err (i.e. standard error device) variables
System.setOut(ps);
whi l e ( someCondi t i onI sTr ue)
Syst em. out . pr i nt l n( bl ahbl ah) ;
}
Java - Fundamentals
45
It is recommended to use logging frameworks like Log4J with SLF4J (Simple Logging Faade for Java),
which uses buffering instead of using default behavior of System.out.println(..) for better performance.
Frameworks like Log4J are configurable, flexible, extensible and easy to use.
Use the NIO package, if you are using JDK 1.4 or later, which uses performance-enhancing features like
buffers to hold data, memory mapping of files, non-blocking I/O operations etc.
I/O performance can be improved by minimizing the calls to the underlying operating systems. The Java
runtime itself cannot know the length of a file, querying the file system for isDirectory(), isFile(), exists() etc
must query the underlying operating system.
Where applicable caching can be used to improve performance by reading in all the lines of a file into a Java
Collection class like an ArrayList or a HashMap and subsequently access the data from an in-memory
collection instead of the disk.
Q 26: What is the main difference between shallow cloning and deep cloning of objects? DC LF MI PI
A 26: The default behavior of an objects clone() method automatically yields a shallow copy. So to achieve a deep copy
the classes must be edited or adjusted.
Shallow copy: If a shallow copy is performed on obj-1 as shown in fig-2 then it is copied but its contained objects
are not. The contained objects Obj-1 and Obj-2 are affected by changes to cloned Obj-2. Java supports shallow
cloning of objects by default when a class implements the java.lang.Cloneable interface.
Deep copy: If a deep copy is performed on obj-1 as shown in fig-3 then not only obj-1 has been copied but the
objects contained within it have been copied as well. Serialization can be used to achieve deep cloning. Deep
cloning through serialization is faster to develop and easier to maintain but carries a performance overhead.
Fig-2:Shallow cloning
Fig-3:Deep cloning
Shallow Vs Deep cloning
Obj-1
contained
Obj-1
contained
Obj-2
Fig-1:Original Object
contains contains
Obj-1
Cloned
Obj-2
contained
Obj-1
contained
Obj-2
Obj-1
contained
Obj-1
contained
Obj-2
Cloned
Obj-2
contained
Obj-1
contained
Obj-2
For example invoking clone() method on a collection like HashMap, List etc returns a shallow copy of HashMap,
List, instances. This means if you clone a HashMap, the map instance is cloned but the keys and values
themselves are not cloned. If you want a deep copy then a simple method is to serialize the HashMap to a
ByteArrayOutputSream and then deserialize it. This creates a deep copy but does require that all keys and values
in the HashMap are Serializable. Main advantage of this approach is that it will deep copy any arbitrary object
graph. Refer Q23 in Java section for deep copying using Serialization. Alternatively you can provide a static
factory method to deep copy. Example: to deep copy a list of Car objects.
publ i c st at i c Li st deepCopy( Li st l i st Car s) {
Li st copiedList = new Ar r ayLi st ( 10) ;
f or ( Obj ect obj ect : l i st Car s) { / / J DK 1. 5 f or each l oop
Car or i gi nal = ( Car ) obj ect ;
Car carCopied = new Car ( ) ; / / i nst ant i at e a new Car obj ect
carCopied. set Col or ( ( or i gi nal . get Col or ( ) ) ) ;
copiedList. add( carCopied) ;
}
r et ur n copi edLi st ;
}
Java - Fundamentals
46
Q 27: What is the difference between an instance variable and a static variable? How does a local variable compare to
an instance or a static variable? Give an example where you might use a static variable? LF FAQ
A 27:
Static variables Instance variables
Class variables are called static variables. There is only one
occurrence of a class variable per JVM per class loader.
When a class is loaded the class variables (aka static
variables) are initialized.
Instance variables are non-static and there is one
occurrence of an instance variable in each class instance
(i.e. each object). Also known as a member variable or a
field.
A static variable is used in the singleton pattern. (Refer Q51 in Java section). A static variable is used with a final
modifier to define constants.
Local variables Instance and static variables
Local variables have a narrower scope than instance
variables.
Instance variables have a narrower scope than static
variables.
The lifetime of a local variable is determined by execution
path and local variables are also known as stack variables
because they live on the stack. Refer Q34 for stack & heap.
Instance and static variables are associated with objects and
therefore live in the heap. Refer Q34 in Java section for
stack & heap.
For a local variable, it is illegal for code to fail to assign it a
value. It is the best practice to declare local variables only
where required as opposed to declaring them upfront and
cluttering up your code with some local variables that never
get used.
Both the static and instance variables always have a value. If
your code does not assign them a value then the run-time
system will implicitly assign a default value (e.g.
null/0/0.0/false).
Note: Java does not support global, universally accessible variables. You can get the same sorts of effects with classes that
have static variables.
Q 28: Give an example where you might use a static method? LF FAQ
A 28: Static methods prove useful for creating utility classes, singleton classes and factory methods (Refer Q51,
Q52 in Java section). Utility classes are not meant to be instantiated. Improper coding of utility classes can lead to
procedural coding. java.lang.Math, java.util.Collections etc are examples of utility classes in Java.
Q 29: What are access modifiers? LF FAQ
A 29:
Modifier Used with Description
public Outer classes, interfaces,
constructors, Inner classes, methods
and field variables
A class or interface may be accessed from outside the
package. Constructors, inner classes, methods and field
variables may be accessed wherever their class is
accessed.
protected Constructors, inner classes, methods,
and field variables.
Accessed by other classes in the same package or any
subclasses of the class in which they are referred (i.e. same
package or different package).
private Constructors, inner classes,
methods and field variables,
Accessed only within the class in which they are declared
No modifier:
(Package by
default).
Outer classes, inner classes,
interfaces, constructors, methods, and
field variables
Accessed only from within the package in which they are
declared.
Q 30: Where and how can you use a private constructor? LF FAQ
A 30: Private constructor is used if you do not want other classes to instantiate the object and to prevent subclassing.
The instantiation is done by a public static method (i.e. a static factory method) within the same class.
Used in the singleton design pattern. (Refer Q51 in Java section).
Used in the factory method design pattern (Refer Q52 in Java section). e.g. java.util.Collections class (Refer
Q16 in Java section).
Used in utility classes e.g. StringUtils etc.
Q 31: What is a final modifier? Explain other Java modifiers? LF FAQ
A 31: A final class cant be extended i.e. A final class can not be subclassed. A final method cant be overridden when its
class is inherited. You cant change value of a final variable (i.e. it is a constant).
Java - Fundamentals
47
Modifier Class Method Variable
static A static inner class is just an inner
class associated with the class,
rather than with an instance of the
class.
A static method is called by classname.method
(e.g Math.random()), can only access static
variables.
Class variables are
called static variables.
There is only one
occurrence of a class
variable per JVM per
class loader.
abstract An abstract class cannot be
instantiated, must be a superclass
and a class must be declared
abstract whenever one or more
methods are abstract.
Method is defined but contains no
implementation code (implementation code is
included in the subclass). If a method is
abstract then the entire class must be abstract.
N/A
synchronized N/A Acquires a lock on the class for static
methods.
Acquires a lock on the instance for non-
static methods.
N/A
transient N/A N/A
variable should not be
serialized.
final Class cannot be inherited (i.e.
extended)
Method cannot be overridden. Makes the variable
immutable.
native N/A Platform dependent. No body, only signature. N/A
Note: Be prepared for tricky questions on modifiers like, what is a volatile? Or what is a const? Etc. The
reason it is tricky is that Java does have these keywords const and volatile as reserved, which means you cant
name your variables with these names but modifier const is not yet added in the language and the modifier
volatile is very rarely used.
The volatile modifier is used on instance variables that may be modified simultaneously by other threads. The
modifier volatile only synchronizes the variable marked as volatile whereas synchronized modifier synchronizes
all variables. Since other threads cannot see local variables, there is no need to mark local variables as volatile.
For example:
volatile i nt number ;
volatile pr i vat e Li st l i st I t ems = nul l ;
Java uses the final modifier to declare constants. A final variable or constant declared as final has a value that
is immutable and cannot be modified to refer to any other objects other than one it was initialized to refer to. So
the final modifier applies only to the value of the variable itself, and not to the object referenced by the variable.
This is where the const modifier can come in very useful if added to the Java language. A reference variable
or a constant marked as const refers to an immutable object that cannot be modified. The reference variable
itself can be modified, if it is not marked as final. The const modifier will be applicable only to non-primitive
types. The primitive types should continue to use the modifier final.
Q. If you want to extend the java.lang.String class, what methods will you override in your extending
class?
You would be tempted to say equals(), hashCode() and toString() based on Q19, Q20 in Java section but the
java.lang.String class is declared final and therefore it cannot be extended.
Q 32: What is the difference between final, finally and finalize() in Java? LF FAQ
A 32:
final - constant declaration. Refer Q31 in Java section.
finally - handles exception. The finally block is optional and provides a mechanism to clean up regardless of
what happens within the try block (except System.exit(0) call). Use the finally block to close files or to release
other system resources like database connections, statements etc. (Refer Q45 in Enterprise section)
finalize() - method helps in garbage collection. A method that is invoked before an object is discarded by the
garbage collector, allowing it to clean up its state. Should not be used to release non-memory resources like
file handles, sockets, database connections etc because Java has only a finite number of these resources and
you do not know when the garbage collection is going to kick in to release these non-memory resources
through the finalize() method. Refer Q19 in Java Section.
Q 33: Why would you prefer a short circuit &&, || operators over logical & , | operators? LF
A 33: Firstly NullPointerException is by far the most common RuntimeException. If you use the logical operator you can
get a NullPointerException. This can be avoided easily by using a short circuit && operator as shown below.
Java - Fundamentals
48
There are other ways to check for null but short circuit && operator can simplify your code by not having to declare
separate if clauses.
i f ( ( obj ! = nul l ) & obj . equal s( newObj ) ) { / / can cause a NullPointerException i f obj == nul l
. . . / / because obj . equal s( newObj ) i s al ways execut ed.
}
Short-circuiting means that an operator only evaluates as far as it has to, not as far as it can. If the variable 'obj'
equals null, it won't even try to evaluate the 'obj.equals(newObj) clause as shown in the following example. This
protects the potential NullPointerException.
i f ( ( obj ! = nul l ) && obj . equal s( newObj ) ) { / / cannot get a NullPointerException because
. . . / / obj . equal s( newObj ) i s execut ed onl y i f obj ! = nul l
}
Secondly, short-circuit && and || operators can improve performance in certain situations. For example:
i f ( ( number <= 7) | | ( doComputeIntensiveAnalysis( number ) <= 13) ) { / / t he CPU i nt ensi ve
. . . . / / comput at i onal met hod i n bol d i s execut ed onl y i f number > 7.
}
Q 34: How does Java allocate stack and heap memory? Explain re-entrant, recursive and idempotent
methods/functions? MI CI
A 34: Each time an object is created in Java it goes into the area of memory known as heap. The primitive variables like
int and double are allocated in the stack (i.e. Last In First Out queue), if they are local variables and in the heap if
they are member variables (i.e. fields of a class). In Java methods and local variables are pushed into stack when
a method is invoked and stack pointer is decremented when a method call is completed. In a multi-threaded
application each thread will have its own stack but will share the same heap. This is why care should be taken in
your code to avoid any concurrent access issues in the heap space. The stack is thread-safe because each thread
will have its own stack with say 1MB RAM allocated for each thread but the heap is not thread-safe unless
guarded with synchronization through your code. The stack space can be increased with the Xss option.
J a v a s t a c k & h e a p m e m o r y a l l o c a t i o n
S t a c k
H e a p
p u b l i c c l a s s S t a c k R e f {
p u b l i c v o i d f i r s t ( ) {
s e c o n d ( ) ;
/ / a f t e r
}
p u b l i c v o i d s e c o n d ( ) {
C a r c = n e w C a r ( ) ;
}
}
f i r s t ( )
s e c o n d ( )
f i r s t ( )
s e c o n d ( ) r e f c
f i r s t ( )
C a r
1
2
3
4
R e f
p u b l i c c l a s s H e a p R e f {
C a r c = n e w C a r ( ) ;
p u b l i c v o i d f i r s t ( ) {
c = n e w C a r ( ) ;
}
}
C a r
c
R e f
C a r
c
C a r
2
1
Java - Fundamentals
49
All Java methods are automatically re-entrant. It means that several threads can be executing the same method
at once, each with its own copy of the local variables. A Java method may call itself without needing any special
declarations. This is known as a recursive method call. Given enough stack space, recursive method calls are
perfectly valid in Java though it is tough to debug. Recursive methods are useful in removing iterations from many
sorts of algorithms. All recursive functions are re-entrant but not all re-entrant functions are recursive. Idempotent
methods are methods, which are written in such a way that repeated calls to the same method with the same
arguments yield same results. For example clustered EJBs, which are written with idempotent methods, can
automatically recover from a server failure as long as it can reach another server (i.e. scalable).
Q 35: Explain Outer and Inner classes (or Nested classes) in Java? When will you use an Inner Class? LF SE
A 35: In Java not all classes have to be defined separate from each other. You can put the definition of one class inside
the definition of another class. The inside class is called an inner class and the enclosing class is called an outer
class. So when you define an inner class, it is a member of the outer class in much the same way as other
members like attributes, methods and constructors.
Q. Where should you use inner classes? Code without inner classes is more maintainable and readable.
When you access private data members of the outer class, the JDK compiler creates package-access member
functions in the outer class for the inner class to access the private members. This leaves a security hole. In
general we should avoid using inner classes. Use inner class only when an inner class is only relevant in the
context of the outer class and/or inner class can be made private so that only outer class can access it. Inner
classes are used primarily to implement helper classes like Iterators, Comparators etc which are used in the
context of an outer class. CO
Member inner class Anonymous inner class
public class MyStack {
private Object[] items = null;
public Iterator iterator() {
return new StackIterator();
}
//inner class
class StackIterator implements Iterator{
public boolean hasNext(){}
}
}
public class MyStack {
private Object[] items = null;
public Iterator iterator() {
return new Iterator {
public boolean hasNext() {}
}
}
}
Explain outer and inner classes?
Class Type Description Example + Class name
Outer
class
Package
member class
or interface
Top level class. Only type JVM
can recognize.
//package scope
class Outside{}
Outside.class
Inner
class
static nested
class or
interface
Defined within the context of the
top-level class. Must be static &
can access static members of its
containing class. No relationship
between the instances of outside
and Inside classes.
//package scope
class Outside {
static class Inside{ }
}
Outside.class ,Outside$Inside.class
Inner
class
Member class Defined within the context of
outer class, but non-static. Until
an object of Outside class has
been created you cant create
Inside.
class Outside{
class Inside(){}
}
Outside.class , Outside$Inside.class
Inner
class
Local class Defined within a block of code.
Can use final local variables and
final method parameters. Only
visible within the block of code
that defines it.
class Outside {
void first() {
final int i = 5;
class Inside{}
}
}
Outside.class , Outside$1$Inside.class
Java - Fundamentals
50
Inner
class
Anonymous
class
Just like local class, but no
name is used. Useful when only
one instance is used in a
method. Most commonly used in
AWT/SWING event model,
Spring framework hibernate call
back methods etc.
//AWT example
class Outside{
void first() {
button.addActionListener ( new ActionListener()
{
public void actionPerformed(ActionEvent e) {
System.out.println(The button was pressed!);
}
});
}
}
Outside.class , Outside$1.class
Note: If you have used the Spring framework with the Hibernate framework (Both are very popular frameworks,
Refer section Emerging Technologies/Frameworks), it is likely that you would have used an anonymous inner
class (i.e. a class declared inside a method) as shown below:
/ / anonymous i nner cl asses can onl y access l ocal var i abl es i f t hey ar e decl ar ed as final
publ i c Pet get Pet ByI d( final St r i ng i d) {
r et ur n ( Pet ) get Hi ber nat eTempl at e( ) . execut e( new HibernateCallback() {
publ i c Obj ect doI nHi ber nat e( Sessi on sessi on) {
Hi ber nat eTempl at e ht = get Hi ber nat eTempl at e( ) ;
/ / can access var i abl e id
r et ur n myPet ;
}
}) ;
}
Q. Are the following valid java statements?
Line: Out er Cl ass. St at i cNest edCl ass nest edObj ect = new Out er Cl ass. St at i cNest edCl ass( ) ;
Yes. The above line is valid. It is an instantiation of a static nested inner class.
Out er Cl ass out er Obj ect = new Out er Cl ass( ) ;
Line: Out er Cl ass. I nner Cl ass i nner Obj ect = out er Obj ect . new I nner Cl ass( ) ;
Yes. The above line is valid. It is an instantiation of a member inner class. An instance of an inner class can exist
only within an instance of an outer class. The sample code for the above is shown below:
publ i c cl ass OuterClass {
st at i c cl ass StaticNestedClass {
St at i cNest edCl ass( ) {
Syst em. out . pr i nt l n( " St at i cNest edCl ass" ) ;
}
}
cl ass InnerClass {
I nner Cl ass( ) {
Syst em. out . pr i nt l n( " I nner Cl ass" ) ;
}
}
}
Q 36: What is type casting? Explain up casting vs. down casting? When do you get ClassCastException? LF DP FAQ
A 36: Type casting means treating a variable of one type as though it is another type.
When up casting primitives as shown below from left to right, automatic conversion occurs. But if you go from
right to left, down casting or explicit casting is required. Casting in Java is safer than in C or other languages that
allow arbitrary casting. Java only lets casts occur when they make sense, such as a cast between a float and an
int. However you can't cast between an int and a String (is an object in Java).
byte short int long float double
int i = 5;
long j = i ; / / Right. Up cast i ng or i mpl i ci t cast i ng
byte b1 = i ; / / Wrong. Compi l e t i me er r or Type Mi smat ch.
byte b2 = ( byte) i ; / / Right. Down cast i ng or expl i ci t cast i ng i s r equi r ed.
Java - Fundamentals
51
When it comes to object references you can always cast from a subclass to a superclass because a subclass
object is also a superclass object. You can cast an object implicitly to a super class type (i.e. upcasting). If this
were not the case polymorphism wouldnt be possible.
Object
Vehicle
Car Bus
BMW
Vehicle v1 = new Car(); //Right.upcasting or implicit casting
Vehicle v2 = new Vehicle();
Car c0 = v1; //Wrong. compile time error "Type Mismatch".
//Explicit or down casting is required
Car c1 = (Car)v1; // Right. down casting or explicit casting.
// v1 has knowledge of Car due to line1
Car c2 = (Car)v2; //Wrong. Runtime exception ClassCastException
//v2 has no knowledge of Car.
Bus b1 = new BMW(); //Wrong. compile time error "Type Mismatch"
Car c3 = new BMW(); //Right.upcasting or implicit casting
Car c4 = (BMW)v1; //Wrong. Runtime exception ClassCastException
Object o = v1; //v1 can only be upcast to its parent or
Car c5 = (Car)v1; //v1 can be down cast to Car due to line 1.
Upcasting vs Downcasting
You can cast down the hierarchy as well but you must explicitly write the cast and the object must be a
legitimate instance of the class you are casting to. The ClassCastException is thrown to indicate that code
has attempted to cast an object to a subclass of which it is not an instance. If you are using J2SE 5.0 then
generics will eliminate the need for casting (Refer Q55 in Java section) and otherwise you can deal with the
problem of incorrect casting in two ways:
Use the exception handling mechanism to catch ClassCastException.
t r y{
Obj ect o = new I nt eger ( 1) ;
Syst em. out . pr i nt l n( ( St r i ng) o) ;
}
catch(ClassCastException cce) {
l ogger . l og( I nval i d cast i ng, String i s expect edNot an Integer) ;
Syst em. out . pr i nt l n( ( ( I nt eger ) o) . t oSt r i ng( ) ) ;
}
Use the instanceof statement to guard against incorrect casting.
i f ( v2 instanceof Car ) {
Car c2 = ( Car ) v2;
}
Design pattern: The instanceof and typecast constructs are shown for the illustration purpose only.
Using these constructs can be unmaintainable due to large if and elseif statements and can affect
performance if used in frequently accessed methods or loops. Look at using visitor design pattern to avoid
these constructs where applicable. (Refer Q11 in How would you go about section).
Points-to-ponder: You can also get a ClassCastException when two different class loaders load the same class because they
are treated as two different classes.
Q 37: What do you know about the Java garbage collector? When does the garbage collection occur? Explain different
types of references in Java? LF MI FAQ
A 37: Each time an object is created in Java, it goes into the area of memory known as heap. The Java heap is called
the garbage collectable heap. The garbage collection cannot be forced. The garbage collector runs in low
memory situations. When it runs, it releases the memory allocated by an unreachable object. The garbage
collector runs on a low priority daemon (i.e. background) thread. You can nicely ask the garbage collector to
collect garbage by calling System.gc() but you cant force it.
Java - Fundamentals
52
What is an unreachable object?
Gar ba ge Coll ec t ion & U nre a cha bl e O bjec ts
r e acha ble
r ea ch abl e
Ca s e 1
Ca r a = ne w C ar ( );
Ca r b = ne w Ca r()
1
r ef a
C ar o b je ct
2
r ef b
C ar o b je ct
Ca s e 2
a = ne w Ca r( )
r e acha ble
r ea ch abl e
unr ea cha ble
2
re f b
Ca r o bj ect
3
re f a
Ca r o bj ect
1
C ar ob je ct
Ca s e 3
a = b
un reac ha ble
r ea ch abl e
unr eac hab le
r
e
f
a
Ca r ob j ect
ref b
Ca r ob j ect
1
C ar o b je ct
2
3
Ca s e 4
a = nu ll ;
b = nu ll ;
unre achabl e
un reac ha ble
unr e achable
Ca r o bj ect
Ca r o bj ect
1
C ar o b je ct
2
3
He a p
An objects life has no meaning unless something has reference to it. If you cant reach it then you cant ask it to
do anything. Then the object becomes unreachable and the garbage collector will figure it out. Java automatically
collects all the unreachable objects periodically and releases the memory consumed by those unreachable objects
to be used by the future reachable objects.
We can use the following options with the Java command to enable tracing for garbage collection events.
j ava -verbose:gc / / r epor t s on each gar bage col l ect i on event .
Explain types of references in J ava? java.lang.ref package can be used to declare soft, weak and phantom
references.
Garbage Collector wont remove a strong reference.
A soft reference will only get removed if memory is low. So it is useful for implementing caches while
avoiding memory leaks.
A weak reference will get removed on the next garbage collection cycle. Can be used for implementing
canonical maps. The java.util.WeakHashMap implements a HashMap with keys held by weak references.
A phantom reference will be finalized but the memory will not be reclaimed. Can be useful when you want to
be notified that an object is about to be collected.
Java - Fundamentals
53
Q 38: If you have a circular reference of objects, but you no longer reference it from an execution thread, will this object
be a potential candidate for garbage collection? LF MI
A 38: Yes. Refer diagram below.
sample code
public void buildCar() {
Car c = new Car();
Engine e = new Engine();
//lets create a circular reference
c.engine = e;
e.car = c;
}
buildCar()
Stack Heap
Car
Engine
Before buildCar() returns
Stack Heap
Car
Engine
After buildCar() returns
Both the Car & Engine are not reachable
and potential candidate for Garbage
Collection.
Garbage Collecting Circular References
Q 39: Discuss the Java error handling mechanism? What is the difference between Runtime (unchecked) exceptions
and checked exceptions? What is the implication of catching all the exceptions with the type Exception? EH BP
FAQ
A 39:
Errors: When a dynamic linking failure or some other hard failure in the virtual machine occurs, the virtual
machine throws an Error. Typical Java programs should not catch Errors. In addition, its unlikely that typical Java
programs will ever throw Errors either.
Exceptions: Most programs throw and catch objects that derive from the Exception class. Exceptions indicate
that a problem occurred but that the problem is not a serious JVM problem. An Exception class has many
subclasses. These descendants indicate various types of exceptions that can occur. For example,
NegativeArraySizeException indicates that a program attempted to create an array with a negative size. One
exception subclass has special meaning in the Java language: RuntimeException. All the exceptions except
RuntimeException are compiler checked exceptions. If a method is capable of throwing a checked exception it
must declare it in its method header or handle it in a try/catch block. Failure to do so raises a compiler error. So
checked exceptions can, at compile time, greatly reduce the occurrence of unhandled exceptions surfacing at
runtime in a given application at the expense of requiring large throws declarations and encouraging use of poorly-
constructed try/catch blocks. Checked exceptions are present in other languages like C++, C#, and Python.
Throwable and its subclasses
Object
Exception
Error
Throwable
RuntimeException IOException
NullPointerException
LinkageError
Java - Fundamentals
54
Runtime Exceptions (unchecked exception)
A RuntimeException class represents exceptions that occur within the Java virtual machine (during runtime). An
example of a runtime exception is NullPointerException. The cost of checking for the runtime exception often
outweighs the benefit of catching it. Attempting to catch or specify all of them all the time would make your code
unreadable and unmaintainable. The compiler allows runtime exceptions to go uncaught and unspecified. If you
like, you can catch these exceptions just like other exceptions. However, you do not have to declare it in your
throws" clause or catch it in your catch clause. In addition, you can create your own RuntimeException
subclasses and this approach is probably preferred at times because checked exceptions can complicate method
signatures and can be difficult to follow.
Q. What are the exception handling best practices: BP
1. Q. Why is it not advisable to catch type Exception? CO
Exception handling in Java is polymorphic in nature. For example if you catch type Exception in your code then it
can catch or throw its descendent types like IOException as well. So if you catch the type Exception before the
type IOException then the type Exception block will catch the entire exceptions and type IOException block is
never reached. In order to catch the type IOException and handle it differently to type Exception, IOException
should be caught first (remember that you cant have a bigger basket above a smaller basket).
try{}
catch(Exception ex){
//this block is reached
}
catch(IOException ioe) {
//this block is never reached
//There is a bigger basket
//above me who will catch it
//before I can.
}
try{}
catch(IOException ioe){
}
catch(Exception ex) {
}
Wrong approach
Right approach
basket
basket
basket
basket
Catching Exceptions
Hint: As shown in the diagram, think
of catching an exception in a basket.
You should always have the smaller
basket above the bigger basket.
Otherwise the bigger basket will
catch all the exceptions and the
smaller basket will not catch any.
The diagram above is an example for illustration only. In practice it is not recommended to catch type
Exception. We should only catch specific subtypes of the Exception class. Having a bigger basket (i.e.
Exception) will hide or cause problems. Since the RunTimeException is a subtype of Exception, catching the type
Exception will catch all the run time exceptions (like NullPointerException, ArrayIndexOutOfBoundsException) as
well.
Example: The FileNotFoundException is extended (i.e. inherited) from the IOException. So (subclasses have to
be caught first) FileNotFoundException (small basket) should be caught before IOException (big basket).
2. Q. Why should you throw an exception early? CO
The exception stack trace helps you pinpoint where an exception occurred by showing you the exact sequence of
method calls that lead to the exception. By throwing your exception early, the exception becomes more accurate
and more specific. Avoid suppressing or ignoring exceptions. Also avoid using exceptions just to get a flow control.
Instead of:
/ / assume t hi s l i ne t hr ows an except i on because f i l ename == nul l .
I nput St r eami n = new Fi l eI nput St r eam( f i l eName) ;
Java - Fundamentals
55
Use the following code because you get a more accurate stack trace:
i f ( f i l ename == nul l ) {
throw new IllegalArgumentException(file name is null);
}
I nput St r eami n = new Fi l eI nput St r eam( f i l eName) ;
3. Why should you catch a checked exception late in a catch {} block?
You should not try to catch the exception before your program can handle it in an appropriate manner. The natural
tendency when a compiler complains about a checked exception is to catch it so that the compiler stops reporting
errors. It is a bad practice to sweep the exceptions under the carpet by catching it and not doing anything with it.
The best practice is to catch the exception at the appropriate layer (e.g. an exception thrown at an integration layer
can be caught at a presentation layer in a catch {} block), where your program can either meaningfully recover
from the exception and continue to execute or log the exception only once in detail, so that user can identify the
cause of the exception.
4. Q. When should you use a checked exception and when should you use an unchecked exception?
Due to heavy use of checked exceptions and minimal use of unchecked exceptions, there has been a hot debate
in the Java community regarding true value of checked exceptions. Use checked exceptions when the client code
can take some useful recovery action based on information in exception. Use unchecked exception when client
code cannot do anything. For example Convert your SQLException into another checked exception if the client
code can recover from it. Convert your SQLException into an unchecked (i.e. RuntimeException) exception, if the
client code can not recover from it. (Note: Hibernate 3 & Spring uses RuntimeExceptions prevalently).
Important: throw an exception early and catch an exception late but do not sweep an exception under the carpet
by catching it and not doing anything with it. This will hide problems and it will be hard to debug and fix. CO
A note on key words for error handling:
throw / throws used to pass an exception to the method that called it.
try block of code will be tried but may cause an exception.
catch declares the block of code, which handles the exception.
finally block of code, which is always executed (except System.exit(0) call) no matter what program flow, occurs
when dealing with an exception.
assert Evaluates a conditional expression to verify the programmers assumption.
Q 40: What is a user defined exception? EH
A 40: User defined exceptions may be implemented by defining a new exception class by extending the Exception class.
publ i c cl ass MyExcept i on ext ends Except i on {
/ * cl ass def i ni t i on of const r uct or s goes her e */
publ i c MyExcept i on( ) {
super ( ) ;
}
publ i c MyExcept i on ( St r i ng er r or Message) {
super ( er r or Message) ;
}
}
Throw and/or throws statement is used to signal the occurrence of an exception. To throw an exception:
throw new MyExcept i on( I t hr ew my own except i on. )
To declare an exception: publ i c myMet hod( ) throws MyExcept i on {}
Q 41: What are the flow control statements in Java? LF
A 41: The flow control statements allow you to conditionally execute statements, to repeatedly execute a block of
statements, or to just change the sequential flow of control.
Java - Fundamentals
56
Flow control
types
Keyword
Looping while, do-while, for
The body of the while loop is executed only if the expression is true, so it may not be executed even
once:
while( i < 5) {. . . }
The body of the do-while loop is executed at least once because the test expression is evaluated
only after executing the loop body. Also, don't forget the ending semicolon after the while
expression.
do { } while( i < 5) ;
The for loop syntax is:
f or ( expr 1; expr 2; expr 3)
{
/ / body
}
expr1 is for initialization, expr2 is the conditional test, and expr3 is the iteration expression.
Any of these three sections can be omitted and the syntax will still be legal:
for( ; ; ) {} / / an endl ess l oop
Decision
making
if-else, switch-case
The if-else statement is used for decision-making -- that is, it decides which course of action needs
to be taken.
if ( x == 5) {} else {. . }
The switch statement is also used for decision-making, based on an integer expression. The
argument passed to the switch and case statements should be int, short, char, or byte. The
argument passed to the case statement should be a literal or a final variable. If no case matches, the
default statement (which is optional) is executed.
i nt i = 1;
switch( i )
{
case 0:
Syst em. out . pr i nt l n( " Zer o" ) ; break; / / i f br eak; i s omi t t ed case 1: al so execut ed
case 1:
Syst em. out . pr i nt l n( " One" ) ; break; / / i f br eak; i s omi t t ed def aul t : al so execut ed
default:
Syst em. out . pr i nt l n( " Def aul t " ) ; break;
}
Branching break, continue, label:, return
The break statement is used to exit from a loop or switch statement, while the continue statement
is used to skip just the current iteration and continue with the next. The return is used to return from
a method based on a condition. The label statements can lead to unreadable and unmaintainable
spaghetti code hence should be avoided.
Exception
handling
try-catch-finally, throw
Exceptions can be used to define ordinary flow control. This is a misuse of the idea of exceptions,
which are meant only for exceptional conditions and hence should be avoided.
Q 42: What is the difference between processes and threads? LF MI CI
A 42: A process is an execution of a program but a thread is a single execution sequence within the process. A process
can contain multiple threads. A thread is sometimes called a lightweight process.
Java - Fundamentals
57
Process (JVM)
Stack Stack Stack
Each thread has its
own stack memory
Thread 1
Thread 3 Thread 2
method1() method1() method1()
Process vs Threads
Heap
Object1
Object
2
Single heap per process
shared by all the threads
A JVM runs in a single process and threads in a JVM share the heap belonging to that process. That is why
several threads may access the same object. Threads share the heap and have their own stack space. This is
how one threads invocation of a method and its local variables are kept thread safe from other threads. But the
heap is not thread-safe and must be synchronized for thread safety.
Q 43: Explain different ways of creating a thread? LF FAQ
A 43: Threads can be used by either :
Extending the Thread class
Implementing the Runnable interface.
cl ass Count er extends Thread {
/ / met hod wher e t he t hr ead execut i on wi l l st ar t
publ i c voi d r un( ) {
/ / l ogi c t o execut e i n a t hr ead
}
/ / l et s see how t o st ar t t he t hr eads
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
Thread t1 = new Counter();
Thread t2 = new Counter();
t 1. st ar t ( ) ; / / st ar t t he f i r st t hr ead. Thi s cal l s t he r un( ) met hod.
t 2. st ar t ( ) ; / / t hi s st ar t s t he 2
nd
t hr ead. Thi s cal l s t he r un( ) met hod.
}
}
cl ass Count er ext ends Base implements Runnable {
/ / met hod wher e t he t hr ead execut i on wi l l st ar t
publ i c voi d r un( ) {
/ / l ogi c t o execut e i n a t hr ead
}
/ / l et us see how t o st ar t t he t hr eads
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
Thread t1 = new Thread(new Counter());
Thread t2 = new Thread(new Counter());
t 1. st ar t ( ) ; / / st ar t t he f i r st t hr ead. Thi s cal l s t he r un( ) met hod.
t 2. st ar t ( ) ; / / t hi s st ar t s t he 2
nd
t hr ead. Thi s cal l s t he r un( ) met hod.
}
}
Q. Which one would you prefer and why? The Runnable interface is preferred, as it does not require your
object to inherit a thread because when you need multiple inheritance, only interfaces can help you. In the above
example we had to extend the Base class so implementing Runnable interface is an obvious choice. Also note
how the threads are started in each of the different cases as shown in the code sample. In an OO approach you
Java - Fundamentals
58
should only extend a class when you want to make it different from its superclass, and change its behavior. By
implementing a Runnable interface instead of extending the Thread class, you are telling to the user that the class
Counter that an object of type Counter will run as a thread.
Q 44: Briefly explain high-level thread states? LF
A 44: The state chart diagram below describes the thread states. (Refer Q107 in Enterprise section for state chart
diagram).
data/sync
received
Thread states(StateMachine diagram)
start();
Runnable
Dead
(finished)
Waiting
Running
(executing)
done
Object.notify();
Object.notifyAll();
Sleeping
Scheduler swap
or Thread.yield();
chosen by
scheduler
Thread.sleep();
Object.wait();
Blocked on I/O
or
Synchronized
another thread closes socket
(Diagram sourced from: http://www.wilsonmar.com/1threads.htm)
Runnable waiting for its turn to be picked for execution by the thread scheduler based on thread priorities.
Running: The processor is actively executing the thread code. It runs until it becomes blocked, or voluntarily
gives up its turn with this static method Thread.yield(). Because of context switching overhead, yield() should
not be used very frequently.
Waiting: A thread is in a blocked state while it waits for some external processing such as file I/O to finish.
Sleeping: Java threads are forcibly put to sleep (suspended) with this overloaded method:
Thread.sleep(milliseconds), Thread.sleep(milliseconds, nanoseconds);
Blocked on I/O: Will move to runnable after I/O condition like reading bytes of data etc changes.
Blocked on synchronization: Will move to Runnable when a lock is acquired.
Dead: The thread is finished working.
Q 45: What is the difference between yield and sleeping? What is the difference between the methods sleep() and
wait()? LF FAQ
A 45: When a task invokes yield(), it changes from running state to runnable state. When a task invokes sleep(), it
changes from running state to waiting/sleeping state.
The method wait(1000), causes the current thread to sleep up to one second. A thread could sleep less than 1
second if it receives the notify() or notifyAll() method call. Refer Q48 in Java section on thread communication.
The call to sleep(1000) causes the current thread to sleep for exactly 1 second.
Q 46: How does thread synchronization occurs inside a monitor? What levels of synchronization can you apply? What is
the difference between synchronized method and synchronized block? LF CI PI FAQ
A 46: In Java programming, each object has a lock. A thread can acquire the lock for an object by using the
synchronized keyword. The synchronized keyword can be applied in method level (coarse grained lock can
affect performance adversely) or block level of code (fine grained lock). Often using a lock on a method level is
too coarse. Why lock up a piece of code that does not access any shared resources by locking up an entire
Java - Fundamentals
59
method. Since each object has a lock, dummy objects can be created to implement block level synchronization.
The block level is more efficient because it does not lock the whole method.
class MethodLevel {
//shared among threads
SharedResource x, y ;
pubic void synchronized method1() {
//multiple threads can't access
}
pubic void synchronized method2() {
//multiple threads can't access
}
public void method3() {
//not synchronized
//multiple threads can access
}
}
class BlockLevel {
//shared among threads
SharedResource x, y ;
//dummy objects for locking
Object xLock = new Object(), yLock = new Object();
pubic void method1() {
synchronized(xLock){
//access x here. thread safe
}
//do something here but don't use SharedResource x, y
// because will not be thread-safe
synchronized(xLock) {
synchronized(yLock) {
//access x,y here. thread safe
}
}
//do something here but don't use SharedResource x, y
//because will not be thread-safe
}//end of method1
}
The JVM uses locks in conjunction with monitors. A monitor is basically a guardian who watches over a sequence
of synchronized code and making sure only one thread at a time executes a synchronized piece of code. Each
monitor is associated with an object reference. When a thread arrives at the first instruction in a block of code it
must obtain a lock on the referenced object. The thread is not allowed to execute the code until it obtains the lock.
Once it has obtained the lock, the thread enters the block of protected code. When the thread leaves the block, no
matter how it leaves the block, it releases the lock on the associated object.
Q. Why synchronization is important? Without synchronization, it is possible for one thread to modify a shared
object while another thread is in the process of using or updating that objects value. This often causes dirty data
and leads to significant errors. The disadvantage of synchronization is that it can cause deadlocks when two
threads are waiting on each other to do something. Also synchronized code has the overhead of acquiring lock,
which can adversely affect the performance.
Q. What is a ThreadLocal class? ThreadLocal is a handy class for simplifying development of thread-safe
concurrent programs by making the object stored in this class not sharable between threads. ThreadLocal class
encapsulates non-thread-safe classes to be safely used in a multi-threaded environment and also allows you to
create per-thread-singleton. For ThreadLocal example: Refer Q15 (What is a Session?) in Emerging
Technologies/Frameworks section. Refer Q51 in Java section for singleton design pattern.
Q 47: What is a daemon thread? LF
A 47: Daemon threads are sometimes called "service" or background threads. These are threads that normally run at a
low priority and provide a basic service to a program when activity on a machine is reduced. An example of a
daemon thread that is continuously running is the garbage collector thread. The JVM exits whenever all non-
daemon threads have completed, which means that all daemon threads are automatically stopped. To make a
thread as a daemon thread in Java myThr ead. setDaemon( true) ;
Q 48: How can threads communicate with each other? How would you implement a producer (one thread) and a
consumer (another thread) passing data (via stack)? LF FAQ
A 48: The wait(), notify(), and notifyAll() methods are used to provide an efficient way for threads to communicate with
each other. This communication solves the consumer-producer problem. This problem occurs when the
producer thread is completing work that the other thread (consumer thread) will use.
Java - Fundamentals
60
Example: If you imagine an application in which one thread (the producer) writes data to a file while a second
thread (the consumer) reads data from the same file. In this example the concurrent threads share the same
resource file. Because these threads share the common resource file they should be synchronized. Also these
two threads should communicate with each other because the consumer thread, which reads the file, should wait
until the producer thread, which writes data to the file and notifies the consumer thread that it has completed its
writing operation.
Lets look at a sample code where count is a shared resource. The consumer thread will wait inside the
consume() method on the producer thread, until the producer thread increments the count inside the produce()
method and subsequently notifies the consumer thread. Once it has been notified, the consumer thread waiting
inside the consume() method will give up its waiting state and completes its method by consuming the count (i.e.
decrementing the count).
Thread communication (Consumer vs Producer threads)
Class ConsumerProducer {
private int count;
public synchronized void consume(){
while(count == 0) {
try{
wait()
}
catch(InterruptedException ie) {
//keep trying
}
}
count --; //consumed
}
private synchronized void produce(){
count++;
notify(); // notify the consumer that count has been incremented.
}
}
Note: For regular classes you can use the Observer interface and the Observable class to implement the
consumer/producer communications with a model/view/controller architecture. The Java programming language
provides support for the Model/View/Controller architecture with two classes:
Observer -- any object that wishes to be notified when the state of another object changes.
Observable -- any object whose state may be of interest, and in whom another object may register an interest.
They are suitable for any system wherein objects need to be automatically notified of changes that occur in other
objects. E.g. Your ConfigMgr class can be notified to reload resource properties on change to *.properties file(s).
Q. What does join() method do? t. j oi n( ) allows the current thread to wait indefinitely until thread t is finished.
t. j oi n ( 5000) allows the current thread to wait for thread t to finish but does not wait longer than 5 seconds.
t r y {
t . join( 5000) ; / / cur r ent t hr ead wai t s f or t hr ead t t o compl et e but does not wai t mor e t han 5 sec
i f ( t . isAlive( ) ) {
/ / t i meout occur r ed. Thr ead t has not f i ni shed
}
el se {
/ / t hr ead t has f i ni shed
}
}
Java - Fundamentals
61
Q 49: If 2 different threads hit 2 different synchronized methods in an object at the same time will they both continue?
LF
A 49: No. Only one method can acquire the lock.
run(){
car1.method2();
}
Thread1
run(){
car1.method1();
car2.method1();
car1.method3()
}
Thread2
run(){
car2.method2();
car2.method3();
}
Thread3
synchronized method1() {}
synchronized method2() {}
method3() {}
Car1 object
synchronized method1() {}
synchronized method2() {}
method3() {}
Car2 object
Thread synchronization
4. Always ok. m
ethod3() is not synchronized
6.Always ok. method3() is not synchronized
1. ok. method1() is not busy.
3. ok. Method2() is not busy
2
. N
o
. m
e
th
o
d
2
() is
b
u
s
y
5. No. method1() is busy.
Note: If your job requires deeper understanding of threads then please refer to the following articles by Allen Holub at
http://www.javaworld.com. There are number of parts (part 1 Part - 8) to the article entitled Programming Java threads in
the real world. URLs for some of the parts are: http://www.javaworld.com/javaworld/jw-09-1998/jw-09-threads.html,
http://www.javaworld.com/javaworld/jw-10-1998/jw-10-toolbox.html, etc.
Q 50: Explain threads blocking on I/O? LF
A 50: Occasionally threads have to block on conditions other than object locks. I/O is the best example of this. Threads
block on I/O (i.e. enters the waiting state) so that other threads may execute while the I/O operation is performed.
When threads are blocked (say due to time consuming reads or writes) on an I/O call inside an objects
synchronized method and also if the other methods of the object are also synchronized then the object is
essentially frozen while the thread is blocked.
Be sure to not synchronize code that makes blocking calls, or make sure that a non-synchronized method
exists on an object with synchronized blocking code. Although this technique requires some care to ensure that
the resulting code is still thread safe, it allows objects to be responsive to other threads when a thread holding its
locks is blocked.
Note: The java.nio.* package was introduced in JDK1.4. The coolest addition is non-blocking I/O (aka NIO that stands for New
I/O). Refer Q24 in Java section for NIO.
Note: Q51 & Q52 in Java section are very popular questions on design patterns.
Q 51: What is a singleton pattern? How do you code it in Java? DP MI CO FAQ
A 51: A singleton is a class that can be instantiated only one time in a JVM per class loader. Repeated calls always
return the same instance. Ensures that a class has only one instance, and provide a global point of access. It
can be an issue if singleton class gets loaded by multiple class loaders or JVMs.
publ i c cl ass Onl yOne {
private static Onl yOne one = new Onl yOne( ) ;
/ / private constructor. Thi s cl ass cannot be i nst ant i at ed f r omout si de and
/ / pr event s subcl assi ng.
private Onl yOne( ) {}
public static Onl yOne get I nst ance( ) {
r et ur n one;
}
}
Java - Fundamentals
62
To use it:
/ / No mat t er how many t i mes you cal l , you get t he same i nst ance of t he obj ect .
Onl yOne myOne = OnlyOne.getInstance();
Note: The constructor must be explicitly declared and should have the private access modifier, so that it cannot
be instantiated from out side the class. The only way to instantiate an instance of class OnlyOne is through the
getInstance() method with a public access modifier.
Q. When to use: Use it when only a single instance of an object is required in memory for a single point of
access. For example the following situations require a single point of access, which gets invoked from various
parts of the code.
Accessing application specific properties through a singleton object, which reads them for the first time from
a properties file and subsequent accesses are returned from in-memory objects. Also there could be
another piece of code, which periodically synchronizes the in-memory properties when the values get
modified in the underlying properties file. This piece of code accesses the in-memory objects through the
singleton object (i.e. global point of access).
Accessing in-memory object cache or object pool, or non-memory based resource pools like sockets,
connections etc through a singleton object (i.e. global point of access).
Q. What is the difference between a singleton class and a static class? Static class is one approach to make a class
singleton by declaring all the methods as static so that you cant create any instance of that class and can call the static methods
directly.
Q 52: What is a factory pattern? DP CO FAQ
A 52: A Factory method pattern (aka Factory pattern) is a creational pattern. The creational patterns abstract the
object instantiation process by hiding how the objects are created and make the system independent of the object
creation process. An Abstract factory pattern is one level of abstraction higher than a factory method pattern,
which means it returns the factory classes.
Factory method pattern (aka Factory pattern) Abstract factory pattern
Factory for what? Factory pattern returns one of the
several product subclasses. You should use a factory
pattern If you have a super class and a number of sub-
classes, and based on some data provided, you have to
return the object of one of the subclasses. Lets look at
a sample code:
Factory pattern
Factory
Product hierachy
+draw()
<<abstract>>
Shape
+draw()
Circle
+draw()
Square
+getShape(int shapeId)()
ShapeFactory
+getShape (int shapeId)()
SimpleShapeFactory
instantiates
public interface Const {
public static final int SHAPE_CIRCLE =1;
public static final int SHAPE_SQUARE =2;
public static final int SHAPE_HEXAGON =3;
}
An Abstract factory pattern is one level of abstraction higher than
a factory method pattern, which means the abstract factory
returns the appropriate factory classes, which will later on
return one of the product subclasses. Lets look at a sample code:
public class ComplexShapeFactory extends ShapeFactory {
throws BadShapeException {
public Shape getShape(int shapeTypeId){
Shape shape = null;
if(shapeTypeId == Const.SHAPE_HEXAGON) {
shape = new Hexagon();//complex shape
}
else throw new BadShapeException
(shapeTypeId= + shapeTypeId);
return shape;
}
}
Now lets look at the abstract factory, which returns one of the
types of ShapeFactory:
public class ShapeFactoryType
throws BadShapeFactoryException {
public static final int TYPE_SIMPLE = 1;
public static final int TYPE_COMPLEX = 2;
public ShapeFactory getShapeFactory(int type) {
ShapeFactory sf = null;
if(type == TYPE_SIMPLE) {
Java - Fundamentals
63
public class ShapeFactory {
public abstract Shape getShape(int shapeId);
}
public class SimpleShapeFactory extends
ShapeFactory throws BadShapeException {
public Shape getShape(int shapeTypeId){
Shape shape = null;
if(shapeTypeId == Const.SHAPE_CIRCLE) {
//in future can reuse or cache objects.
shape = new Circle();
}
else if(shapeTypeId == Const.SHAPE_SQUARE) {
//in future can reuse or cache objects
shape = new Square();
}
else throw new BadShapeException
(ShapeTypeId=+ shapeTypeId);
return shape;
}
}
Now lets look at the calling code, which uses the
factory:
ShapeFactory factory = new SimpleShapeFactory();
//returns a Shape but whether it is a Circle or a
//Square is not known to the caller.
Shape s = factory.getShape(1);
s.draw(); // circle is drawn
//returns a Shape but whether it is a Circle or a
//Square is not known to the caller.
s = factory.getShape(2);
s.draw(); //Square is drawn
sf = new SimpleShapeFactory();
}
else if (type == TYPE_COMPLEX) {
sf = new ComplexShapeFactory();
}
else throw new BadShapeFactoryException(No factory!!);
return sf;
}
}
Now lets look at the calling code, which uses the factory:
ShapeFactoryType abFac = new ShapeFactoryType();
ShapeFactory factory = null;
Shape s = null;
//returns a ShapeFactory but whether it is a
//SimpleShapeFactory or a ComplexShapeFactory is not
//known to the caller.
factory = abFac.getShapeFactory(1);//returns SimpleShapeFactory
//returns a Shape but whether it is a Circle or a Pentagon is
//not known to the caller.
s = factory.getShape(2); //returns square.
s.draw(); //draws a square
//returns a ShapeFactory but whether it is a
//SimpleShapeFactory or a ComplexShapeFactory is not
//known to the caller.
factory = abFac.getShapeFactory(2);
//returns a Shape but whether it is a Circle or a Pentagon is
//not known to the caller.
s = factory.getShape(3); //returns a pentagon.
s.draw(); //draws a pentagon
Q. Why use factory pattern or abstract factory pattern? Factory pattern returns an instance of several (product
hierarchy) subclasses (like Circle, Square etc), but the calling code is unaware of the actual implementation class.
The calling code invokes the method on the interface for example Shape and using polymorphism the correct
draw() method gets invoked [Refer Q10 in Java section for polymorphism]. So, as you can see, the factory pattern
reduces the coupling or the dependencies between the calling code and called objects like Circle, Square etc. This
is a very powerful and common feature in many frameworks. You do not have to create a new Circle or a new
Square on each invocation as shown in the sample code, which is for the purpose of illustration and simplicity. In
future, to conserve memory you can decide to cache objects or reuse objects in your factory with no changes
required to your calling code. You can also load objects in your factory based on attribute(s) read from an external
properties file or some other condition. Another benefit going for the factory is that unlike calling constructors
directly, factory patterns have more meaningful names like getShape(), getInstance() etc, which may make
calling code more clear.
Q. Can we use the singleton pattern within our factory pattern code? Yes. Another important aspect to
consider when writing your factory class is that, it does not make sense to create a new factory object for each
invocation as it is shown in the sample code, which is just fine for the illustration purpose.
ShapeFact or y f act or y = new Si mpl eShapeFact or y( ) ;
To overcome this, you can incorporate the singleton design pattern into your factory pattern code. The singleton
design pattern will create only a single instance of your SimpleShapeFactory class. Since an abstract factory
pattern is unlike factory pattern, where you need to have an instance for each of the two factories (i.e.
SimpleShapeFactory and ComplexShapeFactory) returned, you can still incorporate the singleton pattern as an
access point and have an instance of a HashMap, store your instances of both factories. Now your calling method
uses a static method to get the same instance of your factory, hence conserving memory and promoting object
reuse:
ShapeFact or y f act or y = ShapeFact or y. get Fact or yI nst ance( ) ; / / r et ur ns a si ngl et on
f act or y. get Shape( ) ;
Java - Fundamentals
64
Note: Since questions on singleton pattern and factory pattern are commonly asked in the interviews, they are included as part
of this section. To learn more about design patterns refer Q11, Q12 in How would you go about section?
Q 53: What is a socket? How do you facilitate inter process communication in Java? LF
A 53: A socket is a communication channel, which facilitates inter-process communication (For example
communicating between two JVMs, which may or may not be running on two different physical machines). A
socket is an endpoint for communication. There are two kinds of sockets, depending on whether one wishes to
use a connectionless or a connection-oriented protocol. The connectionless communication protocol of the
Internet is called UDP. The connection-oriented communication protocol of the Internet is called TCP. UDP
sockets are also called datagram sockets. Each socket is uniquely identified on the entire Internet with two
numbers. The first number is a 32-bit (IPV4 or 128-bit is IPV6) integer called the Internet Address (or IP address).
The second number is a 16-bit integer called the port of the socket. The IP address is the location of the machine,
which you are trying to connect to and the port number is the port on which the server you are trying to connect is
running. The port numbers 0 to 1023 are reserved for standard services such as e-mail, FTP, HTTP etc.
Sockets
Sending Process(JVM) Receiving Process(JVM)
Operating System Operating System
IP address: 127.0.0.1
port: 6678
port: 6678
Network communication
sockets
The lifetime of the socket is made of 3 phases: Open Socket Read and Write to Socket Close Socket
To make a socket connection you need to know two things: An IP address and port on which to listen/connect. In
Java you can use the Socket (client side) and ServerSocket (Server side) classes.
Q 54: How will you call a Web server from a stand alone Java application/Swing client/Applet? LF
A 54: Using the java.net.URLConnection and its subclasses like HttpURLConnection and JarURLConnection.
URLConnection HttpClient (i.e. a browser)
Supports HEAD, GET, POST, PUT, DELETE, TRACE and
OPTIONS
Supports HEAD, GET, POST, PUT, DELETE, TRACE and
OPTIONS.
Does not support cookies. Does support cookies.
Can handle protocols other than http like ftp, gopher, mailto
and file.
Handles only http.
publ i c cl ass Test Ser vl et Wr i t er {
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) t hr ows Except i on{
St r i ng host = " l ocal host " ; / / i . e 127. 0. 0. 1
St r i ng pr ot ocol = " ht t p" ; / / r equest / r esponse par adi gm
i nt por t = 18080;
St r i ng st r URL = pr ot ocol + " : / / " + host + " : " + por t + " / myRoot Cont ext / mySer vl et " ;
java.net.URL servletURL = new java.net.URL( st r URL) ;
java.net.URLConnection con = servletURL.openConnection();
con. set DoI nput ( t r ue) ;
con. set DoOut put ( t r ue) ;
con. set UseCaches( f al se) ;
con. set Request Pr oper t y( " Cont ent - Type" , " appl i cat i on/ x- www- f or m- ur l encoded" ) ;
/ / Wr i t e t he ar gument s as post dat a
Obj ect Out put St r eamout = new Obj ect Out put St r eam( con. get Out put St r eam( ) ) ;
out . wr i t eObj ect ( " Hel l o Ser vl et " ) ; / / wr i t e a serializable obj ect t o t he ser vl et .
out . f l ush( ) ;
out . cl ose( ) ;
Java - Fundamentals
65
Obj ect I nput St r eamoi s = new ObjectInputStream(con.getInputStream()); / / t hi s l i ne is a must
/ / even i f you have not hi ng t o r ead back f r omt he web ser ver because ht t p i s a
/ / r equest / r esponse par adi gm.
St r i ng msg = ( St r i ng) oi s. r eadObj ect ( ) ;
Syst em. out . pr i nt l n( msg) ;
}
}
Note: Sun provides JSSE (Java Secure Socket Extension) as the technology to accomplish HTTPS over the Web.
This section would not be complete without discussing some of the exciting changes in the J2SE external version 5.0 and
the internal version 1.5.0 (Tiger) release.
Q 55: Explain some of the new features in J2SE 5.0, which improves ease of development? LF FAQ
A 55: The J2SE 5.0 release is focused along the key areas of ease of development, scalability, performance, quality,
etc. The new features include generics, metadata (aka annotations), autoboxing and auto-unboxing of
primitive types, enhanced for loop, enumerated type, static import, C style formatted output, formatted
input, varargs, etc. The following code sample depicts some of these new features. Brief explanation follows the
sample code, so if you do not understand any part of the code, come back to it after reading the brief explanation.
package sampl e;
/ / static import
i mpor t st at i c sampl e. Sampl eSt at i cVal ues. NUM_ZERO;
i mpor t j ava. ut i l . Ar r ayLi st ;
i mpor t j ava. ut i l . Li st ;
i mpor t j ava. ut i l . Scanner ;
publ i c cl ass Combi nedNewFeat ur es {
enum OddEven {odd, even} / / use of enum keyword. An enum is a special classs.
publ i c st at i c voi d mai n( St r i ng[ ] ar gs) {
/ / read from keyboard using the java.util.Scanner
Scanner keyboar d = new Scanner ( Syst em. i n) ;
Syst em. out . pr i nt l n( " Ent er your f i r st number ?" ) ;
i nt i 1 = keyboar d. next I nt ( ) ;
Syst em. out . pr i nt l n( " Ent er your second number ?" ) ;
i nt i 2 = keyboar d. next I nt ( ) ;
/ / using generics for type safety
Li st <I nt eger > numLi st = new Ar r ayLi st <I nt eger >( ) ;
/ / using auto-boxing to convert primitive int i1,i2 to wrapper Integer object.
numLi st . add( i 1) ;
numLi st . add( i 2) ;
/ / numLi st . add( " j ust t o pr ove t ype saf et y" ) ; / / won't compile! Requires an Integer to be added
/ / " : " shoul d be r ead as " foreach" . So should read as, foreach "num" value in numList.
f or ( I nt eger num: numLi st ) {
/ / using auto-unboxing feature to convert wrapper Integer object "num" to primitive.
i f ( num>= 9) {
/ / C style printf. System.out.printf(String arg0, Object ...arg1).
/ / this feature is possible due to var-args feature.
Syst em. out . pr i nt f ( " numi s: %1s, l i st si ze: %2s \ n" , num, numLi st . si ze( ) ) ;
/ / "%" symbol means we are using the format specifier, "1" means first arg.
/ / Refer java.util.Formatter class API for the format specification details.
}
/ / need not do SampleStaticValues.NUM_ZERO due to static import feature
i f ( num%2 == NUM_ZERO) {
Syst em. out . pr i nt l n( " The num" + num+ " i s: " + OddEven. even) ;
}
el se {
Java - Fundamentals
66
Syst em. out . pr i nt l n( " The num" + num+ " i s: " + OddEven. odd) ;
}
}
Combi nedNewFeat ur es cnf = new Combi nedNewFeat ur es( ) ;
/ / invoking methods using varargs
cnf . addNumber s( i 1) ;
cnf . addNumber s( i 1, i 2) ;
cnf . addNumber s( i 1, i 2, 5) ;
}
/ / method using varargs
publ i c voi d addNumber s( Obj ect . . . ar gs) {
i nt sum= 0;
f or ( Obj ect obj ect : ar gs) {
sum+= ( I nt eger ) obj ect ;
}
Syst em. out . pr i nt l n( " sumi s " + sum) ;
}
@Suppr essWar ni ngs( " depr ecat i on" ) / / metatag (annotation)
publ i c st at i c voi d end( ) {
Thr ead. cur r ent Thr ead( ) . st op( ) ; / / st op( ) i s a depr ecat ed met hod
}
}
package sampl e;
publ i c cl ass Sampl eSt at i cVal ues {
publ i c st at i c i nt NUM_ZERO = 0;
publ i c st at i c i nt NUM_ONE = 0;
}
package sampl e;
publ i c cl ass Ext endedCombi nedNewFeat ur es ext ends Combi nedNewFeat ur es {
@Over r i de / / metatag. If you spell the methodName incorrectly, you will get a compile error.
publ i c voi d addNumber s( Obj ect . . . ar gs) {
/ / over r i des basecl ass met hods
}
@Over r i de / / metatag
publ i c voi d addVal ues( Obj ect . . . ar gs) { / / compile error! must override a superclass method
/ /
}
}
Scanner API provide a more robust mechanism for reading in data types rather than simply parsing strings from buffered
System.in calls. Prior to Scanner feature was introduced, to read from standard input it would be necessary to write
exception handling code and wrap an InputStreamReader and a BufferedReader around System.in. Scanner class
throws an unchecked exception InputMismatchException, which you could optionally catch. Scanner API simplifies your
code as follows:
Scanner keyboar d = new Scanner ( System.in) ; / / no mor e wr appi ng wi t h I nput St r eamReader and
/ / Buf f er edReader ar ound Syst em. i n
Syst em. out . pr i nt l n( " Ent er your f i r st number ?" ) ;
i nt i 1 = keyboar d. next I nt ( ) ; / / no mor e par si ng st r i ngs e. g. new I nt eger ( " 5" ) . i nt Val ue( ) ;
Syst em. out . pr i nt l n( " Ent er your second number ?" ) ;
i nt i 2 = keyboar d. next I nt ( ) ; / / no mor e par si ng st r i ngs e. g. new I nt eger ( st r ) . i nt Val ue( ) ;
Generics allow you to pass types as arguments to classes just like values are passed to methods as parameters.
Generics are mainly intended for Java Collections API. The J2SE 5.0 compiler will check the type for you. So, the error
detection has been moved to compile time as opposed to runtime and ClassCastException is not likely to be thrown. It is
used in a typsafe manner and you do not have to cast when taking values out of the list.
Li st <Integer> numLi st = new Ar r ayLi st <Integer>( ) ; / / used i n a t ypesaf e way.
/ / numLi st . add( " j ust t o pr ove t ype saf et y" ) ; / / won't compile! An Integer value is required.
/ / Er r or det ect i on has been moved t o compi l e t i me as opposed t o Runt i me.
f or ( I nt eger num: numLi st ) { / / you do not have t o cast when you t ake val ues out of t he l i st .
Java - Fundamentals
67
}
Auto boxing/unboxing makes a programmers life easier by not having to write manual code for conversion between
primitive types such as int, float etc and wrapper types Integer, Float etc. The J2SE 5.0 will automatically box and unbox
this for you. So this is a convenience feature and is not a performance booster.
/ / using auto-boxing to convert primitive int i1,i2 to wrapper Integer object.
numLi st . add( i 1) ; / / no mor e code l i ke - > numLi st . add( new I nt eger ( i 1) ) ; aut oboxed f or you
numLi st . add( i 2) ; / / no mor e code l i ke - > numLi st . add( new I nt eger ( i 2) ) ; aut oboxed f or you