Core Java Career Essentials
Core Java Career Essentials
Career Essentials
Focusing on
Java platform, language, classes, objects, OO concepts
& principles, data structures, algorithms, and pattern
matching essentials
By
Arulkumaran Kumaraswamipillai
Sivayini Arulkumaran
The authors have made every effort in the preparation of this book to ensure the accuracy of the
information. However, information in this book is sold without warranty either expressed or implied. The
authors will not be held liable for any damages caused or alleged to be caused either directly or indirectly
by this book.
Thanks to the reviewers: Ankit Garg, Devaka Cooray, Roberto Perillo, Rod Good, and
Sean Clynes.
Table of Contents
Getting Started .........................................................................................5
How can this book help you?...........................................................................................6
Why is this a PERFECT companion?..............................................................................8
What are the technical key areas?....................................................................................9
Platform Essentials....................................................................................15
Why use Java? ...........................................................................................................16
Java platform basics JDK, JRE, JVM, etc..................................................................21
Setting up and running Java...........................................................................................30
How are your classes loaded?........................................................................................34
Compile-time versus runtime........................................................................................39
Language Essentials..................................................................................133
Valid identifiers and reserved keywords......................................................................135
Choosing the right data types......................................................................................144
Widening versus narrowing conversions.....................................................................150
Understanding the operator precedence.......................................................................159
Thinking in binary and bitwise operations .................................................................162
Initializing your data correctly.....................................................................................176
Constants and static imports........................................................................................182
Modifiers and where are they applicable? ..................................................................186
Methods and constructors............................................................................................196
Stack versus Heap........................................................................................................209
Java is pass-by-value....................................................................................................211
Recursive functions.....................................................................................................214
Class, instance, and local variables..............................................................................217
Objects Essentials......................................................................................305
Working with Objects..................................................................................................306
Cloning Objects...........................................................................................................312
Casting Objects............................................................................................................314
Immutable Objects.......................................................................................................318
Working with Enumerations........................................................................................326
Understanding == versus equals( )...........................................................................332
Working with the String class......................................................................................338
Type safety with Generics............................................................................................346
Serializing your objects...............................................................................................364
Garbage Collection......................................................................................................372
Section-1:
Getting Started ...
So you have a java interview coming up in a few days or you want to impress
your peers and superiors with your technical strengths during code review sessions, team
meetings, and stand-ups, and concerned about how to make a good impression? you don't
need to worry if you are familiar with the fundamentals. Most Java interviews and
technical challenges you face at work are structured around the fundamentals and how
well you communicate those fundamentals. So regularly brushing up on these
fundamentals really pays off.
Your analytical, problem solving, and coding skills will also be under scrutiny
along with your ability to get the job done with the right tools. If your fundamentals are
clear and know what tools to use, you can tackle any questions, and find solutions to any
problems and challenges you face. Even if you don't have the exact answer for a
problem, you will know how to go about solving them with a little research if you have a
solid grounding in the fundamentals covered in this book.
A little preparation can make a huge difference to your career success. Preparation
can help you communicate your thoughts more clearly with examples and illustrations.
Preparation can make a good and lasting impression on those who talk with you during
your interviews and team meetings. This impression will be partly influenced by how
prepared you are and how knowledgeable you are about your industry and the challenges
it faces. It will also be influenced by your appearance, attitude, enthusiasm, and
confidence. Good preparation breeds confidence and it shows in the interviews and team
meetings. So prepare well in advance if you just begun to see yourself in your dream
company or would like to go places in your chosen field.
Helps you brush up or be aware of the basics in software development that you
must know. Preparing for the most common interview questions will increase your
chances. If you fail to answer these basic questions, your chances of succeeding in
interviews will be very slim. In fact, 40% - 60% of the professionals irrespective of
their level of experience fail to make an impression in one or more of the following
most common areas.
Coding: You may have to write some simple code, with correct syntax and
logic in Java. You might be asked to explain and critique on snippets of code.
You might be asked to demonstrate both recursive and iterative approaches
to a given problem. You will be assessed on your ability to think about the
exceptional cases, exit conditions, and algorithms.
OO design: You will be asked to define basic OO concepts and principles,
and come up with the classes and interfaces to model a simple problem. A
good weeder question would be to judge your ability to decide when to use
attributes, inheritance, and composition. You might be asked to critique a
system or platform you had worked on. A good OO design question can also
test your coding skills and domain knowledge.
Language fundamentals and best practices: You must have a solid grasp
of the language fundamentals. You might be asked what parts of Java you
don't like and why? You must know about the common pitfalls, anti-patterns,
and how to avoid them. You must know the differences between abstract
classes and interfaces, how the garbage collection works?, etc and relevant
best practices.
Data structures: You must demonstrate basic knowledge of the most
common data structures. Most candidates know about arrays, sets, lists, and
maps but fail to mention trees and graphs. You will be quizzed on real life
examples of where to use a particular data structure and big-O performance
Helps you refresh your memory or learn answers to the technical questions that are
frequently asked in interviews. This can build up your confidence and it really shows
in interviews. Software development field is very vast and competitive, and it pays to
jog your memory even if you are an experienced professional.
All the other applicants may have much the same degrees, certifications, skills, and
experience. But those who are in the know how can make it all come alive and seem
real by selling their practical and technical competencies with illustrations, examples,
and enthusiasm. If you don't relate your qualifications, skills, experience, and abilities
to demonstrated results, most employers will tend to think that you have a great deal
of raw potential but limited immediate usefulness.
Helps you think aloud for some of the more difficult to tackle scenario based and
open-ended questions where the interviewers will be more interested in evaluating
your thought process and approach than expecting a detailed solution or answer.
Helps you progress in your career a lot quicker by enabling you to pro-actively learn
and apply the core technical key areas that will otherwise take years to fully under
stand if you rely on your experience alone. Some mistakes are too expensive to learn
on the job.
This book complements our previously published book in 2005, entitled Java/J2EE
Job Interview Companion, which was well received, and the purpose of this book is to
keep up with the recent developments, cover more on coding, open-ended, and scenario
based interview Q&A, and to incorporate some of the constructive criticisms from the
readers and reviewers. The Java/J2EE Job Interview Companion gives an overview of
many topics from Java to J2EE, XML to UML, and frameworks to emerging technologies.
The Core Java Career Essentials as the name implies, focuses solely on the essentials in
more depth. The ensuing career essentials series will cover the topics not covered here.
Knowledge is knowing what to do, skill is knowing how to do,
virtue is getting it done. Norman Vincent Peale
Practical: What is the point in learning bitwise operators without knowing where to
apply and when to use them? This book is full of practical examples, workable code,
best practices, and potential pitfalls.
Rewarding: The basics described here with examples and sample code can bring
success in job interviews, technical tests, code reviews, and performance appraisals
by learning to sell yourself more effectively.
Focused: Our last book entitled Java/J2EE Job Interview Companion was
providing a bird's eye view of a vast range of technologies and frameworks. While
this approach was well received and provided a road map for job interviews, it lacked
some level of details. This book is focused on more details to extend its usefulness
beyond just interviews by covering a limited number of topics in more depth.
Examples driven: Prospective employers are not just looking for professionals with
just academic qualifications. They are looking for professionals with experience. You
cannot drive a car with just theoretical knowledge. Software development is no
different. So use these examples to code, code, and more code.
Concise: Who wants to read pages after pages about a single topic. There is always
Google to perform additional research on a as needed basis.
Adequate logging, auditing, and alarms to provide better problem diagnostics and
customer support.
Security, data integrity, data retention, fail over and disaster recovery strategies.
Being up 24x7, performance metrics, and adherence to SLAs (Service Level Agree
ments) like response times, timeouts, etc. Service assurance can help improve
customer trust and experience in the system.
If you just take technical design alone, there are many pros and cons for each approach
9
You can earn the appreciation of your superiors and peers by pro-actively and retro
actively solving everyday problems by understanding the key areas. For example, you
can resolve problems relating to performance, memory, concurrency, language
fundamentals, etc. Some of the subtle issues can be hard to understand, reproduce
and solve without the adequate technical skills, and it really pays to have the exper
ience and good understanding of the basics in these key areas.
Understanding some of the nuances and best practices can help you write less errorprone and more robust code. Writing maintainable, supportable, readable, and
testable code will not go unnoticed in code reviews and can also earn you the much
needed recognition. You can also come up with intelligent questions or suggestions
in team/stand-up meetings and group discussions. You can pro-actively identify
potential issues not only in your own code or design, but also in others' code and
design to become a great contributor.
10
You can impress your interviewers by highlighting your past achievements in these
key areas. For example, you can make use of open-ended questions like Tell me
about your self ? or Why do you like software development? to highlight your
strengths and accomplishments.
current software? and do you face any performance issues or customer complaints
that are hard to reproduce? .
Better candidates will control the interview in a nicer way as they understand it is a
two way process. They will quickly understand what the prospective interviewer is
looking for and reflect back on their past experience to relate how they went about
resolving problems and adding value.
Let's look at the key areas relating to software development. Please be aware that not
all key areas are covered in this book.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
Language Fundamentals LF
Specification Fundamentals SF
Design Concepts DC
Design Patterns DP
Performance Considerations PC
Memory Considerations MC
Transaction Management TM
Concurrency Considerations CC
SCalability SC
Exception Handling EH
Best Practices BP
Software Development Processes SP
SEcurity SE
COding for readability, maintainability, supportability, extendability, and testability
CO
15. Quality of Service QS
16. Platform Fundamentals PF
Most of the questions are tagged with relevant technical key area(s) shown above.
Some questions are also tagged with question type and/or its popularity as shown below:
12
Important:
Sample code in this book is making use of static methods and other approaches in
many places for illustration purpose and to keep things simple. This should not be
construed as a best practice.
A commercial standard code will make use of unit tests using either JUnit or
TestNG instead of public static main(String[ ] args) methods for testing and most of
the methods except a fewer utility methods will be non-static. You will hardly see
main methods required except for initial bootstrapping of applications that are run
stand-alone. It will also be making use of Object Oriented principles, Dependency
Injection, Law of Demeter principle, Don't Repeat Yourself (DRY) principle, etc to
make the code more reusable, testable, maintainable and readable.
Comments are over used to explain concepts, and real code should not be cluttered
with comments. The classes, methods, and variables should have meaningful names
and the code should be self-explanatory.
13
14
Section-2:
Platform Essentials
This section is mainly for a beginner level, but I am very surprised to see even
some intermediate to senior level developers lack good understanding of these platform
essentials. This section is a must read for a beginner level. The intermediate and senior
level candidates are highly recommended to brush up on the following frequently asked
questions,
Q1, Q2, Q3, Q7, Q8, Q9, Q10, Q17, Q18, Q19, Q20, Q21, Q22, Q24 and Q25.
Even if you have limited experience, it is better to have an idea as to how to go
about getting it done than to have no idea at all. This will differentiate yourself from
others who have similar experience as you. Good Java interview questions are designed
in a way to analyze if you are capable of applying the basics of a programming language.
15
Platform Essentials
If you want to target multiple platforms, Java is the best solution. Although the
notion of Write Once Run Anywhere (WORA) does not always hold true,
but it is true that the majority of the code you write in Java will run almost
anywhere.
16
The Java platform has been around for a while and has been tested and
deployed in many industries. It is mature both in terms of implementation as
well as APIs. There's less risk that there's some bug in the platform that poses
security risks, or has some intrinsic flaw in its architecture.
The Java platform is standardized and the specifications are publicly available
from various sources. There are many books, on-line resources, tutorials, and
training courses on how to develop on it. Hence, it'll be easier to find
developers who can be productive sooner.
There are plethora of design, development, and deployment tools both opensource and commercial to choose from.
The platform also comes with a rich set of APIs. This means developers spend
less time writing support libraries, and more time developing content for their
applications.
Finally, the usual benefits of the Java platform and its APIs
Java does not support pointers. Pointers are inherently tricky to use and
troublesome.
Java does not support multiple inheritances because it causes more problems
than it solves. Java supports multiple interface inheritance, which allows an
object to inherit many method signatures from different interfaces with the
condition that the inheriting object must implement those inherited methods.
The multiple interface inheritance also allows an object to behave polymorph
ically on those methods.
17
Platform Essentials
Java does not support destructors, but adds a finalize( ) method. Finalize
methods are invoked by the garbage collector prior to reclaiming the memory
occupied by the object, which has the finalize( ) method. This means, you do
not know when the objects are going to be finalized. Avoid using finalize( )
method to release non-memory resources like file handles, sockets, database
connections, etc because the platform 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 resources through the finalize( ) method.
Java does not include structures or unions because the traditional data struc
tures are implemented as an object oriented Java Collections framework.
All the code in Java program is encapsulated within classes therefore Java does
not have global variables or functions.
Q3 Is Java 100% object oriented? If yes, why? And if not, why not? LF FAQ OEQ
A3 I would say Java is not 100% object oriented, but it embodies practical OO concepts.
There are 6 qualities to make a programming language to be pure object oriented. They
are:
1. Encapsulation data hiding and modularity.
2. Inheritance. you define new classes and behavior based on existing classes to
obtain code reuse.
3. Polymorphism the same message sent to different objects results in behavior
that's dependent on the nature of the object receiving the message.
4. All predefined types are objects.
5. All operations are performed by sending messages to objects.
6. All user defined types are objects.
The main reason why Java cannot be considered 100% OO is due to its existence of 8
18
primitive variables (i.e. violates point number 4) like int, long, char, float, etc. These
data types have been excused from being objects for simplicity and to improve
performance. Since primitive data types are not objects, they don't have any qualities
such as inheritance or polymorphism. Even though Java provides immutable wrapper
classes like Integer, Long, Character, etc representing corresponding primitive data as
objects, the fact that it allowed non object oriented primitive variables to exist, makes it
not fully OO.
Another reason why Java is considered not full OO is due to its existence of static
methods and variables (i.e. violates point number 5). Since static methods can be
invoked without instantiating an object, we could say that it breaks the rules of encap
sulation.
Java does not support multiple class inheritance because different classes may have
different variables with same name that may be contradicted and can cause confusions
and result in errors. We could also argue that Java is not 100% OO according to this
point of view. But Java realizes some of the key benefits of multiple inheritance
through its support for multiple interface inheritance. The multiple interface inher
itance gives simplicity by avoiding ambiguity, complexity, misuse, and potential
increased memory usage that is present in multiple class inheritance.
Operator overloading is not possible in Java except for string concatenation and
addition operations. String concatenation and addition example,
System.out.println(1 + 2 + 3);
System.out.println(1 + 2 + 3);
//outputs 33
//outputs 123
Platform Essentials
looks verbose for larger calculations without the operator overloading as shown below:
BigDecimal b = new BigDecimal(25.24);
BigDecimal c = new BigDecimal(3.99);
BigDecimal d = new BigDecimal(2.78);
BigDecimal a = b.subtract(c.multiply(d));
//verbose
Java 7 may have operator overloading for BigDecimal, and it would make your code
more readable as shown below.
BigDecimal a = b (c * d);
//much better
Java language comprising the syntax and semantics for programming has 3
editions Java SE (Standard Edition), Java ME (Micro Edition), and Java EE
(Enterprise Edition).
20
It is a best practice to reuse proven and well tested APIs from Java
Core, Java Optional, and third party APIs from Apache, Spring foundation, etc.
It is also highly recommended to have these API documentation handy while
coding to reuse appropriate well tested methods where possible without having
to write your own.
Java Virtual Machine (JVM) is where all the classes written using the Java
language and the API run on. The Java classes and API are platform
independent but the JVM is not platform independent. You will find different
downloads of the JVM for each Operating System (OS).
Java source code is compiled into byte code that is understood by the JVM.
Byte codes are the machine language of the JVM. The source code is easy for
humans to understand. This enables a programmer to write programs. The
byte code is very difficult for humans to understand. The first four bytes of
every Java class (i.e the byte code) file are specified to be 0xCAFEBABE, a
magic number that can help tools quickly differentiate likely class files from
21
Platform Essentials
non class files.
Java Virtual Machine (JVM) is a software that can be ported onto various
hardware platforms. JVM interprets Java programs that have been compiled
into byte code and usually stored in a *.class file. Byte code can be run on
any computer that has Java interpreter (i.e the JVM). The JVM converts the
byte code into platform (i.e. OS) specific executable machine language a
language understood by computers (i.e. binary 0s and 1s). The machine
language is almost impossible for humans to understand. Every OS has its own
machine language.
JDK: You can download a copy of the Java Development Kit (JDK). Be sure to
download a copy that is appropriate for your operating system and processor. The
JDK contains the compiler (i.e. javac) and other programs and libraries (i.e. jar files)
22
Platform Essentials
groovy script, using the groovyc command, you may want to use a Java decom
piler to inspect the Java source code for the groovy generated class files to
debug or get a better understanding of groovy integration with Java.
To ensure that your code is adequately obfuscated before releasing it into the
public domain.
Fixing and debugging .class files when developers are slow to respond to
questions that need immediate answers.
Server mode is suited for long running server applications, which can be active
for weeks or months at a time. Specially tuned to maximize peak operating
speed. The fastest possible operating speed is more important than fast startup time or smaller runtime memory footprint.
c:\> java -server MyProgram
PF PC MC FAQ
A9 JVMs are available in 32 bits (-d32 JVM argument) and 64 bits (-d64 JVM argument).
64-bit JVMs are typically only available from JDK 5.0 onwards. It is recommended that
the 32-bit be used as the default JVM and 64-bit used if more memory is needed than
what can be allocated to a 32-bit JVM. The Sun Java VM cannot grow beyond ~2GB
on a 32bit server machine even if you install more than 2GB of RAM into your server.
It is recommended that a 64bit OS with larger memory hardware is used when larger
heap sizes are required. For example, >4GB is assigned to the JVM and used for
deployments of >250 concurrent or >2500 casual users.
Q10 How do you monitor the JVMs? PF PC MC FAQ
A10 Since Java SE 5.0, the JRE provides a mean to manage and monitor the Java Virtual
Machine. It comes in two flavors:
The JVM has built-in instrumentation that enables you to monitor and manage
it using Java Management eXtension (JMX). You can also monitor instru
mented applications with JMX. To start a Java application with the
management agent for local monitoring, set the following JVM argument when
25
Platform Essentials
you run your application.
$JAVA_HOME/bin/java -Dcom.sun.management.jmxremote MyApp
To start the JConsole:
$JAVA_HOME/bin/jconsole
26
Symptom
Tool
Insufficient
memory
Memory leaks
Deadlocked
threads waiting for
each
other,
endlessly looping
threads, and racing
threads ending up
waiting for other
long
running
thread to release
the
monitor Thread contention issues can
(known as thread adversely affect performance
contention).
and scalability.
JVM
abruptly Causing an application or service
going down and to be down.
other
network
fault monitoring.
QS Many organizations use commercial tools like Wily's Introscope, Tivoli Monit
oring, etc and SNMP trap handling tools like Nagios to provide quality of service
(QoS) on their mission critical applications.
Q11 What are some of the JVM arguments you have used in your projects? PF
A11
To set a system property that can be retrieved
System.getPropety("name");
using
-Dname=value
Platform Essentials
To enable assertion:
-ea
/JDK1.6.0 The root directory of the JDK software containing license, copyright,
and readme files. Also contains src.zip, the archive of source code for the Java 2
platform.
28
/JDK1.6.0/bin The executable files like javac for all the development tools. The
PATH environment variable should contain an entry to this directory.
/JDK1.6.0/lib Files used by the development tools. Includes tools.jar, which
contains non-core classes for support of the tools and utilities in the JDK.
/JDK1.6.0/jre The root directory of the Java runtime environment. This the
directory referred by the java.home system property.
/JDK1.6.0/jre/bin The executable files for tools and libraries used by the Java
platform.
/JDK1.6.0/jre/lib Code libraries, property settings, and resource files used by the
Java runtime environment. Includes:
rt.jar the Java platforms core API (the bootstrap classes). The rt most likely
stands for RunTime. Some tend to think it stands for RooT, since this jar
contains the root classes.
Platform Essentials
be only one manifest file in an archive. Most uses of JAR files beyond simple archiving
and compression require special information to be in the manifest file. For example,
If you have an application bundled in a JAR file, you need some way to indicate
which class within the JAR file is your application's entry point. The entry
point is the class having a method with signature public static void main(String[
] args). For example,
Main-Class: Test.class
A package within a JAR file can be optionally sealed, which means that all
classes defined in that package must be archived in the same JAR file. You
might want to seal a package, for example, to ensure version consistency
among the classes in your software or as a security measure.
Name: myCompany/myPackage/
Sealed: true
Note: Refer JDK documentation for the manifest format and other options.
Configure your environment. The first environment variable you need to set is
the JAVA_HOME.
On Windows: use the system environment variables console
JAVA_HOME=C:\DEV\java\jdk-1.6.0_11
On Linux:
30
export JAVA_HOME=/usr/java/jdk-1.6.0_11
This system property will be used by your other command line build tools like
ANT and maven.
The second environment variable you need to set is the PATH variable. The
PATH variable contains a list of directories that the operating system uses to
locate executables to be executed by commands like javac, java, etc typed into
the system.
On Windows: use the system environment variables console
PATH=%JAVA_HOME%\bin;x:\maven\tools\maven2.0.9\bin;C:\bea\weblogic81\server\bin;
On Linux:
export PATH=$PATH:$JAVA_HOME/bin
Verify your configurations with the following commands:
DOS Command:
echo %JAVA_HOME%
echo %PATH%
UNIX Command:
$ echo $JAVA_HOME
$ echo $PATH
Platform Essentials
either set up your environment variable correctly or may have to open a new
command window for your changes to your environment variables to be
detected (especially in windows).
To list the available options for javac or java just type the command without
any arguments.
$ javac
$ java
Set the operating system CLASSPATH environment variable to have a jar file
c:/projects/Test/pet.jar, which has the Pet.class file in it. You can also specify both
the location of the jar file containing the class files for the Test project and the
location of the folder containing the packages and the class files for the Test2
project.
CLASSPATH= c:/projects/Test/pet.jar;C:/projects/Test2/bin
Platform Essentials
OR
C:\>java -classpath c:/projects/Test/pet.jar com.xyz.client.Pet
OR
C:\projects\Test\src>java -cp ../bin com/xyz/client/Pet
Important: Two objects loaded by different class loaders are never equal even if they
carry the same values. This means a class is uniquely identified in the context of the
associated classloader. This applies to singletons too, where each classloader will have
its own singleton.
Class loaders are hierarchical. Classes are introduced into the JVM as they are refer
enced by name in a class that is already running in the JVM. So how is the very first
class loaded? The very first class is specially loaded with the help of public static void
main(String[ ] args) method declared in your class. All the subsequently loaded classes
34
are loaded by the classes, which are already loaded and running. A classloader creates a
name space. A loaded class in a JVM is identified by its fully qualified name and its
defining classloader - this is sometimes referred to as the runtime identity of the class.
Consequently, each classloader in the JVM can be said to define its own name space.
Within a name space, all fully- qualified names are unique. This means class au.com.Test
Class defined by classloader A is NOT considered by the JVM to be the same class as
class au.com.TestClass defined by classloader B.
All JVMs include at least one classloader that is embedded within the JVM called
the primordial (or bootstrap) classloader. The JVM has hooks in it to allow user
defined class loaders to be used in place of primordial classloader. The following class
loaders are created by the JVM.
classloader
Reloadable
Explanation
Bootstrap
(primordial)
No
Extensions
No
System
No
Class loaders are hierarchical and use a delegation model when loading a class. Class
loaders request their parent to load the class first before attempting to load it
themselves. When a classloader loads a class, the child class loaders in the hierarchy will
never reload the class again. Hence uniqueness is maintained. Classes loaded by a child
classloader have visibility into classes loaded by its parents up the hierarchy but the
reverse is not true as explained in the above diagram.
35
Platform Essentials
Q18 Explain static vs. dynamic class loading? PF FAQ
A18 Classes are statically loaded with Javas "new" operator.
class MyClass {
public static void main(String args[ ]) {
Car c = new Car( );
}
}
A NoClassDefFoundException is thrown if a class is referenced with Javas "new"
operator (i.e. static loading) but the runtime system cannot find the referenced class.
Dynamic class loading is a technique for programmatically invoking the functions of a
classloader at run time. Classes can be loaded dynamically as shown below:
Class.forName (className);
The above static method returns the class object associated with the class name. The
string className can be supplied dynamically at run time. Unlike the static loading, the
dynamic loading will decide whether to load the class Car or the class Jeep at runtime
based on a properties file or other runtime conditions. Once the class is dynamically
loaded the following method returns an instance of the loaded class. Its just like
creating a class object with no arguments.
class.newInstance ( );
Example:
Jeep myJeep = null ;
//myClassName should be read from a .properties file.
//stay away from hard coding values in your program.
String myClassName = "au.com.Jeep" ;
Class vehicleClass = Class.forName(myClassName) ;
myJeep = (Jeep) vehicleClass.newInstance( );
myJeep.setFuelCapacity(50);
A ClassNotFoundException is thrown when an application tries to load in a class through
its string name using one of the following methods but no definition for the class with
36
When you are running within a managed environment like an application server,
Servlet engine or EJB Container, you must use the context classloader to retrieve a
class as shown below.
ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
if(cl == null) {
cl = getClass( ).getClassLoader( );
}
Class myClazz = cl.loadClass(className);
Q19 What tips would you give to someone who is experiencing a class loading or Class
Not Found exception? OEQ
A19 Class Not Found exceptions could be quite tricky to troubleshoot. Firstly, determine
the jar file that should contain the class file:
$ find . -name "*.jar" -print -exec jar -tf '{}' \; | grep -E "jar$|String\.class"
Secondly, check the version of the jar in the manifest file MANIFEST.MF, access
rights (e.g. read-only) of the jar file, and any jar corruption by trying to unjar it with
"jar -xvf ...". If the class is dynamically loaded, check if you have spelled the class name
correctly.
Thirdly, check if the application is running under the right JDK?
JAVA_HOME environment property
Check the
$ echo $JAVA_HOME
Finally, youll need to have a good understanding of your application servers classloader hierarchy.
37
Platform Essentials
38
Is the missing class packaged with the application (e.g., under WEB-INF/lib)
and being loaded by one of the parent class-loaders? Most application servers
utilize a class-loader hierarchy where WAR files class-loader is a child of EAR
class-loader which in turn is a child of the system (JVM) class-loader. Parent
class-loaders cant go down to request a class loaded from a child class-loader.
The problem occurs if some jars were moved to a shared library but they still
depend on classes packaged with the application.
Running different versions of the same jar loaded by the different class loaders
can cause this issue.
Platform Essentials
public class A {
public int compute(int input) {
return 3 * input;
}
}
public class B extends A {
@Override
public int compute(int input) {
return 4 * input;
}
}
//method #3
//method #4
//method #4
}
}
@Test is an annotation that JUnit framework uses at runtime with the help of
reflection to determine which method(s) to execute within a test class.
@Test(timeout=100)
public void testTimeout( ) {
while(true); //infinite loop
}
The above test fails if it takes more than 100ms to execute at runtime.
@Test (expected=IndexOutOfBoundsException.class)
public void testOutOfBounds( ) {
new ArrayList<Object>( ).get(1);
}
41
Platform Essentials
The above code fails if it does not throw IndexOutOfBoundsException or if it throws a
different exception at runtime. User defined annotations can be processed at runtime
using the new AnnotatedElement and Annotation element interfaces added to the Java
reflection API.
Exceptions: You can have either runtime or compile-time exceptions.
RuntimeException is also known as the unchecked exception indicating not required to
be checked by the compiler. RuntimeException is the superclass of those exceptions that
can be thrown during the execution of a program within the JVM. A method is not
required to declare in its throws clause any subclasses of RuntimeException that might be
thrown during the execution of a method but not caught.
Example: NullPointerException, ArrayIndexOutOfBoundsException, etc
Checked exceptions are verified by the compiler at compile-time that a program
contains handlers like throws clause or try{} catch{} blocks for handling the checked
exceptions, by analyzing which checked exceptions can result from execution of a
method or constructor.
Aspect Oriented Programming (AOP): Aspects can be weaved at compile-time,
post-compile time, load-time or runtime.
42
Compile-time: weaving is the simplest approach. When you have the source
code for an application, the AOP compiler (e.g. ajc AspectJ Compiler) will
compile from source and produce woven class files as output. The invocation
of the weaver is integral to the AOP compilation process. The aspects
themselves may be in source or binary form. If the aspects are required for the
affected classes to compile, then you must weave at compile-time.
Load-time: weaving is simply binary weaving deferred until the point that a
classloader loads a class file and defines the class to the JVM. To support this,
one or more "weaving class loaders", either provided explicitly by the run-time
Runtime: weaving of classes that have already been loaded to the JVM.
Platform Essentials
}
}
public class Child {
public String saySomething( ) {
return new Parent( ).saySomething( ) + , Child is called;
}
}
The Child class delegates the call to the Parent class. Composition can be achieved as
follows:
public class Child {
private Parent parent = null;
public Child( ){
this.parent = new Parent( );
}
public String saySomething( ) {
return this.parent.saySomething( ) + , Child is called;
}
}
Q23 Are marker or tag interfaces obsolete with the advent of annotations (i.e. runtime
annotations)? LF
A23 Everything that can be done with a marker or tag interfaces in earlier versions of Java
can now be done with annotations at runtime using reflection. One of the common
problems with the marker or tag interfaces like Serializable, Cloneable, etc is that when a
class implements them, all of its subclasses inherit them as well whether you want
them to or not. You cannot force your subclasses to unimplement an interface.
Annotations can have parameters of various kinds, and they're much more flexible
than the marker interfaces. This makes tag or marker interfaces obsolete, except for
situations in which empty or tag interfaces can be checked at compile-time using the
type-system in the compiler whereas annotation check has to be done at runtime as
shown below:
Using an annotation at runtime:
44
package annotations1;
public @interface Deletable { }
// annotation definition
package annotations1;
@Deletable
package annotations1;
public class Test {
public void delete(Object obj){
Deletable ann = obj.getClass( ).getAnnotation(Deletable.class); // Line A
if(ann != null) {
//go ahead and delete
}
else {
throw new RuntimeException("Not a deleteable object");
}
}
}
Using a tag or marker interface at compile-time:
package markerinterface;
public interface Deletable { }
package markerinterface;
public class ClassWithDeletableObjects implements Deletable {
//....
}
package markerinterface;
45
Platform Essentials
public class Test {
// works only with Deletable objects. Compile-time error if a Deletable
// object is not passed.
public void delete(Deletable obj){
// go ahead and delete
}
}
So marker or tag interfaces are only rarely required going forward for situations
described below,
In the very rare event of the profiling indicating that the runtime checks are
expensive due to being accessed very frequently, and compile-time checks
with interfaces as shown above is preferred.
In the event of existing marker interfaces like Serializable, Cloneable, etc are
used or Java 5 or later versions cannot be temporarily used.
BP The best practice going forward is to use an annotation as illustrated above instead
of a marker interface since it solves the problem of forcing the subclasses to inherit
the interface. For example, since the class Number implements Serializable, any subclass
such as Integer or AtomicInteger does too. As per the above example using an annotation,
if you don't want a particular subclass of ClassWithDeletableObjects to be deleted, you
can achieve it as shown below:
package annotations1;
public @interface Deletable {
boolean value( ) default true;
}
The class that must not be deleted can be defined as shown below,
@Deletable(false)
public class NotDeletableObjects extends ClassWithDeletableObjects {
//...
}
46
The few lines from Line A onwards in the Test class needs to be changed from
Deletable ann = obj.getClass( ).getAnnotation(Deletable.class); // Line A
if(ann != null) {
//go ahead and delete
}
to
Deletable ann = obj.getClass( ).getAnnotation(Deletable.class);
if(ann != null & ann.value( )) {
//go ahead and delete
}
BP PC The above code checks for the boolean value supplied as well. So the annota
tions are more explicit and flexible than the marker interfaces. The marker interfaces
using instanceof operator seems to be a bit faster than the equivalent Class.isAnnotation
Present at the time of writing. As mentioned earlier, design principles must not be
compromised for gaining smaller performance gains. Premature optimization is bad. If
profiling indicates that a particular annotation is adversely impacting performance then
look at alternative solutions to address that particular problem.
Q24 What is the difference between line A & line B in the following code snippet?
PC
COQ
//line A
//line B
}
A24 Line A, evaluates the product at compile-time, and Line B evaluates the product at
runtime. If you use a Java Decompiler, and decompile the compiled ConstantFolding.
47
Platform Essentials
class file, you will see why
public class ConstantFolding
{
static final int number1 = 5;
static final int number2 = 6;
static int number3 = 5;
static int number4 = 6;
public static void main(String[ ] args)
{
int product1 = 30;
int product2 = number3 * number4;
}
}
Constant folding is an optimization technique used by the Java compiler. Since final
variables cannot change, they can be optimized. Java Decompiler and javap command
are handy tool for inspecting the compiled (i.e. byte code ) code.
Q. Can you think of other scenarios other than code optimization, where inspecting a
compiled code is useful? OEQ
A. Generics in Java are compile-time constructs, and it is very handy to inspect a
compiled class file to understand and troubleshoot generics.
Q25 What is a Java debugger? What are the different ways to debug your code? PF
A25 The Java Platform Debugger Architecture (JPDA) consists of three interfaces designed
for use by debuggers in development environments for desktop systems.
48
The Java Virtual Machine Tools Interface (JVM TI) defines the services a VM
must provide for debugging.
The Java Debug Interface (JDI) defines information and requests at the user
code level.
The Java Debug Wire Protocol (JDWP) defines the format of information and
requests transferred between the process being debugged and the debugger
front end, which implements the Java Debug Interface (JDI).
The JPDA allows you to peek into your code at runtime, and debug your code. The jdb
is an implementation of the Java Debugger Interface. The jdb is a simple commandline debugger for Java classes providing inspection and debugging of a local or remote
Java Virtual Machine. JDK ships with jdb. Easier way to debug your code is to use the
visual debugging provided by your IDE (e.g. Eclipse, Net Beans, Websphere Applic
ation Developer, IntelliJ, etc)
Q. What are the different ways to add break points to your code?
A. You can add break point to any specific line in your code, also you can add break
point by exception. This will allow your program to halt at runtime when a particular
exception is thrown like a NullpointerException.
Q. Why is remote debugging important and how will you debug a remote Java applic
ation?
A. It is increasingly essential with the globalization to be able to debug a Java applic
ation that is deployed remotely, in another country or city. You will come across
scenarios where an application might be running fine in your sandbox (i.e. local
desktop), but might be buggy when running in another environment or country.
Say you want to remotely debug an application called MyApp.jar that is running
remotely, you can set up your desktop to be able to debug it by enabling the remote
debugging as shown below:
java -Xdebug -Xrunjdwp:transport=dt_socket,address=888,server=y -jar MyApp.jar
The above command tells the MyApp.jar to start a server socket on port 888, and
publish the debugging messages using the jdwp, which stands for Java Debug Wire
Protocol.
Q. What are some of the challenges you faced in debugging your application? What
tips would you give?
A.
Debugging multi-threaded applications can be tricky because whenever one
thread reaches a break-point, all other threads also stopped.
Platform Essentials
Tips: Carefully analyze the thread stack traces. Use the debugging features like step
into, step over, step out, and inspection of the runtime values provided by the visual
debugging tools. Where necessary, include adequate debug statements in your code for
a more complex situation. Finally, know your Java fundamentals well enough and
acquire enough experience to be able to come up with a list of possible causes and
narrow it down to the root cause.
50
Section-3:
Knowing your way around Java
This section is mainly for intermediate to senior level. If you are a beginner, skim
through this section, and come back to it after reading the other sections. This section is
mainly used to judge your experience. So where possible give practical examples by
reflecting back on your past experience using the SAR (Situation-Action-Result)
technique. The answers provided here are hints only. As an experienced professional,
your experience and views may differ. Experienced professionals know what works and
what doesn't. They know what tools to use to simplify a task at hand.
Good developers are opinionated based on their past experience. They can handle
scenario based questions. They may pause a while to reflect back on their experience, but
won't be stumped by the open ended questions. Reflecting back on your past experience
and a little preparation prior to your interviews and meetings will really boost your
performance.
This is a brush up section that will not only help you sell yourself more
effectively, but also will help you reflect back on your past experience and refresh your
memory. This section will add value only if you perform well relating to sections 4 7
on fundamentals.
This section will also provide valuable insights into getting your job done more
effectively, and making yourself heard in team meetings by contributing suggestions or
raising opinions. This does not mean that you will have to be inflexible. Most
importantly, the good developers get things done by complimenting their technical skills
with good soft-skills and right attitude.
51
Judging Experience
As explained earlier, open ended questions give you a great opportunity to prove your caliber
and usefulness to the organization in a well-rounded manner. You have no control over what
questions get asked in interviews, but you have control over what messages, skills, exper
ience, and personal attributes you want to nicely get across to your prospective employers or
interviewers without bragging, being too critical, or coming across inflexible. The openended questions give you the chance to make an impression. A very common open-ended
question is tell me about yourself ? OEQ
Q1 Can you list some of the Java features you used recently? LF
A1 [Hint] JDK 5.0 (external version 5.0 and internal version 1.5.0) is a major feature
release that largely centered on enhancing ease-of-development.
Features:
52
Instead the information can be maintained in a source file itself. This is also
Java's answer to the XDoclet framework used prior to JDK 5.0 (JSR-175).
Annotations allow you to add runtime metadata to classes, fields, and methods.
Everything that can be done with marker or tag interfaces in earlier versions of
Java can now be done with annotations at runtime using reflection.
Libraries:
The Collections framework has been enhanced with support for generics,
enhanced for loop, and autoboxing. Three new interfaces named Queue, Block
ingQueue, and ConcurrentMap have been added. More efficient copy-on-write
List and Set implementations have also been added.
The Java API for XML Processing (JAXP) has been included in the JDK so it
doesn't need to be downloaded separately. (JSR-206)
The concurrency utility package java.util.concurrent was included as part of
the standard edition 5.0, which previously had to be downloaded separately.
This package provides a scalable, thread-safe, and high-performance building
blocks for developing concurrent applications.
JDK 6.0 (external version 6.0 and internal version 1.6.0) release is centered on being
Web Services friendly, mixing of Java with other scripting languages, and general
enhancements to existing libraries.
One of the most significant features of this release is support for the Java API
for XML Web Services (JAX-WS), version 2.0. JAX-WS 2.0 includes Java
Architecture for XML Binding (JAXB) 2.0 and SOAP with Attachments API
for Java (SAAJ) 1.3. So Java SE 6.0 is SOA (Service Oriented Architecture)
friendly, and Apache Axis framework is no longer required.
Java SE 6.0 also includes a new framework and developer APIs to allow mixing
of Java technology with scripting languages such as PHP, Python, JavaScript,
and Ruby. This is very useful for prototyping, experimentation of an algorithm
or function, and one off or ad hoc tasks like transforming a text file into a
needed format, looking for a specific item in large log files, getting a subset of
data from a database and packing it as a text file, etc. All of the above tasks can
be done in a language like Java, but it can be faster and easier to do them in a
scripting language like PERL, Ruby, or Python. BP It is a best practice to
always use the right or best tool for the right job without just being overly
passionate about Java alone.
53
Q2 Who are your role models? What books really inspired you? What was the recent Java
book you read? What websites do you use to keep your knowledge current? OEQ
A2 [Hint]
Role models:
54
Head First Design Patterns by Eric T Freeman, Elisabeth Robson, Bert Bates,
Kathy Sierra
Java Concurrency in Practice by Brian Goetz
Java Puzzlers by Joshua Bloch and Neal Gafter
55
58
Exception class hierarchy: There are counter arguments in favor of checked excep
tions. The development community is divided on this topic.
Regardless of this checked versus unchecked exception debate, the exception class
hierarchy in general is less intuitive and confusing at times. For example, SQLException
is used to report both badly constructed SQL code due to programming errors and
failure of database connections due to environmental factors. Exception hierarchy
could be simplified as shown below.
Checked exceptions denote that an action can be taken to recover from the situation or
the situation can be handled more meaningfully. For example, connectivity issues can
be recovered from after a few retries. Business exceptions like ReachedMaximum
DailyLimitException can be used meaningfully to pass information to the user or to take
an alternative course of action like sending an email, writing to the audit tables in the
database, and notifying other systems via message queues.
The java.util.Date and Time classes in JDK are badly designed and verbose. As
discussed later in this section, the third-party library Joda-Time will make your life
much easier, especially when complex date related calculations are required. JDK 7 will
change the date-time classes significantly with the JSR 310, This will fix the main
problem of java.util.Date and java.util.Calendar being mutable objects. The JSR 310 is
built around the same basic date/time concepts used in Joda-Time like a discrete
59
60
}
}
Output:
Exception in thread "main" java.lang.NullPointerException
at Unbelievable.main(Unbelievable.java:5)
61
// illegal
Wild cards in generics can make your code more complex to use and under
stand. It is not worth puzzling over complicated generics statements.
public static <T extends Comparable<? super T>> void sort(List<T>list) { .
...
}
In simple cases, generics is clearly a huge improvement in both clarity and
safety over just casting all over the place. So understanding how generics works,
its pitfalls, and limitations can assist you decide when to use and when to back
off, and avoid using it.
Q5 Can you list the Java SE features you like to be added in the future? LF
A5 [Hint]
Closures: are useful for implementing abstractions that involve behavior. A closure is a
named or anonymous subroutine. It looks like Java 7 will have closures. Closures can
make your life as a developer much easier. Not sure of the actual syntax for Closures at
this stage, but it could be something like:
For example, to run a function in a thread pool,
myThreadPool.submit(#( ) { for (int i = 1; i < 1000; i++) performTask(i) });
#( ) {...} --> is a function (i.e. an anonymous function)
#( ) --> empty paranthesis indicate that the function has no arguments.
62
Currently (up to Java 6), custom sorting of objects in a list requires an implementation
of a Comparator interface. Closure could simplify this as follows to sort strings in a list
by its length in an ascending order as shown here:
Collections.sort( listStrings, #(String str1, String str2) str1.length( ) - str2.length( ));
JavaScript is widely used in web development, and in JavaScript a closure is created
every time you create a function within a function. When using a closure, you will have
access to all the variables in the enclosing (i.e. the parent) function.
var calculate = function(x) {
var const = 2;
return function(y) {
return x + y + const; // has visibility to parent variable 'const'
};
}
var plus5 = calculate(5);
alert(plus5(3));
alert(plus5(7));
alert(plus5(10));
Closures are first class objects in groovy, and in many ways it resembles anonymous
inner classes in Java. The above functionality can be implemented in groovy as shown
below:
def calculate = {x,y -> x + y + 2} //closure
def plus5 = { calculate.call(5,it)}
//"it" is like "this" in java. Here stands for
//values passed in to plus5 call like 3, 7 or 10
println plus5.call(3)
println plus5.call(7)
println plus5.call(10)
//prints 10
//prints 14
//prints 17
Mixins: Java does not support multiple-inheritance, but it is possible to have the
concept of "mixins inheritance", which are "partial classes" that you can bolt onto
your class, giving you something very much like multiple inheritance. The concept of
"mixins inheritances" are great when you want to refactor common functionality from
two classes having different super classes. Currently Java does not have "mixins" like
63
64
//use TreeNode
two different libraries (or a library and the application) require different
versions of the same third library. If both versions of the third library use the
same class names, there is no way to load both versions of the third library
with the same classloader.
classes loaded by different class loaders may interact in complex ways not fully
understood by a developer, leading to inexplicable errors or bugs.
The Java Module System (JSR 277) was initiated to address some of the issues caused
by the JAR files. The Java Module System defines a distribution format and a repos
65
//property
//property
//a normal field
String toString( ) {
return "I am ${name} ${lastName}."
}
}
The invoking code in Java is as shown below
public class Organization {
public static void main( String args[ ] ) {
Person person = new Person( ); .
person.setName( "Peter" );
person.setLastName( "Smith" );
System.out.println( person.toString( ) );
}
}
Finally, you may prefer fixing or improving the existing limitations and gotchas in Java
before adding any new features as it could make things worse. Most of these pitfalls
are brought about by the effort to maintain the backward compatibility.
Note: Being able to compare strengths and weaknesses of Java with other
programming and scripting languages will demonstrate some of the recognizable
qualities of good programmers. Good programmers get excited chatting about techno
logies, and are passionate about diversifying on the technology stack. So learn different
technologies, frameworks, and tools, and be opinionated about which are better for
various scenarios.
Q6 Give me a high level description of your experience with the Java platform? OEQ
A6 [Hint]
Working experience with J2SE from 1.3 up to 1.6. Strong experience on J2SE
67
68
Working experience with J2EE from 1.2 up to 1.5. Recent experience on JEE
1.5 including EJB3 and JAX-WS.
Strong server side system integration experience using JMS, Web Services
(both SOAP & Restful), RMI, and XML technologies like JAXB, XPath,
XQuery, XSL, XSD, etc, and middle-ware products like IBM MQSeries,
webMethods, Tibco, and Oracle Service Bus.
Working experience with the ETL (Extract, Transform, and Load) batch jobs,
using Unix shell scripts, BCP (bulk copy) utility, command line SQL tools like
iSQL for Sybase database, SQL*Plus for Oracle database, and SQLCMD for
the SQL server, and scheduling tools like Control-M or Unix Cron jobs. jobs.
Working experience with build and automation systems like ANT, Maven,
CruiseControl, Bamboo, IVY, and Hudson.
Note: The above list highlights many of the sought-after technical skills, but
streamline your answer based on your experience, the role, job specification, and the
briefing you got from your prospective employer.
Q7 What are your favorite areas of the Java APIs and why? LF CO CC
A7 [Hint]
EH
// result = 100
}
@Override
public Integer call( ) throws Exception {
return number * 5;
}
}
CopyOnWriteArrayList and CopyOnWriteSet are very useful in multi-threaded
applications in situations where number of traversals greatly outnumber insertions or
removals. If you are using a normal ArrayList in situations where modifications are
possible through read and write operations, you are likely to get the ConcurrentModifica
tionException. But using a CopyOnWriteArrayList, the writing task will create a new
copy of the data if it needs to add data, thus ensuring that the reader will always have
valid data (though potentially stale data) to display without the ConcurrentModifica
tionException.
The TimeUnit enumerated type includes handy time related utilities. For example,
70
Instead of:
Thread.sleep(10000);
The Java Collections framework provides high performance and high-quality imple
mentations of useful data structures and algorithms. In most cases you don't need to
create your own data structures and algorithms. It also has several convenience
methods for wrapping additional functionality if required. The Java Collections
framework is one of the most frequently used Java APIs whether you are using a client
side or server side Java development.
The java.util.regex is one of the greatest additions to Java 1.4. The addition of
MatchResult to Java 5.0 makes it a lot easier to obtain all of the state as a
MatchResult. The java.util.regex is also one of the popular Java APIs whether you are
using a client side or server side Java development.
Example 1:
import java.util.Scanner;
import java.util.regex.MatchResult;
public class Regex1 {
public static void main(String[ ] args) {
String input = "1 car 2 cars Red car Blue car";
Scanner s = new Scanner(input);
s.findInLine("(\\d+)\\s+car[s]?\\s+(\\d+)\\s+car[s]?\\s+
(\\w+)\\s+car[s]?\\s+(\\w+)");
MatchResult result = s.match( );
for (int i = 1; i <= result.groupCount( ); i++) {
71
}
}
}
Output:
Found 'car' at (2,5)
Found 'cars' at (8,12)
Found 'car' at (17,20)
Found 'car' at (26,29)
Q8 What are some of the very common runtime (or unchecked) exceptions you have
come across in Java? When is a ConcurrentModificationException thrown? LF
A8 NullPointerException, ClassCastException (very common prior to JDK 5), ArrayIndex
OutOfBoundsException, StringIndexOutOfBoundsException, and ConcurrentModificationEx
ception. The ArrayLists can throw ConcurrentModificationException:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//throws java.util.ConcurrentModificationException
public class Concurrent2 {
public final static void main(String args[ ]) {
List<String> list = new ArrayList<String>( );
list.add("A");
list.add("B");
Iterator<String> i = list.iterator( );
while (i.hasNext( )) {
System.out.println(i.next( ));
// exception after adding C
list.add("C");
}
System.out.println("After: " + list);
}
}
Output:
73
ConcurrentHashMap.iterator( ) will return each element once at most and will not ever
throw a ConcurrentModificationException, but may or may not reflect stale data due to
insertions or removals that occurred since the iterator was constructed.
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Concurrent6 implements Runnable {
private ConcurrentMap<String, Integer> counts =
new ConcurrentHashMap<String, Integer>(10);
private static final String KEY = "key";
public static void main(String[ ] args) {
ExecutorService pool = Executors.newFixedThreadPool(10);
try {
Concurrent6 concur = new Concurrent6( );
int totalRuns = 20;
int i = 0;
while (i < totalRuns) {
pool.execute(concur);
// invokes run( )
i++;
}
} finally {
pool.shutdown( );
}
}
//This method is thread-safe, but not atomic.
private void count( ) {
Integer cnt = counts.get(KEY);
cnt = (cnt == null) ? 1 : (cnt + 1);
counts.put(KEY, cnt);
75
Output:
Count for pool-1-thread-1 is 1
Count for pool-1-thread-1 is 11
Count for pool-1-thread-9 is 10
Count for pool-1-thread-9 is 13
Count for pool-1-thread-9 is 14
Count for pool-1-thread-7 is 9
Count for pool-1-thread-7 is 16
Count for pool-1-thread-7 is 17
Count for pool-1-thread-7 is 18
Count for pool-1-thread-7 is 19
Count for pool-1-thread-7 is 20
Count for pool-1-thread-5 is 8
Count for pool-1-thread-10 is 7
Count for pool-1-thread-3 is 6
Count for pool-1-thread-6 is 5
Count for pool-1-thread-8 is 4
Count for pool-1-thread-4 is 3
Count for pool-1-thread-2 is 2
Count for pool-1-thread-9 is 15
Count for pool-1-thread-1 is 12
This is more efficient compared to synchronizing the whole counts every time
because we rarely expect another thread to sneak in to read the same count. If this
situation does happen, the above code simply loops and tries again.
Q10 What open source libraries/frameworks do you have experience with? LF OEQ
A10 [Hint]
78
CollectionUtils.isEmpty(myList);
CollectionUtils.containsInstance(myList, myObject);
ArrayUtils.isEmpty(myArray);
return new HashCodeBuilder(17,
37).append(name).append(age).toHashCode( );
return new EqualsBuilder( ).appendSuper(super.equals(obj))
.append(name, rhs.name)
.append(age, rhs.age)
.isEquals( );
Note: Google Collections library from Google Core Libraries (for Java 1.6 aka
Guava) can be an alternative to Apache commons collection library.
Joda-Time: has been created to radically change date and time handling in
Java. The JDK classes Date and Calendar are very badly designed and not
intuitive. Joda classes are immutable (hence thread-safe) and more intuitive. For
example, to represent 1st of January 2009 midnight:
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import static java.lang.System.out;
import org.joda.time.DateTime;
public class Calendar1 {
private static final String FORMAT = "yyyy/MMM/dd HH:mm:ss";
public static void main(String[ ] args) {
Calendar cal = GregorianCalendar.getInstance( );
// define it locally as this class is not thread-safe
SimpleDateFormat sdf = new SimpleDateFormat(FORMAT);
// You may think it is midnight 1st of Jan 2009
cal.set(2009, 1, 1, 0, 0, 0);
79
80
Dozer is a JavaBean to JavaBean mapper that recursively copies data from one
object to another. Typically, these Java beans will be of different complex types.
It also makes your code more readable without the nested loops and null
reference checks.
Spring: utility APIs like org.springframework.util. Assert help you write fail-fast
code by validating arguments. Useful for identifying programmer errors early
81
Many other frameworks like Hibernate, Struts, Tiles, Maven, ANT, IVY, Terra
cotta, OSCache, Log4J, etc.
Q11 In your experience, what are some of the most common errors Java programmers
make? LF CO CC EH
A11
Not being aware of data over flows, using == with float or double compar
isons, and storing money in floating point variables.
Comparing two objects using == instead of .equals( ). When you use ==, you
are actually comparing two object references, to see if they point to the same
object. For example:
public class StringEquals {
public static void main(String[ ] args) {
String s1 = "Hello";
String s2 = new String(s1);
String s3 = "Hello";
System.out.println(s1 + " equals " + s2 + " -> " +
s1.equals(s2));
System.out.println(s1 + " == " + s2 + " -> " + (s1 == s2));
System.out.println(s1 + " == " + s3+ " -> " + (s1 == s3));
}
}
82
//true
//false
//true
The variable s1 refers to the String instance created by "Hello". The object
referred to by s2 is created with s1 as an initializer,. thus the contents of the
two String objects are identical, but they are distinct objects. This means that s1
and s2 do not refer to the same object and are, therefore, not ==, but equals( ) as
they have the same value "Hello". The s1 == s3 is true, as they both point to
the same object due to internal caching.
If you plan to add objects of your own class to sets or maps, your class should
override hashCode( ) and equals( ) methods. If a class implements a Comparable
interface, the compareTo(..) must return zero, if and only if the equals(..) method
returns true for the same non-null argument, and vice versa. Violating these
contracts may cause unexpected results.
Not thinking about immutability, thread safety, and atomicity every time
designing a Java class. To be more specific, not using proper synchronization or
immutable objects. Given the architecture of the JVM, you need to only be
concerned with instance and class variables when you worry about thread
safety. Because all threads share the same heap, and the heap is where all
instance variables are stored, and multiple threads can attempt to use the same
object's instance variables concurrently. You needn't worry about multithreaded access to local variables, method parameters, and return values,
because these variables reside on the Java stack. In the JVM, each thread is
awarded its own Java stack. No thread can see or use any local variables, return
values, or parameters belonging to another thread.
Using floating point to try to represent exact quantities like monetary amounts.
Double or float are not recommended for monetary calculations, since they
always carry small rounding differences. Either use BigDecimal or represent
them with smallest possible units without the decimals like in cents using int or
long. For example,
import java.math.BigDecimal;
// $2.00 - $1.10
public class FloatingPoint {
84
//optional
//90 cents
Use of Integer (immutable wrapper object) vs int (primitive), and this goes for
any primitive version of an object. This is specifically an annoyance caused by
not thinking of Integer as different from int since you can treat them the same
much of the time due to auto-boxing.
public class AutoBoxing2 {
public static void main(String[ ] args) {
int p1 = 6;
int p2 = 6;
Integer o1 = new Integer(6);
Integer o2 = new Integer(6);
Integer o3 = Integer.valueOf(6);
Integer o4 = Integer.valueOf(6);
System.out.println(6 == p1);
System.out.println(p1 == p2);
System.out.println(p1 == o1);
// Prints true
// Prints true
// Prints true
85
}
}
Note: o3 and o4 points to the same object as the same instance is returned due
to caching when valueOf( ) method is used.
86
Using a java.util.LinkedList for random access. The LinkedLists are designed for
sequential access. The stacks and queues are designed for sequential access as
well. Using these collection classes for random access (e.g. get(i) ) has the
complexity of O(n). For random access, use a List if duplicates are allowed,
use a Set if duplicates are not allowed, and use an array if the size is known
and duplicates are allowed, as the arrays are more efficient than Lists.
Note: Even if you are not an experienced professional, you can proactively learn the
potential pitfalls and avoid them. You can also use this to your advantage to prove your
technical capabilities at job interviews, team meetings, and code review sessions. You
must learn from others' mistakes and experience rather than waiting to experience it
87
88
Search for a file with a specific name in a set of files in the current (i.e. .)
directory.
find . -name "rc.conf" -print
Searching for old files. Finding files that are 7 days old or finding a file that has
not been accessed for 30 days more, and files that are larger than 100k:
find . -mtime 7 -print
find . -type f -atime +30 -print
find . -type f -size +100k -ls
The grep and egrep commands are very handy for tearing through text files and
finding exactly what you want very quickly.
List all the log files that have a particular search text.
grep -l "Missing Characteristic Value" *.log*
Tip: The Java LinkageError is tricky to resolve and it indicates that a class has some
dependency on another class; however, the latter class has incompatibly changed after
the compilation of the former class. Since plethora of third party jar files are used in
89
export JAVA_HOME=/usr/java/jdk1.5.0_09
export PATH=$PATH:${JAVA_HOME}/bin
export CLASSPATH=./:${JAVA_HOME}/lib/rt.jar
Working with the Java processes and checking your network configurations. These are
useful tools for monitoring processes, connections, and diagnosing problems.
90
0.0.0.0:0
LISTENING
4784
ping <hostname>
telnet <hostname> <port-no>
ping www.example.com
telnet www.example.com 80
wget <URL>
wget http://www.example.com/
wget ftp://ftp.example.com/.sample.tar.gz
nslookup www.example.com
The sudo command stands for "superuser do". It prompts you for your personal
password and confirms your request to execute a command by checking a file, called
"sudoers", which the system administrator configures. Using the sudoers file, system
administrators can give certain users or groups access to some or all commands
without those users having to know the root password. It also logs all commands and
arguments so there is a record of who used it for what, and when.
92
history
(display all recent commands)
history | grep ps
(display ps related commands)
!!
(repeat the previous command)
!45
(!n > 45 is the number from history list. Repeat this command)
!sudo
Note: If you are running on Windows, install Cygwin or MSYS, which gives you a
Unix like environment for Windows to practice on.
Q13 Can you give some examples of where you used sed and awk utilities? PF
A13 Note: find, grep, sed, and awk are cornerstones of shell programming. The last
question covered some practical examples of find and grep. The awk and sed
commands are discussed below.
Awk is used for processing text based data, either in data streams or files. It extens
ively uses regular expressions. Here is a stream based example, using space as the
default delimiter.
$ date "+%Y %m %d"
outputs: 2011 01 31, if you want to extract only year
$ date "+%Y %m %d" | awk '{print $1}'
outputs: 2011, and if you want to extract date/month
$ date "+%Y %m %d" | awk '{print $3 "/" $2}'
outputs: 31/01.
Another typical example would be to extract the shell script name as shown below:
#!/bin/sh
#This is a comment
echo $0
echo `basename $0`
SCRIPTNAME=`basename $0 | awk -F. '{print $1}'`
echo "script names is $SCRIPTNAME"
EXTENSION=`basename $0 | awk -F. '{print $2}'`
echo "extension is $EXTENSION"
93
below:
Note: The commands s (substitute), d (delete) , p (print), etc work on the pattern
space.
$ echo day market | sed -e s/day/night/
The above s command substitutes first occurrence of day with night. The out
put is:
night market
the s in s/day/night/ stands for substitute or replace.
$ echo day market, day job | sed -e s/day/night/g
The output is:
night market, night job
The g in s/day/night/g stands for global replacement. i.e. replace every occurrence
of day with night.
In real life examples, the ETL jobs uses shell scripts to validate and load data from feed
files that look as shown below (e.g. MyFeedFile):
#SourceSystem:MortgageCustomers
#TERM.DEPOSIT.ACCOUNTS.20100603.01:45
2083490~MRS Liz RICHES~1055.43~2008/06/06 00:00:00~2009/09/04
2083491~MRS Liz Jones~1055.43~2008/06/06 00:00:00~2009/09/04
#END
The records starting with # are the header records and ending with # is the trailer
95
97
x exchanges the hold buffer with the pattern buffer, p prints out the pattern
space. d deletes the current pattern space.
/jar$/{x;d;}; Applies the regular expression jar$ that matches lines ending with
jar like ./deploy/MyApp/WEB-INF/stax-api.jar. It swaps this value with the hold
buffer, and deletes pattern space. Initially the hold buffer will be empty. The line
ending with jar will be moved to the hold buffer as shown in the diagram.
/class$/ {x; p;} Applies the regular expression class$ that matches text ending
with
class
like
2254
Thu
May
06
15:35:48
EST
2010
javax/xml/namespace/QName.class. It swaps the hold buffer with the pattern space,
and then prints the pattern space. For example, the line ending with jar residing in
the hold buffer will be moved to the pattern space and printed. The lines ending with
class will be moved to the hold buffer.
Note: The above examples have only scratched the surface of the awk and sed utilities.
There are plethora of online resources to expand your experience and knowledge on
these utilities. Reference: http://www.catonmat.net/blog/sed-one-liners-explainedpart-one.
Q14 Can you answer the following Unix questions? PF
Q. Can you explain the following Unix command?
$ date ; ps -ef | awk '{print $1}' | sed -e '1d'| sort | uniq | wc -l >> activity.log
A. The date prints the date and time. The ps -ef outputs all the running processes
with the UID (user ids) as shown below:
98
w
2
x
1
7 means the user can read, write, and execute (i.e. 4+2+1). Same as chmod u+rwx
file
5 means the group can read and execute (i.e. 4+1). Same as chmod g+rx file
4 means others can only read (i.e. 4). Same as chmod g+r file
Q. How will you remove execute access to the group in the above example?
A. One of the the following 2 ways
$ chmod g-x file
$ chmod 744 file
#line1
#line2
Note: In line1, you can use + to add a permission and - to remove a permission.
A - is used here because you are removing the execute permission from the group.
Note: Every user has a default set of permissions which apply to all files created by
that user, unless the software explicitly sets something else. This is often called the
'umask'. It is either inherited from the login process, or set in the .cshrc or .login file
which configures an individual account, or it can be run manually.
Q. How do you create a new file in Unix?
A. $ touch MyFile.txt
100
Validates the extract file to ensure that it has the right header and trailer
records.
Counts the number of detail records.
Uses a command-line utility known as bcp (bulk copy) to load the data from
the csv extract file into a staging table in the database.
${BCP} staging_fund in fund_extract_file.csv -U user1 -S fund_server -P
pwd -e $ERRLOG -t\, -c
Note that the -t\, indicate that the extract file fund_extract_file.csv is comma
delimited, and the data will be loaded in to a staging table named staging_fund
running on a database server named fund_server. The credentials to log in
are user1/pwd.
Uses an interactive command-line SQL tool like iSQL (i.e. for the Sybase
database ) or SQL*Plus (i.e. for the Oracle database) to execute a database
stored procedure that will move the fund rates data from the de-normalized
staging table to a number of other normalized tables used by the term deposit
application.
${ISQL} -U $user1 -S $fund_server -D fund_database -P pwd -o
$TEMP_ROWCOUNT<<
102
EOF
declare @rowcount int
SELECT @rowcount = COUNT(*) FROM staging_fund
SELECT "ROWCOUNT=" + convert(varchar, @rowcount)
go
EOF
Compares the number of records loaded into the staging table against the
record count that was performed in the earlier step.
If the count matches return the batch process as a success. Otherwise, mark it
as a failure with an exit code of 2, and email the error message.
TEMPTABLE_ROWCOUNT=`grep ROWCOUNT $TEMP_ROWCOUNT
| cut -d= -f2`
#forced conversion into numeric
TEMPTABLE_ROWCOUNT=`expr $TEMPTABLE_ROWCOUNT + 0`
if [ $TEMPTABLE_ROWCOUNT -ne $ROWCOUNT ] ; then
echo "failed to fully load into database" >> $LOG
${MAIL} -s "Error Loading Funds" ${FUND_EMAIL} < ${LOG}
exit 2
fi
Firstly, you will be defining a simple online query language for the end users to
use. For example,
display inactive users;
display new users;
104
Secondly, you will be writing a grammar or definition file for the query
language you just created to validate and parse the entries. Parser generation
tools like JavaCC, ANTLR, etc take this grammar definition file as input to
automatically generate a number of .java source files containing complex logic
Finally, you will use these auto-generated parser source files in your application
code to validate and tokenize a stream of input characters from the online
user query language into tokens. These tokens can be used to programmat
ically generate an SQL statement to retrieve relevant data from the database
and passed back to the user.
Annotation Processing Tool (APT) was introduced in JRE 1.5 along with annota
tions. This very powerful tool enables you to generate new derived source files, class
files, deployment descriptors, etc from a number of base files (i.e. java source code
files) and annotations. To use APT, you need to have tools.jar in your classpath.
APT determines what annotations are present on the source code being operated on.
Next, APT looks for annotation processor factories you've written. The tool asks the
factories what annotations they process. Then APT asks a factory to provide an
annotation processor if the factory processes an annotation present in source files
being operated on. Next, the annotation processors are run. If the processors have
generated new source files, APT will repeat this process until no new source files are
generated.
Example: Generate a simple Java source file named ClassPrinter.java in a package
named annotation.example that simply prints the class names of all supplied source
files.
package annotation.example;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.apt.AnnotationProcessorFactory;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;
105
+ processedOnce );
if(processedOnce) {
return;
}
writer = env.getFiler( ).createSourceFile(
"annotation.example.ClassPrinter");
writer.println("package annotation.example;");
writer.println( );
writer.println("public class ClassPrinter {");
writer.println( );
writer.println(" public static void main(String[ ] args) {");
for (TypeDeclaration typeDeclaration : env
.getTypeDeclarations( )) {
writer.println(String.format(
" System.out.println(\"Class: %s\");",
typeDeclaration.getQualifiedName( )));
}
writer.println(" }");
writer.println( );
writer.println("}");
processedOnce = true;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
To run the apt:
apt -cp ./bin -s ./gen-src -factory
annotation.example.ClassPrinterAnnotationProcessorFactory src/*.java
-cp : where to find class files and annotation processor factories.
-s : where to place source files generated by the annotation processor
"ClassPrinterAnnotationProcessor".
-factory: Name of AnnotationProcessorFactory to use
107
108
Beyond Compare for comparing both ASCII and binary files and directories.
This is a very handy utility, if you want to compare different versions of the
same ASCII file to understand what has changed or to compare two archive
files to ascertain what new files have been added or deleted. Very handy
especially during the build and release phases of the software development life
cycle (SDLC).
Notepad++ for editing and viewing text files and log files. It has powerful
editing features, uses less CPU, and opens larger files.
WinZip or 7-zip for working with the archived files (e.g. .zip files).
PuTTY for SSH and Telnet implementation on Windows. You can connect to
other Unix/Linux environments using SSH or Telnet. This tool also allows you
to perform SSH tunneling or also known as port forwarding to bypass firewalls
where certain ports are blocked. Common ports for example
POP3,SMTP,HTTP and FTP can be easily tunneled using SSH.
FileZilla or WinSCP to transfer files using FTP or SCP respectively. Log files
or database extract files can be transferred from a Unix machine to your
sandbox (i.e. local machine) using these utilities, and explored further using
Notepad++ as it highlights search text or database extracts can be executed
against a database.
Ethereal or Wireshark for analyzing the network protocols & issues. Devel
oping applications that interact with web services presents a unique set of
problems like not knowing exactly what message was sent to the server, or
what response was received. Some of the most difficult bugs to track down are
caused by a disconnect between what you think you are sending to the server,
and what is actually going across the wire. These tools are commonly called
"packet sniffers" and capture all network packets that move across your
network interface. Examining the contents of these packets and the order in
which they were sent and received can be a useful debugging technique.
JMeter for performance testing. Badboy can be used to record your scripts
during functional testing, and then save the same script as a JMeter file to do
performance testing using JMeter. There are other commercial performance
testing tools like HP Load Runner.
XML editors like XMLSpy and Oxygen to work with XML related techno
logies like XSDs, XSLs, WSDLs, XQuery, XPath, etc.
Client side debugging tools like FireBug for FireFox and Fiddler to debug
HTML, JavaScript, CSS, and Ajax requests/responses. There are many other
handy FireFox plugins like Modify Headers, Live HTTP headers, and
Tamper Data to test and debug client side.
Static analysis tools: Static analysis tools are used to find out programming errors in
the code by analyzing their byte code.
110
FindBugs is a program which uses static analysis to look for bugs in Java code.
PMD scans Java source code and looks for potential problems like unused
code, duplicate code, overcomplicated expressions, suboptimal code, etc.
Any good software development should have good documentation. This includes
technical specifications, user manuals, support documentations, etc. These documents
are generally produced using either Microsoft Word application or Oracle's Open
Office writer. A good documentation must be concise with adequate diagrams, proper
versions and access control. The following tools come in very handy.
Q. What tools have you used for the build and release processes? Can you also discuss
some of the benefits of using those tools? SD
A.
Maven and/or Ant with IVY to build and package the relevant artifacts.
Bamboo, Continuum, CruiseControl, Hudson (more poplar), etc for
continuous build and integration.
Unix based package management tool like RPM for release and installation.
Use tools like Maven or Ant with Ivy for build & dependency management:
Java, but compliments it with simpler syntax, dynamic typing, and features like closures
and builders. In some cases, it makes more sense to use Groovy over Java. For
example,
You can do all the same things with JUnit in Groovy that you can do in the
Java language with far fewer keystrokes. The JUnit run time is built into the
Groovy runtime, and Groovy unit tests are easily scriptable with build tools like
ANT, and provides many additional JUnit assertion statements and you can
mock with Groovy Mocks. For example, to test for three persons with names
Peter, Jason, and Tom in Java
List<Person> persons = s.getPeople( );
assertEquals(3, persons.getSize( ));
assertEquals("Peter", persons.get(0).getName( ));
assertEquals("Jason", persons.get(1).getName( ));
assertEquals("Tom", persons.get(2).getName( ));
Same results can be achieved in Groovy with a single line as shown below.
assertEquals(['Peter', 'Jason', 'Tom'], s.persons.collect {it.name})
Note: it means this person. It compares each person object for his/her
name, and compares it with the supplied names. You can do more with less
code in groovy.
You can write a generic online forms engine in Java/JEE, and customize its
behavior with simple Groovy driven scripts for specific online form applica
tions like term deposit forms, cash management account forms, etc..
You can use Java with Groovy or Jython for rapid prototyping. For example,
you can build web applications using the GroovyServlet. Groovy has a concept
that lets you write Java servlets in Groovy called Groovelets. Even the JDK is
boosted with some nice features from Groovy to work with Files and mark
up languages like XML, HTML, etc.
Some of the other scripting languages like JavaScript and Unix shell scripting are kind
of must know scripting languages to be a well rounded developer, as many applica
tions are Web based and run on Unix machines. JavaScript is really unavoidable these
113
A19 These questions have been addressed in the Java/J2EE Job Interview Companion
(400+ Q&A).
114
Java Platform API Specification for Java core APIs and third party library APIs.
Java Language Specification (JLS) is worth reading a bit at a time. For example,
section 3.8 covers the Identifiers and 3.9 covers the Keywords.
116
JDK Tools and Utilities reference documentation covers the tools you use to
create, build, and run applications.
Q21 In your experience, why is it important to have relevant APIs handy while coding? LF
COQ
A21
It is a best practice to reuse proven, well-tested, and robust APIs as opposed to
reinventing the wheel or writing sub-standard functions. Writing APIs is not a
trivial task.
These APIs and methods make your code more readable and maintainable. It
can not only help you write one liner code, but also can warn you of some
hidden dangers like if a particular class is thread-safe, if a particular method is
deprecated and relevant replacement for a deprecated method, and other
recommendations you can find like the one under java.lang.Object class' equals( )
method details as described below:
"Note that it is generally necessary to override the hashCode( ) method whenever
this method is overridden, so as to maintain the general contract for the
hashCode( ) method, which states that equal objects must have equal hash
codes."
Lets look at this with some interesting examples.
Example1: The BigDecimal API clearly states that the BigDecimal(double)
constructor is not recommended as it is unpredictable and BigDecimal(String)
must be used instead. Try running the following code?
import java.math.BigDecimal;
//$2.00 - $1.10
public class JavaAPI {
public static void main(String[ ] args) {
//Don't: unpredictable and not recommended
117
118
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
public class OneLiner {
private static final String FRUITS = "Apple, Mango, Orange, Grapes";
public static void main(String[ ] args) {
// No need to do this
StringTokenizer st = new StringTokenizer(FRUITS, ",");
List<String> listFruits = new ArrayList<String>( );
while (st.hasMoreTokens( )) {
listFruits.add(st.nextToken( ));
}
String[ ] fruits1 = new String[listFruits.size( )];
fruits1 = listFruits.toArray(fruits1);
System.out.println(Arrays.deepToString(fruits1));
// Do this instead
// one liner tokenizing using Regex
String[ ] fruits2 = FRUITS.split(",\\s*");
// prints only the reference
System.out.println("reference only --> " + fruits2);
// No need to do this
int count = 0;
for (String fruit : fruits2) {
++count;
System.out.print(fruit);
if (count <= fruits2.length - 1) {
System.out.print(", ");
}
else {
System.out.print("\n");
}
119
Q23 What do you expect to see in a high level or detailed business requirements
document? OEQ
A23
Purpose, constraints, scope inclusions, scope exclusions, etc to give an intro
duction to the project.
Business objectives clearly defined, so that the design alternatives can be appro
priately evaluated. For example,
What if the rebate elections are made after the monthly process is
started?
What if the accounts are moved from one adviser to another adviser?
Q24 What tips do you give to someone who wants to write a technical documentation?
A24
Be clear about why you are creating this documentation and who will be
reading it. Put yourself in their shoes.
Gather information in the domains where you are not the subject matter
expert.
Organize and outline information. Fill the information you already have and
leave areas blank where you need to gather more information.
Identify gaps in the requirements, get it clarified, and updated.
Q25 Do you use test driven development? Why / Why not? COQ SP
A25 [Hint:] Yes.
Gives you a better understanding of what you're going to write. Gets you to
clearly think what the inputs are and what the output is. Helps you separate
the concerns by getting you to think about the single responsibility principle
(SRP).
Enforces a better test coverage. This gives you the confidence to refactor your
code in the future, since you have a good coverage.
Q26 What tips do you give someone who is writing the unit testing? COQ SP
122
A26
Write unit tests and re-factor your code where necessary to make it more
maintainable and readable. BP It is a best practice to write your unit tests first
along with an empty implementation of your functionality For example, a
function that just returns false or null so that your tests fail. Then fill the empty
functions with proper implementations to pass your tests.
Tests should not depend on other tests. Use mock objects where appropriate to
ensure that the tests are re runnable without the fear of having the right data
in the database. Alternatively, the dbUnit framework allows you to create a data
set, which is automatically created into the real database before running the test
code and can clean up its mess at the end if necessary.
Think of all possible inputs your test should pass for it to be correct. For
example, can you think of all possible input values you need to test for the
following method?
public class isOdd {
public static boolean isOdd (int i) {
return i % 2 == 1;
}
}
// buggy
You need to test with an odd number (e.g. 3), even number (e.g. 4), zero (e.g 0),
and a negative number (e.g. -3). If you closely pay attention, you will notice that
the above code will fail for negative odd numbers. Say i = -3;
-3 % 2 returns -1;
-1 == 1 returns false. This should actually be true since -1 is an odd number.
If tested for all possible conditions, this error can be picked up during testing
and rectified as follows,
public class isOdd {
public static boolean isOdd (int i) {
return i % 2 != 0;
}
}
//fixed
123
Include negative test cases in your test plan. Under ideal testing conditions,
network timeouts don't occur. it is critical that attention is given to the problem
of network timeouts during application development, for both network clients
and network servers. Infinite timeouts can cause your application to hang for
ever.
socket.connect(remote, 30000);
java.net.Socket.setSoTimeout(30000) ;
Write your code for better testability. The key to any unit testing, as the term
unit implies, being able to instantiate and test a small portion of the application
and asserting that it does something correctly with both positive and negative
test cases. If you can only instantiate a large portion or the whole application,
then you cannot have a small unit test. So always look for signs that can make
your code harder to unit test while writing your code. Please refer to Misko
Hevery's thought provoking online articles for writing better testable code at
http://misko.hevery.com/.
Avoid constructing complex object graphs in your constructor. Use a
factory or builder class to construct complex objects.
Use dependency injection to more loosely couple your callee from
the caller. The dependency injection frameworks (aka Inversion of
Control (IoC) containers) like Guice, Spring, HiveMind, etc make your
code more testable, maintainable, and flexible through looser coupling.
Judiciously apply the Law of Demeter to minimize coupling and
improve testability. The Law of Demeter was originally formulated as a
style rule for designing OO systems by only talking to your immediate
object. For example. Prefer writing,
customer.getName( );
to a longer version shown below:
order.getContact( ).getCustomer( ).getName( );
124
Apply the fail fast principle (aka design by contract). A fail-fast code will
immediately report any failure or conditions attempting to continue a possiblyflawed process. Firstly, define your preconditions which check if the object is
125
126
Know your OO concepts and apply them. For example, use of encapsu
lation lets you create well-defined boundaries which are only connected by
narrow bridges. Another example would be to prefer interface-inheritance
with composition to implementation inheritance for loose coupling and better
flexibility.
Be aware of the design principles that allow your code to be more flexible
to extend, reuse, and understand. DC DP
business logic and data access logic are segregated into their own
classes.
Apply well proven design patterns like Gang of Four (GoF) design patterns
that help you solve recurring problems in software development. Based on the
GoF authors' experience, a good software design should Program to an
interface, not an implementation and object composition should be favored
over class inheritance (aka implementation inheritance).
Q28 Can you list some of the key aspects you keep in mind while coding to ensure quality
& robustness? COQ BP DC DP
A28
Use the right tool to get the job done. For example, don't use String opera
tions to parse or write XML documents. There are a number of considerations
to look out for like:
Use an appropriate XML library like Xerces, JDom, StAX, JiBX, etc to get the
job done correctly and efficiently.
When coding, think about thread-safety and atomicity. Issues arising from
these are harder to detect and test.
Always write readable code. Don't rely too much on comments. Make your
code self-explanatory with meaningful variable, method and class names.
"Any fool can write code that a computer can understand.
129
by Martin Fowler
Instead of writing:
int a;
Use:
int maxAge;
If you have meaningful variable and method names, you don't have to clutter
your code with comments. Books like Clean Code: A Handbook of Agile
Software Craftsmanship by Robert C. Martin will be a good start to write
clean code.
Reduce state with local variables or make your code inherently thread-safe
with immutable objects where possible.
Where possible reuse well proven and tested API methods from the Java
code libraries and open-source libraries without having to write your own.
130
System resources are finite and manage them properly. The non-memory
resources like file handles, sockets, database connections, LDAP connections,
etc need to be closed properly in a finally block once finished with them. Don't
rely on the finalize( ) method to close these finite resources as you can't know
when a finalize( ) method will be invoked by the garbage collector. It may take
hours for the garbage collector to invoke a finalize( ) method. Creating and
destroying some of these resources like threads and connections frequently can
be expensive. Hence, maintain a pool of these resources and return them to
the pool to be reused. Use proper time out values for the network connec
tions, to prevent a thread being blocked infinitely. The heap memory is finite
and use proper object reuse (e.g. immutable objects) strategies. Don't assume
that generating a file is small enough to fit into the available heap memory as
shown below:
byte[ ] readFile = generatePDF(...);
The above code is vulnerable to memory issues if accessed concurrently by
multiple threads. Don't use byte arrays for bulk data. Use streams to write the
data to a file or database. Make it a point to buffer your streams to minimize
the number of read/write call to the native system.
InputStream in = new BufferedInputStream(new
FileInputStream(myFile));
Q29 What are mock objects and when do you use them? COQ
A29 Mock objects are simulated objects that mimic the behavior of real objects in
controlled ways. Mock objects can be used in unit tests and other parts of your code to
simulate interactions with resources that are
Some of the frameworks used in Java for creating mock objects are EasyMock, Mockito,
PowerMock, and jMock. The Mockito framework is quite easy to learn, and the PowerMock
extends Mockito and EasyMock to provide support for private and static methods. The
PowerMock also contains other useful features such as suppressing static initializers and
constructors.
131
At the end of the day, Information Technology exists for the business. Business must
be able to respond to changing customer needs and wants very quickly. So take the best
parts of the tool kits and attack projects in a way that makes sense for that project. It is
also at times required to resort to tactical or hybrid solutions as opposed to strategical
or cleaner solution, to meet the business objectives. A typical example would be that a
business might be structured and operates in a certain way, for example by products or
services. A good developer will look at things from both business and technology
perspective. This is why in real life, you will often end up having hybrid solutions to get
the best of both worlds as opposed to religiously following what the proponents say.
132
Section-4:
Language Essentials
This section is mainly for beginner to intermediate level. It also contains useful
information for senior level candidates, and worth having a quick browse through, especially
if you are required to sit for a basic Java technical test. Even among experienced profes
sionals, there are a breed of developers who know how to get things done by using various
frameworks, tools, and googling without fully understanding the fundamentals. Under
standing the core Java fundamentals are essential not only from the point of view of passing
the technical tests used for screening potential candidates and to perform well in technical
job interviews, but also from the point of view of showing off your technical capabilities
within limits to your peers and superiors during code review sessions, team meetings, and
situations where you were able to resolve an intermittent, critical, or obscure problem more
quickly than others as you understand the nitty-gritty details.
It's important to let others know about the good things you are accomplishing. Don't
think that by just working hard that you'll get noticed. You need to let people know,
133
Language Essentials
especially your leaders like to know these things because they can't know everything that is
going on, so speak up when required to. A little preparation prior to a code review session
or an important team meeting that was going to discuss a class loading issue can make a
huge difference in making an impact. All you have to do is refresh your memory on the
basics.
134
What are identifiers in Java? What does the following code do? Can you talk us
through the code highlighting some of the key language and design features? COQ
LF BP
package chapter2.com;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public final class ValidIdentifiers {
private enum Validity {
Valid, InvalidIdentifierStart, InvalidIdentierPart, ReservedKeyWord,
ReservedLiteral
};
private static final String[ ] RESERVED_KEYWORDS = { "abstract",
"continue", "for", "new", "switch", "assert", "default",
"if", "package", "synchronized", "boolean", "do", "goto",
"private", "this", "break", "double", "implements",
"protected", "throw", "byte", "else", "import", "public",
"throws", "case", "enum", "instanceof", "return",
"transient", "catch", "extends", "int", "short", "try",
"char", "final", "interface", "static", "void", "class",
"finally", "long", "strictfp", "volatile", "const",
"float", "native", "super", "while" };
private static final String[ ] RESERVED_LITERALS = {"true", "false", "null"};
private static Set<String> KEYWORDS = new HashSet<String>(
(int)(RESERVED_KEYWORDS.length/0.75));
private static Set<String> LITERALS = new HashSet<String>(
(int)(RESERVED_LITERALS.length/0.75));
135
Language Essentials
static {
List<String> list = Arrays.asList(RESERVED_KEYWORDS);
KEYWORDS = new HashSet<String>(list);
List<String> listLit = Arrays.asList(RESERVED_LITERALS);
LITERALS = new HashSet<String>(listLit);
}
public static final Validity valid(String input) {
if (input.length( ) == 0
|| !Character.isJavaIdentifierStart(input.charAt(0))) {
return Validity.InvalidIdentifierStart;
}
for (int i = 1; i < input.length( ); i++) {
if (!Character.isJavaIdentifierPart(input.charAt(i))) {
return Validity.InvalidIdentierPart;
}
}
if (KEYWORDS.contains(input)) {
return Validity.ReservedKeyWord;
}
if (LITERALS.contains(input)) {
return Validity.ReservedLiteral;
}
return Validity.Valid;
}
}
A1
Identifiers are names you give to your variables, constants, classes, interfaces and
methods.
The above code verifies if a given input is a valid identifier, complying with the
following rules:
136
The rest of the characters in the identifier can be a letter, underscore, currency
sign, or digit. Note that spaces are NOT allowed in identifiers.
Identifiers are case-sensitive. This means that age and Age are considered as
different identifiers.
Identifiers cannot match any of Java's reserved words like for, int, etc or
literals like null, true, and false..
Note: true, false, and null might seem like keywords, but they are actually
literals; you cannot use them as identifiers in your programs.
Note: const and goto are reserved, but not currently used. enum was added in Java 5.
strictfp allows you to have more predictable control over floating-point arithmetic.
Q. Can you talk us through the code highlighting some of the key language and
design features?
A.
Use of enums and generics indicates that this code must be using JDK
version 1.5 or greater.
DC It follows the code to interface design principle. For example, the Set,
List, etc shown below are interfaces.
private static Set<String> KEYWORDS = new HashSet<String>(
(int)(RESERVED_KEYWORDS.length/0.75));
//...
List<String> list = Arrays.asList(RESERVED_KEYWORDS);
BP Making use of the Java API methods where possible. For example, the
methods shown below from the Character and Arrays classes, simplify your
code. So don't memorize the Java API, but keep it handy and constantly refer
to it.
137
Language Essentials
Character.isJavaIdentifierStart(input.charAt(0))
Character.isJavaIdentifierPart(input.charAt(i))
List<String> list = Arrays.asList(RESERVED_KEYWORDS);
BP:
BP Checking for null and empty string as a precondition in the beginning of
the valid(String input) method.
if (input == null || input.length( ) == 0 ...)
The above code snippet is a slight deviation of the fail fast principle, which
states that check and report any possible failures as soon as possible. Testing
your code for failure points will lead to better, safer, and more reliable code.
The Apache commons library class StringUtils can be introduced here. The
method isEmpty (String input) can be used to check for both null and empty
string. This library does have other useful methods that can be used elsewhere
in the application code to enforce the fail fast principle.
StringUtils.isEmpty(input)
138
Note: Even though the above recommendations are debatable depending on various
other factors, it is better to know the possible options to demonstrate your experience
than to have no ideas at all.
Q2
A2
$Ident1
_Ident1
-Ident1
2Ident1
private
private1
null
Ident-1
Ident$1
\u00A3Ident1
\u00A5Ident1
The legal identifiers are a,b, f, i, j, and k. Let's make use of the class ValidIdentifiers
shown in Q1, to test the identifiers.
package chapter2.com;
//using JDK 1.5 static import
import static chapter2.com.ValidIdentifiers.valid;
import static java.lang.System.out;
public class ValidIdentifiersExample {
public static void main(String[ ] args) {
/**
* A valid identifier start consists of letters (e.g.
* a-zA-Z), digits(0-9),underscores (_) and currency symbols
* ($,\u00A3(Pound),etc),But cannot start with a digit.
139
Language Essentials
*/
out.println("a) $Ident1=" + valid("$Ident1"));
out.println("b) _Ident1=" + valid("_Ident1"));
out.println("c) -Ident1=" + valid("-Ident1"));
out.println("d) 2Ident1=" + valid("2Ident1"));
//Valid
//Valid
//InvalidIdentifierStart
//InvalidIdentifierStart
/**
* an identifier cannot have the same spelling as one of
* Java's reserved words like final, synchronized, etc or
* a reserved literal
*/
out.println("e) private=" + valid("private"));
out.println("f) private1=" + valid("private1"));
out.println("g) null=" + valid("null"));
//ReservedKeyWord
//Valid
//ReservedLiteral
/**
* Cannot use anything other than letters,digits, under-scores and
* currency symbols in part of the identifier
*/
out.println("h) Ident-1=" + valid("Ident-1"));
out.println("i) Ident$1=" + valid("Ident$1"));
//InvalidIdentierPart
//Valid
//Valid
//Valid
}
}
Q. What is an escape character? Can you list some character escape codes in Java?
What are the differences between decimal, octal, and hexadecimal literals?
A. The escape character, the back slash \, is the character that signals the following
character is not what it normally means. Java provides escape sequences for several
non-graphical characters. All characters can be specified as a hexadecimal Unicode
140
character (\udddd) with some as an octal character (\ddd where the first d is limited
to 0-3, and the others 0-7). Refer: http://www.ascii-code.com/
import static java.lang.System.out;
public class EscapeCharacters2 {
public static void main(String[ ] args) {
char c1 = 'A';
//Decimal value of 65
char c2 = '\n';
//new line character
char c3 = '\u0041';
//Hexadecimal character = Decimal value of 65
char c4 = 65;
out.println(c1 + "\t" + c3 ); //prints 'A <tab> A'
out.print(c2);
//prints a 'new line'
out.println(c4);
//Decimal value of 65, which prints letter 'A'
out.println("\\ ");
//prints back slash
out.println("\' \" ");
//prints a single quote followed by a double quote
out.println("\f");
//prints a form feed
out.println("\101");
//Octal value, representing a decimal value of 65
// 1x82 + 0x81 + 1x80 = 65. Prints letter 'A'
out.println("\377");
//Max Octal value -- Decimal value 255
// 3x82 + 7x81 + 7x80 = 255.
//prints a symbol '', according to ASCII table
// value of 255
out.print("\r");
out.println("\u0041");
out.print("\uFFFF");
}
}
141
Language Essentials
Q. How does Java treat Unicode escapes within string literals?
A. The compiler translates Unicode escapes into the characters they represent before
parsing into tokens, such as string literals.
public class UnicodeRepresentation {
public static void main(String[ ] args) {
System.out.println("\u00A3-Pound, \u00A5-Yen");
}
}
Ouput:
-Pound, -Yen
Q3
A3
142
friend
NULL
implement
synchronized
throws
implements
synchronize
volatile
transient
native
interface
TRUE
new
true
strictfp
instanceof
then
throw
this
Note: true is a reserved literal not a reserved keyword. Also, take care with the spelling.
implements ending with an 's' is a keyword, but implement is not. synchronized ending with
'ed' is a keyword, but synchronize is not. Unlike other languages, then is not a keyword.
You only have if and else in Java. Both throw and throws are keywords.
Q4
A4
Why is it a best practice to adhere to naming conventions and what are they? BP
Naming conventions make programs more understandable by making them easier to
read. They can also give information about the function of the identifier. For example,
whether it's a constant, package, or class. The conventions really depend on the
individual place you're coding for. In general,
If you really want to begin a variable name with a digit, prefix the name you'd like to
have (e.g. 9pins) with an underscore, e.g. _9pins. Otherwise use something like ninePins.
Some try to differentiate between member variables, local variables, and arguments
with different patterns. For example,
Coding standards are a key best practice in many development processes, especially in
agile development. Some recommendations simply define a standard way to layout
code or to name classes or variables, while others are designed to make code more
reliable or better performing. There is no real objective reason to prefer one style over
the other as long as it is consistent across the team or organization. Checkstyle is an
open-source tool that can help enforce coding standards and best practices.
143
Language Essentials
How would you go about choosing the right data types for your application? What are
wrapper classes, and why do you need them? LF BP FAQ
A5
Java is what is known as a strongly typed language. This means Java only accepts
specific values within specific variables or parameters. Some languages, such as JavaS
cript, PHP, and Perl are weakly typed languages.
1. Know the data limits to prevent any data overflow
Choose the most appropriate data type based on the minimum and maximum limit. If
you are not too sure, select a larger value to avoid any data overflow. For example,
...
public class OverFlow {
public static void main(String[ ] args) {
// 1 byte - 8 bits -2^7 to 2^7-1
out.println("Min byte:" + Byte.MIN_VALUE);
out.println("Max byte:" + Byte.MAX_VALUE);
// 2 bytes - 16 bits -2^15 to 2^15-1
out.println("Min short:" + Short.MIN_VALUE);
out.println("Max short:" + Short.MAX_VALUE);
// -128
// 127
// -32768
// 32767
144
// -2147483648
// 2147483647
// 1.4E-45
// 3.4028235E38
// 2100000007
// 4200000000
Language Essentials
Each primitive data type has a corresponding wrapper class like Integer, Long, Character,
Float, Double, etc. There are 8 primitive variables and as many wrapper objects. In Java
5, additional wrapper classes like AtomicInteger, AtomicLong, AtomicBoolean and Atomic
Reference<V> were introduced to provide atomic operations for addition, increment,
and assignment. These additional classes are mutable and cannot be used as a
replacement for regular immutable wrapper classes.
Why prefer wrapper objects to primitives? Wrapper objects can be initialized to
null. This can't be done with primitives. Many programmers initialize numbers to 0 or
-1 to signify default values, but depending on the scenario, this may be incorrect or
misleading. Wrapper objects are very useful for optional data. Databases almost always
have a significant fraction of their fields as optional (that is, as possibly NULL). In
addition, the forms submitted in Web applications can contain optional parameters.
Both NULL database fields and missing request parameters naturally map to null
object references. With primitives, there is no such natural mapping.
BP Wrapper objects will also set the scene for a NullPointerException when something is
being used incorrectly, which is much more programmer-friendly as it fails fast than
some arbitrary exception buried down the line. Preferably, check for null early on in
the method and report it immediately where applicable to adhere to the fail fast
principle.
CC The wrapper objects are immutable, hence inherently thread-safe. Other threads
can only read the values set by the thread that initialized this object.
PC When you create wrapper objects, use the valueOf( ) static factory method for
efficiency.
Integer i2 = new Integer(5);
Integer i1 = Integer.valueOf(5);
to be auto-unboxed before use. Thus there is an extra step for the JVM to perform.
For example, in order to perform arithmetic on an Integer, it must first be converted to
an int before the arithmetic can be performed. In many business applications this
rarely matters unless you were writing something very number-crunching or profiling
indicates that the auto-boxing is a performance or memory issue in a particular part of
your code as it is executed very frequently.
Anti-pattern: Watch out for premature-optimization anti-pattern where you are
tempted to code for a perceived (whether rightly-or-wrongly) performance gain and
sacrificing good design and maintainability.
Q6
When working with floating-point data types, what are some of the key considera
tions? LF BP COQ FAQ
A6
Language Essentials
exponent indicates the positive or negative power of the radix that the mantissa and
sign should be multiplied by. The four components are combined as follows to get
the floating-point value:
sign * mantissa * radix exponent
Floating-point numbers in the JVM use a radix of two as computers only understand
binary. Refer: http://www.h-schmidt.net/FloatApplet/IEEE754.html
sign * mantissa * 2 exponent
This means:
0.5 is represented as 1 * 1.0 * 2-1
-0.25 is represented as -1 * 1.0 * 2-2
0.1 is represented as 1 * 1.6 * 2-4
Floating point arithmetic is rarely exact. While some numbers, such as 0.5, can be
exactly represented as a binary (base 2) decimal (since 0.5 equals 2 -1), other numbers,
such as 0.1, cannot be. As a result, floating point operations may result in rounding
errors, yielding a result that is close to, but not equal to the result you might expect. In
the above FloatingPointEquality example, f will never be exactly 10.0. Use greater than
or less than operators instead.
2. Use long, int, or BigDecimal for storing money, and performing monetary
calculations.
As discussed earlier, floating point data types like float, double, Float, or Double can
have strange results as shown below, and use either the BigDecimal or int/long repres
enting the value in its lowest units like cents..
import java.math.BigDecimal;
import java.math.RoundingMode;
public class FloatingPoint2 {
public static void main(String[ ] args) {
int itemCount = 100;
float unitCost = 0.30f;
148
Language Essentials
A7
byte short char int long float double
(1 byte)
(2 bytes)
(4 bytes)
(8 bytes)
Left to right (e.g. byte to short) is a widening conversion and considered safe
because there is no chance for data loss. For example, byte has a range between
-128 and 127 and short has a wider range between -32768 and 32767. So when you
go from left to right, the data types are implicitly cast by the compiler since it is
safe to do so.
Right to left (e.g. short to byte) is a narrowing conversion and considered unsafe
because there is a chance for data loss. So when you go from right to left, the
compiler expects you to explicitly cast the data to clearly state that it is safe to do
so. If you do not cast explicitly, you will get a compile-time error. For example,
package chapter2.com;
public class DataTypes1 {
/**
* Think about the resulting data type
* if you type a numeric value of 5 means of type int.
* if you type a numeric value of 5L or 5l means of type long.
* if you type a decimal value of 5.0f or 5.0F means of type float
* if you type a decimal value of 5.0, 5.0D, or 5.0d means of type double
*/
public static void main(String[ ] args) {
/**
* signed default values. Anything outside the valid range will cause a
* compile-time error requiring either explicit cast or use of a wider
* data type to fix it. Implicit cast is applied automatically when
150
b = 30;
b = 128;
/**
* unsigned default value
*/
char c = '\u0000';
boolean bool = false;
/**
* left to right widening assignment is generally okay. Implicitly cast
* to the right type. Exception being assigning a byte or short to a
* char.
*/
s = b;
i = s;
l = i;
f = l;
d = f;
i = c;
l = c;
d = s;
/**
151
Language Essentials
* compile-time error. Can't do this
*/
c = s;
//Not okay: type char is unsigned & type short is signed.
/**
* right to left narrowing assignment causes compile-time error if
* not explicitly cast to the right type. Even though the values assigned like i, l,
* f, and d are either 0 or 0.0, the actual values can only be evaluated at run
* time. At compile-time only the type of the data is known, and its actual
* value gets evaluated only at run-time. It is okay to say b = 0; as it is evaluated
* at compile-time.
*/
b = i;
i = l;
l = f;
f = d;
// Not
// Not
// Not
// Not
/**
* fix above compile-time errors with explicit casting.
**/
c = (char)s;
b = (byte) i;
i = (int) l;
l = (long) f;
f = (float) d;
}
}
Note: byte and short are signed data types and they cannot be implicitly cast to
unsigned char data type even though it is a widening conversion. As per the code in
the above example, the short s is first converted to an int using a widening
conversion and then casts it to a char using a narrowing conversion.
Short (s) widening Int --narrowing Char (c)
152
Q8
A8
// 125 good
// -127 bad
Can you explain why the code snippet under (a) gives a compile-time error and code
snippet under (b) compiles and runs with an output of 20? COQ LF
a)
byte b = 10;
b = b + 10;
// line 1
// line 2
b)
153
Language Essentials
byte b = 10;
b+=10;
A9
//line 1
//line 2
Firstly, b is a variable and its value would not be known at compile-time. Its
value will be evaluated only at runtime. Adding 10 to the variable b might
push it outside the byte range as it is not known at compile-time.
154
long l = 0L;
float f = 0.0F;
double d = 0.0;
155
Language Essentials
/**
* ### Simple operations using 'binary numeric promotions' ###
* explicit cast is required to fix compile-time errors
*/
b = b + 10;
i = i + 20;
s = s + 5;
l = l + 5;
i = l + 5;
i = i + 3.0f;
i = i + 3.0;
d = d + 20;
f = f + 20;
f = d + 20;
f = f + 20.0f;
d= d + 20.0;
/**
* Compound Operators, such as +=, -=, *= all contain
* an explicit cast, even though it's not shown.
*
* b+=250; expands to b = (byte) (b + 250); so no compile-time error.
* This can cause unexpected results as shown below due to
* loss of data discussed earlier.
*/
156
// b=0, i=30
b += 250;
System.out.println("b=" + b);
i *= 2147483647;
System.out.println("i=" + i);
}
}
Q10 What is wrong with the following code, and how will you fix it? LF COQ
package chapter2.com;
public class IntegerMaxValue {
public int addFive(int input) {
if (input + 5 > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Invalid input: " + input);
}
return input + 5;
}
public static void main(String[ ] args) {
IntegerMaxValue imv = new IntegerMaxValue( );
System.out.println("value = " + imv.addFive(5));
}
}
A10 As the integer will never exceed Intger.MAX_VALUE, the IllegalArgumentException will
never be thrown. If you try and invoke the method with an integer value within 5
short of its maximum value, data overflow takes place after (input + 5) is evaluated,
resulting in incorrect result, but an exception will never be thrown. The corrected
method addFiveCorrected(..) is added below:
package chapter2.com;
public class IntegerMaxValue {
157
Language Essentials
//..
public int addFiveCorrected(long input) {
if (input + 5> Integer.MAX_VALUE) {
throw new IllegalArgumentException(
"addFiveCorrected( ) -- Invlid input: " + input);
}
// fail fast
/**
* (long+int) is implicitly cast to a long due to numeric promotion
* explicit casting is required to convert implicitly cast long to an int
**/
return (int) (input + 5);
}
public static void main(String[ ] args) {
IntegerMaxValue imv = new IntegerMaxValue( );
//...
System.out.println("addFiveCorrected( ) call value = " +
imv.addFiveCorrected(5));
System.out.println("addFiveCorrected( ) call value = "
+ imv.addFiveCorrected(Integer.MAX_VALUE + 1L));
}
}
Output:
addFiveCorrected( ) call value = 10
Exception in thread "main" java.lang.IllegalArgumentException:
addFiveCorrected( ) -- Invlid input: 2147483648
at chapter2.com.IntegerMaxValue.addFiveCorrected(IntegerMaxValue.java:18)
at chapter2.com.IntegerMaxValue.main(IntegerMaxValue.java:33)
Hence, be aware of the data types and its valid ranges when used in conditional
checks.
158
.
159
Language Essentials
Q13 What is the output of the following code snippet? LF COQ
package chapter2.com;
public class PrePostOperators {
public static void main(String[ ] args) {
int x = 5;
int y = ++x;
int z = y++;
// line 1
// line 2
// line 3
// line 3
// added line
// line 3
Note: This is a common mistake beginners make, especially x=x++ and just x++ do
have different meanings. For example,
int x= 0;
x=x++;
System.out.println(x);
The value of x printed above is 0. This is because the evaluation takes place as
shown below:
int oldX = 0;
x= x+1;
x = oldX;
If you were expecting to get 1, you can fix this by changing the code as follows,
int x = 0;
x++;
System.out.println(x);
or
int x = 0;
System.out.println(++x);
A post-increment (i.e. i++) or pre-increment (++i) in a loop generally makes no
difference. The latter is preferable as it is sometimes more efficient.
Q14 What is the output of the following code snippet? LF
package chapter2.com;
public class NaN {
public static void main(String[ ] args) {
161
Language Essentials
System.out.println("Output=" + 2.0/0.0); //floating-point division by zero
System.out.println("Output=" + -2.0/0.0); //floating-point division by zero
System.out.println("Output=" + 2 /0);
//integer division by zero
}
}
A14 Floating point division by 0.0 returns a NaN (Not a Number) and integer divisions by
0 returns a runtime exception.
Output:
Output=Infinity
Output=-Infinity
Exception in thread "main" java.lang.ArithmeticException: / by zero
at chapter2.com.NaN.main(NaN.java:7)
Q15 What will be the output of following operations? LF
int i = 10 / 3;
int r = 10 % 3;
A15 The integer division results in dropping the remainder of the operation
int i = 10 /3;
// i = 3
Java provides a mechanism to get the remainder of a division operation through the
modulus (aka remainder) operator, denoted by the percent character (%).
int r = 10 % 3;
// r = 1
Q16 Can you convert the decimal value of 127 to binary, octal, and hexadecimal values?
LF
A16 Binary:
Bit number
Binary has 2 possible values
0 (off) and 1(on).
Expanded
Bits that need to be on (i.e. 1)
to add up to 127 are
27
26
25
24
23
22
21
20
128
64
32
16
82
81
80
21
20
22
21
20
22
21
20
The octal value is 0177. (First zero denotes octal representation ). You can verify your
result by converting this back to decimal as follows:
1 x 82 + 7 x 81 + 7 x 80 = 64 + 56 + 7 = 127.
Hexadecimal:
Binary value for 127 is
163
Language Essentials
Hexadecimal has 16 possible
values from 0 to 15 denoted
by 0,1, 2, 3, 4, 5, 6, 7, 8, 9, A,
B, C, D, E, F (i.e. A is 10, B is
11 and F is 15)
161
160
22
21
20
23
22
21
20
Hexadecimal representation of
127.
The hexadecimal value is 0x7F. (0x denotes hexadecimal). You can verify your result by
converting this back to decimal as follows:
7 x 161 + 15 x 160 = 112 + 15 = 127.
Q. How will you convert 0x7F back to binary? LF
Hexadecimal value
7
Value
15
7
8
23
22
21
20
23
22
21
20
The binary value for 0x7F is 0111 111. Here is a sample Java program.
package chapter2.com;
public class Binary1 {
public static void main(String[ ] args) {
164
//127
// 127
//1111111
//1111111
}
}
Q17 How do you represent negative numbers in Java? What is the negative binary value for
the number 37? LF
A17 Negative numbers use 2's complement notation. In order to get 2's complement:
Step 1: take the bit pattern for the equivalent positive number and invert all bits. All
1's to 0's, and all 0's to 1's. This is 1's complement achieved with the Java's unary
operator ~. Unary is a single operand. For example, 5 is 00000101 and ~5 is
11111010.
Step 2: add 1 to the above result. -5 is 11111010 + 000000001 = 11111011.
To evaluate -37:
27
26
25
24
23
22
21
20
128
64
32
16
37 in binary is (32 + 4 + 1)
1 in binary =
165
Language Essentials
Note: For signed bits, the most significant bit (i.e. left most bit) represents the sign. 0
means a positive number and 1 means a negative number. In order to put things into
perspective, some byte values in the range of -128 to 127 are:
-1
-2
-5
-9
-20
-37 -127
129
-128
127
37
30
1 0
128
127
37
30
1 0
-37 in binary
37 + -37 =
0
0
0
0
0
0
0
0
Note: The possible values for binary are 0 and 1. Similar to decimal addition, when
you add 1 and 1, you get 0 and 1 carried over to the left. For example,
0 + 0 = 0;
1 + 0 = 1;
0 + 1 = 1;
1 + 1 = 0; (and 1 is carried over to the left).
Q18 What are the common bitwise operations? LF
A18
b1
b2
b1 | b2
(inclusive
OR)
b1 & b2
(AND)
b1 ^ b2
(exclusive
XOR)
~ b1
(NOT)
166
Operations
>> 2 , slide to right 2 bit places, dropping lower order bits filling on left with
the sign bit. Shifting right by 1 bit is equivalent to dividing by 2 with the
following exception that -1 >> 1 = -1 because of sign extension. -1/2 should
be 0. Other negative numbers will work fine. So x >> n is equivalent to x / 2 n.
36 >> 2 is 36 / 22 = 9.
128
64
32
16
36 in binary is
(The significant bit is 0 in
bold face). Shaded area is
shifted out.
36 >> 2
8+1=9
Note: 36 >> 1 = 18 and 36 >> 2 = 9. The sign bit 0 means a positive number, and 1
means a negative number. The significant bit is the left most bit.
128
64
32
16
-36 in binary
(The significant bit is 1 in
bold face). Shaded area is
shifted out
128 + 64 + 32 + 16 + 4 + 2 + 1 = 247,
which is -9
<< signed left shift
<< 3 , slide to left 3 bit places, dropping higher order bits filling on right with
0's. Shifting left by 1 bit is equivalent to multiplying by 2 . You calculate 2 to
the nth power with x << n. if x = 5, then x << 3 is equivalent to 5 * 2 3 = 40
where n is 3.
128
64
32
16
1
167
Language Essentials
5 in binary is
(The significant bit is 0
in bold face). Shaded
area is shifted out.
5 << 3 ((Highlighted
area is inserted))
32 + 8 = 40
>>> unsigned right shift
>>> 2 , slide to right 2 bit places, dropping lower order bits and filling on left
with the 0 bit. Shifting right by 1 bit is equivalent to dividing by 2, but this
won't work for negative numbers because the sign bit gets converted to 0 with
an unsigned shift.
The & bitwise operator is used for masking and | bitwise operator is used for
combining. Often bit manipulations are used for packing several fields into a single
int and then unpacking them again. You can achieve this with >>>, <<, &, | and ~
bitwise operators. Rarely you might use the signed right shift operator >>.
Example 1: Extract low order three bits from x, where x = 37.
The operation can be written as x & 0x07 where 0x07 is equivalent to the masking
bits 00000111.
Binary
Hexadecimal 0x
168
27
26
25
24
23
22
21
20
128
64
32
16
x in binary (32 + 4 + 1)
x & 0x07
are
27
26
25
24
23
22
21
20
128
64
32
16
x in binary (32 + 4 + 1)
0
169
Language Essentials
Hence the 5th and 6th bits of the value x are 0 and 1.
package chapter2.com;
public class Binary3 {
public static void main(String[ ] args) {
Byte x = 37;
Byte result = (byte) (x >>> 4 & 0x03);
System.out.println("Extract 5th and 6th bits of x = " +
Integer.toBinaryString(result));
System.out.println("Numeric value of result = " + result);
}
}
Output:
Extract 5th and 6th bits of x = 10
Numeric value of result = 2
Example 3: Say value of x=37, and value of y=7. You can now combine the 2 low
order bits of x into bit positions 5 & 6 with the 3 low order bits of y (i.e. bit positions
1, 2, and 3).
Bits
170
128
64
32
16
x in binary (32 + 4 + 1)
y in binary (4+2+1)
package chapter2.com;
public class Binary4 {
public static void main(String[ ] args) {
Byte x = 37;
Byte y = 7;
byte result = (byte)(x << 4 | y);
System.out.println("Combined result = " + Integer.toBinaryString(result));
System.out.println("Numeric value of result = " + result);
}
}
Output:
Combined result = 1010111
Numeric value of result = 87
Q19 Can you list some practical applications where the bitwise operations can be applied?
LF COQ
A19 Example 1: To pack and unpack values. For example, to represent
To pack this info: (((age << 1) | gender ) << 8 ) | height. For example, age = 25,
gender = 1, and height = 255cm. Shift the age by 1 bit, and combine it with gender,
and then shift the age and gender by 8 bits and combine it with the height.
Packing
Age
Ge
nde
r
Height
171
Language Essentials
16 15 14
Bits
13 12 11 10
Gender (1 male) 0
using 1 bit
((age << 1) | 0
gender ) << 8 ),
shift age and
gender by 8 bits.
Combine height 0
with age and
gender:
(age <<
gender
1)
package chapter2.com;
public class Binary5 {
172
Ge
Height range
nd
er 128 + 64 + 32 + 16 + 8 + 4 +
=
2 + 1 = 255.
1
//0011001111111111
//unpacking
System.out.println("height=" + (val & 0xff));
//extract last 8 bits.
System.out.println("gender=" + ((val >>> 8) & 1)); //extract bit 9
System.out.println("age=" + ((val >>> 9)));
//extract bits 10 16.
}
}
Output:
packed=13311
packed binary=11001111111111
height=255
gender=1
age=25
Unpacking (or extracting) height: Extract the low order 8 bits.
packed value:
0 0 1 1 0 0 1 1 1 1 1
1 1
Masking: 0xFF
0 0 0
1 1
Extract height =
value & 0xFF:
0 0 0
1 1
27
26
25 24
23
height:
22 21 20
128 + 64 + 32 + 16 + 8 + 4 +
2 + 1 = 255
1 1 1
173
Language Essentials
8 lower order bits 0
(i.e. shaded area)
are shifted out.
0 0 0
0 1 1
0 0 0
0 0 1
0 0 0
0 0
value >>> 8
Masking: 1
Extract gender =
0
(value >>> 8) &1:
0 1
20
gender:
Unpacking (or extracting) age: Extract bits 10 16 (i.e. higher order bits ).
packed value:
0 0 1 1 0 0 1 1 1 1 1 1 1 1 1 1
Extract age =
value >>> 9:
0 0 0
0 0 1
26 25
24
23
22 21 20
24 + 23 + 20 = 16 + 8 + 1 =
25
age:
Example 2: To compactly represent a number of attributes like being bold, italics, etc
of a character in a text editor. This is a more practical example.
shadow
blink
subscript
superscript
strikethrough
underline
italics
bold
package chapter2.com;
import java.util.Arrays;
public class Binary6 {
public static void main(String[ ] args) {
byte[ ] vals = { 0, 1, 0, 1, 0, 0, 0, 1 };
byte value = pack(vals);
System.out.println("packedValue=" + value);
174
// 81
System.out.println("unpackedValues="
+ Arrays.toString(unpack(value)));
// [0, 1, 0, 1, 0, 0, 0, 1]
}
public static byte pack(byte[ ] vals) {
byte result = 0;
for (byte bit : vals) {
result = (byte) ((result << 1) | (bit & 1));
}
return result;
}
public static byte[ ] unpack(byte val) {
byte[ ] result = new byte[8];
for (int i = 0; i < 8; i++) {
result[i] = (byte) ((val >> (7 - i)) & 1);
}
return result;
}
}
Example 3: If you can think of anything as slots or switches that need to be flagged
on or off, you can think of bitwise operators. For example, if you want to mark some
events on a calendar.
6
Saturday
5
Friday
4
Thursday
3
Wednesday
2
Tuesday
1
Monday
0
Sunday
Language Essentials
public static void main(String[ ] args) {
System.out.println(MULTIPLY);
System.out.println(DIVIDE);
}
// 640
// 10
Local variables in Java must be initialized before they are first read. So it's perfectly
OK to first declare a local variable without initializing it, and initialize it later down the
code, and then use it. But if you try to use it without initializing it anywhere at all or if
your initialization takes place within a conditional block and you try to use it outside
the conditional blocks as in the above sample code, you will get a compile-time error
when you try to use it.
You can fix the above code as follows:
package chapter2.com;
public class VariableInitialization2 {
public static void main(String[ ] args) {
int x = 10, y;
if (x < 10) {
y = 1;
System.out.println("y is " + y);
}
if (x >= 10) {
y = 2;
System.out.println("y is " + y);
}
}
}
or by initializing it prior to using it.
package chapter2.com;
public class VariableInitialization2 {
public static void main(String[ ] args) {
int x = 10, y;
// y is declared but not initialized
y=0;
// y is initialized
if (x < 10) {
y = 1;
}
177
Language Essentials
if (x >= 10) {
y = 2;
}
System.out.println("y is " + y);
// y is used
}
}
It can also be fixed by changing the second if condition to an else condition so
that the compiler knows for sure that one of the two conditions will be reached.
package chapter2.com;
public class VariableInitialization4 {
public static void main(String[ ] args) {
int x = 10, y;
if (x < 10) {
// Either the if or else will be executed
y = 1;
// y would be initialized to 1
}
else {
y = 2;
// y would be initialized to 2
}
System.out.println("y is " + y);
}
}
The instance and static variables don't require to be explicitly initialized at all before
using them. They are automatically initialized with appropriate default values.
package chapter2.com;
import static java.lang.System.out;
public class VariableInitialization {
//instance & static variables are initialized by default
private int iInstance;
private static String sStaticInstance;
178
// iLocal2 is initialized
// iLocal3 is initialized
// iLocal4 is initialized
// 0
// null
}
}
Q21 Can you tell me what is the use of a static block in a class with an example? Is it a
good practice to use static blocks? LF BP COQ
A21 When a class is loaded, all blocks that are declared static and dont have function name
(i.e. static initializers) are executed even before the constructors are executed. As
the name suggests, they are typically used to initialize static fields. If you need to do
computation (e.g. for loop to fill an array, determine the value based on a conditional
logic, date computation using a calendar, etc) in order to initialize your static variables,
you can declare a static block which gets executed exactly once, when the class is first
loaded. It's a normal block of code enclosed within a pair of braces and preceded by a
'static' keyword. These blocks can be anywhere in the class definition where you can
have a field or a method. The Java runtime guarantees that all the static initialization
blocks are called in the order in which they appear in the source code and this happens
while loading of the class in the memory.
package chapter2.com;
public class StaticInitilization {
private static int[ ] square;
private static final int MAX = 10;
static {
square = new int[MAX+1];
179
Language Essentials
for (int i = 0; i <= MAX; i++) {
square[i] = i * i;
}
}
public static void main(String[ ] args) {
System.out.println("Square of 7 = " + square[7]);
System.out.println("Square of 9 = " + square[9]);
}
// evaluates to 49
// evaluates to 81
}
BP A private static method is a suitable alternative to static initialization blocks. In
fact, it has advantages over static initialization blocks, as you can re-use a private static
method to re-initialize static fields. Static blocks are also harder to unit test. So use
static blocks judiciously. If you have to use it, move the body of the static block into a
private static method, and call this static method from your static block as shown
below.
package chapter2.com;
public class StaticInitilization {
private static int[ ] square;
private static final int MAX = 10;
static {
initilizeSquares(MAX);
}
private static final void initilizeSquares(int max) {
square = new int[max];
for (int i = 0; i < max; i++) {
square[i] = i * i;
}
}
public static void main(String[ ] args) {
System.out.println("Square of 7 = " + square[7]);
System.out.println("Square of 9 = " + square[9]);
180
// evaluates to 49
// evaluates to 81
// evaluates to 1600
}
}
Q22 How will you provide a member variable named dueDate with an initial default value
set to first day of the following month? LF COQ
A22 Like static initializers, you can use an initializer block for instance variables. Initializer
blocks for instance variables look just like static initializer blocks, but without the
'static' keyword.
package chapter2.com;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class Initilization2 {
private Date dueDate;
//initializer block
{
Calendar cal = GregorianCalendar.getInstance( );
cal.add(Calendar.MONTH, 1);
cal.set(Calendar.DAY_OF_MONTH, 1);
dueDate = cal.getTime( );
//dueDate defaults to first day of next month
}
//...
public static void main(String[ ] args) {
Initilization2 init = new Initilization2( );
System.out.println("dueDate = " + init.dueDate); // first day of next month
}
}
Q23 Is there anything wrong with the following code snippet? COQ
181
Language Essentials
package chapter2.com;
public class VariableInitialization3 {
private static final String CONSTANT_VALUE = null;
// line A
static {
// load values based on some condition or .properties file.
CONSTANT_VALUE = "loaded";
// line B
}
}
A23 Yes. Compile-time error at line B.
CONSTANT_VALUE = "loaded";
indicating that the final field VariableInitialization3.CONSTANT_VALUE cannot be
assigned.
Q. How will you fix it?
A. Remove the initial null assignment in line A. Final variables can only be assigned
once.
private static final String CONSTANT_VALUE;
// fixed
183
Language Essentials
As per the above example, the class ConstantsInterfaceAntiPattern only requires 2
constants (i.e. TRIANGLE_AREA_PREFIX and SPHERE_-VOLUME_PREFIX)
from the interface Constants, which has 98 other constants not relevant to the
ConstantsInterfaceAntiPattern class. The problem is that by implementing the
Constants interface for convenience reason, all the 100 constants from the interface
Constants end up being a part of the public API of the client class ConstantsInter
faceAntiPattern. This not only breaks inheritance, but also the 100 constants will end
up in your JavaDoc for ConstantsInterfaceAntiPattern class. A better approach
would be to use a constants class say MyConsts to only define the relevant
constants.
package chapter2.com;
public final class MyConsts {
//Marked private so that this class can never be instantiated from outside
private MyConsts( ) {
//to prevent the native (i.e. from within) class from calling it
throw new AssertionError( );
};
public static final double TRIANGLE_AREA_PREFIX = 1.0/2.0;
public static final double SPHERE_VOLUME_PREFIX = 4.0/3.0;
}
If you are using a J2SE version prior to 1.5, then it is necessary to fully qualify the
constant with the class name as shown below:
MyConsts.TRIANGLE_AREA_PREFIX
MyConsts.SPHERE_VOLUME_PREFIX
If you are using a J2SE version 1.5 or later, static imports can be used as shown
below:
package chapter2.com;
// static imports from Java 5
import static chapter2.com.MyConsts.*;
import static java.lang.Math.PI;
184
Language Essentials
s.NEW_LINE to be referenced as just NEW_LINE.
2) When you require frequent access to static members from one or two java classes.
For example, a scientific or engineering application might make wide use of Math.PI.
BP It is also a bad practice to have one class that contains all constants that are
used throughout in your application. It is not a good practice to have unrelated
constants in the same class. This creates a coupling between otherwise unrelated
components through this class having only constants. This inhibits code reuse as this
class may be the only thing they have in common. You may want to later extract a
particular component to be used in a different application. This will require you to
ship the component classes with constants as well. The best practice is to put the
constants where they belong. For example, RebateConstants, ManagedFundConstants,
SecurityConstants, etc. Constants should not be used across component boundaries.
186
package chapter2.com;
final class MyConsts {
private MyConsts( ){ };
static final double TRIANGLE_AREA_PREFIX = 1.0/2.0;
static final double SPHERE_VOLUME_PREFIX = 4.0/3.0;
}
A28
Final classes cannot be extended from outside or within this class (e.g. using a
static inner class). An outer class can only either be package-private scoped (i.e.
no access modifier) as shown above or public scoped. A package-private
scoped class cannot be accessed from outside this (i.e. chapter2.com) package.
final class MyConsts { ...}
The variables are package-private scoped so that they can only be visible
within the same package i.e. chapter2.com.
The variables are marked static to indicate that they can not be attached to a
particular object, but rather to the class as a whole.
The variables are marked final to indicate that they can be assigned or
initialized only once.
Q. How would you go about making the MyConsts class accessible to the classes in
other packages?
A. Firstly, the class should be marked public so that it can be accessed from other
packages. Secondly, the variables should be marked public so that they will be visible
to classes in other packages.
187
Language Essentials
Q. What does the following code snippet do? Discuss the modifiers used? COQ
package chapter2.com;
public final class AnimalFactory {
static final private AnimalFactory singleton;
static {
try {
//... perform initialization here
singleton = new AnimalFactory( );
} catch (Throwable e) {
throw new RuntimeException(e.getMessage( ));
}
}
private AnimalFactory( ) {}
static public AnimalFactory getInstance( ) {
return singleton;
}
}
A.
188
DP
The getInstance( ) method is marked public so that it can be accessed from any
class within any package. It is also marked static to indicate that it is not
attached to a particular instance (i.e. an object), but to the class itself.
The AnimalFactory is eagerly initialized when the class is loaded, and hence
AnimalODHFactory( );
}
package chapter2.com;
189
Language Essentials
public final class AnimalODHFactory {
AnimalODHFactory( ) { }
// Returns instance of the singleton
static public AnimalODHFactory getInstance( ) {
return AnimalODHFactoryHolder.instance;
}
}
Note: Also do your research on per thread singleton idiom using a ThreadLocal
class, or the WeakSingleton design pattern using weak references.
Q. If you have ClassA under package pkga as shown below, how will you ensure
that the method processA( ) is visible to all classes under pkga and all classes that
extend ClassA from other packages like pkgb? LF COQ
package pkga;
public class ClassA {
void processA( ) {
//processA ...
}
}
A. Since the method processA( ) does not have any explicit access modifier, it is
package-private scoped by default. This means any class in the package pkga can
access it. In order to enable it to be accessed by any subclasses of ClassA from other
packages like pkgb, it needs to be marked with the access modifier protected as
shown below:
package pkga;
public class ClassA {
protected void processA( ) {
//processA
}
190
}
The "ClassB" can now access method "processA( )" from "ClassA" as shown below.
package pkgb;
import pkga.ClassA;
public class ClassB extends ClassA {
public void processB( ) {
super.processA( );
}
}
Q. Can you access ClassA's processA( ) method from a class ClassC as shown
below that resides in the package pkgb? LF COQ
package pkgb;
import pkga.ClassA;
public class ClassC {
public void processC( ) {
new ClassA( ).processA( );
}
}
A. The answer is No because the ClassC does not extend ClassA. The protected
scope is only visible to subclasses in other packages. If you want any class from other
packages to be able to see it, the access modifier of processA( ) needs to be public
as shown below:
package pkga;
public class ClassA {
191
Language Essentials
public void processA( ) {
//processA
}
}
Q. Is there a way to prevent ClassB from overriding the processA( ) method from
ClassA?
A. Yes. Mark the method as final. Final methods cannot be overriden.
package pkga;
public class ClassA {
public final void processA( ) {
//processA
}
}
Q29 If you want to extend the java.lang.String class, what methods will you override in your
extending class? LF
A29 You would be tempted to say equals( ), hashCode( ) and toString( ) methods, but the
java.lang.String class is declared as final and therefore it cannot be extended.
Q30 Are 'volatile' and 'const' valid modifiers in Java? LF FAQ
A30 Very rarely used modifiers like strictfp, native, transient, and volatile are valid
modifiers. The modifier const is a reserved keyword in Java, but it is not added to
the language yet. So currently, const is an invalid modifier.
Q. What is the difference between the modifiers volatile and synchronized?
A. Volatile modifier is used on instance variables that may be modified simultaneously
by other threads. The modifier volatile only synchronizes the variables marked as
volatile whereas synchronized modifier synchronizes all variables. Local variables are
not required to be marked as volatile because other threads cannot see them.
Q. If added to Java, how would the modifier const differ from a final modifier?
A. A final variable 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. The following declarations are
real constants as String is an immutable class and int is not an object. So once
192
// prints 5
// prints 6
}
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
193
Language Essentials
object that cannot be modified.
Q. Since the const keyword is not yet added to Java, how would you make sure that
a static final variable remains a constant? COQ
A. By ensuring that the static final variables either refer to primitive data types or
immutable objects. The immutable objects will be discussed under section entitled
Objects Essentials.
Q31 How would you go about determining what access levels to use in your code? BP
FAQ
A31
Declare all your instance variables (aka attributes or fields) as private and
provide public getXXX & setXXX access methods. This prevents you from
being lazy and grabbing internal things you should not or corrupting a
variable. For example, you can implement a fail fast approach to prevent a
variable named age from incorrectly being assigned with a negative value.
package chapter2.com;
public class Person {
private Short age;
// best practice
194
// bad practice
Then it is easy for any external class to corrupt the age as,
personInstance.age = -5;
Don't declare any methods or variables as protected with a view that it may be
required in the future. Declare them as protected only if the subclasses
absolutely need them to be declared as protected.
Package private access level is useful if you want to open features to your
subclasses within the same package, but want to restrict access outside the
package to those who might not understand the full design or consequences
of using a feature.
Thinking about the right access modifier to use also forces you to think through how
objects and classes are going to interact with each other.
Q32 If you were to give some tips on modifiers, what would they be? LF
A32
'abstract' and 'native' methods have no body. If a method is declared native
means, the method implementation is provided external to the JVM in a native
language such as C. A class that has at least one abstract method must be
declared abstract. A method cannot be declared both 'abstract' and 'native'.
abstract void process( );
native void process( );
Methods that are 'abstract' cannot be declared 'private', 'static', 'final', 'native',
'strictfp' or 'synchronized'.
195
Language Essentials
A class, method or variable declaration can contain only one of the following
access modifiers public, protected, or private. If it does not have any of the
above access modifiers, it is package-private scoped by default.
'static' means belong to a class rather than an instance (i.e. an object) of a class.
'static' variables and methods might better have been called per class variables
and methods. There is nothing static (unchanging) about them. 'static' refers to
a method or variable that is not attached to a particular object, but rather to a
class as a whole. When both 'static' and 'final' modifiers are applied to a
variable, it becomes a constant. All static methods are implicitly final, as they
cannot be overriden. It is not an error to mark them as final, but it is redundant
and considered a bad practice. static methods can call instance methods only if
they use their own object references explicitly, and cannot use the implicit
keyword 'this'.
Surprisingly, the java compiler does not complain if you declare a transient
field as static or final. These should be compile-time errors because a
"transient" part of an object's state is assumed to be changing within each
instance, and it can not be static or final. There is no point in declaring a static
member field as transient, since transient means: "do not serialize", and static
fields would not be serialized as they belong to a class and not to an individual
instance. It is important to keep in mind that the process of serialization is
concerned with an object's current state. So declaring a transient variable as
static or final does not make any sense.
Local variables can only be declared as 'final'. Local variables are implicitly
private to the block in which they are declared. You cannot use any other
access modifiers explicitly. Don't have a synchronized block for local variables
as they are always thread-safe. Local variables are stored in a stack and each
thread will have its own stack.
happens if you do not provide a constructor? Can you call one constructor from
another? Are constructors inherited? How do you call a super class's constructor?
LF FAQ
A33 A constructor will be automatically invoked when an object is created using the new
keyword whereas a method has to be called explicitly. Constructors are used to
initialize the instance variables (aka fields) of an object. Constructors are similar to
methods, but with some important differences.
197
Language Essentials
//no parameter constructor
public ConstructorChild( ) { } // implicitly calls parent class' constructor
// with the construct super( );
public ConstructorChild(Integer fld1, String fld2, Float fld3) {
this(fld1, fld2);
// cannot have both super(...) & this () here.
this.fld3 = fld3;
// illegal to have super(...) or this(...) anywhere else except in the first line.
}
public ConstructorChild(Integer fld1, String fld2) {
super( );
// cannot have both super( ) & this here.
this.fld1 = fld1;
this.fld2 = fld2;
}
}
Q. Can you call one constructor from another? LF FAQ
A. You can call one constructor from another with the this(...) construct, but it must
be the first line of a constructor.
Q. Are constructors inherited? LF DC
A. Constructors are not fully inherited in a sense that you cannot create an instance of
a subclass using a constructor of it's superclass. One of the main reasons is because
you probably don't want to override a super class's constructor, which would be
possible if they were inherited. By giving a developer the ability to override a super
class' constructor, you would erode the encapsulation abilities of the language.
Q. How do you call a super class's constructor?
A. You call a super class's constructor with the super(...) construct.
Q. Can a constructor throw exceptions?
A. Yes.
Q34 Where and how can you use a private constructor? LF DP FAQ
A34 Private constructor is used, if you do not want other classes to instantiate the object,
and to prevent subclassing. The instantiation is done by the same class. Not defining
any constructors would not work because if you don't define a constructor for a class,
a default no parameter constructor is automatically created by the compiler at compile
198
Q35 What are static factory methods? Can you give examples from the Java API? What
design patterns does it use? Can you describe the benefits of static factory methods
and usefulness of private constructors? LF DP FAQ
A35 Static factory methods are an alternative to creating objects through constructors.
Unlike constructors, static factory methods are not required to create a new object (i.e.
a duplicate object) each time they are invoked. The static factory methods are more
testable than complex constructors. They also have a more meaningful names than
constructors like:
Integer.valueOf("5"), MyConnection.newInstance(..), Arrays.asList(..), BigDecimal.
valueOf(3.0),BigInteger.valueOf(389), String.valueOf(5), etc.
Instead of:
String[ ] myArray = {"Java", "J2EE", "XML", "JNDI"};
for (int i = 0; i < myArray.length; i++) {
System.out.println(myArray[i]);
}
You can use:
String[ ] myArray = {"Java", "J2EE", "XML", "JNDI"};
System.out.println(Arrays.asList(myArray)); // factory method
The following static factory method is an alternative to a constructor. It converts a
boolean primitive value to a Boolean wrapper object.
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
199
Language Essentials
DP The static factory method uses the factory method design pattern. Common
names for factory methods include getInstance(..) and valueOf(..). These names are not
mandatory - choose whatever makes sense for each case. When you need to create a
very large number of objects, each requires an amount of memory to store the
objects' state. In some cases, the objects being created may include information that is
often duplicated. Immutable instances can be cached and reused. Where this is true,
the "flyweight" design pattern can be used. The flyweight design pattern often uses a
variation on the "factory method" design pattern for the generation of the shared
objects. The factory receives a request for a flyweight instance. If a matching object is
already in the cache, that particular object is returned. If not, a new flyweight is
generated. Usually the full set of available flyweight objects is held within the factory
in a collection that can be accessed quickly, such as a HashMap. Here is an example of
a cache.
package chapter2.com;
import java.util.HashMap;
import java.util.Map;
public class PrefixedWord {
private String prefix;
private String word;
//cache
private static Map<String, PrefixedWord> wordCache =
new HashMap<String, PrefixedWord>(50);
// cannot be constructed from outside
private PrefixedWord(String prefix, String word) {
this.prefix = prefix;
this.word = word;
}
//static factory method that caches values
public static PrefixedWord valueOf(String prefix, String word) {
String key = prefix + word;
200
201
Language Essentials
/**
* Compile-time error: Implicit super constructor
* MySingletonFactory( ) is not visible
*/
public MyExtSingletonFactory( ) {
//implicitly invokes the super class's constructor with super( );
}
}
But, strictly speaking, a non-final class with a private constructor can be extended
from inside, using a static inner class as shown below. This is generally not a good
practice.
package chapter2.com;
public class MySingletonFactory {
private static final MySingletonFactory instance =
new MySingletonFactory( );
private MySingletonFactory( ) {
}
public static MySingletonFactory getInstance( ) {
return instance;
}
public static class ExtSingletonFactory extends
MySingletonFactory {
public ExtSingletonFactory( ){
//do something
}
}
}
So it is a good practice to add the final keyword to your class declaration. If you
want to extend a singleton class from outside, change the access modifier of your
constructor from private to protected.
202
Q37 What are some of the do's and don'ts with respect to constructors in order to write
more testable code? What tip would you give your fellow developers to make their
code more testable? BP DP FAQ
A37 Constructors should predominantly have just field assignments. Constructors should
not have any complex object graph construction logic. The complex object graphs
should be constructed via a factory or builder design pattern. According to Misko
Hevery's Guide to Writing Testable Code at http://java.dzone.com/articles/guideto-writing-testable-code), following are the warning signs to watch out for while
writing a constructor:
Q. What tip would you give your fellow developers to make their code more testable?
A.
Firstly, use testing frameworks like JUnit, TestNG, DbUnit, and XMLUnit, and
mocking frameworks like EasyMock or Mockito. Follow the write test cases
first principle as it not only promotes better coverage of test cases, but also
encourages you to write more testable code.
Most importantly, unit testing is all about testing your code in isolation. To
achieve this, care must be taken not to mix object construction with applic
ation logic. You can achieve this isolation through a popular concept known
as the dependency injection, where a dependency is injected instead of
constructing them within an application. So dependency injection makes your
application more testable by allowing you to inject a small subset of your
application in to your unit tests. This small subset can be constructed
independently of your whole system due to looser coupling between your
classes and objects.
Q. What if you have to work with some legacy code, and don't have the luxury to
203
Language Essentials
introduce a dependency injection framework like Spring, Guice, or HiveMind?
COQ
DP
A. The key aspect of making your code more testable is to request for services, and
not to look for services. Avoid looking for services by instantiating services with the
new keyword. Request for services with service locators using either JNDI look ups
or factories using the factory design pattern. A factory design pattern with code to
interface design principle can loosely couple a caller from a callee. This will make
your code not only more testable, but also more flexible and extendable.
Q38 When is a method said to be overloaded and when is a method said to be overridden?
What are their differences? What is a co-variant return type? LF FAQ
A38 Method overloading is the primary way in which, polymorphism is implemented in
Java. Overloading lets you define the same operation in different ways for different
data. An overloaded method:
The actual method called depends on the number, order, and data types of arguments
passed, and determined at compile-time (aka compile-time polymorphism). It does
not depend on the return types or names of the arguments passed.
Method overriding allows a subclass to re-define a non-static method it inherits from
its super class. Overriding lets you define the same operation in different ways for
different object types. Late-binding also supports overriding. Overriding methods,
204
appear in subclasses.
have the same name as a super class method.
have the same parameter list as a super class method.
have the same return type as as a super class method. Until the J2SE 5.0
release, it was true that a class could not override the return type of the
methods it inherits from a super class.
the access modifier for the overriding method must not be more restrictive
than the access modifier of the super class method. For example, if the super
class method is protected, the overriding method must either be protected or
public
the overriding method can only specify all, none, or a subset of the exception
classes (including their subclasses) specified in the throws clause of the
overridden method in the super class..
class BaseClass{
public void getInvestAmount(int rate) {}
}
class MyClass extends BaseClass {
@override
public void getInvestAmount(int rate) { }
}
The actual method called depends on the type on which the method is invoked, and
determined at runt-time (aka runtime polymorphism).
BP It is a best practice to use the override annotation as shown above to ensure that
you have not mistyped or used the wrong arguments for your overriding method.
Q39 There is a class X.java and it has a public method. Class Y extends class X. How
would you prevent a method in class X from being accessed in class Y using Y's
instance? LF FAQ
A39 Override the method in class X with the same method name and signature in class Y.
Q. Can you write a new static method in the subclass Y that has the same signature as
the one in the super class X?
A. Yes. But this is called hiding or shadowing and not overriding.
Q. Can you also declare a public or protected field in the subclass Y with the same
name as the one in the super class X, thus hiding it?
A. Yes. But this approach is not recommended as it breaks encapsulation. Always
205
Language Essentials
declare your fields as private, and provide access via public or protected methods.
Q40 What do you understand by the term co-variant in Java? LF FAQ
A40 An argument or return type in an overridden method that is made more specialized is
called to be co-variant. So you can use co-variant return types to minimize up casting
and down casting. For example,
package covariant;
public class Parent {
Parent process( ) {
System.out.println("Parent process( ) called");
return this;
}
}
If you were using J2SE5.0 or later version:
package covariant;
public class Child extends Parent {
/**
* return type could be either Child or Parent
*/
Child process( ) {
System.out.println("Child process( ) called");
return this;
}
}
If you were using a Java version prior to J2SE 5.0:
package covariant;
public class ChildPreJ2SE5 extends Parent {
/**
206
Language Essentials
parameter list must conform to the following syntax:
type ... variableName
The ellipsis (...) identifies a variable number of arguments, and is demonstrated in the
following example.
package varargs;
public class WithoutVarargs {
static int sumWithoutVarargs(int[ ] numbers) {
int sum = 0;
for (int i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
static int sumWithVarargs(int... numbers) {
int sum = 0;
for (int i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
}
public static void main(String[ ] args) {
// need to construct the array of numbers first
int[ ] i1 = { 3, 7, 9, 8 };
int sum = sumWithoutVarargs(i1);
// need to construct the array of numbers first
int[ ] i2 = { 9, 9 };
sum = sumWithoutVarargs(i2);
// with varargs, you can pass it directly
sum = sumWithVarargs(3, 7, 9, 8);
sum = sumWithVarargs(9,9);
208
}
}
Q43 What is the difference between an argument and a parameter? LF
A43 The words argument and parameter are often used interchangeably. In Java, argument
is an expression in the comma-separated list in a method call. For example,
sum = sumWithVarargs(3, 7, 9, 8);
// arguments
Language Essentials
}
A44 Instance variables and Objects are stored on the Heap. The SimpleDateFormat
objects go to the heap. The instance variable instanceSdf also goes to the heap. Heap is
where the state is maintained, and when you get memory leaks, this is where your
profiler helps you to find the allocation of memory. Object members or instance
variables are stored on the heap along with the object. Therefore, if two threads call a
method on the same object instance and this method updates object members, the
method is not thread safe.
Local variables and methods are stored on the Stack. So the method
instanceMethod( ) and the local variable localSdf go to the stack. Each thread will have its
own stack. Local variables and methods are stored in each thread's own stack. That
means that local variables are never shared between threads. That also means that all
local primitive variables are thread safe. Local references to objects are a bit different.
The reference itself is not shared and sits in the stack. But local object referenced is
not stored in each thread's local stack. All objects are stored in the shared heap. If an
object created locally never escapes the method it was created in, it is thread safe. In
fact you can also pass it on to other methods and objects as long as none of these
methods or objects make the passed object available to other threads.
210
Java is pass-by-value
Q44 Explain the statement Java is always pass by value? LF FAQ
A44 Other languages use pass-by-reference or pass-by-pointer. But in Java, no matter what
type of argument (i.e. a primitive variable or an object reference) you pass, the corres
ponding parameter will get a copy of that data, which is exactly how pass-by-value
211
Language Essentials
(i.e. copy-by-value) works. Even though the definition is quite straight forward, the
way the primitives and object references behave when passed by value, will be
different. For example, If the passed in argument was a primitive value like int, char,
etc, the passed in primitive value is copied to the method parameter. Modifying the
copied parameter will not modify the original primitive value. On the contrary, if
the passed in argument was an object reference, the passed in reference is copied to
the method parameter. The copied reference will still be pointing to the same object.
So if you modify the object value through the copied reference, the original object
will be modified. For example,
package chapter2.com;
public class PassByValue {
int val = 10;
int[ ] values = {10};
212
// primitive reference
// object reference
// prints 10
// prints 10
//object reference
System.out.println("values[0] before mutate=" + instance.values[0]);// prints 10
instance.mutateObject(instance.values);
System.out.println("values[0] after mutate=" + instance.values[0]); // prints 100
}
}
Note: Regardless of what type of array (i.e. primitive array or object array ) youre
working with, the array identifier is actually a handle to a true object thats created on
the heap.
Q45 The value of Point p before the following method calls is (10,20). What will be the
value of Point p after executing the following method calls? LF COQ
Scenario 1:
static void mutatePoint(Point p) {
p.x = 50;
p.y=100;
}
Scenario 2:
213
Language Essentials
static void mutatePoint(Point p) {
p = new Point(50,100);
}
A45 Scenario 1:
Point p = (50,100), as the copied reference will still be pointing and modifying the
original Point (10,2 0) object through the mutatePoint( ) method.
Scenario 2:
Point p = (10,20), as the copied reference will be creating and pointing to the newly
created Point (50, 100) object.
Recursive functions
Q46 How would you take advantage of Java being a stack based language? What is a reentrant method? LF FAQ
A46 Recursive method calls are possible with stack based languages and re-entrant
methods.
A re-entrant method would be one that can safely be entered, even when the same
method is being executed, further down the call stack of the same thread. A non-reentrant method would not be safe to use in that way. For example, writing or
logging to a file can potentially corrupt that file, if that method were to be reentrant.
A function is recursive if it calls itself. Given enough stack space, recursive
method calls are perfectly valid in Java though it is tough to debug. Recursive
functions are useful in removing iterations from many sorts of algorithms. All
recursive functions are re-entrant, but not all re-entrant functions are recursive.
Stack uses LIFO (Last In First Out), so it remembers its caller and knows whom to
return when the function has to return. Recursion makes use of system stack for
storing the return addresses of the function calls.
public class RecursiveCall {
214
BP PC Recursion might not be the efficient way to code, but recursive functions
215
Language Essentials
are shorter, simpler, and easier to read and understand. Recursive functions are very
handy in working with tree structures and avoiding unsightly nested for loops. If a
particular recursive function is identified to be a real performance bottleneck as it is
invoked very frequently or it is easy enough to implement using iteration like the
sample code below, then favor iteration over recursion. The iterative approach is
shown below:
public class Iteration {
public int countA(String input) {
if (input == null || input.length( ) == 0) {
return 0;
}
int count = 0;
for (int i = 0; i < input.length( ); i++) {
if(input.substring(i, i+1).equals("A")){
count++;
}
}
return count;
}
public static void main(String[ ] args) {
System.out.println(new Iteration( ).countA("AAA rating"));
}
// 3
}
Q47 What are idempotent methods? LF FAQ
A47 Idempotent methods are methods, which are written in such a way that repeated calls
to the same method with the same arguments yield the 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.
package chapter2.com;
import java.util.ArrayList;
import java.util.List;
216
Language Essentials
method calls. Declare and initialize local variables just before using them. Declaring
local variables without using them immediately may unnecessarily increase their scope.
This decreases legibility, and increases the likelihood of errors. Local variables are
implicitly thread-safe and synchronization is not required. There are exceptional
scenarios where the local variables need to be declared before immediately using them.
For example,
When a variable in the try block needs to be visible in the finally block.
Some variables that are used within a loop.
package chapter2.com;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Declared {
public static void main(String[ ] args) {
/**
* local variable is declared and initialized outside
* try block to be visible in the finally block
*/
Scanner sc = new Scanner(System.in);
try {
/**
* local variable 'keepGoing' & 'typedValues" are
* declared & initialized well before used
* in the while loop.
*/
boolean keepGoing = true;
List<String> typedValues = new ArrayList<String>(20);
while (keepGoing) {
System.out.print("Enter Something? [type QUIT to exit] ");
/**
* local variable 'answer' is declared &
* initialized immediately before getting used
* 'answer' is out of scope outside the while loop.
218
*/
String answer = sc.next( );
if ("QUIT".equalsIgnoreCase(answer)) {
keepGoing = false;
}
typedValues.add(answer);
}
/**
* local variable i is declared and initialized
* immediately before used. 'typedValues' is declared
* outside the while loop, hence visible here.
*/
for (int i = 0; i < typedValues.size( ); i++) {
System.out.println("Input Number " + (i + 1) + " = "
+ typedValues.get(i));
}
} finally {
sc.close( );
}
}
}
Output:
Enter Something? [type QUIT to exit] test1
Enter Something? [type QUIT to exit] Test2
Enter Something? [type QUIT to exit] quit
Input Number 1 = test1
Input Number 2 = Test2
Input Number 3 = quit
Instance (aka member) variables are declared in a class, but outside a method. They
are also called member variables, fields or attributes. These variables are created when
an object is created and allocated in the heap. Use: Hold values that must be refer
enced by more than one method. Lifetime: Creates when an instance of a class is
created with new. Destroyed when there are no more references to the containing
object, and the garbage collector has removed it from the heap. Scope/visibility: Can
be seen by all methods in the class. Which other classes can see them depends on the
219
Language Essentials
access modifiers. Declaration/initial Value: Declared anywhere at a class level. Can
be initialized at declaration or in constructor. Unlike local variables, these variables are
implicitly initialized to its default values (0, 0.0, or false) if not explicitly initialized. BP
Best practice/caveat: Typically declared as private and access is provided to outside
classes via public, protected, or package-private scoped methods with proper checks or
validation. As the objects can be accessed simultaneously by multiple threads, use
these variables in a thread-safe manner. Never use same name for your local, instance,
and class variables. If same names were used for an instance variable and a local
variable, the local variable hides the instance variable, unless the instance variable is
prefixed with the this keyword. Meaning this object's member or instance variable.
It is a best practice to use a naming convention to distinguish between the variable
types. For example, prefixed with 'this' or a naming convention like m_sameNameVar
where 'm' stands for member variable.
package chapter2.com;
public class VariableNamingIssue {
int sameNameVar = 5;
//instance variable
220
Language Essentials
int a = 6;
public static void main(String[ ] args) {
System.out.println("a is " + a);
}
}
A49 Compile-time error. Since 'a' is an instance variable, you cannot access it without
actually creating an instance of an object and qualifying it with the reference.
package chapter2.com;
public class AccessingInstanceVariables {
int a = 6;
//instance variable
/**
* static method
*/
public static void main(String[ ] args) {
AccessingInstanceVariables ref = new AccessingInstanceVariables( );
System.out.println("a is " + ref.a);
// prints 6
ref.addFive( );
}
/**
* non-static (aka instance) method can refer it directly.
*/
public void addFive( ) {
System.out.println("a is " + (a += 5));
// prints 11
addTwo( );
}
/**
* non-static (aka instance) method can refer it directly.
*/
public void addTwo( ) {
System.out.println("a is " + (a += 2));
// prints 13
222
}
}
Even though there is no name conflict, improve readability by qualifying it with this
as shown below.
package chapter2.com;
public class AccessingInstanceVariables {
int a = 6; // instance variable
public static void main(String[ ] args) {
AccessingInstanceVariables ref = new AccessingInstanceVariables( );
System.out.println("a is " + ref.a);
// prints 6
ref.addFive( );
}
public void addFive( ) {
System.out.println("a is " + (this.a += 5));
this.addTwo( );
}
public void addTwo( ) {
System.out.println("a is " + (this.a += 2));
}
// prints 11
// prints 13
223
Language Essentials
224
Section-5:
This section is for all. If you asked me to pick a section that is most popular with the
interviewers, this is it. If you don't perform well in this section, your success rate in inter
views will be very low. Good interviewers will be getting you to analyze or code for a
particular scenario. They will be observing your decisions with interfaces and classes, and
question your decisions to ascertain your technical skills, analytical skills, and communication
skills. You can't memorize your answers. This section requires some level of experience to
fully understand. It has enough examples for beginners to get some level of familiarization.
Be ready to be asked about some tricky questions around the topics discussed in this
section. Java interview questions are meant to analyze your technical bent of mind. You have
to tell the way you are going to solve a particular problem with Java or any other
programming language of your choice. So the key is to ask the right questions and then
apply what you had learned. Keep practicing the examples provided here, and experiment
with them until you get a good grasp.
225
Q2 What is a class? What are the valid modifiers of a top level class? LF
A2 A class is a template for multiple objects with similar features. In another words, A
class defines responsibilities (i.e. characteristics and behaviors) that are common to
every object.
Modifiers: A top level class can either be public or package-private (i.e. no access
modifier) scoped. It can be final concrete, abstract, or non-final concrete (i.e. no final
or abstract modifiers).
public class A { }
226
class A { }
public abstract class A { }
public final class A { }
You can write a new static method in the subclass that has the same signature
as the one in the superclass, thus hiding it (not recommended).
Q5 What is the output of the following code snippet? Give your reasons and recommend
ations? COQ
package subclass1;
public abstract class Animal {
String name = "animal";
public String getName( ){
return this.name;
}
}
package subclass1;
public class Cat extends Animal {
String name = "cat";
public String getName( ){
return this.name;
}
}
package subclass1;
public class Example {
public static void main(String[ ] args) {
Animal animal = new Cat( );
Cat cat = new Cat( );
System.out.println(animal.name);
System.out.println(cat.name);
System.out.println(((Cat)animal).name);
228
System.out.println(((Animal)cat).name);
System.out.println(animal.getName( ));
System.out.println(cat.getName( ));
}
}
A5
Recommendations:
Secondly, the variable name is scoped package-private. This can break encap
sulation by allowing incorrect direct assignments like
cat.name = null;
from outside classes like Example2 within this package. Hence, it is recom
mended to change the variable name from package-private scoped to private
scoped so that it cannot be directly modified as shown above. The modific
ation is only allowed through a setter method that can validate the input
argument(s) before assigning.
Where the Animal could be an interface or an abstract class. The code below incor
porates the above recommendations.
package subclass1a;
231
}
}
package subclass1a;
import static java.lang.System.out;
public class Example {
public static void main(String[ ] args) {
//stored obj type = Cat & referencing obj type = Animal
Animal animal = new Cat( );
//stored obj type = Dog & referencing obj type = Animal
Animal animal2 = new Dog( );
out.println("Stored obj is a --> " + animal.getName( ));
out.println("Stored obj is a --> " + animal2.getName( ));
}
}
Output:
Stored obj is a --> cat
Stored obj is a --> dog
Q6 What happens when a parent class and a child class each have a static method with
the same signature? LF
A6 The behavior of static methods will be similar to the variable shadowing, and not
recommended. It will be invoking the static method of the referencing static object
type, and NOT the dynamic object type being stored.
234
package badrestaurant;
public interface Manager extends Person {
public void managePeople( );
}
package badrestaurant;
public interface Waiter extends Person {
public void takeOrders( );
}
package badrestaurant;
public class Bob implements Manager, Waiter {
@Override
public void managePeople( ) {
//implementation goes here
}
@Override
public void takeOrders( ) {
//implementation goes here
}
}
package badrestaurant;
public class Jane implements Waiter {
@Override
public List<String> takeOrders( ) {
//implementation goes here
}
}
The Restaurant class uses the above classes as shown below.
235
The above classes are badly designed for the reasons described below.
The name should be an attribute, and not a class like Bob or Jane. A good OO
design should hide non-essential details through abstraction. If the restaurant
employs more persons, you don't want the system to be inflexible and create
new classes like Peter, Jason, etc for every new employee.
The above solution's incorrect usage of the interfaces for the job roles like
Waiter, Manager, etc will make your classes very rigid and tightly coupled by
requiring static structural changes. What if Bob becomes a full-time manager?
You will have to remove the interface Waiter from the class Bob. What if Jane
becomes a manager? You will have to change the interface Waiter with Manager.
The above drawbacks in the design can be fixed as shown below by asking the right
questions. Basically waiter, manager, etc are roles an employee plays. You can abstract
it out as shown below.
package goodrestuarant;
public interface Role {
public String getName( );
public void perform( );
}
236
package goodrestuarant;
public class Waiter implements Role {
private String roleName;
public Waiter(String roleName) {
this.roleName = roleName;
}
@Override
public String getName( ) {
return this.roleName;
}
@Override
public void perform( ) {
//implementation goes here
}
}
package goodrestuarant;
public class Manager implements Role {
private String roleName;
public Manager(String roleName) {
this.roleName = roleName;
}
@Override
public String getName( ) {
return this.roleName;
}
@Override
public void perform( ) {
237
this.roles = roles;
}
public void addRole(Role role){
if(role == null){
throw new IllegalArgumentException("Role cannot be null");
}
roles.add(role);
}
public void removeRole(Role role){
if(role == null){
throw new IllegalArgumentException("Role cannot be null");
}
roles.remove(role);
}
}
The following Restaurant class shows how flexible, extensible, and maintainable the
above design is.
package goodrestuarant;
import java.util.List;
public class Restaurant {
public static void main(String[ ] args) {
Employee emp1 = new Employee ("Bob");
Role waiter = new Waiter("waiter");
Role manager = new Manager("manager");
emp1.addRole(waiter);
emp1.addRole(manager);
Employee emp2 = new Employee("Jane");
emp2.addRole(waiter);
239
240
SRP
Single Respons A class should only have a single purpose (i.e. cohesive),
ibility Principle and all its methods should work together to achieve this
goal.
OCP
Open
Principle
LSP
Liskov Substi Derived classes must be substitutable for their base classes.
tution Principle Derived classes must have the same intent, but different
implementation.
Another way to look at this principle is to think of design
by contract. A sub class should honor the contracts made
by its parent classes.
ISP
Interface Segreg Make fine grained interfaces that are client specific. While
ation Principle
SRP addresses high cohesion in the class level, ISP
promotes high cohesion in the interface level.
DIP
Dependency
Inversion
Principle
Abstraction refers to hiding all the non-essential details from the user.
Abstraction comes in two forms: abstracting the behavior and abstracting the
data. For example, a person driving a car only needs to know about how to use
a steering wheel, gear, accelerator, etc, but does not need to know about the
internal details like how the engine works? how the transmission works?, how
much fuel is released on acceleration?, etc. Thus, abstraction lets you focus on
what the object does instead of how it does.
Abstraction gives you the ability to conceptualize things by ignoring the irrel
evant details. If you refer to an object as a vehicle that can be used in place of
an actual vehicle like a car, bus, van, etc, you are making an abstraction. You
can make an abstraction at different levels by handling details at different
levels. A Vehicle class can focus on common behaviors like forward(..), reverse(..),
turn(..) etc and attributes like make, model, etc of a vehicle. A set of derived
classes like Car, Bus, Truck, etc can focus on more specific details of a vehicle.
This gives you another level of abstraction by allowing you to refer to an
object as a car that can be used in place of different makes like a Toyota, Ford,
Volvo, etc and models like Camry, Corolla, etc by capturing the make and
model as attributes.
Abstraction is all about managing complexities at package, class, interface, and
method levels. Can you imagine how complex your class hierarchy will become
if you represent the make and model as classes instead of attributes within a
class? You may end up with thousands of classes from different make and
model if not tens of thousands. Good programmers develop this essential skill
241
Inheritance is a way to form new classes using classes that have already been
defined. As some believe, the main focus of inheritance is not to achieve code
reuse. In a software system, you will often find objects that are much like
other objects, except for a few differences. Inheritance is a mechanism used
mainly to achieve categorization, modularity, clear representation of
concepts, and separation of concerns. Categorization is a process in which
ideas and objects are recognized, differentiated, and understood. For example,
a car, truck, bus, etc are categorized as a vehicle. It implies that objects are
grouped into categories for a specific purpose. Using inheritance, you can build
a hierarchy of concepts separated in concerns at different levels of abstraction.
Inheritance also facilitates polymorphism. Another added benefit of inher
itance is code reuse. The code reuse is achieved through a type of inheritance
known as the implementation inheritance to be more specific. But code reuse
242
is NOT the main driver for using inheritance and there is a better approach
known as composition to achieve code reuse. This will be covered in detail
later. There are 2 types of inheritances as shown below:
// id is encapsulated
// location is encapsulated
Animal, etc extending the Animal class to provide an abstraction for wild animals like
zebra, giraffe, etc and circus animals like lion, tiger, elephant, etc respectively. An
Animal is a further abstraction generalizing FarmAnimal, WildAnimal, and CircusAnimal.
The Location is coded as an enumeration for simplicity. The Location itself can be an
abstract class or an interface providing an abstraction for OpenLocation, EnclosedLocation,
and SecuredLocation further abstracting specific location details like barn, pen, pasture,
pigsty, stable, cage, etc. The location details can be represented with attributes like
name, type, etc.
The FarmAnimal class is also well encapsulated by declaring the attribute location as
private. Hence the location variable cannot be directly accessed. Assignment is only
allowed through the constructor and move(Location location) method, only after a
successful precondition check with the validateLocation(...) method. The
validateLocation(...) itself marked private as it is an internal detail that does not have to
be exposed to the caller. In practice, the public move(..) method can make use of many
other private methods that are hidden from the caller. The caller only needs to know
what can be done with an Animal. For example, they can be moved from one location
to another. The internal details as to how the animals are moved is not exposed to the
caller. These implementation details are specific to FarmAnimal, WildAnimal, and
CircusAnimal classes.
package subclass2;
public class Cat extends Animal {
@Override
public void about( ) {
super.about( );
//code reuse via implementation inheritance
intro( );
//code reuse via implementation inheritance
System.out.println("There are wild and domestic cats.");
}
@Override
public void sound( ) {
System.out.println("Meow Meow");
}
}
package subclass2;
public class Example {
248
249
}
The AnimalHelper class is a composed class, and will be reused for the Dog class as
shown below.
package subclass2b;
//implements multiple interfaces
public class Dog implements Animal, Soundable {
private String name = "dog";
private AnimalHelper helper = new AnimalHelper( ); //composition
public String getName( ){
return this.name;
}
public void sound( ) {
System.out.println("Wow Wow");
}
public void about( ) {
helper.about( );
//code reuse via composition
helper.intro( );
//code reuse via composition
System.out.println("There are wild and domestic dogs.");
}
}
The composed helper class is:
package subclass2b;
public class AnimalHelper {
public void about( ) {
System.out.println("It is a living organism.");
}
public void intro( ) {
251
// Line A
// Line B
example.aboutAnimal(cat);
example.makeSound(cat);
example.aboutAnimal(dog);
example.makeSound(dog);
example.printName(cat);
example.printName(dog);
}
//takes an Animal as a parameter
public void makeSound(Animal animal) {
if(!(animal instanceof Soundable)){
throw new IllegalArgumentException(animal + "is not soundable!!");
}
((Soundable) animal).sound( ); // depending on the stored animal, right
// method gets executed polymorphically.
}
//takes an Animal as a parameter
252
253
Note: In UML terms, association relationship denotes that two classes are connected
with each other. A navigability arrow on an association shows which direction the
association can be traversed or queried. For example, a Dog can be queried about its
AnimalHelper or traversed to the AnimalHelper. The multiplicity of an association end
is the number of possible instances of the class associated with a single instance of
the other end. In the above example, there can be only one AnimalHelper for each Dog.
Association example,
// A Message has an association with a Person
public class Message {
private Person recipient;
private Person sender;
private String text;
public Message(Person recipient, Person sender, String text) {
this.recipient = recipient;
this.sender = sender;
this.text = text;
}
254
}
Aggregation and composition are two types of associations. An aggregation is a
weaker relationship as shown below where the life cycle of a Product is not controlled
by the LineItem. If a line item is deleted, the associated product does not have to be
deleted as well. This product can be used by other line items in other orders.
// A LineItem is an aggregate of Product & Quantity
public class LineItem {
private Product product;
private Quantity qty;
public LineItem(Product product, Quantity qty) {
this.product = product;
// product & qty are created outside this
this.qty = qty;
//constructor and passed in.
}
//...
}
Composition is a stronger relationship where the life cycle of the composed class is
managed by the composing class as shown below. If an order is deleted, the line item
needs to be deleted as well.
//An Order is composed of a LineItem and controls life cycle of the LineItem
public class Order {
private LineItem lineItem;
public Order( ) {
this.lineItem = new LineItem(...); //lineItem is built in the enclosing
//object's (i.e. Order's) constructor.
}
//....
}
Conceptually an animal is composed of legs, head, heart, etc and aggregated (or
255
Q15 What can an interface do that an abstract class cannot? What are the differences
between abstract classes and interfaces? LF FAQ
256
A15
Abstract class
Interface
A class may extend only one abstract A class may implement several interfaces.
class. This gives you implementation This gives you multiple interface inher
inheritance.
itance.
Has implementation
abstract methods.
methods
The visibility of abstract methods can The visibility of interface methods can be
be public, protected or package-private. either public or package-private (i.e. no
access modifier).
Represents is-a/is-an relationship. For Represents can-do or -able relationship.
example,
For example,
class Cat extends Animal {
}
}
}
}
This can be read as, a Cat is-an animal. This can be read as MyDevice is Runnable or
MyDevice can run as a separate Thread.
257
258
259
inheritance with examples and specify which Java supports? in section Platform
Essentials. It is always tempting to create an inheritance hierarchy to get all the
functionality provided by a common base class. This is a pitfall and care should be
taken in modeling an is-a relationship as discussed later. For example,
Q24 What questions do you ask yourself to choose composition (i.e. has-a relationship) for
code reuse over implementation inheritance (i.e. is-a relationship)? DC FAQ SBQ
A24 Do my subclasses only change the implementation and not the meaning or
internal intent of the base class? Is every object of type Dog really is-an object of
type Animal? Have I checked this for Liskov Substitution Principle?
According to Liskov substitution principle (LSP), a Square is not a Rectangle
provided they are mutable. Mathematically a square is a rectangle, but behaviorally a
rectangle needs to have both length and width, whereas a square only needs a width.
Another typical example would be, an Account class having a method called calculateIn
terest(..). You can derive two subclasses named SavingsAccount and ChequeAccount that
reuse the super class method. But you cannot have another class called a MortgageAc
count to subclass the above Account class. This will break the Liskov substitution
principle because the intent is different. The savings and cheque accounts calculate
the interest due to the customer, but the mortgage or home loan accounts calculate the
interest due to the bank.
Violation of LSP results in all kinds of mess like failing unit tests, unexpected or
strange behavior, and violation of open closed principle (OCP) as you end up having
if-else or switch statements to resolve the correct subclass. For example,
if(shape instanceof Square){
//....
}
else if (shape instanceof Rectangle){
//...
261
262
If you implement it with interface inheritance, and composition for code reuse, you
can think of circus dog as a role that a dog plays. These roles provide an abstraction
to be used with any other animals like cat, horse, donkey, etc, and not just dogs. The
role becomes a has a relationship. There will be an attribute of interface type Role
defined in the Dog class as a composition that can take on different subtypes (using
interface inheritance) such as CircusRole, DomesticRole, GuideRole, SnifferRole, and
StrayRole at runtime. The locality can also be modeled similar to the role as a compos
ition. This will enable different combinations of roles and localities to be constructed
at runtime with 1 dog + 5 roles + 3 localities = 9 classes and 3 interfaces (i.e. Animal,
Role and Locality). As the number of roles, localities, and types of animals increases, the
gap widens between the two approaches. You will get a better abstraction with looser
coupling with this approach as composition is dynamic and takes place at runtime
compared to implementation inheritance, which is static.
The following code snippet demonstrates the power of interface inheritance with
composition. Firstly, define the interfaces,
263
265
dog2.display( );
dog3.display( );
dog4.display( );
}
}
Output:
ABBY
I guide people.
I am a local dog
ABEL
I play on circuses.
I am an international dog
ABERCROMBIE
I guide people.
I am an international dog
ABRACADABRA
I play on circuses.
I am a local dog
Note: The programming language like OT/J from the Object Teams
(http://www.objectteams.org/) is a Java extension that implements natively the idea of
objects playing roles.
Q25 Can you give an example where composition is favored over implementation inher
itance? DC DP OEQ
A25 The GoF design patterns favor use of interfaces with composition for code reuse over
abstract classes. Design patterns like strategy (e.g. Java Collection framework) lets you
swap new algorithms and processes into your program without altering the objects
that use them. Other popular design patterns that favor interfaces are decorator (e.g.
the Java I/O classes) and proxy (e.g. RMI and EJB).
Q26 Can you give an example of the Java API that favors composition? DC DP OEQ
A26 The Java IO classes that use composition to construct different combinations using
the decorator design pattern at runtime.
//construct a reader
StringReader sr = new StringReader(Some Text....);
267
//...
return animal;
}
}
A27 The above class represents 3 different responsibilities.
Hence, the above class violates the Single Responsibility Principle (SRP), which
states that a class should have only one reason to change. This principle is based on
cohesion. Cohesion is a measure of how strongly a class focuses on its responsibil
ities. It is of the following two types:
High cohesion: This means that a class is designed to carry on a specific and
precise task. Using high cohesion, methods are easier to understand, as they
perform a single task.
Low cohesion: This means that a class is designed to carry on various tasks.
Using low cohesion, methods are difficult to understand and maintain.
Hence the above code suffers from low cohesion. The above code can be improved as
shown below. The Animal class is re-factored to have only a single responsibility of
uniquely identifying an animal.
package principle_srp1a;
public class Animal {
private Integer id;
private String name;
public Integer getId( ) {
return id;
}
public void setId(Integer id) {
269
count += 4;
} else if (animal instanceof Spider) {
count += 8;
} else if (animal instanceof Ostritch) {
count += 2;
}
}
return count;
}
}
package principle_ocp1;
import java.util.ArrayList;
import java.util.List;
public class Example {
public static void main(String[ ] args) {
List<Animal> list = new ArrayList<Animal>( );
Animal animal = new Cat( );
list.add(animal);
animal = new Spider( );
list.add(animal);
animal = new Ostritch( );
list.add(animal);
int count = new AnimalLegsCounter( ).count(list);
System.out.println("Total count for " + list.size( ) + " animals = " + count);
}
}
A28 The above code violates the open closed principle (OCP). As time goes on, you may
want to add more animals like dog, crab, dragon fly, etc with differing number of legs.
This will require you to modify the AnimalLegCounter class' count(...) method with more
else-if statements like:
if (animal instanceof Cat || animal instanceof Dog) {
273
Spider) {
Ostritch) {
Crab) {
DragonFly) {
This shows that the class AnimalLegCounter is not closed for modification as you will
have to modify it in order to extend it. In other words, this means it is not open for
extension. The above example is a trivial one, but in real world applications, the code
base might be 10 to 1000 times larger and modifying a class is not a trivial task.
Any time you see big if/else or switch statements, you should think about
polymorphism. The code below shows how the real counting logic can be moved to
the count( ) method in individual animal classes, and the AnimalLegCounter classes'
count( ) method only makes use of the count( ) method in the Animal classes to make it
adhere to the OCP principle using polymorphism as shown below.
package principle_ocp2;
public interface Animal {
//other methods are left out for brevity
public abstract int count( );
}
package principle_ocp2;
public class Cat implements Animal {
//other methods are left out for brevity
@Override
public int count( ) {
return 4;
}
}
274
package principle_ocp2;
public class Ostritch implements Animal {
//other methods are left out for brevity
@Override
public int count( ) {
return 2;
}
}
package principle_ocp2;
public class Spider implements Animal {
//other methods are left out for brevity
@Override
public int count( ) {
return 8;
}
}
package principle_ocp2;
import java.util.List;
public class AnimalLegsCounter {
public int count(List<Animal> animals) {
int count = 0;
for (Animal animal : animals) {
count += animal.count( );
}
return count;
}
}
275
}
package principle_dip1;
public class TigerHelper {
public void help( ){
//......
}
}
A29 The above three classes are tightly coupled with each other. The high level class
CircusService depends on the lower level class TigerHandler and the TigerHandler in turn
depends on the lower level class TigerHelper. All these classes are directly dependent on
the implementation as opposed to the abstraction. If some time in the future, the
circus service needs to deal with an elephant as opposed to a tiger, and you will have to
change the CircusService class to use the newly written ElephantHandler instead of the
TigerHandler. If you want to use an ImprovedTigerHelper instead of the TigerHelper, you
will have to change all the handler classes that use the TigerHelper. If the CircusService
wants to expand into other animals, then there will be more changes across various
classes. This shows that the above code is rigid. This situation can be improved by
applying the dependency inversion principle.
277
High level modules should not depend upon low level modules. Both should
depend upon abstractions.
Abstractions should not depend upon details. Details should depend upon
abstractions.
When this principle is applied, the higher level classes will not be working directly with
the lower level classes, but with an abstract layer. This gives us the flexibility at the cost
of increased effort. The classes that are less likely to change don't need to apply this
principle. Blindly applying this principle can make a system more complex.
The above code can be improved by using the dependency inversion principle (DIP)
as shown below:
Firstly define the abstraction layer.
package principle_dip2;
public interface AnimalHandler {
public abstract void handle( );
}
package principle_dip2;
public interface AnimalHelper {
public abstract void help( );
}
Now the implementation that depends on the abstraction as opposed to the imple
mentation.
package principle_dip2;
public class CircusService {
AnimalHandler handler;
278
true standard on Dependency Injection. CDI is a part of the Java EE 6 stack, meaning
an application running in a Java EE 6 compatible container can leverage CDI out-ofthe-box. Weld is the reference implementation of CDI.
Q31 Can you list some of the key principles to remember while designing your classes? DC
OEQ
A31
Favor composition over inheritance.
Don't Repeat Yourself (DRY principle). Code once and only once.
Find what varies and encapsulate it.
Code to an interface, and not to an implementation.
Strive for loose coupling and high cohesion.
Use design principles and patterns, but use them judiciously. Design for current
requirements without anticipating future requirements, and over complicating things.
A well designed (i.e. loosely coupled and more cohesive) system can easily lend itself
to future changes, whilst keeping the system less complex.
281
The above class diagram was generated using the open source tool argoUML. In the
above design, the animals are tightly coupled to the barn. The above design can be
made more flexible to handle other farm animals like hen, etc as shown below. The
282
design below is better because of lower coupling between the animals and the
enclosures like Barn, HenHouse, etc. Similar changes can be applied between the
Enclosure and the WoodenPlank so that the enclosures can be made with other materials
like Mesh, etc. While designing, you can also think of the design principles discussed
above and patterns like factory pattern or dependency injection to promote looser
coupling between entities.
283
The car park needs to cater for different types of car parks like regular, handi
capped, and compact.
It should keep track of empty and filled spaces.
It should also cater for valet parking
Map out the classes that would be required. Use a UML class diagram as shown above.
Here are some points to get started.
Note: Care must be taken to have a right balance between over engineering and not
engineering at all. All depends on requirements and which parts of the design are most
likely to grow in the future phases. The only way to get better at designing is with
experience and keeping all the design concepts, principles, and patterns in mind to
design a loosely coupled system that is simple, easy to understand, flexible and
maintainable.
284
//invariant
// constructor
public NegativeInteger(Integer val) {
if (!isNegative(val)) {
throw new IllegalArgumentException(
"Invalid negative number = " + val);
}
this.value = val;
}
public Integer addNumber(Integer val) {
if (!isNegative(val)) {
throw new IllegalArgumentException(
"Invalid negative number = " + val);
}
return this.value + val;
}
private boolean isNegative(Integer val) {
return val.intValue( ) < 0;
}
285
10
You could also have a loop invariant that could be verified for being true at the
beginning and end of each iteration. For example, you could have a loop that
processes a list of orders by moving them from list1 to list2, and you could say that
the invariant is list1.size( ) + list2.size( ) = constant, at the beginning or end of the loop.
If the above invariant check failed, it could indicate that there is a bug in the logic. For
example, you may have forgotten to add the processed element from list1 onto list2.
286
package chapter2.com;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.xml.bind.ValidationException;
import org.apache.commons.lang.StringUtils;
public class LoopInvariant {
public static void main(String[] args) throws ValidationException {
String[ ] input = {"age:9", "age=6", "gender:M" , "gender=Male"};
List<String> outPut = find(input, "=");
System.out.println(outPut);
}
public static List<String> find(String[ ] input, String toFind) throws
ValidationException {
//precondition check
if(input == null || StringUtils.isEmpty(toFind)){
throw new IllegalArgumentException("Invlid input !!");
}
int initialInputSize = input.length;
//loop invariant
//B
Q35 What do you understand by the term design by contract? DC FAQ COQ
A35 The design by contract ensures code quality by enforcing that the services offered by
classes and interfaces adhere to unambiguous contracts. The design by contract is very
useful for designing classes and interfaces. These contracts are like checking for an
empty or null string, checking for negative numbers, checking for a particular range,
etc.
In general the contracts checked are:
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 or a top level 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 member variables, methods and constructors.
There are two varieties of inner classes, static inner classes and non-static inner classes.
The difference is suggested in the name itself, the static inner classes are associated
with the enclosing class (as any other static member) while the non-static inner
classes are associated with the "object" of the enclosing class. Inner classes are a
phenomenon of the compiler. It converts the inner classes as plain class files with
some strange class names like OuterClass$InnerClass.class, so that the virtual machine
does not distinguish between regular and inner class files.
Access Modifiers: Unlike outer or top most class, which can only use public or
package-private (i.e. default) access modifiers, the inner classes can have any access
modifiers.
Inner classes can be categorized as below:
1. Static inner class
2. Non-static inner class
a) member class.
b) anonymous class.
c) local class.
Static inner class:
In effect, a static nested class is behaviorally a top-level class that has been nested in
another top-level class for packaging convenience. Static member classes can be
thought of as regular classes that are simply defined inside another class. They have
complete access to all the enclosing class's static member variables and methods.
package innerstatic;
public class OuterClass {
private static int d = 7;
private int e = 6;
//instance variable
291
//instance variable
As with instance methods and variables, an inner class is associated with an instance
of its enclosing class, and has direct access to that object's methods and member
variables. Also, because an inner class is associated with an instance, it cannot define
any static members itself. Like with a static inner class, the instance inner class is
known as qualified by its containing class name.
Access Modifiers: Since a non-static inner class cannot exist without an enclosing
instance, most non-static inner classes are either private or package-private (i.e. no
access modifier) scoped.
Q. So what is the real benefit of non-static inner classes? LF COQ
A. The inner class instance has access to the instance variables of the containing class
instance. These enclosing instance members are referred to inside the inner class via
just their simple names, and not via "this" as this in the inner class refers to the inner
class instance, and not the associated containing class instance.
package inner.nonstatic;
public class OuterClass {
private static int d = 7;
private int e = 6;
private void outerMethod( ){
InnerClass ic = new InnerClass( );
System.out.println("i=" + ic.i);
ic.execute( );
}
//instance variable
//instance variable
293
other classes can not access the MyCollectionIterator directly. All the classes are not aware
of MyCollectionIterator, and they just use the Iterator interface. The non-static inner
classes are also useful as adapters, where the keys or values in a Map can be viewed as a
Set by creating an inner class that implements the Set interface.
Q37 Can you have an inner class inside a method and what variables can you access? LF
FAQ
A37 Yes, you can also declare an inner class within the body of a method without naming
it. These classes are known as anonymous inner classes.
They can use local variables and parameters of the method, but only ones which are
declared "final". This is because the local class instance must maintain a separate copy
of the variable, as it may out-live the method. So as not to have the confusion of two
modifiable variables with the same name in the same scope, the variable is forced to be
non-modifiable.
Q. Can you provide an example from the Java API?
A. Anonymous classes are very powerful, but care must be taken not to over use it as it
can clutter up your top level classes and consequently compromise the readability. In
certain cases, it can improve readability as shown below.
In Java GUI development where they are attached as listeners:
goButton.addActionListener (new ActionListener( )
{
public void actionPerformed(ActionEvent e )
{
doImportantStuff( );
}
}
);
Another most common use of anonymous inner class is as comparators inside a
method:
List<String> listOfValues = new ArrayList<String>( );
Collections.sort(listOfValues, new Comparator<String>( ) {
public int compare(String s1, String s2) {
return s1.compareTo(s2);
295
Most of the languages specify a callback method by passing the address of the
subroutine to the system to the request it is calling back from, but Java performs the
same thing by using interfaces. Java does not allow passing the address of a
subroutine, but allows passing an instance of a class that implements the standard
interface. For this purpose, anonymous classes are mainly used as they support a better
compact definition of the class that is required as a new class. The following code
snippets illustrates a callback method with inner member interface and an anonymous
inner class.
package callback;
//Outer class
public class CallBackExample {
//inner interface
public interface CallBack {
int calculate(int a, int b);
}
public static void main(String[ ] args) {
//anonymous inner class
CallBack adder = new CallBack( ) {
public int calculate(int a, int b) {
return a + b;
}
};
CallBack multiplier = new CallBack( ) {
public int calculate(int a, int b) {
return a * b;
}
};
System.out.println(adder.calculate(5, 10));
System.out.println(multiplier.calculate(5, 10));
//prints 15
//prints 50
}
}
297
298
help you organize files within your project. For example, java.io package does
something related to I/O and java.net package does something to do with
networking and so on.
resolve naming conflicts when different packages have classes with the same
name. For example, the StringUtils class is provided by both the Spring
framework and Apache commons library as org.springframework.util.Strin
gUtils and org.apache.commons.lang.StringUtils respectively.
If we tend to put all .java files into a single package, as the project gets bigger, it
would become a nightmare to manage all your files.
Q40 How do you go about designing good packages? DC
A40 The packages exist solely because classes are not sufficient to group code. When
designing a package, you will have to ask a number of questions like,
300
What if I just want to release the packages (e.g. com.xyz.dao.*) related to just
data access?
What happens if I distribute my jar files (e.g. RMI or Web Service client jars)
to the client applications and the totally unexpected thing happens by showing
up bugs? I need to be able to determine whether the client had an old version
of the package, as the newest version definitely fixed all the remaining bugs. I
Packages create a dependency hierarchy. For example, all the classes related to database
operation can be placed in a package called com.xyz.dao. Another package called
com.xyz.client depends on the services offered by the package "com.xyz.dao". Make
use of the package UML diagrams to visualize and analyze package dependencies.
When designing packages, think of reuse, granularity, release management, unit testing,
distribution, and the level of coupling (i.e. dependency) with the other packages. There
are a number of principles that can help you design good packages.
302
Common-Closure Principle (CCP) helps you identify the coupling among the
different packages. This principle looks at the maintainability rather than reusability. The packages should be loosely coupled by not having multiple
reasons to change. Its preferable that changes occur in just one package rather
than distributed along the whole system. So packages need to be looked at
from change and distribution point of view.
Note: Refactor your packages when it makes more sense to do so by moving classes
from one package to another or to a newly created package.
Book recommendation: This has only scratched the surface on design principles. For
a more detailed understanding, refer to Agile Software Development, Principles,
Patterns, and Practices by Robert C. Martin.
304
Section-6:
Objects Essentials
An object (or instance) is an executable copy of a class. In the last section, you
looked at the object oriented concepts, design principles, working with classes, and inter
faces. It is a must to have a good understanding in working with the objects. You will be
performing various operations like creating, cloning, casting, comparing, modifying, serial
izing, and garbage collecting your objects.
The general preparation for a Java job interview can be summed up in four steps:
research, predict questions, prepare answers and practice. The step 2 to predict questions is
often not an easy task, but you can always prepare for most common interview questions
relating to OO concepts, classes, interfaces, working with objects, and language funda
mentals. Still more than 50% of the candidates don't answer these questions right. If you
can demonstrate that your fundamentals are solid by answering these questions with
examples and illustrations, you will stand a good chance of succeeding. This will be valued a
lot more than happening to know or having experience with a sought-after framework the
very moment. In other words, if you don't get these fundamental questions right, knowing
the sought-after frameworks is not going to do you any good.
305
Objects Essentials
You must pay attention to whether your implementation of these methods will
continue to work correctly if sub classed. If your class is not meant for
extension, then declare your class as final.
These methods have to adhere to contracts, and when implementing or
overriding a method, the contracts must be satisfied.
methods. Here are some key points to keep in mind while implementing these
methods,
package chapter2.com;
public final class Pet {
int id;
String name;
/**
* use @override annotation to prevent the danger of misspelling
* method name or incorrect method signature.
*/
@Override
public boolean equals(Object that){
//check for self-comparison
if ( this == that ) return true;
/**
* use instanceof instead of getClass here for two reasons
307
Objects Essentials
* 1. it can match any super type, and not just one class;
* 2. explicit check for "that == null" is not required as
* "null instanceof Pet" always returns false.
**/
if ( ! (that instanceof Pet) )
return false;
Pet pet = (Pet)that;
return this.id == pet.id && this != null && this.name.equals(pet.name);
}
/**
* fields id & name are used in both equals( ) and
* hashCode( ) methods.
*/
@Override
public int hashCode( ) {
int hash = 9;
hash = (31 * hash) + id;
hash = (31 * hash) + (null == name ? 0 : name.hashCode( ));
return hash;
}
}
Use Apache's HashCodeBuilder & EqualsBuilder classes to simplify your implementation,
especially when you have a large number of member variables. Commonclipse is an
eclipse plugin for jakarta commons-lang users. It is very handy for automatic gener
ation of toString( ), hashCode( ), equals(..), and compareTo( ) methods.
package chapter2.com;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
public final class Pet2 {
int id;
String name;
308
@Override
public boolean equals(Object that) {
if (this == that)
return true;
if (!(that instanceof Pet2))
return false;
Pet2 pet = (Pet2) that;
return new EqualsBuilder( ).append(this.id, pet.id).append(
this.name, pet.name).isEquals( );
}
/**
* both fields id & name are used in equals( ),
* so both fields must be used in hashCode( ) as well.
*/
@Override
public int hashCode( ) {
//pick 2 hard-coded, odd & >0 int values as arguments
return new HashCodeBuilder(1, 31).append(this.id).append(
this.name).toHashCode( );
}
}
Q03 When should you override a toString( ) method? LF COQ
A03 You can use System.out.println( ) or logger.info(...) to print any object. The toString( )
method of an object gets invoked automatically, when an object reference is passed in
the System.out.println(refPet) or logger.info(refPet) method. However for good results, your
class should have a toString( ) method that overrides Object class's default implement
ation by formatting the object's data in a sensible way and returning a String.
Otherwise all that's printed is the class name followed by an @ sign and then
unsigned hexadecimal representation of the hashCode. For example, If the Pet class
doesn't override the toString( ) method as shown below, by default Pet@162b91 will be
printed via toString( ) default implementation in the Object class. The public class Pet3
can be read as public class Pet3 extends Object.
309
Objects Essentials
package chapter2.com;
public class Pet3 {
int id;
String name;
@Override
public String toString( ) {
return new StringBuilder( ).append("id:" + id).append(
",name:" + name).toString( );
}
}
Output:
id:<id>, name:<name>
This is by far the easiest method to override. Still you can make mistakes like, say you
have the following toString( ) implementation for the BasePet class,
@Override
public String toString( ) {
return BasePet - + id= + id + , name= + name;
}
The above method is fine if not extended by any other class. If the above method is
extended by a Cat class, the output will still have the hard coded BasePet - . This
can be fixed as follows,
@Override
public String toString( ) {
return this.getClass( ) + - + id= + id + , name= + name;
}
If the above code is called from a Cat class, it will print Cat - followed by the id
and name. If it is called from a Dog class, it will print Dog - followed by the id and
name.
Q04 Can you override clone( ) and finalize( ) methods in the Object class? How do you disable
310
You should call the finalize method of the super class in case it has to clean up.
protected void finalize( ) throws Throwable {
try{
//finalize subclass state
}
311
Objects Essentials
catch(Throwable t){
//log the exception
}
finally {
super.finalize( );
}
}
You should not depend on the finalize method being called. There is no
guarantee that when (or if) objects will be garbage collected and thus no
guarantee that the finalize method will be called before the running program
terminates.
Finally, the code in finalize method might fail and throw exceptions. Catch
these exceptions so that the finalize method can continue.
Cloning Objects
The reason for making a local copy of an object is if youre going to modify that object and
you dont want to modify the callers object.
Q05 What is the main difference between shallow cloning and deep cloning of objects? LF
A05 Shallow copy: If a shallow copy is performed, the contained objects are not cloned.
Java supports shallow cloning of objects by default when a class implements the
java.lang.Cloneable interface. For example, invoking clone( ) method on a collection like
HashMap, List, etc returns a shallow copy of the HashMap, List, instances. This means
if you clone a HashMap, the map instance is cloned but the keys and values themselves
are not cloned, but are shared by pointing to the original objects.
312
Deep copy: If a deep copy is performed, then not only the original object has been
copied, but the objects contained within it have been copied as well. Serialization can
be used to achieve deep cloning. For example, you can serialize a HashMap to a
ByteArrayOutputStream and then deserialize it. This creates a deep copy, but does require
that all keys and values in the HashMap to be Serializable. The main advantage of this
approach is that it will deep copy any arbitrary object graph. Deep cloning through
serialization is faster to develop and easier to maintain, but carries a performance
overhead. Alternatively, you can provide a static factory method to deep copy as shown
below:
public static List deepCopy(List listCars) {
List copiedList = new ArrayList(10);
for (Object object : listCars) {
Car original = (Car)object;
Car carCopied = new Car( );
carCopied.setColor((original.getColor( )));
copiedList.add(carCopied);
}
return copiedList;
}
313
Objects Essentials
Casting Objects
Understand implicit and explicit casting. Also, be clear about overriding and hiding (aka
shadowing). They are two different things.
Q06 What is type casting? Explain up casting vs. down casting? When do you get ClassCas
tException? LF FAQ
A06 Type casting means treating a variable of one type as though it is another type.
byte short char int long float double
(1 byte)
(2 bytes)
(2 bytes)
(8 bytes)
When up casting primitives from left to right, automatic conversion occurs. But if you
go from right to left, down casting or explicit casting is required. This was already
discussed under Choosing the right data types with widening versus narrowing
conversions, in Language Essentials section.
When it comes to object references, you can always cast from a subclass to a super
class because a subclass object is also a super class object. You can cast an object
implicitly to a super class type (i.e. up casting). If this were not the case,
polymorphism wouldnt be possible.
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 ClassCastEx
ception 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 minimize the
need for casting, and otherwise you can deal with the problem of incorrect down
casting in two ways:
Using the exception handling mechanism to catch ClassCastException:
Object o = null;
try{
o = new Integer(1);
System.out.println((String) o);
}
catch(ClassCastException cce) {
logger.log(Invalid casting, String is expectedNot an Integer);
314
315
Objects Essentials
public String getVal( ) {
return val;
}
}
package shaddowing;
public class Child extends Parent {
protected String val = "child";
public String getVal( ) {
return val;
}
public static void main(String[ ] args) {
Child c = new Child( );
System.out.println("val=" + c.val);
//prints "child"
Parent p = c;
System.out.println("val=" + p.val);
316
}
}
Note: So when you override a method, you get the benefit of polymorphic behavior,
but when you hide or shadow, you don't get the polymorphic behavior.
Note: If you happen to have a parameter in a constructor or a method having the
same name as an instance variable, you must prefix the instance variable with this
keyword. Otherwise the parameter will be hiding or shadowing your instance variable.
For example,
Class Fruit {
private String name = null;
public Fruit(String name) {
this.name = name;
//name = name
Objects Essentials
// or shadowed by the parameter.
}
}
Immutable Objects
"Classes should be immutable unless there's a very good reason to make them mutable....If a
class cannot be made immutable, limit its mutability as much as possible."
-- by Joshua Bloch
Q09 What is an immutable object? How do you create an immutable type? What are the
advantages of immutable objects? LF BP FAQ
A09 Immutable objects are objects whose state (the object's data) cannot change after
construction. Examples of immutable objects from the JDK include String and
wrapper classes like Integer, Double, Character, etc.
Q. How do you create an immutable type?
Make the class final so that it cannot be extended or use static factories and
keep constructors private.
public final class MyImmutable { }
318
Don't 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.
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 other mutable objects like arrays, collections and
mutable classes like Date by:
Allow hashCode( ) method to use lazy initialization, by caching its return value.
Q10 Why is it a best practice to implement the user defined key class as an immutable
object? LF COQ
A10 Immutable objects generally make the best map keys as the keys cannot be modified
once they have been added to the Map. In general String, Integer, or Long are used as
keys, which are immutable objects. If you define your own key class, make sure that
they are immutable. Otherwise, if the keys are accidentally modified after adding to a
Map, you will never be able to retrieve the stored value as the key values have been
changed.
319
Objects Essentials
This is a common pitfall many Java developers, especially beginners fall for.
As shown, when Maps are used in Java, the equals( ) and hashCode( ) methods are impli
citly invoked. If these methods are incorrectly implemented or the keys are modified
once added to the map, then unpredictable behavior will be experienced, and these
behaviors are harder to debug. The hashCode( ) and equals( ) methods are implicitly
invoked to determine where the key is stored. These methods are called again to
retrieve the stored key. If they are implemented inconsistently or the key is mutated,
then the stored object cannot be retrieved as the returned values of these methods will
vary in between different invocations. To be more specific, the hashCode( ) method is
called to determine the key index (aka the bucket) of the array, and the equals( ) method
is called to retrieve the exact key from the list of keys belonging to that particular key
index (or bucket) as the same bucket will be holding multiple keys linked to multiple
values. Remember the contract between these two methods? If 2 objects are equal,
they must return the same hashCode( ) value, but the reverse is not true. Which means,
if 2 objects return the same hashCode( ) value does not mean that those 2 objects are
equal( ).
320
Objects Essentials
myImmutableRef.getMyArray( )[0] = 7;
// mutate value
System.out.println("After constructing " + myImmutableRef);
}
}
Output:
Before constructing Numbers are: [1, 2]
After constructing Numbers are: [7, 5]
As you can see that the array values have been mutated (i.e. modified) through the
references that escaped. You can fix the above code by defensively copying the array
before returning in the getMyArray( ) method as well as in the constructor. This is
essential because Java's references are passed by value. You can fix the above problem
as follows,
package chapter2.com;
import java.util.Arrays;
public final class MyImmutable2 {
private final Integer[ ] myArray;
public MyImmutable2(Integer[ ] anArray) {
this.myArray = anArray.clone( );
}
public Integer[ ] getMyArray( ) {
return myArray.clone( );
}
public String toString( ) {
StringBuffer sb = new StringBuffer("Numbers are: ");
sb.append(Arrays.deepToString(myArray));
return sb.toString( );
}
}
322
Output:
Before constructing Numbers are: [1, 2]
After constructing Numbers are: [1, 2]
Q12 How would you defensively copy a Date field in your immutable class? COQ
A12
package chapter2.com;
import java.util.Date;
public final class MyDiary {
private final Date myDate;
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
}
}
You can try with and without defensive copying by using the following test code,
package chapter2.com;
import java.util.Date;
public class MyDiaryTest {
public static void main(String[ ] args) {
Date myDate = new Date( );
MyDiary ref = new MyDiary(myDate);
System.out.println("myDate before = " + ref.getDate( ));
myDate = ref.getDate( );
323
Objects Essentials
myDate.setDate(25);
//mutate myDate
System.out.println("myDate after = " + ref.getDate( ));
}
}
Q13 How will you prevent the caller from adding or removing elements from pets? How
will you make this code fully immutable? COQ
package chapter2.com;
import java.util.List;
public class PetCage {
private final List<Pet> pets;
public PetCage(List<Pet> pets) {
this.pets = pets;
}
public List<Pet> getPets( ) {
return pets;
}
}
A13 An instance of PetCage class is not immutable because one can add or remove pets
either by obtaining the pets by calling getPets( ) or by retaining a reference to the List
object passed when an object of this class is constructed. The following change
partially solves the immutability problem.
package chapter2.com;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PetCage {
private final List<Pet> pets;
public PetCage(List<Pet> pets) {
324
When you obtain an iterator, which holds a reference to the underlying array, the array
referenced by the iterator is effectively immutable and therefore can be traversed
without synchronization or risk of concurrent modification. This eliminates the need
to either clone the list before traversal or synchronize on the list during traversal. If
reads are much more frequent than insertions or removals, which is the case very
often, the Copy-on-Write collections and ConcurrentHashMaps offer better performance
and development convenience. The development convenience is provided not needing
to deal with synchronization, deep cloning, or ConcurrentModificationException. The
325
Objects Essentials
ConcurrentModificationException is generally thrown by an ArrayList, HashSet, or a
HashMap implementation when you try to try to modify (e.g. remove) an object from a
collection while iterating over it.
Allows invalid values. Any integer value can be assigned, although there are
only 4 valid choices .
season = 25;
package withoutenum;
import java.util.ArrayList;
import java.util.List;
//not a best practice
public class Weather {
public static final int WINTER = 0;
public static final int SPRING = 1;
public static final int SUMMER = 2;
public static final int FALL = 3;
326
Objects Essentials
import java.util.ArrayList;
import java.util.List;
public class Weather {
public enum Season {WINTER, SPRING, SUMMER, FALL}
private final Season season;
that the case values don't have to be qualified with the enum class name, which can be
determined from the switch control value.
public static void main(String[ ] args) {
Weather w = new Weather(Season.WINTER);
switch (w.season) {
//values are not fully qualified with the enum type Season.
case WINTER:
System.out.println("It is Winter");
break;
case SPRING:
System.out.println("It is Spring");
break;
case SUMMER:
System.out.println("It is Summer");
break;
case FALL:
System.out.println("It is Fall");
break;
default:
break;
}
Note: The case statements are not fully qualified with the enum name. As shown
above it is just WINTER, and not Season.WINTER.
Q17 How will you get an integer equivalent of an enum value?
A17 Using the ordinal( ) method.
LF
w.season.ordinal( );
Q18 How will you convert a String value to an enum value? LF
A18 Using the valueOf( ) method. You can use the methods name( ) or toString( ) to convert
an enum value back to a string value.
Q19 Would you use equals( ) or == to compare enum values? LF
A19 Both equals( .. ) and == amount to the same thing, and can be used interchangeably as
enums are implicitly public static final.
329
Objects Essentials
Q20 Do you have to override equals( ) and hashCode( ) methods for enum? LF
A20 No. Since you can only create one instance (i.e. singleton) of each season, you don't
have to override these methods. The class Enum declares equals( ), hashCode( ), clone( ),
and compareTo( ) methods as final. You can override the toString( ) method to provide
better information for debugging.
Q21 Are enums immutable? LF DP
A21 Yes. As with any class, it is easy to provide methods in an enum type which can change
the state of an enum constant. Hence, the term "enum constant" is misleading. What
is constant is the identity of the enum element, not its state. Perhaps a better term
would have been "enum" instead of "enum constant". The enum classes can have
behavior, hence the responsibility is on the developers to make it immutable.
Since enum is basically a special class type, and can have methods and fields just like
any other class, you can apply the "template method" design pattern to create enumer
ations that are factories or command objects. Here is a simple example of a
"command" enumeration. You can also implement your own method to do a reverse
lookup of a weather based on a given weather code as illustrated below.
package withenum;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
public enum Season {
WINTER("WT") {
@Override
public void execute( ) {
System.out.println("Winter...");
}
},
SPRING("SP") {
@Override
public void execute( ) {
System.out.println("Spring....");
}
},
330
SUMMER("SM") {
@Override
public void execute( ) {
System.out.println("Summer...");
}
},
FALL("FL") {
@Override
public void execute( ) {
System.out.println("Fall...");
}
};
private static final Map<String, Season> lookup =
new HashMap<String, Season>( );
static {
for (Season s : EnumSet.allOf(Season.class))
lookup.put(s.getCode( ), s);
}
private String code;
private Season(String code) {
this.code = code;
}
public String getCode( ) {
return code;
}
//template method
public abstract void execute( );
//reverse lookup method
public static Season getByCode(int code) {
return lookup.get(code);
}
331
Objects Essentials
}
Unsightly switch/case or if/else statements can be minimized or avoided with the help
of template method pattern shown above. The client code would use something like
season.execute( ). The static getByCode(String) method provides the reverse lookup by
simply getting the value from the Map. The static block that populates the Map, uses a
specialized implementation of Set, java.util.EnumSet, which has better performance
than java.util.HashSet. Java 5.0 also provides a more compact and specialized imple
mentation of Map for enums with java.util.EnumMap.
332
The code snippet below explains how the String class follows the standard rule as
explained above and a variation to the above rule when the String objects are pooled
for performance using the flyweight design pattern.
package chapter2.com;
public class String2 {
public static void main(String[ ] args) {
String s1 = new String("A");
String s2 = new String("A");
333
Objects Essentials
//standard: follows the == and equals( ) rule applicable to any Java object
if (s1 == s2) {
// shallow comparison
System.out.println("references/identities are equal"); // never reaches here
// as s1 & s2 point to
// 2 different objects
}
if (s1.equals(s2)) {
// deep comparison
System.out.println("values are equal");
// this line is printed as s1 & s2
// have the same value of A
}
//variation: does not follow the == and equals rule
//Specific to String objects declared as shown below
//This is an example of the fly-weight design pattern.
String s3 = "A";
String s4 = "A";
if (s3 == s4) {
// shallow comparison
System.out.println("references/identities are equal");
}
if (s3.equals(s4)) {
System.out.println("values are equal");
}
// this line
// is printed
// deep comparison
// this line is also printed
}
}
Q23 What happens when you run the following code? LF COQ FAQ
Boolean b1 = new Boolean(false);
Boolean b2 = Boolean.FALSE;
if(b1 == b2) {
System.out.println("Equal");
}
334
else{
System.out.println("Not Equal");
}
A23 Prints Not Equal.
The == is a shallow comparison that only compares the references. The references are
not equal. If you want to print Equal, perform a deep comparison as shown below,
which compares the values.
if (b1.equals(b2)){
System.out.println("Equal");
}
else {
System.out.println("Not Equal");
}
//gets printed
Q24 Can you discuss the output of the following code? COQ
public class PrimitiveAndObjectEquals {
public static void main(String[ ] args) {
int a = 5;
int b = 5;
Integer c = new Integer(5);
Integer d = new Integer(5);
if (a == b) {
System.out.println("primitives a and b are ==");
}
//Line 1
if (c == d) {
System.out.println("Objects c and d are ==");
}
//Line 2
if (c.equals(d)) {
System.out.println("Objects c and d are equals( )");
//Line 3
335
Objects Essentials
}
if (a == d) {
//Line 4
System.out
.println("Primitive a and Object d are == due to auto unboxing");
}
}
}
A24 Output is:
primitives a and b are ==
Objects c and d are equals( )
Primitive a and Object d are == due to auto unboxing
//Line 1
//Line 2
}
}
}
337
Objects Essentials
Line 1 has been accepted by the compiler even though that condition will never be
true.
Line 2 forces the compiler to scream that you can't compare apples with oranges.
338
The above code creates only two new objects, the StringBuffer and the final String that is
returned. BP The StringBuffer expands as it reaches its capacity, which can be costly if
performed frequently. So it would be better to initialize the StringBuffer with the correct
size from the start as shown above with the size of 110 if the initial size is known or
estimate the size appropriately to minimize the number of times it has to expand.
Note: The creation of extra strings is not limited to the overloaded mathematical
operator "+", but there are several other methods like concat( ), trim( ), substring( ), and
replace( ) in the String class that generate new string instances. So use StringBuffer or
StringBuilder for computation intensive operations to get better performance.
Q27 Can you write a method that reverses a given String? COQ FAQ
A27
public class ReverseString {
public static void main(String[ ] args) {
System.out.println(reverse("big brown fox"));
System.out.println(reverse(""));
}
public static String reverse(String input) {
if(input == null || input.length( ) == 0){
return input;
}
return new StringBuilder(input).reverse( ).toString( );
}
}
BP It is always a best practice to reuse the API methods as shown above with the
StringBuilder(input).reverse( ) method as it is fast, efficient (uses bitwise operations)
and knows how to handle Unicode surrogate pairs, which most other solutions ignore.
The above code handles null and empty strings, and a StringBuilder is used as opposed
to a thread-safe StringBuffer, as the StringBuilder is locally defined, and local variables are
implicitly thread-safe.
Some interviewers might probe you to write other lesser elegant code using either
recursion or iterative swapping. Some developers find it very difficult to handle
339
Objects Essentials
recursion, especially to work out the termination condition. All recursive methods need
to have a condition to terminate the recursion.
public class ReverseString2 {
public String reverse(String str) {
// exit or termination condition
if ((null == str) || (str.length( ) <= 1)) {
return str;
}
// put the first character (i.e. charAt(0)) to the end. String indices are 0 based.
// and recurse with 2nd character (i.e. substring(1)) onwards
return reverse(str.substring(1)) + str.charAt(0);
}
}
The iterative swapping approach can be demonstrated as shown below.
0
2
v
3
e
5
r
6
s
lhsIdx
rhsIdx
Swap lhsIdx with the rhsIdx, and increment the lhsIdx and decrement the rhsIdx.
0
1
2
3
4
5
6
e
lhsIdx
rhsIdx
Repeat the above step as long as the lhsIdx is less than the rhsIdx.
e
lhsIdx
e
rhsIdx
e
lhsIdx
rhsIdx
340
Now the exit (or loop termination) condition has reached since lhsIdx >= rhsIdx.
Let's look at the code:
public class ReverseString3 {
public String reverse(String str) {
// validate
if ((null == str) || (str.length( ) <= 1)) {
return str;
}
char[ ] chars = str.toCharArray( );
int rhsIdx = chars.length - 1;
//iteratively swap until exit condition lhsIdx < rhsIdx is reached
for (int lhsIdx = 0; lhsIdx < rhsIdx; lhsIdx++) {
char temp = chars[lhsIdx];
chars[lhsIdx] = chars[rhsIdx];
chars[rhsIdx--] = temp;
}
return new String(chars);
}
}
Q28 Can you talk through the output of the following code snippet? COQ FAQ
package chapter2.com;
public class String1 {
public static void main(String[ ] args) {
StringBuffer sb = new StringBuffer("watering");
sb.replace(2,6, "WATER");
sb.delete(1,4);
String result = sb.substring(2,4);
System.out.println("result=" + result);
}
// Line 7
// Line 8
// Line 9
}
341
Objects Essentials
A28 Remember two things:
Indexes
Output:
result=ER
Q29 Can you explain how Strings are interned in Java? LF DP FAQ
A29 String class is designed with the Flyweight design pattern. 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, a new 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.
Two String objects are created by the code shown below. Hence s1 == s2 returns false.
//Two new objects are created. Not recommended.
String s1 = new String("A");
String s2 = new String("A");
342
Instead use:
String s1 = "A";
String s2 = "A";
s1 and s2 point to the same String object in the pool. Hence s1 == s2 returns true.
Q30 Why String has been made immutable in Java? LF SE
A30
The main reason for immutability is security. In Java you pass file names, host
names, user names, passwords, etc as String. For example, had String been
mutable, 'user1' could log into a Java application using his user-name and
password credentials, and then possibly change the name of his password file
name, which is a String object from 'password1' to 'password2' before JVM
actually places the native OS system call to open the file. This is a serious
security breach allowing 'user1' to open user2's password file.
Immutable classes are ideal for representing values of abstract data (i.e. value
objects) types like numbers, enumerated types, etc. If you need a different
value, create a different object. In Java Integer, Long, Float, Character, BigInteger
and BigDecimal are all immutable objects. Immutable classes are inherently
thread-safe, hence they are less error prone and can be used safely in a multithreaded environment for better scalability. Optimization strategies like
caching, pooling, etc can be easily applied to improve performance.
Q31 Can you list some escape characters in Java? Where are they generally used? LF
A31 Escape characters (also called escape sequences or escape codes) are used to signal an
alternative interpretation of a series of characters.
\n
\t
\b
\r
\f
\\
\'
\"
All characters can be specified as hexadecimal Unicode characters (\uxxxx) with some
343
Objects Essentials
as octal characters (\ddd where the first d is limited to 0-3, and the others 0-7 - same
as \u0000-\u00ff).
\d Octal
\ud Unicode character
Most commonly, escape characters are used to solve the problem of using special
characters inside a string declaration.
String str = "The question is \"to be or not to be\""
System.out.print("\nCreate a new line before and after.\n");
System.out.print("\nThe Pound symbol is --> \u00A3");
System.out.print("\n The Yen symbol is --> \u00A5");
Tip: When using a new line character in your code, it is important to understand that it
varies among the different operating systems. For example, Windows uses a \n\r,
Mac uses a \r, and the UNIX based systems uses a \n. So, you need to use the
line.separator property to make your code portable as shown below:
private static final String NEWLINE = System.getProperty("line.separator");
Q32 How will you split the following string of text into individual vehicle types? COQ
Car,Jeep, Wagon Scooter
Truck, Van
}
Output:
Vehicle = "Car"
Vehicle = "Jeep"
Vehicle = "Wagon"
Vehicle = "Scooter"
Vehicle = "Truck"
Vehicle = "Van"
Refer to java.util.regex.Pattern class API for regex (i.e. Regular Expression) constructs.
The split( ) method takes a regex pattern to split the given string.
Q33 What are the different ways to concatenate strings? Which approach is most efficient?
LF PC COQ
A33
Plus (+) operator.
String s1 = John + Davies;
The efficiency depends on what you are concatenating and how you are concatenating
it.
Concatenating constants: Plus operator is more efficient than the other two
as the JVM optimizes constants.
String s1 = John + Davies;
345
Objects Essentials
In summary,
Objects Essentials
public class WithGenerics {
public static void main(String[ ] args) {
//can only add Integers
List<Integer> list1 = new ArrayList<Integer>( );
list1.add(new Integer(5));
//can only add Strings
List<String> list2 = new ArrayList<String>( );
list2.add("A String");
//can only add Floats
List<Float> list3 = new ArrayList<Float>( );
list3.add(new Float(3.0));
//compile error if you try to add a wrong type
//list1.add("NOT ALLOWED"); //list1 contains Integers NOT String
//no casting is needed
Integer i = list1.get(0);
String s1 = list2.get(0);
Float f1 = list3.get(0);
}
}
The for-each loop that was added in JDK 1.5 works well with generics.
for(String aString : list2){
System.out.println(aString);
}
Q35 What do you understand by the term type erasure with regards to generics? LF
FAQ
A35 Rule 1: Java generics differ from C++ templates. Java generics (at least until JDK 7),
generate only one compiled version of a generic class or method regardless of the
number of types used. During compile-time, all the parametrized type information
within the angle brackets are erased and the compiled class file will look similar to code
348
written prior to JDK 5.0. In other words, Java does not support runtime generics.
Q. Why was it done this way?
A. This was done this way to achieve backward compatibility.
package generics;
import java.util.HashMap;
import java.util.Map;
public class TypeErasure {
public static void main(String[ ] args) {
Map<String, Integer> map = new HashMap<String, Integer>( );
map.put("Key1",new Integer(1));
Integer val1 = map.get("Key1");
//casting is not required
System.out.println(val1);
}
}
If you run the compiled class file (i.e. TypeErasure.class) with the help of a Java decom
piler discussed under platform essentials, you will get the following source code.
package generics;
import java.util.HashMap;
import java.util.Map;
public class TypeErasure
{
public static void main(String[ ] args)
{
Map map = new HashMap( );
map.put("Key1", new Integer(1));
Integer val1 = (Integer)map.get("Key1");
System.out.println(val1);
}
}
349
Objects Essentials
As you can see, all the angle brackets have disappeared, and casting has been added.
Q36 What does the following code fragment print? LF COQ
List<String> list1 = new ArrayList<String>( );
List<Integer> list2 = new ArrayList<Integer>( );
System.out.println(list1.getClass( ) == list2.getClass( ));
A36 It prints true because of type erasure(i.e. Rule 1), all instances of a generic class
have the same runtime class, regardless of their actual type parameter. This also
mean, there is no sense in checking generic information at runtime. The following
code is illegal.
if(list1 instanceof List<String>)
//illegal
The JVM can actually check to see if it's a List, but it can't check whether it is a list of
Strings or Integers because the type information in angle brackets have been erased.
Never ignore warnings like this from the compiler. You also cannot handle different
versions of the same exception as shown below because type erasure does not allow it.
//illegal
try {
//....
}
catch(MyException<Integer> ex1){
//....
}
catch(MyException<String> ex2){
//....
}
Q37 What are the differences among LF FAQ
350
//illegal
Objects Essentials
System.out.println("peeling " + this.getClass( ).getSimpleName( ));
}
}
package generics;
public class Orange implements Fruit {
@Override
public void peel( ) {
System.out.println("peeling " + this.getClass( ).getSimpleName( ));
}
}
package generics;
import java.util.ArrayList;
import java.util.List;
public class Generics3 {
public static void main(String[ ] args) {
List<Fruit> fruitBasket = new ArrayList<Fruit>( );
fruitBasket.add(new Orange( ));
fruitBasket.add(new Mango( ));
processFruits(fruitBasket);
//compile-time error
}
//Won't work with List<Object> because a List<Object> is not a super
//type for all fruits (i.e. List<Fruit>).
public static void processFruits(List<Object> fruitBasket){
for (Object object : fruitBasket) {
((Fruit)object).peel( );
}
}
}
The processFruits(...) method can be fixed with the wild card character ? as discussed
next.
352
//legal
//legal
Objects Essentials
this method for a List<Mango> or List<Orange>. As discussed earlier in Rule 2, a
Mango or Orange might be a sub type of Fruit, but a List<Mango> or List<Orange> is
not a sub type of List<Fruit>. This means the following declaration is illegal.
List<Fruit> fruitBasket = new ArrayList<Orange>( ); // illegal as per Rule 2.
and also, the following processFruits(List<Fruit> fruitBasket) as shown below
public static void processFruits(List<Fruit> fruitBasket){
for (Fruit fruit : fruitBasket) {
fruit.peel( );
}
}
will only work if you declare your fruitBasket as follows:
List<Fruit> fruitBasket = new ArrayList<Fruit>( );
//legal
But will throw a compile-time error if you pass a List<Orange> as a reference to the
processFruits(List<Fruit> fruitBasket) method due to Rule 2 as shown below:
List<Orange> fruitBasket = new ArrayList<Orange>( ); //legal
processFruits(fruitBasket);
//compile-time error
Q. Why was the fruitBasket declared as follows?
List<Fruit> fruitBasket = new ArrayList<Fruit>( );
and not as shown below?
List<?> fruitBasket = new ArrayList<?>( );
List<?> fruitBasket = new ArrayList<? extends Fruit>( );
//illegal
//illegal
//illegal
//illegal
355
Objects Essentials
}
//I can process any type of Fruit
public static void processFruits(List<? extends Fruit> fruitBasket){
for (Fruit fruit : fruitBasket) {
fruit.peel( );
}
}
}
Output:
peeling Orange
peeling Mango
peeling Orange
peeling Mango
Q38 Is it possible to generify your own Java class? LF
A38 Yes.
package chapter2.com;
public class MyGenericClass<T> {
T objType;
public MyGenericClass(T type) {
this.objType = type;
}
public T getObjType( ) {
return objType;
}
public void setObjType(T objType) {
this.objType = objType;
}
public static void main(String[ ] args) {
356
Objects Essentials
If you closely examine the above code, you would notice that the compiler has
performed auto-boxing as generics does not support primitive types. The angle
brackets have been removed for val1 & val2 declarations and appropriate castings have
been added to convert from type T to Integer and Long types.
Tip: If you are not too sure, refer to the compiled class to better understand the code
by decompiling it using a Java decompiler.
Q. What do you understand by the term type argument inference? LF
A. Rule 6: The type inference happens when the compiler can deduce the type
arguments of a generic type or method from a given context information. There are 2
situations in which the type argument inference is attempted during compile-time.
1. When an object of a generic type is created as demonstrated in the
MyGenericClass<T>.
//T is inferred as an Integer
MyGenericClass<Integer> val1 = new MyGenericClass<Integer>(37);
//T is inferred as a Long
MyGenericClass<Long> val2 = new MyGenericClass<Long>(250L);
2. When a generic method is invoked. For example,
package generics;
import java.util.ArrayList;
import java.util.List;
public class MyBasket {
/**
* The 'src' is the inferred type T or its sub type and the 'dest' is the
* inferred type T or its super type.
*/
public static <T> void copy(List<? extends T> src,
List<? super T> dest) {
for (T obj : src) {
dest.add(obj);
358
}
}
public static void main(String[] args) {
List<Orange> orangeBasket = new ArrayList<Orange>(10);
List<Mango> mangoBasket = new ArrayList<Mango>(10);
orangeBasket.add(new Orange());
mangoBasket.add(new Mango());
List<Fruit> fruitBasket = new ArrayList<Fruit>(10);
List<Orange> orangeBasket2 = new ArrayList<Orange>(10);
orangeBasket2.add(new Orange());
List<Mango> mangoBasket2 = new ArrayList<Mango>(10);
mangoBasket2.add(new Mango());
List<Fruit> fruitBasket2 = new ArrayList<Fruit>(10);
fruitBasket2.add(new Mango());
MyBasket.copy(orangeBasket2, orangeBasket); // T is an Orange
MyBasket.copy(mangoBasket2, mangoBasket); // T is a Mango
MyBasket.<Orange> copy(orangeBasket, fruitBasket); // T is an Orange
MyBasket.<Mango> copy(mangoBasket, fruitBasket); // T is a Mango
MyBasket.copy(fruitBasket2, fruitBasket);
// T is a Fruit
Objects Essentials
fruit.peel();
}
}
}
If you comment the 2 lines that result in compile-time error, you will get the following
output,
peeling Orange
peeling Orange
peeling Mango
peeling Mango
peeling Mango
The copy(...) method ensures that fruits from a mixed fruit basket cannot be copied
to a basket that only holds oranges or mangoes. But a mixed fruit basket allows fruits
to be copied from any basket.
Q39 Is the following code snippet legal? If yes why and if not why not? LF COQ
public MyGenericClass( ) {
this.objType = new T( );
}
A39 It is not legal as new T( ) will cause a compile-time error. This is partially because
there's no guarantee that the target class for raw type T has a constructor that takes
zero parameters and partially due to type erasure where the raw type "T" does not have
any way of knowing the type of object you want to construct at runtime due to Rule 1
discussed earlier.
Q40 Is the following code snippet legal? If yes why and if not why not? LF COQ
public class MyGenericClass<T,S> {
static T objType;
public static void process(List<T> list) {
// ...
}
}
360
//compile-time error
//compile-time error
}
Unlike C++, Java uses the same class at runtime for both MyGenericClass<Integer> and
MyGenericClass<Long>. Static members, by definition are bound to the class rather than
the instance. If the code above were allowed, then type safety could be compromised
if an instance of MyGenericClass<Integer> stores an Integer value and an instance of
MyGenericClass<Long> retrieves it as a Long.
Rule 7: Static members are not allowed to have reference to their type parameters due
to Rule 1. Since the static members are not allowed to have reference to their type
parameters, generic enum is also not allowed as enum values are static data members.
enum State <T> {
//....
}
//illegal
//Line A
361
Objects Essentials
public static void main(String[ ] args) {
List<Integer> listIntegers = new ArrayList<Integer>( );
Integer value1 = new Integer(37);
addValue(value1, listIntegers);
//T is inferred as an Integer
System.out.println("listIntegers=" + listIntegers);
List<String> listString = new ArrayList<String>( );
String value2 = "Test";
addValue(value2, listString);
//T is inferred as a String
System.out.println("listString=" + listString);
}
}
Note: If you had used the wildcard List<?> instead of List<T> on line A, it would
not have been possible to add elements due to Rule 5. You will get a compile-time
error. So how does the compiler know the type of T? It infers this from your use of
the method as discussed in Rule 6. The generated class file looks pretty much the same
as the source file without the <Integer> and <String> angle brackets as shown below
once decompiled.
package chapter2.com;
import java.util.ArrayList;
import java.util.List;
public class MyGenericMethod
{
public static <T> void addValue(T value, List<T> list)
{
list.add(value);
}
public static void main(String[ ] args) {
List listIntegers = new ArrayList( );
Integer value1 = new Integer(37);
addValue(value1, listIntegers);
System.out.println("listIntegers=" + listIntegers);
362
Objects Essentials
import java.util.Iterator;
import java.util.List;
public class GenericsWithIterators {
public static void main(String[ ] args) {
List<Integer> listIntegers = new ArrayList<Integer>( );
listIntegers.add(5);
listIntegers.add(3);
//1
//2
//3
Iterator it = listIntegers.listIterator( );
//4
while(it.hasNext( )){
Integer i = it.next( );
System.out.println(i);
}
//5
//6
//7
}
}
A44 Line 4 will cause compile-time error on line 6 as the iterator is not generic. To fix this,
replace line 4 with:
Iterator<Integer> it = listIntegers.listIterator( );
// fix 1
// fix 2
The fix 1 is preferred. When you get an iterator, keyset, or values from a collection,
assign it to an appropriate parametrized type as shown in fix 1.
b. BeanInfo
c. Remote
d. Serializable
A45 d. Serializable
Q46 What is serialization? How would you exclude a field of a class from serialization or
what is a transient variable? What happens to static fields during serialization? What are
the common uses of serialization? What is a serial version id? Are there any disad
vantages in using serialization?LF FAQ
A46 Object 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. This simply allows the serialization
mechanism to verify that a class can be persisted, typically to a file. The common
process of serialization is also called marshaling or deflating when an object is
flattened into byte streams. The flattened byte streams can be unmarshaled or
inflated back to an object.
Rule #1: The object to be persisted must implement the Serializable interface
or inherit that interface from its object hierarchy. Alternatively, you can use an
Externalizable interface to have full control over your serialization process. For
365
Objects Essentials
example, to construct an object from a pdf file.
Rule #2: The object to be persisted must mark all non-serializable fields as
transient. For example, file handles, sockets, threads, etc.
Rule #3: You should make sure that all the included objects are also serial
izable. If any of the objects is not serializable, then it throws a NotSerializ
ableException. In the example shown below, the Pet class implements the
Serializable interface, and also the containing field types String and Integer are
also serializable.
Rule #4: Base or parent class fields are only handled if the base class itself is
serializable.
Rule #5: Serialization ignores static fields, because they are not part of any
particular state.
The Pet class shown below can be persisted based on the above mentioned rules.
package serialization;
import java.io.File;
import java.io.Serializable;
public class Pet implements Serializable {
private static final long serialVersionUID = 1L;
//classes Integer and String implements serializable
private Integer id;
private String name;
//to show that transient fields are not serialized
private transient File petFile = new File("DummyPet.txt");
//to show that static fields are ignored
private static Integer petCount = new Integer(0);
public Pet(Integer id, String name) {
this.id = id;
this.name = name;
366
}
public Integer getId( ) {
return id;
}
public String getName( ) {
return name;
}
public void incrementCount( ) {
petCount++;
}
@Override
public String toString( ) {
return "[id=" + id + ",name=" + name + ",petFile=" + petFile
+ ",petCount=" + petCount + "]";
}
}
You can serialize the Pet object as shown below:
package serialization;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class FlattenPet {
public static void main(String[ ] args) throws IOException {
String filename = "C://temp/pet.ser";
if (args.length > 0) {
filename = args[0];
}
Pet myPet = new Pet(7, "Jimmy");
myPet.incrementCount( );
FileOutputStream fos = null;
367
Objects Essentials
ObjectOutputStream out = null;
try {
fos = new FileOutputStream(filename);
out = new ObjectOutputStream(fos);
// serialization takes place here
out.writeObject(myPet);
} finally {
if (out != null) {
out.close( );
}
}
}
}
You can deserialize the pet.ser byte stream file back to an object as shown below:
package serialization;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class InflatePet {
public static void main(String[ ] args) throws IOException {
String filename = "C://temp/pet.ser";
Pet myPet = null;
FileInputStream fis = null;
ObjectInputStream in = null;
try {
fis = new FileInputStream(filename);
in = new ObjectInputStream(fis);
//deserialization occur here
myPet = (Pet) in.readObject( );
} catch (ClassNotFoundException ex) {
ex.printStackTrace( );
368
}
finally {
if (in != null) {
in.close( );
}
}
// print out restored myPet to see what were serialized
// what were not serialized.
System.out.println("Inflated pet: " + myPet);
}
}
Output:
Inflated pet: [id=7,name=Jimmy,petFile=null,petCount=0]
As you can see in the output, even though the petCount was incremented to 1, you got
0 back as static fields will not be serialized. The petFile is null, even though is initialized
as it is marked transient. Only non-transient and non-static fields id and name are
serialized as shown in the output.
Q47 How would you exclude a field of a class from serialization? LF FAQ
A47 By marking it as transient. The fields marked as 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.
Q48 What happens to static fields during serialization? LF FAQ
A48 Serialization persists only the state of a single object. Static fields are not part of the
state of an object - they're effectively the state of the class shared by many other
instances.
Q49 What are the common uses of serialization? Can you give me an instance where you
used serialization? LF FAQ
A49
allows you to persist objects with state to a text file on a disk, and re-assemble
them by reading this back in. Application servers can do this to conserve
memory. For example, stateful EJBs can be activated and passivated using
369
Objects Essentials
serialization. The objects stored in an HTTP session should be serializable to
support in-memory replication of sessions to achieve scalability.
allows you to send objects from one Java process to another using sockets,
RMI, RPC, etc.
Depends on reflection.
Has an incredibly verbose data format.
Is very easy to send surplus data.
A52 An object must implement either Serializable or Externalizable interface before it can
be written to a byte stream. When you use Serializable interface, your class is serialized
automatically by default. But you can override writeObject(..) and readObject(...) methods
to control or customize your object serialization process. For example, you can add the
following methods to your Pet class.
private void writeObject(ObjectOutputStream out)
throws IOException {
//any write customization goes in this method
System.out
.println("Started writing object .....................");
out.writeObject(this);
}
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
//any read customization goes in this method
System.out
.println("Started reading object .....................");
in.readObject( );
}
Note: Both the above methods must be declared private.
No changes are required for FlattenPet and InflatePet classes. If you re-run InflatePet by
including the above methods in the Pet class, you will get,
Output:
Started reading object .....................
Inflated pet: [id=null,name=null,petFile=null,petCount=0]
When you use Externalizable interface instead of the Serializable interface, you have a
complete control over your class's serialization process. This interface contains two
methods namely readExternal(...) and writeExternal(...) to achieve this total customiz
ation. You can change the Pet class to implement the Externalizable interface and then
provide implementation for following 2 methods.
371
Objects Essentials
public void writeExternal(ObjectOutput out) throws IOException;
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException;
An example situation for this full control will be to read and write PDF files with a
Java application. If you know how to write and read PDF (the sequence of bytes
required), you could provide the PDF specific protocol in the writeExternal(...) and
readExternal(...) methods.
Just as before, there is no difference in how a class that implements Externalizable is
used. Just call writeObject( ) or readObject and, those externalizable methods will be called
automatically.
Garbage Collection
A number of recorded questions asked in Java interviews were related to garbage collection.
Q53 What is the difference between final, finally and finalize( ) in Java? LF FAQ
A53
final - is a constant declaration.
finally - handles exception. The finally block is optional and provides a
mechanism to clean up regardless of an exception is thrown or not.
finalize( ) - method helps in garbage collection. This method 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.
The System.runFinalization( ) is a hint or request to run all the finalizers of
eligible objects, but no guarantee is made that finalization will run immediately
after the request.
Q54 What do you know about the Java garbage collector? When does the garbage collection
occur? LF FAQ
372
A54
Objects Essentials
Objects created with new keyword are placed in heap memory. When an object
is no longer referenced by the program (i.e. unreachable), the heap space it
occupies can be recycled so that the space is made available for subsequent
new objects. The garbage collector must somehow determine which objects are
no longer referenced by the program and make available the heap space
occupied by such unreferenced objects.
374
Weak reference: A weak reference, simply put, is a reference that isn't strong
enough to force an object to remain in memory. Weak references allow you to
leverage the garbage collector's ability to determine reachability for you, so you
don't have to do it yourself. You create a weak reference like this:
Car c1 = new Car( );
//referent is c1 is a strong reference
WeakReference<Car> wr = new WeakReference<Car>(c1);
A weak reference is a holder for a reference to an object, called the referent.
Weak references and weak collections are powerful tools for heap management,
allowing the application to use a more sophisticated notion of reachability,
rather than the "all or nothing" reachability offered by ordinary (i.e. strong)
references. For example,
A WeakHashMap stores the keys using WeakReference objects, which means that
as soon as the key is not referenced from somewhere else in your program, the
entry may be removed and is available for garbage collection.
package reference;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;
public class WeakerMap {
private static Map<String, String> map;
public static void main(String args[ ]) {
//substitute with HashMap and compare
map = new WeakHashMap<String, String>( );
map.put(new String("John"), "Smith");
Runnable runner = new Runnable( ) {
public void run( ) {
while (map.containsKey("John")) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException ignored) {
//Line A
375
Objects Essentials
}
System.out.println("Checking for John");
System.gc( ); //nicely ask
}
System.out.println("No more John");
}
};
Thread t = new Thread(runner);
t.start( );
try {
t.join( );
// blocks the main thread until thread t finishes
} catch (InterruptedException ie) {
ie.printStackTrace( );
}
}
}
Output:
Checking for John
No more John
If you replace WeakHasMap with HashMap on line A, you will have an endless
loop printing Checking for empty as the entry John never gets removed
from the HashMap.
376
The garbage collector may or may not reclaim a softly reachable object
depending on how recently the object was created or accessed, but is required
to clear all soft references before throwing an OutOfMemoryError.
Phantom reference: Unlike soft and weak reference objects, which can
optionally be created without associating them with a reference queue,
phantom reference objects cannot be instantiated without associating the
referent with a reference queue. You can use the arrival of a phantom
reference in a reference queue to trigger some action before it becomes
unreachable by invoking the clear( ) method. So phantomly reachable objects
are objects that have been finalized( ) but not reclaimed. The garbage collector
will never clear a phantom reference. All phantom references must be explicitly
cleared by the program.
ReferenceQueue<String> referent = new ReferenceQueue<String>( );
String str = "ABC";
PhantomReference<String> ref = new
PhantomReference<String>(str, referent);
Q56 If you have a circular reference of objects, but you no longer reference it from an
execution thread (e.g. main thread), will this object be a potential candidate for garbage
collection? LF
A56
Yes. If you no longer reference it from an executing thread, then this object is
unreachable and potential candidate for garbage collection. An object becomes eligible
for Garbage Collection when no live thread can access it.
Q57 What are the different ways to make an object eligible for Garbage Collection when it
377
Objects Essentials
A57
Q58 When does the GC stop? Who controls the GC? Can GC be forced? LF FAQ
A58 GC runs on a low priority daemon thread started by the JVM. This thread stops when
all non-daemon threads stop.
The JVM controls the GC.
No. The Garbage Collection can not be forced, but there are a few ways by which it
can be requested, but there is no guarantee that these requests will be taken care of by
the JVM.
System.gc( ) ;
or
Runtime.getRuntime( ).gc( );
Q59 Which part of the memory is involved in Garbage Collection? Stack or Heap? LF
A59 Heap.
Q60 How do you know if your garbage collector is under heavy use? What are the best
practices to minimize heavy use of garbage collection? LF MC
A60 If you see a jigsaw pattern in your memory profiler, this is an indication of a memory
leak.
378
Strive for immutable objects as they not only keep your design simple and
thread-safe, but also promotes object reuse through caching.
The rule of thumb should be to use object pooling only when their creation
consumes significant resources such as database connection pooling, socket
connection pooling (HTTP, RMI, CORBA, Web Service, etc), thread pooling,
bitmaps or other graphics objects, etc. If it is about creating a new String
object, then code readability, maintainability, and simplicity should have higher
precedence. Object allocation is relatively fast in JDKs 5 and higher as object
creation with the "new" operator is faster ( needing 10 instructions) than it
used to be, and GC efficiency is also much improved.
Premature optimization is the root cause for many other issues. For
example, writing an object pool is not a trivial task. It has its own pitfalls and
complexities like,
The state of the objects returned to the pool is reset back to a sensible
state for the next use of the object.
The presence of stale state causes the object to behave differently.
The security of your system can be compromised if an object contains
confidential data (e.g. credit card numbers) that isn't cleared before the
object is passed to a different client.
If the objects in the pool are not immutable or thread-safe and
accessed concurrently by multiple threads, thread-safety needs to be
properly implemented to prevent parallel threads from grabbing and
trying to reuse the same object concurrently.
So profile your application first to prove that the GC is over worked. If your
GC is over worked, and is using your CPU, then your application will be
starved for CPU, causing it to pause. This situation will require your interven
tions to reduce over working of the garbage collector in one or more of the
following means.
Allocating the right amount of memory to your JVM at program startup. There are many approaches to this, but the best thing is to run your
program with memory diagnostics, and see how much memory you're
really using, then allocate minimum and maximum heap sizes accord
ingly. In general minimum and maximum values are set to the same
value.
379
Objects Essentials
If you had decided that object reuse is the way to go then try it with
well proven and tested caching products like, EHCache, Terracotta,
Tangersol, GridGain, etc.
Reducing the scope of the variables where possible. For example, local
variables are stored in the stack. The stack based allocation is more
efficient.
Based on Brian Goet'z article entitled Java theory and practice: Urban
performance legends, revisited Escape analysis is an optimization
that has been talked about for a long time, and it is finally here -- the
current builds of Mustang (Java SE 6) can do escape analysis and
convert heap allocation to stack allocation (or no allocation) where
appropriate. The use of escape analysis to eliminate some allocations
results in even faster average allocation times, reduced memory
footprint, and fewer cache misses. Further, optimizing away some alloc
ations reduces pressure on the garbage collector and allows collection
to run less often.
Set your collection data structure with the appropriate initial capacity so
that it does not have to resize frequently by discarding the old
collection of objects and creating a new collection.
Map<Integer,String> mYMap =
new HashMap<Integer, String> (250);
380
Q61 Assume that the following utility method is very frequently accessed by parallel
threads, and profiling indicates that this method is causing GC to over work. How
would you go about improving the following implementation? LF COQ
package simpledate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ConvertDateUtil {
private static final String FORMAT = "yyyy-MM-dd";
public static void format(Date date) {
DateFormat df = new SimpleDateFormat(FORMAT);
df.format(date);
}
}
A61 This is only a hypothetical question. For example, the physical memory on the machine
could be limited. The above code in general should not cause any performance issue or
frequent garbage collection with the modern JVMs. The above code is also easy to
read and understand. If the profiler indicates that the GC is being over worked or to
understand what options are available for creation of much more complex objects like
a bitmap or graphic objects, the above method can be implemented with the following
considerations:
381
Objects Essentials
As you had seen earlier, soft references get garbage collected only in low
memory situations. So it is useful for implementing caches with soft references.
package simpledate;
import java.lang.ref.SoftReference;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ConvertDateUtil2 {
private static final String FORMAT = "yyyy-MM-dd";
private static final ThreadLocal<SoftReference<DateFormat>> repo =
new ThreadLocal<SoftReference<DateFormat>>( );
public static void format(Date date) {
DateFormat df = null;
SoftReference<DateFormat> softRef = repo.get( );
// if softRef is not garbage collected
if (softRef != null) {
df = softRef.get( );
}
// if garbage collected or not initialized
else {
df = new SimpleDateFormat(FORMAT);
softRef = new SoftReference<DateFormat>(df);
repo.set(softRef);
}
df.format(date);
}
}
382
Section-7:
The data structures and logic processing are prevalently used in programming. It
is important to have a basic understanding of the most common data structures like
arrays, lists, sets, maps, trees, and graphs, and the basics of the big-O algorithmic
complexity analysis. You should know why a particular data structure or an algorithm
needs to be used for a given usage pattern. You should know the abstract data types such
as List, Stack, or Set and the corresponding concrete implementations like ArrayList,
HashSet, HashMap, etc.
It is also imperative to understand the performance versus memory tradeoffs
between two different concrete implementations of an abstract data structure. The data
structures can be traversed iteratively(e.g. arrays, lists, sets) or recursively (e.g. tree).
Various sorting, partitioning, and searching algorithms can be applied to various data
structures. Most data structures internally use an array or a list to provide a more
specialized functionality. Thread-safety and immutability are other two considerations in
using different data structures. The following are some common questions answered in
this section.
If Java did not have a Map or Set implementation, how would you go about
implementing your own?
What are the common data structures, and where would you use them?
383
Why would you prefer a short circuit &&, || operators over logical & , |
operators? LF COQ
Firstly, NullPointerException is by far the most common runtime exception. If you use a
logical operator, you can get a NullPointerException. This can be avoided easily by using
a short circuit operator as shown below. There are other ways to check for null, but
short circuit && operator can simplify your code by not having to declare separate if
clauses.
if((obj != null) & obj.equals(newObj)) {
.
}
Short-circuiting means that an operator only evaluates as far as it has to, not as far as it
can. If the variable 'obj' is null, it won't even try to evaluate the 'obj.equals(newObj)
clause as shown below. This protects the potential NullPointerException.
if((obj != null) && obj.equals(newObj)) { // cannot get a NullPointerException
...
// because obj.equals(newObj) is
// executed only if obj != null returns true
}
PC Secondly, short-circuit && and || operators can improve performance in
certain situations. For example:
//the CPU intensive method in bold is executed only if number is > 7
if((number <= 7) || (doComputeIntensiveAnalysis (number) <= 13)) {
}
Q2
A2
An end-less loop.
public static void main(String[ ] args) {
try {
384
// won't reach
System.exit(1) statement.
public class Temp {
public static void main(String[ ] args) {
try {
System.out.println("This line is printed .....");
System.exit(1);
}
finally{
System.out.println("Finally block is reached.");
}
}
// won't reach
Q3
386
} finally {
return 12;
}
}
Q4
A4
Q5
A5
387
Q7
A7
Q8
A8
If you have large if-else or switch-case statements, what would come to your mind? LF
COQ
Replacing the procedural statements with an object oriented approach using
polymorphism. Large if-else or switch-case statements are harder to to read, maintain,
and extend.
Q. What will be printed with the following code snippet?
public static void main(String[ ] args) {
double value = 3;
switch (value) {
case 1:
System.out.println("A");
break;
case 2:
388
System.out.println("B");
break;
case 3:
System.out.println("C");
default:
System.out.println("Z");
}
}
A. The above code will result in a compile-time error. Unlike if-then and if-then-else,
the switch statement allows for any number of possible execution paths. But switch
works only with enums and primitive data type int or int convertible primitive data
types such as byte, short, and char. One of the new features added in Java 7 is the
capability to switch on a String. The switch statement when used with a String uses the
equals( ) method to compare the given expression to each value in the case statement
and is therefore case-sensitive and will throw a NullPointerException if the expression is
null.
Q. How will you fix the above switch statement?
A. Change the variable value from type double to type int. Also, the case 3 statement
must have a break statement after printing C, otherwise the default value Z will
be printed as well.
Q9
A9
389
390
Set
Map
SortedSet
TreeSet
SortedMap
TreeMap
ConcurrentMap
ConcurrentHashMap
Queue
BlockingQueue
Iterator
ListIterator
Q12 What are the common data structures, and where would you use them? LF COQ
OEQ
A12 Arrays are the most commonly used data structure. Arrays are of fixed size, indexed,
and all containing elements are of the same type (i.e. a homogenous collection). For
example, storing employee details just read from the database as EmployeeDetail[ ],
converting and storing a string as a byte array for further manipulation or processing,
etc. BP Wrap an array in a class to protect it from being inadvertently altered. This
would be true for other data structures as well.
Lists are known as arrays that can grow. These data structures are generally backed by
a fixed sized array and they resize themselves as necessary. A list can have duplicate
elements. For example, adding new line items to an order that stores its line items as a
list, removing all expired products from a list of products, etc. BP Initialize them
with an appropriate initial size to minimize the number of resizes.
391
package bigo;
class Link {
public int id;
public Sring name;
public Link next;
}
// data
// data
// reference to next link
HashMaps are amortized constant-time access data structures that map keys to
values. This data structure is backed by an array. It uses a hashing functionality to
identify a location, and some type of collision detection algorithm is used to handle
values that hash to the same location. For example, storing employee records with
employee number as the key, storing name/value pairs read from a properties file, etc.
BP Initialize them with an appropriate initial size to minimize the number of resizes.
Trees are the data structures that contain nodes with optional data elements and one
or more child elements, and possibly each child element references the parent element
to represent a hierarchical or ordered set of data elements. For example, a hierarchy
of employees in an organization, storing the XML data as a hierarchy, etc. If every
node in a tree can have utmost 2 children, the tree is called a binary tree. The binary
trees are very common because the shape of a binary tree makes it easy to search and
insert data. The edges in a tree represent quick ways to navigate from node to node.
393
Java does not provide an implementation for this but it can be easily implemented as
shown below. Just make a class Node with an ArrayList holding links to other Nodes.
package bigo;
import java.util.ArrayList;
import java.util.List;
public class Node {
private String name;
private List<Node> children = new ArrayList<Node>( );
private Node parent;
public Node getParent( ) {
return parent;
}
public void setParent(Node parent) {
this.parent = parent;
}
public Node(String name) {
this.name = name;
}
children.add(child);
}
public void removeChild(Node child) {
children.remove(child);
}
public String toString( ) {
return name;
}
}
Graphs are data structures that represent arbitrary relationships between members of
any data sets that can be represented as networks of nodes and edges. A tree structure
is essentially a more organized graph where each node can only have one parent node.
Unlike a tree, a graph's shape is dictated by a physical or abstract problem. For
example, nodes (or vertices) in a graph may represent cities, while edges may
represent airline flight routes between the cities.
To make a Java graph class, you will have to work out the way in which the inform
ation can be stored and accessed. A graph will be using some of the data structures
mentioned above. The Java API does not provide an implementation for this. But
there are a number of third party libraries like JUNG, JGraphT, and JDSL (does not
seem to support generics). The following example shows one of many ways to
represent a graph data structure.
395
A graph must represent its vertices and edges in the code. Two vertices in a graph are
adjacent if they form an edge. The vertex objects can be placed in a Map as shown
above. A map is used for a fast retrieval by vertex name. Edges can be represented
with an adjacency value list as shown above. A path is a sequence of vertices in which
each successive pair is an edge as in LA NY London Sydney LA. To be
more specific, this is a cyclic path because the first and last vertices are the same. The
following code shows a very basic graph class.
package bigo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Graph {
protected Map<String, List<String>> adjacencyMap;
public Graph(int size) {
this.adjacencyMap = new HashMap<String, List<String>>(size);
}
public boolean addVertex(String vertex) {
if (adjacencyMap.containsKey(vertex))
return false;
396
NY
London
Sydney
Paris
LA
NY
London
Sydney
Paris
1
0
0
0
0
Note: 1 means an edge between two vertices (i.e. adjacent), and 0 means not adjacent.
package bigo;
class Vertex {
public String name;
public boolean hasVisited;
public Vertex(String name)
{
this.name = name;
hasVisited = false;
}
}
package bigo;
class Graph2 {
398
400
Q. What can you tell about the performance of a HashMap compared to a TreeMap?
Which one would you prefer?
A. A balanced tree does have O (log n) performance. The TreeMap class in Java
maintains key/value objects in a sorted order by using a red-black tree. A red-black
tree is a balanced binary tree. Keeping the binary tree balanced ensures the fast
insertion, removal, and lookup time of O (log n). This is not as fast as a HashMap,
which is O(1) , but the TreeMap has the advantage of that the keys are in sorted order
which opens up a lot of other capabilities.
Which one to choose? PC
The decision as to using an unordered collection like a HashSet or HasMap versus
using a sorted data structure like a TreeSet or TreeMap depends mainly on the usage
pattern, and to some extent on the data size and the environment you run it on. The
practical reason for keeping the elements in sorted order is for frequent and faster
retrieval of sorted data if the inserts and updates are frequent. If the need for a sorted
result is infrequent like prior to producing a report or running a batch process, then
maintaining an unordered collection and sorting them only when it is really required
with Collections.sort(...) could sometimes be more efficient than maintaining the ordered
elements. This is only an opinion, and no one can offer you a correct answer. Even the
complexity theories like Big-O notation like O(n) assume possibly large values of n.
In practice, a O(n) algorithm can be much faster than a O(log n) algorithm, provided
the data set that is handled is sufficiently small. One algorithm might perform better
401
402
package bigo;
import java.util.Arrays;
public class InsertingElementsToArray {
public static void insertSortedArray(String toInsert) {
String[ ] sortedArray = { "A", "C", "D" };
/*
* Binary search returns the index of the search item
* if found, otherwise returns the minus insertion point. This example
* returns index = -2, which means the elemnt is not found and needs to
* be inserted as a second element.
*/
int index = Arrays.binarySearch(sortedArray, toInsert);
if (index < 0) {
// not found.
Insert at
Algorithm
front
back
middle
front
back
middle
ArrayList
O (1)
O (1)
O (1)
O (N)
O (1)
O (N)
LinkedList
O (1)
O (1)
O (n)
O (1)
O (1)
O (1)
Unless you have lots of inserts in the front and middle and the performance bench
marks clearly shows that LinkedList is the way to go, it is better to use an ArrayList.
Q14 Describe typical use cases or examples of common uses for various data structures
provided by the Collection framework in Java? LF OEQ
A14 You choose data structures based on your projected usage patterns, memory and
performance considerations.
Usage patterns :
404
Use arrays when the amount of data is reasonably small and the amount of
data is predictable in advance. If insertion speed is important, use an
unordered array. An array cannot be used for everything because searching an
unordered array is slow with O(n), and deletion of both ordered and
unordered array takes O(n) to fill the hole.
If the initial collection size cannot be determined upfront, the primary imple
mentations like ArrayList, HashMap, or HashSet should do the job unless you
require a special usage pattern. Those special usage patterns are like access
sequences like FIFO, LIFO, etc, duplicates are allowed or not, and ordering
needs to be maintained or not, etc.
Use a List if duplicates are allowed, and a Set if duplicates are not allowed.
Use a stack for Last In First Out (LIFO) access. For example, you may want to
track online forms as a user opens them and then be able to back out of the
open forms in the reverse order. That is, you may want to store form refer
ences in the stack and then, as the user clicks the OK button on each form,
bring the correct form to the top by popping out the most recent form from
the stack. You could also build your own application profiler by storing the
current time in the stack for each method as you push it on to the stack and
then subtracting that from the current time as you pop it out from the stack to
find out how long it took to execute that method. The Stack implementation
class in Java extends the Vector class, which has all methods synchronized.
Hence it is not recommended to use the Stack implementation. A more
complete and consistent set of LIFO stack operations is provided by the
Dequeue interface, which stands for double-ended queue and its imple
mentations.
Use a TreeSet or TreeMap if you like your objects to be in sorted order by using
a balanced red-black tree. A red-black tree is a balanced binary tree, meaning a
parent node has maximum 2 children and as an entry is inserted, the tree is
monitored as to keep it well-balanced. Balanced binary tree ensures fast lookup
405
A cache is a memory location where you can store data that is otherwise
expensive to obtain frequently from a database, ldap, flat file, or other external
systems. A WeakHashMap is not good for caching because a WeakHashMap
stores the keys using WeakReference objects that means as soon as the key is not
strongly referenced from somewhere else in your program, the entry may be
removed and be available for garbage collection. This is not good, and what
you really want to have is your cached objects removed from your map only
when the JVM is running low on memory. This is where a SoftReference comes
in handy. A SoftReference will only be garbage-collected when the JVM is
running low on memory and the object that the key is pointing to is not being
accessed from any other strong reference. The standard Java library does not
provide a Map implementation using a SoftReference, but you can implement
your own by extending the AbstractMap class.
Implementing your own cache mechanism is often not a trivial task. Cache
needs to be regularly updated, and possibly distributed. A better option would
be to use one of the third-party frameworks like OSCache, Ehcache, JCS and
Cache4J.
Threading considerations: CC
406
If accessed by a single thread, synchronization is not required, and arrays, lists, sets,
stacks, etc can be used as a local variable. If your collections are used as a local
variables, the synchronization is a complete overkill, and degrades performance
considerably. On the contrary, it is a bad practice to assume that the application is
always going to be single threaded and use data structures in a thread unsafe manner,
for example declaring it as an instance or static variable. What if the application needs
to scale to handle concurrent access from multiple threads?
If accessed by multiple threads, prefer a concurrent collection like a copy-on-write
lists and sets, concurrent maps, etc over a synchronized collection for a more
optimized concurrent access. Stay away from the legacy classes like Vectors and HashT
ables. In a multi-threaded environment, some operations may need to be atomic to
produce correct results. This may require appropriate synchronizations (i.e. locks).
Improper implementation in a multi-threaded environment can cause unexpected
behaviors and results.
Performance and memory considerations: PC MC
The choices you make for a program's data structures and algorithms affect that
program's memory usage (for data structures) and CPU time (for algorithms that
interact with those data structures). Sometimes you discover an inverse relationship
between memory usage and CPU time. For example, a one-dimensional array occupies
less memory than a doubly linked list that needs to associate links with data items to
find each data item's predecessor and successor. This requires extra memory. In
contrast, a one-dimensional array's insertion/deletion algorithms are slower than a
doubly linked list's equivalent algorithms because inserting a data item into or deleting
a data item from a one-dimensional array requires data item movement to expose an
empty element for insertion or close an element made empty by deletion. Here are
some points to keep in mind.
// not allowed.
408
410
412
Immutable objects should be used as keys for the HashMaps: Generally you
use java.lang.Integer or 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. If you want to insert a new key, then you will
always have to instantiate a new object as you cannot modify an immutable
object. If the keys were made mutable, you could accidentally modify the key
after adding to a HashMap, which can result in you not being able to access the
object later on. The object will still be in the HashMap, but you will not be able
to retrieve it as you have the wrong key (i.e. a mutated key).
Firstly, decide on the backing data structure. Arrays are fast and memory
efficient. Hence an array can be used to back up the map. This will be an
indexed bucket.
Decide on a hashing algorithm to index the array and store the entries in a
particular slot of the array.
Decide on a strategy to store two or more keys that result in the same hash
code value. More objects can be linked with each other occupying the same
index. This means each bucket can contain 0..N entries. This linking strategy is
shown in the diagram.
Decide on an approach to evaluate the hash code, which determines the array
bucket index to use.
int index = Math.abs(key.hashCode( ) % buckets.length);
414
Come up with a strategy for resizing the backing array when the capacity is
reached. This process is known as rehashing whereby existing items are
mapped to new bucket locations.
Consider implementing the Map interface and fill in the empty methods that
need to be implemented.
The following code snippets demonstrate how you could go about implementing your
own HashMap class.
package bigo;
public class MapEntry <K, V> {
415
else {
//if the key is not found
int index = evalBucketIndex(key);
if (buckets[index] != null) {
//if the bucket is already allocated
entry = linkEntry(buckets[index] , key, value); //link it to an entry already
//attached to that bucket
} else {
//if the bucket is not allocated
entry = new MapEntry<K, V>(key, value); //allocate a new bucket
buckets[index] = entry;
}
}
return entry.getValue( );
}
/**
* Hashing function. The more spaced out the indices are, the better the
* performance is. Only positive values can be used as an array index.
*/
private int evalBucketIndex(K key) {
int index = Math.abs(key.hashCode( ) % buckets.length);
return index;
}
/**
* Return the matching entry if found, otherwise return null
*/
private MapEntry<K, V> getMatchingEntry(K key) {
MapEntry<K, V> entry = buckets[evalBucketIndex((K) key)];
// same index can have more entries linked by "next" attribute
// in the MapEntry class. So find the one where key matches
while (entry != null && !key.equals(entry.getKey( ))) {
entry = entry.getNext( );
}
// conditional operator
return entry != null ? entry : null;
}
417
418
Here are some code snippets to get started. Define the interface first.
package bigo;
public interface Stack<E> {
E push(E item);
E pop( );
boolean empty( );
//other methods like peek( ), etc omitted for brevity
}
Now provide the implementation class.
package bigo;
public class MyStack<E> implements Stack<E> {
419
dequeue( ):
Note: Use the LIFO operations provided by the Deque interface and its implementa
tions as opposed to the Stack implantation, which extends the legacy Vector class.
421
Sorting Elements
Q21 If I mention the interface names Comparable or Comparator, what does come to your
mind? Why do we need these interfaces? LF
A21 Sorting. SortedSet and SortedMap interfaces maintain sorted order. The elements are
sorted as you add or remove them. The other interfaces like List or Set don't sort
elements as you add or remove. So you need to sort them on a as needed basis.
If you store objects of type String or Integer in a List or Set, and would like to occasionally sort
them, say for reporting purpose, you can do so as shown below as String or Integer by default
implements the Comparable interface and provides a compareTo(..) method to be called while
sorting.
package sorting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Sorting1 {
public static void main(String[ ] args) {
List<String> myShoppingList = new ArrayList<String>( );
myShoppingList.add("Cereal");
myShoppingList.add("Apples");
myShoppingList.add("Soap");
myShoppingList.add("Brush");
System.out.println("Before sorting: " + myShoppingList);
// invokes compareTo method implemented in the String class.
Collections.sort(myShoppingList);
System.out.println("After sorting: " + myShoppingList);
}
}
Output:
Before sorting: [Cereal, Apples, Soap, Brush]
After sorting: [Apples, Brush, Cereal, Soap]
422
As you can see that the items are sorted lexicographically. This is the default imple
mentation provided by the compareTo(...) method in the java.lang.String class. What if you
have a special reporting requirement to sort by length of the item name. This is where
the Comparator interface comes in handy by giving you more control over ordering.
You can define your own ordering logic through the compare(...) method as shown
below using the Comparator interface.
package sorting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Sorting2 {
public static void main(String[ ] args) {
List<String> myShoppingList = new ArrayList<String>( );
myShoppingList.add("Cereal");
myShoppingList.add("Apples");
myShoppingList.add("Soap");
myShoppingList.add("Brush");
myShoppingList.add(null);
System.out.println("Before sorting: " + myShoppingList);
//Anonymous inner class.
Collections.sort(myShoppingList, new Comparator<String>( ) {
@Override
public int compare(String o1, String o2) {
if(o1 == null) {
o1 = "";
}
if(o2 == null) {
o2 = "";
}
return new Integer(o1.length( )).compareTo(o2.length( ));
}
});
423
}
Output:
Before sorting: [[1,Dog], [2,Rabit], [3,Cat], [2,Hamster]]
After sorting: [[1,Dog], [2,Hamster], [2,Rabit], [3,Cat]]
Note: The above class is using an anonymous class to sort, but if you require to reuse
the sorting in a number of places, you must move the compare(...) method to its own
class.
Q. What contract do you need to watch out for when writing your own comparator?
A. As per the Java API for java.util.Comparator, caution should be exercised when
using a comparator capable of imposing an ordering inconsistent with the
equals(...) method.
if compare(o1,o2) == 0 then o1.equals(o2) should be true.
if compare(o1,o2) != 0 then o1.equals(o2) should be false.
Algorithms
Q22 Can you write an algorithm to.....? COQ
A22 Swap two variables?
package algorithms;
public class Swap {
public static void main(String[ ] args) {
int x = 5;
int y = 6;
//store 'x' in a temp variable
int temp = x;
x = y;
y = temp;
427
has O(n * log n) but can go up to O(n 2) in a worst case scenario, and this happens
especially with already sorted sequences.
package algorithms;
import java.util.Arrays;
public class QuickSort {
public static void main(String[ ] args) {
Integer[ ] values = { 30, 12, 18, 0, -5, 72, 424 };
System.out.println("Before:" + Arrays.deepToString(values));
Arrays.sort(values);
System.out.println("After:" + Arrays.deepToString(values));
}
}
Q. How will you partition the following numbers 7 3 6 8 2 9 5 4 around the pivotal
number of 5 so that the lower values are in front of it and higher values are behind it?
COQ
7
5 (pivot)
^
^
Start two pointers, one at array index 0 and the other at array index 7, which is the last
element in the array. Advance the left pointer forward until a value greater than the
pivot (i.e. 5) is encountered. Advance the right pointer backwards until a value less
than the pivot (i.e 5) is encountered. In this case, the current left pointer value 7 is
greater than pivot (i.e. 5) and the right pointer value 4 is less than the pivot (i.e. 5), so
swap them as shown below before advancing as shown below with the shading. The
pointers have also advanced to the next position. Also note that the left pointer has
skipped the value 3 as it is less than pivot value5. The right pointer has skipped
the values 5 and 9 as they are equal and greater than the pivot value of 5
respectively.
4
5 (pivot)
^
^
The left pointer value 6 is greater than pivot (i.e. 5) and the right pointer value 2 is less
than the pivot (i.e. 5), so swap them as shown below before advancing.
429
5 (pivot)
^ ^
When the pointers are pointing to the same value as shown above, swap it with the
pivot value as shown below.
4
5 (pivot)
^^
As you can see now, the smaller values (or numbers) are in front of the pivot value 5
and higher values are behind it. This array is now called partitioned. The partitioning
is used recursively by the quick sorting algorithm to sort elements. The above parti
tioning algorithm can be applied as shown below.
package algorithms;
public class Partition {
public static int partition(int[ ] values, int leftIndex,
int rightIndex, int pivotIndex) {
int i = leftIndex;
int j = rightIndex;
while (i < j) {
//moving from left to right
while (i <= j && values[i] <= values[pivotIndex])
i++;
//moving from right to left
while (j >= i && values[j] >= values[pivotIndex])
j--;
if (i < j)
swap(values, i, j);
}
//swap the current index value with the pivot index value
swap(values, i, pivotIndex);
return i;
// return the pivot index
}
430
5 (pivot)
5 (pivot)
^
Move from left to right, and swap with the pointer if the current value is less than the
pivot value 5. 3 is less than the pivot value of 5, so swap it with the pointer value.
After swapping, move the pointer forward one index.
3
5 (pivot)
^
Skipping values 6 and 8 as they are greater than the pivot value of 5. 2 is less
than the pivot value, so swap it with the current pointer value. After swapping, move
the pointer forward one index.
431
5 (pivot)
^
4 is less than the pivot value, so swap it with the current pointer value and move the
pointer forward one index.
3
5 (pivot)
^
No more smaller values than the pivot value of 5 can be found, so swap the pivot
value with the current pointer value.
3
5 (pivot)
^
The code for the above explanation can be written as shown below:
package algorithms;
public class Partition2 {
public static int partition(int[ ] values, int leftIndex,
int rightIndex, int pivotIndex) {
int pivotValue = values[pivotIndex];
// send pivot item to the back
swap(values, pivotIndex, rightIndex);
// keep track of where the front is
int index = leftIndex;
// check from the front to the back
for (int i = leftIndex; i < rightIndex; i++)
{
// swap if the current value is less than the pivot
if (values[i] < pivotValue) {
swap(values, i, index);
index++;
}
}
432
}
public static void swap(int[ ] values, int i, int j) {
int temp = values[i];
values[i] = values[j];
values[j] = temp;
}
public static void main(String[ ] args) {
int[ ] values ={ 7, 3, 6, 8, 2, 9, 5, 4};
partition(values, 0, values.length - 1, values.length - 2);
//prints 3,2,4,5,7,9,6,8
for (int i = 0; i < values.length; i++) {
System.out.print(values[i]);
if(i < values.length-1)
System.out.print(",");
}
}
}
Q. How would yo go about applying the QuickSort algorithm to sort the values 7 3 6 8
2 9 5 4 without using the Arrays.sort( ...).
A. The key steps involved are:
Since this is a recursive algorithm, you need a terminating or exit condition that does
not make a recursive call. The terminating condition is when the rightIndex is less than
or equal to the leftIndex.
package algorithms;
433
434
Q. Can you write a function that checks if a given array is already sorted either in
ascending or descending order? COQ
A.
package algorithms;
public class AlgorithmUtils {
public static boolean sorted(int[ ] input) {
if (input == null || input.length == 0) {
throw new IllegalArgumentException("Invalid Input.");
}
boolean ascending = false;
int first = input[0];
int second = input[1];
// ascending
if (first < second)
ascending = true;
for (int i = 0; i < input.length-1; i++) {
if ((ascending && input[i] > input[i + 1])
|| (!ascending && input[i] < input[i + 1]))
return false;
}
return true;
}
//...
}
Q. How will you ensure that the above code is functioning it correctly? COQ BP
A. By writing unit tests that cover positive scenarios like an array sorted in ascending
order and an array sorted in descending order and also a negative scenario like an
unsorted array. Also, test for exceptional conditions like a null or empty array.
Q. Can you write a code to search for number 5 in 7 3 6 8 2 9 5 4? COQ
435
Firstly sort it, using the quick sort algorithm discussed earlier.
436
left idx
mid idx
right idx
Divide the array into two halves around the center (left idx + right idx)/2 = (0 + 7)/2
= 3. In this case, the idx 3 (which has the value of 5) is the middle index. Compare the
search value of 5 with the mid idx. If the search value (i.e. 5) is greater than mid idx
value, then the search value may or may not be on the RHS of the mid idx. The LHS
can be safely ignored. If the search value is less than the mid idx, then the search value
may or may not be on the LHS of the idx. The RHS can be safely ignored. If the
search value is neither less than or greater, then the search value is the mid idx. In this
example, the value of 5 is in the mid idx, and hence will be found immediately. To
illustrate this, search for a value of 8 as shown below.
0
left idx
mid idx
right idx
The search value (i.e. 8 ) is greater than the mid idx value of 5, so set the left idx to
mid idx + 1 = 4, and then recompute the new mid idx to (left idx + right idx)/2 = (4
+ 7)/2 = 5
0
left idx
mid idx
right idx
The search value of 8 is still greater than the mid idx, hence the left idx becomes mid
idx + 1 = 5 + 1 = 6, and the new mid idx becomes (left idx + right idx) / 2 = (6 +
7) / 2 = 6.
0
left idx
mid idx
right idx
The search value of 8 is equal to the value of the mid idx, hence the value is found.
Also, note that the if the value were not be found, the terminating condition is the left
437
438
@SuppressWarnings("unchecked")
public static boolean found(Comparable[ ] input, Comparable compare) {
if (input == null || input.length == 0) {
throw new IllegalArgumentException("Invalid Input");
}
int leftIdx = 0;
int rightIdx = input.length - 1;
int midIdx;
while (leftIdx <= rightIdx) {
midIdx = (leftIdx + rightIdx) / 2;
if (compare.compareTo(input[midIdx]) > 0)
leftIdx = midIdx + 1;
else if (compare.compareTo(input[midIdx]) < 0)
rightIdx = midIdx - 1;
else
return true;
}
return false;
}
// ....
}
The above code can be used with an Integer, BigDecimal, String, and any custom class
that implements the interface Comparable.
Note: There are other algorithms for sorting like merge sort, heap sort, insert sort,
etc. Also do your research on the interpolation search, which is a slight variation from
the binary search algorithm. This algorithm is a bit more complex and efficient than
the binary search algorithm.
Q23 What are the different binary tree traversal mechanisms? COQ
A23 Traversing a tree means visiting all the nodes of a tree in order. Many different binary
tree algorithms involve traversals. For example, if you wish to count the number of
employees in an organizational chart you must visit each node. If you wish to find the
439
A
1
Depth = 1
Depth = 2
Depth = 3
Prints: ABDCEGHFI
In preorder traversal, each node is visited before any of its children. The code snippet
for recursion is shown below.
440
System.out.println(node.getName( ));
traverse(node.getLeft( ));
traverse(node.getRight( ));
}
The above algorithm can be achieved iteratively without recursion as shown below by
using a Dequeue (i.e. LIFO using a double-ended queue).
public static void preorderIterative(Node node) {
Deque<Node> s = new ArrayDeque<Node>(10);
s.push(node); // push the root node
while (!s.isEmpty( )) {
node = s.pop( );
System.out.print(node.getValue( ));
if (node.getRight( ) != null) { // push the right node first,
s.push(node.getRight( ));
// as a stack is LIFO
}
if (node.getLeft( ) != null) { // push the left node last,
s.push(node.getLeft( ));
// as a stack is LIFO
}
}
}
The diagram below shows how a double ended queue is used for traversal. Each pass
within the while loop will have a value popped up (e.g. A), and its children pushed in
(e.g. B and C).
441
G
B
A
Start
Pass 1
Pass 2
Pass 3
Pass 4
Pass 5
Pass 6
Pass 7
I
Pass 8
Pass 9
In-order traversal
In a binary tree in-order traversal, left child tree is visited first, then visit the parent
node, and then visit the right child tree.
Depth = 0
Depth = 1
3
7
Prints: BDAGEHCFI
The recursive approach is shown below.
//inorder traversal for a binary tree
public void traverse(Node node) {
//Exit condition for recursion
if (node == null) {
442
Depth = 2
Depth = 3
return;
}
traverse(node.getLeft( ));
System.out.println(node.getName( ));
traverse(node.getRight( ));
}
The iterative approach is shown below using a LIFO approach:
public static void inorderIterative(Node node) {
Deque<Node> s = new ArrayDeque<Node>(10);
while (!s.isEmpty() || null != node) {
if (null != node) {
s.push(node);
node = node.left;
} else {
node = s.pop();
System.out.print(node.getValue());
node = node.right;
}
}
}
The diagram below shows how a double ended queue is used for traversal. All depends
on how the elements are popped in and popped out.
The values that are popped out of the stack and printed .
B
D A
G E
H C
G
A
B
D
A A A A
2 3
4 5
E E E
H
C C C C C C
13 14 15 16
9 10 11 12
I
17 18 19
443
A
8
C
7
F
5
Depth = 1
Depth = 2
Depth = 3
Prints: DBGHEIFCA
//postorder traversal for a binary tree
public void traverse(Node node) {
//Exit condition for recursion
if (node == null) {
return;
//otherwise this will be an endless loop
}
traverse(node.getLeft( ));
//recurse left node
traverse(node.getRight( ));
//recurse right node
System.out.println(node.getName( ));
}
Q. What is an expression tree?
A. An expression tree is a binary tree that represents a mathematical expression. It is
shown below to demonstrate tree traversals discussed above.
444
+
3
*
4
+
5
Preorder traversal: + 3 * 4 + 52
Inorder traversal: 3 + 4 * 5 + 2
Postorder traversal: 3452+*+
Note: Check the sample classes treetraversal.Node and treetraversal.NodeTest to
try the non-recursive approach for inorder and postorder traversals.
Q. Can you give some real life examples of these tree traversals?
A.
Preorder traversal can be used to create or clone an existing tree. The parent needs to
exist before the children can be added. Evaluation of expressions in prefix notation
and processing of the abstract syntax trees by compilers (e.g. JavaCC, ANTLR, etc) are
based on preorder traversal. The DefaultMutableTreeNode is a general purpose tree data
structure in the package javax.swing.tree. The getNextNode( ) method in this class returns
the node that follows a preorder traversal. A JavaScript method like getElementsBy
TagName(...) could use a preorder traversal to return the elements from a HTML
DOM (Document Object Model) tree. It can also be used in your web pages for the
site map, menus, and the bread crumb navigation due to its simplicity of retrieving
parents and children.
Postorder traversal is used for evaluating of expressions in post-fix, and by the
Reverse Polish Notation (RPN), which is used by the compilers. For example, a
machine language will require a notation like 2 3 +. A postorder is required for
destroying a tree. All the children needs to be destroyed before the parent can be
destroyed.
445
The WHERE clause SQL expressions can be parsed using inorder tree traversal. For
example the SQL expression
SELECT * from employees WHERE emp.name = 'john' and emp.age > 10
can be parsed for WHERE clause using an in-order traversal as shown below:
446
// FIFO
Node tmp;
while (q.size( ) > 0) {
tmp = q.remove( );
if (tmp.getName( ).equals(search)) {
return true;
}
if (tmp.getLeft( ) != null) {
q.add(tmp.getLeft( ));
}
if (tmp.getRight( ) != null) {
q.add(tmp.getRight( ));
}
}
return false;
}
Depth First Searching (DFS)
A DFS searches through a tree starting at the root, and goes straight down a single
branch until a leaf is reached. Then, it repeats the same process with its nearest
448
ancestor that has not been searched through yet. As per the diagram, it will be
searched in the order of a, b, d, h, e, c, f, and g.
The code snippet below demonstrates the DFS for a binary tree.
public boolean dfsSearch(Node node, String search) {
//exit condition for recursion
if (node == null) {
return false;
}
//exit condition for recursion
if (node.getName( ).equals(search)) {
return true;
}
//recursive calls
return dfsSearch(node.getLeft( ), search) || dfsSearch(node.getRight( ), search) ;
}
Logic Processing
The interviewers are not only looking for your logic processing and coding capability, but
also your reasoning ability and engineering skills. Think aloud where possible with the logical
steps and key considerations like readability, maintainability, performance, memory utiliz
ation, possible test scenarios both positive and negative unit tests, alternative approaches,
pros, and cons for each alternative, etc. Don't compromise on readability, extendability, and
maintainability for a small performance gain.
Your approach is as important if not more important than getting it right. The interviewers
will also be looking to see if you are willing to change your code without the I know it all
attitude and your willingness to learn. Time limitation and nervousness can play a part in
arriving at the most efficient solution, but your approach and engineering skills can give
interviewers the confidence that you are on the right track and can get the job done if they
know how you are thinking.
449
450
Q26 Write a program that will return whichever value is nearest to the value of 100 from
two given int numbers. COQ
A26 You can firstly write the pseudo code as follows:
package chapter2.com;
public class CloseTo100 {
public static int calculate(int input1, int input2) {
//compute the difference. Negative values are allowed as well
int iput1Diff = Math.abs(100 - input1);
int iput2Diff = Math.abs(100 - input2);
//compare the difference
if (iput1Diff < iput2Diff) return input1;
else if (iput2Diff < iput1Diff) return input2;
else return input1;
//if tie, just return one
}
public static void main(String[ ] args) {
//+ve numbers
System.out.println("+ve numbers=" + calculate(50,90));
//-ve numbers
System.out.println("-ve numbers=" + calculate(-50,-90));
//equal numbers
System.out.println("equal numbers=" + calculate(50,50));
//greater than 100
System.out.println(">100 numbers=" + calculate(85,105));
System.out.println("<100 numbers=" + calculate(95,110));
}
}
451
Recursive approach:
package chapter2.com;
public class Factorial1 {
public static int evaluate(int input) {
if (input < 0) {
throw new RuntimeException("Undefined for -ve: " + input);
}
//0! == 1! == 1
if (input <= 1) {
return 1;
}
return input * evaluate(input - 1);
452
}
public static void main(String[ ] args) {
// zero
System.out.println("0! = " + evaluate(0));
// non-zero +ve number
System.out.println("5! = " + evaluate(5));
// Large +ve number
System.out.println("20! = " + evaluate(20));
}
}
Output:
0! = 1
5! = 120
20! = -2102132736
//right
}
Output:
0! = 1
5! = 120
20! = 2432902008176640000
Iterative approach:
MC Writing a recursive factorial method may not be the recommended approach as
recursive solution's memory usage is O(N) instead of O(1). Recursive functions are
more suited in situations where the iterative approach will either be more complex or
harder to understand. For example, nested loops that are more than 2 levels deep.
Recursive functions are more suited for composite objects having a tree or hierarchical
structure. Since recursion takes stack memory space, it should be used carefully. If
your tree is very large, the traversal function should be implemented iteratively. The
iterative approach is illustrated below.
package chapter2.com;
import java.math.BigInteger;
public class Factorial3 {
public static BigInteger evaluate(int input) {
if(input < 0) {
454
Pick one number from the array of inputNumbers and compare against other
numbers in the array of inputNumbers to see if 2 numbers add up to the given
sum.
If the picked number adds up to the given sum with at least one other number
from the array, return true. Otherwise repeat this process by picking the next
number from the array of inputNumbers to see if it adds up to the given sum.
If none of the 2 numbers in the array add up to the given sum, return false.
package chapter2.com;
public class FindPairWithSum1 {
public static boolean hasSum(int[ ] inputNumbers, int sum) {
//nested for loops.
//int n = inputNumbers.length. max loops --> O(n2)
for (int i = 0; i < inputNumbers.length; i++) {
for (int j = i + 1; j < inputNumbers.length; j++) {
if (inputNumbers[i] + inputNumbers[j] == sum) {
return true;
}
}
}
return false;
}
public static void main(String[ ] args) {
System.out.println(hasSum(new int[ ]{3,5,8,9},8));
System.out.println(hasSum(new int[ ]{3,5,8,9},7));
System.out.println(hasSum(new int[ ]{3,5,8,9,8,9,7,8,9},3));
System.out.println(hasSum(new int[ ]{9,8,5,3},8));
System.out.println(hasSum(new int[ ]{9,8,-5,3},4));
System.out.println(hasSum(new int[ ]{9,8,-5,-3},-8));
456
package chapter2.com;
import java.util.HashSet;
import java.util.Set;
public class FindPairWithSum2 {
public static boolean hasSum(int[ ] inputNumbers, int sum) {
//using a set.
//int n = inputNumbers.length. max loops --> n
Set<Integer> setNumbers = new HashSet<Integer>( );
for (int i = 0; i < inputNumbers.length; i++) {
int requiredNumber = sum - inputNumbers[i];
if(setNumbers.contains(inputNumbers[i])) {
return true;
}
else{
setNumbers.add(requiredNumber);
}
}
return false;
}
public static void main(String[ ] args) {
System.out.println(hasSum(new int[ ]{3,5,8,9},8));
458
459
package chapter2.com;
import java.util.Arrays;
public class FindPairWithSum3 {
public static boolean hasSum(int[ ] inputNumbers, int sum) {
//using two pointers.
//int n = inputNumbers.length. max loops --> n-1
int start = 0;
//left pointer
int end = inputNumbers.length -1;
//right pointer
Arrays.sort(inputNumbers);
//sort the array
while (start != end) {
if(sum == inputNumbers[start] + inputNumbers[end]) {
return true;
}
else if(sum > inputNumbers[start] + inputNumbers[end] ) {
++start;
// move forward the lhs pointer
}
else {
--end;
// move backward the rhs pointer
}
}
return false;
460
}
public static void main(String[ ] args) {
System.out.println(hasSum(new int[ ]{3,5,8,9},8));
System.out.println(hasSum(new int[ ]{3,5,8,9},7));
System.out.println(hasSum(new int[ ]{3,5,8,9,8,9,7,8,9},3));
System.out.println(hasSum(new int[ ]{9,8,5,3},8));
System.out.println(hasSum(new int[ ]{9,8,-5,3},4));
System.out.println(hasSum(new int[ ]{9,8,-5,-3},-8));
System.out.println(hasSum(new int[ ]{9,8,-5,-3},5));
}
}
Output:
true
false
false
true
true
true
true
Pros: Easy to read and understand. More efficient than nested loops for larger arrays.
Its maximum loop count is n-1. Say for an array with 500 numbers, max loop count is
499. Can be used for negative numbers.
Cons: Array of numbers must be sorted prior to use. Using a quick sort algorithm will
incur an additional O(N * log N) performance.
Which approach to choose? Using another collection or two pointers. Incorporate
performance and memory usage benchmarks.
Q29 Can you write a code to evaluate( ) a simple mathematical calculation as shown below
using a binary tree? COQ
3 + 4 * ( 5 + 2 ) = 31
A29 A binary tree is made of nodes, where each node contains a "left" pointer, a "right"
461
= 31
+
3
= 28
+
5
=7
package binarytree1;
public class Node {
enum OPERATOR { ADD, SUBTRACT, MULTIPLY, DIVIDE };
//store the values
private Integer lhsValue, rhsValue;
// operator node that links value nodes
private OPERATOR operator = null;
private Node lhsNode, rhsNode;
public Node( ) {}
public void link(Node lhsNode, Node rhsNode) {
this.lhsNode = lhsNode;
this.rhsNode = rhsNode;
}
// ... relevant getters & setters
462
operation was added, all you have to do is to add a new operation node and provide an
appropriate implementation via the the evaluate( ) method. This shows that the above
code is open for extension and closed for modification. It also harnesses the power of
OO concept known as polymorphism.
package binarytree2;
public class TreeCalculator {
public static void main(String[ ] args) {
// 3 + 4 * (5+2)
Node five = new OperandNode(5);
Node two = new OperandNode(2);
Node result1 = new AddOperationNode(five, two);
System.out.println("node result1=" + result1.evaluate( )); // 7
Node four = new OperandNode(4);
Node result2 = new MultiplyOperationNode(four, result1);
System.out.println("node result2=" + result2.evaluate( )); // 28
Node three = new OperandNode(3);
Node result3 = new AddOperationNode(three,result2);
System.out.println("node result3=" + result3.evaluate( )); // 31
}
}
Q30 Can you suggest the most appropriate data structure for each of the following
scenarios given below?
A30 Q. A Java program to read a text file and print each of the unique words in alpha
betical order together with the number of times the word occurs in the text?
A. A TreeMap automatically sorts new keys on insertion, no sorting after wards is
needed. So it can store the words in alphabetical order. Alternatively, you can use a
TreeBag, which is an implementation of a Bag interface provided by the Apache
Commons' collection framework. A bag can hold a number of copies of each object.
For example,
467
arrangements of edges,
Directed, weighted edges
Directed, unweighted edges
Undirected, weighted edges
Undirected, unweighted edges
Note: There are many other real life examples for a graph data structure like a city
regional telephone network, a flight network, a courier service network, a link
structure of a web page where the nodes are the web pages available at the website
and links from one page to another as directed edges, etc.
Q. How would you represent an image in the form of a bitmap?
A. You need to use a one-dimensional array of pixel data. Each entry in the array
represents a single pixel in the image.
int[ ] pixels = new int [width*height];
The two-dimensional pixel data are flattened into a one-dimensional array by placing
the pixel at position (x, y) into the array at position (y * width+x).
Q. What data structure would you use to store any arbitrary two dimensional data
from a database table?
A. Create a Java bean class named ColumnData that has a Map to represent column
names as Map keys, and column values as Map values. The row data will be represented
as a list of ColumnData.
List<ColumnData> list = new ArrayList<ColumnData>(100);
Q. Customers waiting for a teller in a bank?
A. In real life a line of customers waiting for service of some kind is represented by a
Queue. The rule that determines who goes next is called a queuing discipline. The
simplest queuing discipline is called FIFO, for "first-in-first-out". The other most
common queuing discipline is priority queueing, in which each customer is assigned
a priority, and the customer with the highest priority goes first, regardless of the order
of arrival. The priority can be based on anything: what time a flight leaves, how many
groceries the customer has, how important the customer is, or if you are traveling
with young children, etc.
469
470
Exception handling
Exception handling is used to prevent application from being stuck due to unusual occur
rences. If the exceptions are handled properly, the application will never get terminated
abruptly. Bad exception handling can make your application harder to read, maintain and
debug.
Q31 Why does Java have exceptions? What is the difference between checked and
unchecked exceptions? LF FAQ
A31 Java does have exceptions to handle exceptional situations.
java.lang.Object
|
+--java.lang.Throwable
|
+--java.lang.Exception
| |
| +--java.lang.ClassNotFoundException
| |
| +--java.io.IOException
| | |
| | +--java.io.FileNotFoundException
| |
| +--java.lang.RuntimeException
|
|
|
+--java.lang.NullPointerException
|
|
|
+--java.lang.IndexOutOfBoundsException
|
|
|
+--java.lang.ArrayIndexOutOfBoundsException
|
+--java.lang.Error
|
+--java.lang.VirtualMachineError
|
+--java.lang.OutOfMemoryError
471
A checked exception can throw compile-time errors to force the calling code
to either catch the exception or declare it in a throws clause. For example,
ClassNotFoundException, FileNotFoundException, IOException, SQLException,
PersistenceException, etc are checked exceptions. These exceptions occur mainly
due to conditions that are not in programmer's control. These include condi
tions such as connection errors to database, LDAP servers, external services,
and other network failures.
In case of an unchecked exception, the compiler doesn't force client programs
(aka calling programs) to either catch the exception or declare it in a throws
clause. In fact, client programs may not even have to know that the exception
could be thrown. For example, StringIndexOutOfBoundsException, ClassCastEx
ception, ArrayIndexOutOfBoundsException, NullPointerException, etc are unchecked
exceptions. If you wish, you can handle them. Unchecked exceptions occur
mainly due to programming errors like invoking a method on a null object
(NullPointerException), an invalid argument is passed to a method (IllegalArgu
mentException), trying to access an array index beyond the allocated memory
(ArrayIndexOutOfBoundsException), etc.
472
IOException and FileNotFoundException as well. So if you catch the type Exception before
the type IOException, then the type Exception block will catch all its sub class types as
well, and IOException block will never be reached. In order to catch the type
IOException and handle it differently to type Exception, IOException should be caught
before catching the type Exception. The FileNotFoundException should be caught before
IOException, if you want to handle it differently to type IOException. For example,
package exceptions;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
public class ExceptionsArePolymorphic {
public static void writeFile(String filePath, String text)
throws IOException {
File file = new File(filePath);
FileWriter fw = null;
try {
if (text == null || text.length( ) == 0) {
throw new IllegalArgumentException(
"Nothing to write: text= " + text);
}
fw = new FileWriter(file);
// can throw FileNotFoundException.
fw.write(text);
// can throw IOException.
} catch (FileNotFoundException fnfe) {
throw fnfe;
} catch (IOException ioe) {
throw new RuntimeException("Error writing: " + ioe);
} catch (Exception ex) {
throw new RuntimeException("Unexpected error: " + ex);
} finally {
if (fw != null) {
fw.close( );
//always cleanup in a finally block.
}
475
476
When writing exception handling, always ask yourself What action can the client
code take when a particular exception occurs?
Why favor unchecked exceptions? Unchecked exceptions make your code more
readable by not cluttering your code with try-catch blocks and aggregated throws
clauses. The less you handle exceptions, the less chance of writing bad error handling
code like sloppy catch blocks that suppress the exception, using exceptions for flow
control, not properly abstracting throws clauses, etc. Some of the important points to
keep in mind if you take this approach are:
Write negative unit tests that can also act as documentation. It will serve as a
documentation that can also be executed as shown below:
477
478
When forced to catch or throw exceptions, developers can write sloppy catch
blocks as shown below:
//Bad practice to suppress exceptions. It hides bugs.
try {
methodCallThatCanTrowException( );
}
//catch and suppress the exception. Very bad.
catch(Exception ex){
}
The above code not only sweeps checked exceptions under the carpet, but also
the unchecked exceptions. It's okay to ignore exceptions that are thrown from
a finally block. This is because the code in the finally block is intended to be
"cleanup" code, and you could decide to treat exceptions occurring there as
secondary, and to put an explicit catch and just log the exception. This is one
of the few cases where it's fairly legitimate to just ignore the exception. A
finally block should also not have a return statement as it can discard excep
tions.
479
Don't make your interfaces vague by throwing type Exception. It will be unclear
as to which exceptions can be thrown from the methods. Use classes derived
from the type Exception. For example IOException.
480
Avoid catch blocks that just re-throw a caught exception. This just increases
your code size without adding any real value.
public void method1( ) {
try {
//Do something
}
catch(SomeException ex){
throw ex;
}
}
If you prefer a balanced approach by mixing both approaches based on its merits,
strive for consistency as mixing exception types often results in confusions and incon
sistent use. For example, on a per package basis as suggested by Rod Johnson.
Q34 What is the difference between exception and error? LF
A34 When a dynamic linking failure or some other hard failure in the JVM occurs, the
JVM throws an Error. Errors are unchecked. Typical Java programs should not catch
Errors. In addition, its unlikely that typical Java programs will ever throw Errors either.
A typical example for this would be an OutOfMemoryError thrown when the JVM
cannot allocate an object because it is out of memory, and no more memory could be
made available by the garbage collector.
Q35 What is a user defined exception? When would you define your own exception? LF
FAQ
A35 User defined exceptions are custom exceptions defined by either extending the
Exception or RuntimeException classes. It can also extend any other subclass of
Exception.
481
Throw an exception early because the exception stack trace helps you
pinpoint where an exception occurred by showing you the exact sequence of
method calls that leads 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 this line throws an exception because filename == null.
InputStream in = new FileInputStream(fileName);
Use the following code because you will get a more accurate stack trace:
if(filename == null) {
throw new IllegalArgumentException(file name is null);
}
InputStream in = new FileInputStream(fileName);
484
Catch an exception late. You should not try to catch an 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.
Don't lose your stack trace. Throwing a new exception from a catch block
without passing the original exception into the new exception can cause some
part of the stack trace to be lost, and can make debugging less effective.
//Good
Note: The Java 7 may have an improved catch clause by allowing you to catch
multiple exceptions and handle them the same way.
try {
return klass.newInstance( );
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
485
486
Section-8:
The reason regular expressions are so useful is that they can be used to
target what you need in any string with the flexibility of being able to use your
matches to process the given string. Regular Expressions are integrated into
many programmer tools and programming languages.
Regular expressions are used to search, replace, filter, and validate data.
Each time you use Regular Expressions, you are invoking a powerful search
engine. This search engine is one that searches through text looking for specific
patterns.
Many find regular expressions to be a bit tricky, but if you can learn
how to handle regular expressions you will have one of the most simplest,
cleanest and efficient tools in your arsenal to take on a variety of problems.
487
Regex are everywhere. Even many experienced programmers are intimidated by it,
and knowing how to use regexp effectively will help you stand out from the crowd.
Regex help you write short code.
Regex saves time.
Regex are fast if written with performance in mind.
Note: Please refer to the java.util.regex.Pattern class API for the summary of regular
expression constructs.
Q01 How will you go about implementing the following validation rules for a user name?
A01 The above rules can be implemented with a regular expression as shown below:
package regex;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class UserNameRegex {
public static final String PATTERN =
"^[a-zA-Z][a-zA-Z0-9._-]{0,15}[a-zA-Z0-9]$";
public static final Pattern p = Pattern.compile(PATTERN);
488
PC Not compiling the regular expression can be costly if Pattern.matches( ) is used over
and over again with the same expression in a loop or frequent method calls because
the matches( ) method will re-compile the expression every time it is used. You can also
re-use the Matcher object for different input strings by calling the method reset( ).
What does this pattern mean?
^
[a-zA-Z]
[a-zA-Z0-9._-]{0,15}
[a-zA-Z0-9]
Beginning
of a line
Valid start
characters.
Should start
with an
alphabet. Must
occur once.
Valid end
End of a
characters.
line.
Should not end
with . - or
_. Must
occur once.
How will you test this? Test with JUnit. The junit.jar file must be in the classpath.
package regex;
import org.junit.Assert;
import org.junit.Test;
public class UserNameRegexTest {
@Test
public void testMinLength() {
Assert.assertFalse("can't be <2", UserNameRegex.apply("P"));
}
@Test
public void testMaxLength() {
Assert.assertFalse("Can't be >17", UserNameRegex
489
But the above regex is not going to meet the requirement. The above regex will only
ensure that the valid characters are used and the password is of correct length (i.e.
between 6 and 15 characters). But it won't ensure that there is at least one digit from 09, one lower case and one uppercase. So, the above regex will return true for
passwords like
Johnpwd
john123
This is where the regex with look ahead comes to the rescue. The pattern below uses
look ahead with the syntax ?=. The regex pattern with look ahead is shown below:
((?=[A-Za-z]*[0-9])(?=[0-9A-Z]*[a-z])(?=[0-9a-z]*[A-Z])){6,15}
public static void correct() {
String PATTERN =
"((?=[A-Za-z]*[0-9])(?=[0-9A-Z]*[a-z])(?=[0-9a-z]*[A-Z])){6,15}";
Pattern p = Pattern.compile(PATTERN);
Matcher matcher = p.matcher("John123");
System.out.println(matcher.find( ));
//prints true;
matcher = p.matcher("john123");
System.out.println(matcher.find( ));
//prints false;
matcher = p.matcher("johnpwd");
System.out.println(matcher.find( ));
//prints false;
}
Don't be overwhelmed by long regex patterns as shown above. Once you divide them
into smaller chunks as shown below, things become much clearer.
(?=[A-Za-z]*[0-9])
(?=[0-9a-z]*[A-Z])
}
Outputs:
true
John
1
J
o
J
As you can see,
Q03 What does .*, +, and ? mean with respect to regular expressions? Where do you look
for the summary of Java regular expression constructs?
A03 *, +, and ? are known as (greedy) quantifiers as they quantify the preceding
character(s). For example,
. matches any character.
.* matches any character repeated 0 or more times.
.+ matches any character repeated 1 or more times.
.? matches any character repeated 0 or 1 time. This means optional.
[a-zA-Z]* Any alphabet repeated 0 or more times.
[a-zA-Z]+ Any alphabet repeated 1 or more times.
[a-zA-Z]? Any alphabet repeated 0 or 1.
Note: These are not wild characters. *, +, and ? are regex repetitions. The {x,y} is
also used for repetition. For example, [a]{3,5} means the letter a repeated at least 3
times or a maximum of 5 times. In fact, internally the Pattaren.java class implements,
493
a* as a{0, 0x7FFFFFFF}
a+ as a{1, 0x7FFFFFFF}
The values in square brackets (i.e. [ ] ) are known as character sets. The ., *, ?, etc are
escaped and used as literal values within the square brackets. Here are some examples
of the character sets.
Q04 What does grouping mean with regards to regular expressions? Would you prefer
capturing or non-capturing group?
A04 A group is a pair of parentheses used to group sub patterns. For example, c(a|u)t
matches cat or cut. A group also captures the matching text within the parentheses.
The groups are numbered from left to right, outside to inside. For example, (x(y*))+
(z*) has 3 explicit and 1 implicit groups. The implicit group 0 is the entire match.
494
Capturing groups are numbered by counting their opening parentheses from left to
right. The captured sub sequence may be used later in the expression, via a back
reference, and may also be retrieved from the matcher once the match operation is
complete. Groups beginning with ?: are pure, non-capturing groups that do not
capture text and do not count towards the group total. For example, (?:x(?:y*))+(?:z*)
will only have group 0 being the entire match.
package regex;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexGroup {
public static void main(String[ ] args) {
CharSequence inputStr = "xyyxyyzz";
String patternCapturing = "(x(y*))+(z*)";
String patternNonCapturing= "(?:x(?:y*))+(?:z*)";
System.out.println("====Capturing=====");
apply(inputStr,patternCapturing);
System.out.println("====Non Capturing=====");
apply(inputStr,patternNonCapturing);
}
public static void apply(CharSequence inputStr, String patternStr) {
// Compile and use regular expression
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(inputStr);
boolean matchFound = matcher.find();
if (matchFound) {
// Get all groups for this match
for (int i = 0; i <= matcher.groupCount( ); i++) {
System.out.println("group " + i + " --> "
+ matcher.group(i));
}
495
498
[a-z][A-Z]*
[a-z][A-Z]+
[a-z][A-Z]?
[a-z][A-Z]{1,2}
[a-z][A-Z]{2,}
[a-z][A-Z]{,6}
[a-z][A-Z]*?
[a-z][A-Z]+?
[a-z][A-Z]??
[a-z][A-Z]{1,2}?
[a-z][A-Z]{2,}?
[a-z][A-Z]{,6}?
[a-z][A-Z]*+
[a-z][A-Z]++
[a-z][A-Z]?+
[a-z][A-Z]{1,2}+
[a-z][A-Z]{2,}+
[a-z][A-Z]{,6}+
package regex;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GreedyReluctantPossessive {
public static void main(String[ ] args) {
String input = "xfooxxfooxxxxfooxxxxxxxxfoofo";
// this is the default for *, +, and ?.
// fetches the whole String eagerly and then start to backtrack
String eagerPattern = ".*foo";
System.out.println("=======Greedy========");
execute(input, eagerPattern);
// Backtrack to the beginning and start fetching one at a time
String reluctantPattern = ".*?foo";
System.out.println("=======Reluctant========");
execute(input, reluctantPattern);
// fetches the whole String eagerly, but won't let go of what was fetched
String possessivePattern = ".*+foo";
System.out.println("=======Possessive========");
execute(input, possessivePattern);
// possessive pattern is more suited for matching text in quotes.
String input2 = "a=\"xfoo\" some text b=\"xfooo\""
+ "some more text c=\"xfo\"";
possessivePattern = "\"xfoo\"*+";
System.out.println("=====Possessive match for text in quotes=====");
execute(input2, possessivePattern);
}
private static void execute(String input, String pattern) {
Pattern p = Pattern.compile(pattern);
Matcher matcher = p.matcher(input);
499
Like most regex engines, Java uses NFA (Non deterministic Finite Automaton)
approach. This means the engine scans the regex components one by one, and
progresses on the input string accordingly. However, it will go back in order to explore
matching alternatives when a dead end is found. Alternatives result from regex struc
tures such as quantifiers (*, +, ?) and alternation (e.g. a|b|c|d). This exploration
technique is called backtracking. In a more simpler term, backtracking means return
to attempt an untried option.
PC The .* and alternation (e.g. a|b|c) regexes must be used sparingly. For example, if
you want to retrieve everything between two "@" characters in an input string, instead
of using "@(.*)@", it's much better to use "@([^@]*)@". This regex can be further
optimized by minimizing the backtracking with the help of a possessive quantifier
*+ instead of a greedy quantifier *. Say you are using the pattern "@([^@]*)@"
with a long input string that does not have any @. Because * is a greedy quantifier,
it will grab all the characters until the end of the string, and then it will backtrack,
giving back one character at a time. The expression will fail only when it can't
backtrack anymore. If you change the pattern to use "@([^@]*+)@" with a possessive
quantifier *+, the new pattern fails faster because once it has tried to match all the
characters that are not a @, it does not backtrack; Instead it fails right there. Take
care not to compromise correctness for a small optimization.
PC Regular expressions like "(COLON|COMMA|COLUMN)" have a reputation for
being slow, so watch out for them. Firstly, the order of alternation counts. So, place the
more common options in the front to be matched faster. Secondly, extract out the
more common ones. For example, CO(MMA|L(ON|UMN)). Since COMMA is more
common, it is used first. The "CO" and "L" are extracted out as they are more
common.
Q06 Can your write a function that will replace all tokens delimited by @ with a given
String?
Sample Input = Hello @name@, where are you @name@?
A06
package regex;
import org.apache.commons.lang.StringUtils;
501
"<!--<tag-1>abc</tag-1>\n<tag-2>abc</tag-2> -->";
Pattern p = Pattern.compile(PATTERN, Pattern.MULTILINE);
Matcher matcher = p.matcher(COMMENT);
boolean matchFound = matcher.find();
System.out.println(matchFound);
if (matchFound) {
System.out.println(matcher.group(1));
}
}
Output:
true
<tag-1>abc</tag-1>
<tag-2>abc</tag-2>
^<!--
(([^-]+|-(?!->))*)
[^-]
([^-]+|-(?!->))
([^-]+|-(?!->))*
-->$
Note: The positive and negative look aheads are very powerful if you want to match
something followed by or not followed by respectively.
Important: It is not a best practice to parse an XML document with regexes. An XML
is not a regular language. You cannot parse it using a regular expression. An expression
that you think will work will break when you get nested tags, then when you fix that it
will break on XML comments, then CDATA sections, then processor directives, then
name spaces, etc. So favor using an XMLParser and XPath to parse XML documents.
505
\\S+
Q09 How would you configure the Java regex engine to be case insensitive?
A09 The Pattern class has a number of constants to configure the engine for case insens
itive search, multi-line search, comments included in regex, etc.
public static void main(String[ ] args) {
String PATTERN = "john";
final String COMMENT = "John";
Pattern p = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE);
Matcher matcher = p.matcher(COMMENT);
boolean matchFound = matcher.find();
System.out.println(matchFound);
}
The above CASE_INSENSITIVE pattern constant can be re-written with the
configuration flags to be included within the regex pattern as shown below:
public static void main(String[ ] args) {
String PATTERN = "(?i)john";
//?i means ignore case
final String COMMENT = "John";
Pattern p = Pattern.compile(PATTERN);
Matcher matcher = p.matcher(COMMENT);
boolean matchFound = matcher.find();
System.out.println(matchFound);
}
If you want to include comments for the above pattern, you can configure the
matching engine as shown below:
// i --> ignore case, x --> allow comments
String PATTERN = "(?ix)john #look for john ignoring case";
506
<from>^/accounts/([a-z0-9]+)/$</from>
<to>/accounts/display.do?accountId=$1</to>
</rule>
<rule>
<from>^/accounts/([a-z0-9]+)/edit$</from>
<to>/accounts/edit.do?accountId=$1</to>
</rule>
As per the above example, a RESTful URL like /accounts/005678924/ will be
forwarded /accounts/display.do?accountId=005678924
Outbound rules:
<outbound-rule>
<from>^/accounts/list.do$</from>
<to>/accounts/</to>
</outbound-rule>
<outbound-rule>
<from>^/accounts/display.do\?accountId=([a-z0-9]+)$</from>
<to>/accounts/$1/</to>
</outbound-rule>
<outbound-rule>
<from>^/accounts/edit.do\?accountId=([a-z0-9]+)$</from>
<to>/accounts/$1/edit</to>
</outbound-rule>
The outbound URL rule will revert /accounts/display.do?accountId=005678924
back to the RESTful URL /accounts/005678924/ to be displayed on the client side
browser. As you can see the rules are based on regex.
4. Regex can be used to split files containing delimited data. For example,
extracting values from a CSV file (i.e. a file containing comma separated values).
package regex;
public class SplitCSV {
public static void main(String[ ] args) {
String line = "John A, Peter O`Sullivan,\"James, Junior \",\"\"";
509
(?=([^\"]*\"[^\"]*\")*[^\"]*$)
510
[^\"]*
\"
Q. How would you go about implementing the above without using the String.split( )
function?
A. As sown below,
package regex;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SplitCSV2 {
public static void main(String[] args) {
String line = "John A, Peter O`Sullivan,\"Peter, James \",\"\"";
Pattern p = Pattern
.compile("\\s*,\\s*(?=([^\"]*\"[^\"]*\")*[^\"]*$)");
Matcher m = p.matcher(line);
int i = 0;
while (m.find( )) {
System.out.println(line.substring(i, m.start( )));
i = m.end( );
}
if(i< line.length( ))
System.out.println(line.substring(i));
}
}
5. Regex can be used in Maven pom.xml or Ant build.xml files. For example,
maven ant-run plug-in can use the ant task replaceregexp to find and replace values
in files using regular expressions as shown below:
511
512
7. Regex can be used to assert data in regression and load testing tools like
selenium and JMeter to assert values. Also note that these tools support assertion
using an XPath, which would be more appropriate than using a regex in many
situations. A selenium based JUnit test in Java would look something like:
boolean result = selenium.isTextPresent("regexp:" + "\\$4,925\\.87");
assertTrue(result);
where $ has a special meaning (i.e. end of line) in regex, hence it is escaped with \$,
and . has a special meaning in regex (i.e. match any character), and it is escaped with
\.. Since \ has a special meaning (i.e. escape character) in Java, it has to be
escaped with an additional \ as shown above with \\$ and \\.. The above
regex verifies presence of amount $4,925.87 in a web page.
In JMeter regex tester, to extract name and value from a web page containing
something like,
<input type="text" name="firstname" value="Peter" />
you could use a regex like,
513
The extracted value can be used as a query string for the subsequent request. The $1 is
group 1 that captures the name firstname and $2 is group 2 that captures the value
Peter. The extracted pattern ($1=$2) is stored with the reference name
FIRST_NAME_PARAN to be used later.
As shown below, the value stored in the FIRST_NAME_PARAM is added to the path
as a query string.
514
515
516
(0?[1-9]|[12][0-9]|3[01])/
(0?[1-9]|1[012])/
((19|20)\\d\\d)
Q. How will you represent time in both 12 hour and 24 hour format using regex?
A.
12 hour format:
(1[012]|[1-9]):([0-5][0-9])\\s?(?i)(am|pm)
(1[012]|[1-9])
([0-5][0-9])
Matches 00 to 59.
\\s?
(?i)
(?i)(am|pm)
24 hour format:
([01]?[0-9]|2[0-3]):[0-5][0-9]
([01]?[0-9]|2[0-3])
Matches ':' .
[0-5][0-9]
Matches 00 to 59.
(jpg|png|gif|bmp)
)$)
End of group #2, end of the string and end of group #1.
Q12 Which of the following will be matched by the regex .*[^ a-zA-Z0-9].* ?
a)
b)
c)
d)
e)
ON_LINE
ONLINE
online9
on-line
on line
A12 a) and d) will match true due to presence of _ and - respectively. The regex reads
NOT blank space, a-z, A-Z or 0-9 preceded or followed by zero or more characters.
The above regex is looking for presence of any character other than a-z, A-Z, 0-9, and
a blank space.
Q13 Which of the following will be matched by the regex .*[^0-9].* ?
a) 12345678
b) 123-456
c) 123 456
A13 b) and c) will be matched due to presence of - and blank space respectively. The
above regex is looking for presence of anything other than a digit.
Q14 If you had a special requirement to strip out currencies from a specific report, how
would you go about stripping it out?
A14 There are many ways to achieve this like using a Formatter like a DecimalFormatter, etc. It
can also be achieved using a regular expression like:
String output = input.replaceAll("\\p{Sc}", "");
Where,
518
519
Section-9
520
Enjoy analyzing the pros, cons, and trade-offs of each design and development
alternatives to arrive at an effective and workable solution that can adapt to growing
and changing business needs.
Software development can be complex as there are so many moving parts, and it is
quite satisfying to get your work through the SDLC (Software Development Life
Cycle) phases like design, development, deployment, testing, documentation,
support, and hand-over. There is always a variety of tasks and responsibilities to be
performed. Opportunity to applying agile practices where appropriate, makes your
journey even more enjoyable by interacting more closely with the multidisciplinary
teams.
It involves both the technical side as well as the people side to have your project
completed successfully.
521
Access to plethora of frameworks and libraries that can improve productivity and
quality of a software. There is always something new to learn.
Working on the first iteration of a typical use case to come up with the full vertical
slice (i.e. end to end), where key decisions need to be made and vital tasks need to be
executed. For example,
Choice of technologies, frameworks, and tools.
Validating the baseline architecture.
Analyzing the pros and cons of design alternatives, and identifying gaps in
requirements and design.
Custom frameworks or utility classes are to be written for ease of devel
opment.
Processes need to be put in place for build management, dependency
management, deployment management, change management, release
management, automated testing, etc.
Writing code for multiple platforms. Separating protocol dependent code from
business logic dependent code to promote code reuse.
522
Getting the work through inception, elaboration, construction, and transition phases,
and to finally see it meeting customers' needs.
If you know that the position you're applying for might possibly involve managing
others, and you have been working towards management previously, pointing that
out in your answer aligns your plans with the company's goals.
If you have been using the technologies and frameworks required by the employer,
then highlight your experience with those technologies and frameworks.
523
If you have the domain and business knowledge required by your prospective
employer, mention it.
Accomplishments in key areas like fixing thread-safety issues, designing and devel
oping custom frameworks to improve productivity, identifying performance bottle
necks and tuning performance, fixing memory leaks, fixing bugs relating to language
fundamentals, etc.
Your resume should already have a personal statement that discusses your qualities in the most positive terms possible. Make sure you are familiar with your resume. If
you need to learn more about this, refer to my book entitled Java/J2EE Resume
Companion at http://www.lulu.com/java-success, which is full of examples.
524
It is not a good practice to reinvent the wheel. The interviewer is trying to evaluate
your level of technical knowledge and thought process. He/she is only expecting
you to know the approach. Consider it as a brain storming session.
Firstly, code to interface. Create a class that implements the java.util.Map interface
with empty methods.
Decide on the backing data structure. Arrays are fast and memory efficient. Hence
an array can be used to back up the map. This will be an indexed bucket.
Decide on a hashing algorithm to index into the array and store an object in that slot
of the array.
long bucketIndex = function(key, arrayLength);
Come up with a strategy to detect and resolve collisions. Collisions can be detected
by having each slot of the array point to a LinkedList. That contains key-value pairs.
You can create a class called Entry that has attributes key and value as type Object.
Come up with a strategy for resizing when the capacity is reached. This process is
known as rehashing whereby existing items are mapped to new bucket locations.
representation can
525
How will you assess that Im doing a good job? Would there be opportunities to
grow if I can prove myself ?
What are the key challenges in the first few months of the role?
What are the reasons that the job came about? What is the size of the current team?
527
a number of choices. Firstly, you need to have the knowledge to come up with the valid
choices. You will have to work out the pros and cons for each choice. Nothing is black and
white in real world. There are trade-offs to be made. Only experience can give you the
expertise to make the the right decision under a given circumstance. So, I urge you to not
just read it, but apply it. Once you experience it, you may not even agree with some of my
reasoning. When you start to do so, you start to think and act like an experienced profes
sional. If you are already an experienced professional, use it as a quick refresher as it is not
easy to remember everything. Whether you are already an accomplished professional or not,
always remember that good technical skills must be complemented with good soft skills and
right attitude. Be passionate about what you do, but don't be inflexible. Understand the
importance of good communication skills, and remember that the software development is
a team sport.
529
Description
aka
Also Known As
ANTLR
AOP
API
APT
BCEL
EE
Enterprise Edition
ETL
FIFO
i.e.
That is
IDE
JAR
Java ARchive
JAX-WS
JAXB
JAXP
JDK
JIT
Just In Time
JMX
JPA
JRE
JSR
JVM
LIFO
MBean
Managed Bean
ME
Micro Edition
530
NPE
NullPointerException
OO
Object Oriented
OOA
OOD
OS
Operating System
POJO
Regex
Regular Expressions
SAAJ
SE
Standard Edition
SNMP
SQL
531
Index
Classes and Interfaces Essentials..................................................................................................
Applying the design principles....................................................................................................
Can you list some of the key principles to remember while designing your classes?.................................281
How can you improve on the following code snippet?...................................................................................276
How will you improve on the following code snippets with appropriate design principles?....................268
Is there anything wrong with the following code snippet?.............................................................................272
What is your understanding of some of the following most talked terms like Dependency Inversion
Principle (DIP), Dependency Injection (DI) and Inversion of Control (IoC)?..........................................279
532
What can an interface do that an abstract class cannot? What are the differences between abstract classes
and interfaces?....................................................................................................................................................... 256
What do you understand by abstract classes and interfaces?..........................................................................247
What is the significance of abstract classes & interfaces with respect to OO design?...............................256
What modifiers can be used in an interface?.................................................................................................... 259
When would you prefer one over the other?.................................................................................................... 258
Judging Experience.....................................................................................................................
Can you explain thread-safety and atomicity with an example? What do you understand by optimistic
versus pessimistic locking?..................................................................................................................................... 74
Can you list some of the Java features you used recently?................................................................................52
Can you list the Java SE features you dislike and why?.....................................................................................58
Can you list the Java SE features you like to be added in the future?.............................................................62
Can you list the Java SE features you really love and why?...............................................................................55
Give me a high level description of your experience with the Java platform?..............................................67
533
Language Essentials.......................................................................................................................
Choosing the right data types......................................................................................................
How would you go about choosing the right data types for your application? What are wrapper classes,
and why do you need them?................................................................................................................................ 144
When working with floating-point data types, what are some of the key considerations?........................147
Java is pass-by-value...................................................................................................................
Explain the statement Java is always pass by value?......................................................................................... 211
The value of Point p before the following method calls is (10,20). What will be the value of Point p after
executing the following method calls?............................................................................................................... 213
534
Recursive functions.....................................................................................................................
How would you take advantage of Java being a stack based language? What is a re-entrant method?...214
What are idempotent methods?.......................................................................................................................... 216
535
Sorting Elements.........................................................................................................................
If I mention the interface names Comparable or Comparator, what does come to your mind? Why do
we need these interfaces?..................................................................................................................................... 422
536
Which of the following will be matched by the regex .*[^ a-zA-Z0-9].* ?........................518
Which of the following will be matched by the regex .*[^0-9].* ?....................................518
Write a regular expression for the following scenarios?........................................................515
Objects Essentials...........................................................................................................................
Casting Objects...........................................................................................................................
If you have a reference variable of a parent class type, and you assign a child class object to that variable,
and then you invoke a static method that is present in both parent and child classes, which method will
be invoked?............................................................................................................................................................ 316
What do you understand by variable shadowing?............................................................................................ 315
What is type casting? Explain up casting vs. down casting? When do you get ClassCastException?......314
Cloning Objects...........................................................................................................................
What is the main difference between shallow cloning and deep cloning of objects?................................312
Garbage Collection......................................................................................................................
Assume that the following utility method is very frequently accessed by parallel threads, and profiling
indicates that this method is causing GC to over work. How would you go about improving the
following implementation?.................................................................................................................................. 381
Explain different types of references in Java?.................................................................................................. 374
How do you know if your garbage collector is under heavy use? What are the best practices to minimize
heavy use of garbage collection?........................................................................................................................ 378
If you have a circular reference of objects, but you no longer reference it from an execution thread (e.g.
main thread), will this object be a potential candidate for garbage collection?...........................................377
What are the different ways to make an object eligible for Garbage Collection when it is no longer
needed?................................................................................................................................................................... 377
What do you know about the Java garbage collector? When does the garbage collection occur?...........372
What is the difference between final, finally and finalize( ) in Java?..............................................................372
When does the GC stop? Who controls the GC? Can GC be forced?........................................................378
Which part of the memory is involved in Garbage Collection? Stack or Heap?........................................378
Immutable Objects......................................................................................................................
How will you prevent the caller from adding or removing elements from pets? How will you make this
code fully immutable?........................................................................................................................................... 324
How would you defensively copy a Date field in your immutable class?.....................................................323
What about data that changes sometimes? Is there any way to obtain the benefits of immutability with
the added benefit of thread-safety for data that changes less frequently?...................................................325
What is an immutable object? How do you create an immutable type? What are the advantages of
immutable objects?............................................................................................................................................... 318
Why is it a best practice to implement the user defined key class as an immutable object?......................319
537
Platform Essentials.........................................................................................................................
538
Handling Exceptions...................................................................................................................
What are the best practices in regards to exception handling?......................................................................483
What is a user defined exception? When would you define your own exception?......................................481
What is the difference between exception and error?.....................................................................................481
When do you want to use checked exceptions and when do you want to use unchecked exceptions?...476
Why does Java have exceptions? What is the difference between checked and unchecked exceptions?. 471
Will the following code compile?........................................................................................................................ 474
Logic Processing.........................................................................................................................
Can you write a code to evaluate( ) a simple mathematical calculation as shown below using a binary
tree?......................................................................................................................................................................... 461
Write a method which takes the parameters (int[ ] inputNumbers, int sum) and checks input to find the
pair of integer values which totals to the sum. If found returns true, else returns false?.........................455
Write a program that allows you to create an integer array of 5 elements with the following values: int
numbers[ ]={5,2,4,3,1}. The program computes the sum of first 5 elements and stores them at element
6, computes the average and stores it at element 7 and identifies the smallest value from the array and
stores it at element 8............................................................................................................................................. 450
Write a program that will return factorial of n (usually written as n!), which is the product of all integers
up to and including n (1 * 2 * 3 * ... * n). Can you explain both recursive and iterative approaches?
Which approach would you prefer?................................................................................................................... 452
Write a program that will return whichever value is nearest to the value of 100 from two given int
numbers.................................................................................................................................................................. 451
539
540
"Any intelligent fool can make things bigger, more complex, and more violent. It takes a
touch of genius -- and a lot of courage -- to move in the opposite direction."
- Albert Einstein
If you cant explain it to a six year old, you dont understand it yourself.
- Albert Einstein
541
542