Grokking The Java Developer Interview More Than 200 Questions To Crack The Java, Spring, SpringBoot & Hibernate Interview-200-327
Grokking The Java Developer Interview More Than 200 Questions To Crack The Java, Spring, SpringBoot & Hibernate Interview-200-327
As, you can see, the output is as expected because only one thread is
accessing the count, let’s see what will happen in case the count variable is
accessed by more than one thread, un-comment the code regarding second
thread t2 and run the main class:
Output:
The expected output was 100 but we got a different output, if you run the
above program you will see a different output and it will be anywhere
between 50 and 100. The reason for this is that 2 threads are accessing a
mutable variable without any synchronization. One solution that will be
coming to your mind will be using synchronization block, and yes this
problem can be solved using that but it will have a performance impact, as
threads will acquire the lock, update the value and release the lock, and
then giving other threads access to the shared mutable variable.
But java has provided Atomic wrapper classes for this purpose that can be
used to achieve this atomic operation without using Synchronization.
Let’s see the change in our Runnable:
Output:
Question 94: What is Collection Framework?
Answer: Collection framework represents an architecture to store and
manipulate a group of objects. All the classes and interfaces of this
framework are present in java.util package.
Some points:
- Iterable interface is the root interface for all collection classes, it
has one abstract method iterator()
- Collection interface extends the Iterable interface
Output:
Although, we have made the list as final but still we are able to add
elements into it, remember applying final keyword to a reference variable
ensures that it will not be referenced again meaning you cannot give a new
reference to list variable:
Output:
Guava library also provides certain ways to make immutable list and Java 9
has List.of() method.
There are other utility methods also, to make unmodifiable collections:
Load factor:
/**
* The load factor used when none specified in
constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
Node class:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
Program showing that hashmap’s capacity gets doubled after load factor’s
threshold value breaches:
Output:
Change the for loop condition from i<13 to i<=13, see below:
Output:
Here, Employee class has not given equals() and hashCode() method
implementation, so Object’s class equals() and hashCode() methods will be
used when we use this Employee class as hashmap’s key, and remember,
equals() method of Object class compares the reference.
TestHashMap.java:
Can you predict the output of this one?
Output:
Here, Employee objects e1 and e2 are same but they are both inserted in
the HashMap because both are created using new keyword and holding a
different reference, and as the Object’s equals() method checks reference,
they both are unique.
And as for objects e3 and e4, they both are pointing to same reference (e4 =
e3), so they are equal according to Object’s equals() method hence the value
of e3 which was 300 gets replaced with the value 400 of the same key e4,
and finally size of HashMap is 3.
Well, nothing’s changed here. Because even though e1 and e2 are equal
according to our newly implemented equals() method, they still have
different hashCode as the Object’s class hashCode() is used. So the equals
and hashCode contract is not followed and both e1, e2 got inserted in
HashMap.
set.contains() method:
public boolean contains(Object o) {
return map.containsKey(o);
}
The passed object is given to map.containsKey() method, as the HashSet’s
values are stored as the keys of internal map.
NOTE: If you are adding a custom class object inside the HashSet, do follow
equals and hashCode contract. You can be asked the equals and hashCode
scenarios questions, just like we discussed in HashMap (Question 105).
Output:
Program 3:
Here, we are sorting based on Employee name,
Output:
Let’s look at a program where we pass a Comparator in the TreeMap
constructor, and sort the Employee object’s based on age in descending
order:
Program 4:
Output:
Here, in Employee class, I have not implemented equals() and hashCode()
TreeMap’s Javadoc:
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable,
java.io.Serializable
{
/**
* The comparator used to maintain order in this tree
map, or
* null if it uses the natural ordering of its keys.
*
* @serial
*/
private final Comparator<? super K> comparator;
private transient Entry<K,V> root;
TreeSet Javadoc:
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable,
java.io.Serializable
public TreeSet() {
this(new TreeMap<E,Object>());
}
Output:
But they don’t throw the exception, if the collection is modified by Iterator’s
remove() method.
Program 2:
Output:
Javadoc:
arrayList.iterator() method:
public Iterator<E> iterator() {
return new Itr();
}
Itr is a private nested class in ArrayList:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned;
-1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
Itr.next() method:
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
Output:
Here, iterator is reflecting the element which was added during the iteration
operation.
Java Collection framework is very important topic when preparing for the
interviews, apart from the above questions, you can read about Stack,
Queue topics also, but if you are short on time, what we have discussed so-
far should be enough for the interview.
Question 114: What is the difference between a Monolith
and Micro-service architecture?
Answer: In monolithic architecture, applications are built as one large
system, whereas in micro-service architecture we divide the application into
modular components which are independent of each other.
Monolithic architecture has some advantages:
- Development is quite simple
- Testing a monolith is also simple, just start the application and do
the end-to-end testing, Selenium can be used to do the
automation testing
- These applications are easier to deploy, as only one single jar/war
needs to be deployed
- Scaling is simple when the application size is small, we just have to
deploy one more instance of our monolith and distribute the
traffic using a load balancer
- Network latency is very low/none because of one single codebase
However, there are various disadvantages of monolith architecture as well:
Java Class B:
Using Java Based Configuration:
When using Java based configuration, the constructor needs to be
annotated with @Autowired annotation to inject the dependencies,
Our classes A and B will be annotated with @Component (or any other
stereotype annotation), so that they will be managed by Spring.
Java class A:
Java class B:
You can give any name to your initialization and destroy methods, and here
is our Test class
By implementing InitializingBean and DisposableBean interfaces
InitializingBean interface has afterPropertiesSet() method which can be used
to execute some initialization task for a bean and DisposableBean interface
has a destroy() method which can be used to execute some cleanup task.
Here is our Test class,
And, in the xml file:
Spring uses CGLIB to create the proxy object and the proxy object delegates
method calls to the real object. In the above example, we are using
ScopedProxyMode.TARGET_CLASS which causes an AOP proxy to be injected
at the target injection point. The default Proxy mode is
ScopedProxyMode.NO.
To avoid CGLIB usage, configure the proxy mode with
ScopedProxyMode.INTERFACES and it will use JDK dynamic proxy.
We can also define the port number that our embedded server will run on,
using
server.port=9000
We can have different application.properties file for different environments
like dev, stage and prod.
When you add the exclusion tag and save the pom.xml, you will see that the
tomcat dependency will be removed from Maven Dependencies, then you
can add another server’s dependency like the one below:
Behind the scenes, Spring uses Jackson library (that comes with spring-boot-
starter-web) to map the json request to the pojo class.
MappingJackson2HttpMessageConverter is used when the incoming request
is of content-type application/json.
Now, to resolve this you can give names to your Rectangle and Circle class,
like:
And you will use @Qualifier annotation to specify which bean should be
autowired, like:
Now, Spring will not get confused as to what bean it has to autowire.
NOTE, you can also use @Qualifier annotation to give names to your
Rectangle and Circle classes, like
Also, you can specify a ‘rollbackFor’ attribute and specify which exception
types must cause a transaction rollback (a transaction with Runtime
exceptions and errors are by default rolled back).
If your process() method is calling another bean method, then you can also
annotate that method with @Transactional and set the propagation level to
decide whether this method should execute in the same transaction or it
requires a new transaction.
Or a specific controller,
- When using @Bean, you have the control over the bean creation
logic.
- @Bean is a method level annotation, the body of the method
contains the logic for creating the bean instance and this method
returns the instance which will be registered in the spring
application context.
- Using @Bean, you can register the classes from 3rd party libraries
into the application context
- @Bean annotation is usually declared in configuration classes.
In this example, we are consuming a GET web service and converting the
response to the object of User class.
Similarly, there are other methods to consume POST, DELETE web services
like exchange() and delete() respectively.
AOP terminology:
Aspect: Aspect is a class that implements the application concerns that cuts
across multiple classes, such as transaction management and logging.
Aspects can be a normal class configured through Spring XML configuration
or we can use @Aspect annotation.
Join Point: a point during the execution of a program, such as the execution
of a method or the handling of an exception. In Spring AOP, a join point
always represents a method execution.
Advice: advices are actions that are taken for a particular join point. There
are different types of advices, like, before, after and around advice.
Pointcut: pointcut is an expression that is matched with join points to
determine whether advice needs to be applied or not.
Target Object: objects on which advices are applied by one or more aspects.
Since Spring AOP is implemented using runtime proxies, this object will
always be a proxied object.
AOP Proxy: an object created by the AOP framework in order to implement
the aspect contracts (advise method executions and so on). In the Spring
Framework, an AOP proxy will be a JDK dynamic proxy or a CGLIB proxy.
Weaving: It is the process of linking aspects with other objects to create the
advised proxy objects. This can be done at compile time, load time or at
runtime. Spring AOP performs weaving at the runtime.
All the above topics are advanced but I will explain about them a little, so
you can go out and read more about it.
Question 162: What do you know about Eureka Naming
Server?
Answer:
To know about the need of Eureka Naming Server and what it does, let’s
consider an example. Suppose you have 5 micro-services which are
experiencing heavy traffic and you want to deploy multiple instances of
these 5 micro-services and use a load balancer to distribute the traffic
among these instances. Now, when you create new instances of your micro-
service, you have to configure these in your load balancer, so that load
balancer can distribute traffic properly. Now, when your network traffic will
reduce then you will most likely want to remove some instances of your
micro-service, means you will have to remove the configuration from your
load balancer. I think, you see the problem here.
This manual work that you are doing can be avoided by using Eureka naming
server. Whenever a new service instance is being created/deleted, it will
first register/de-register itself to the Eureka naming server. Then you can
simply configure a Ribbon client (Load Balancer) which will talk with Eureka
Naming server to know about the currently running instances of your service
and Ribbon will properly distribute the load between them. Also, if one
service, serviceA wants to talk with another service, serviceB then also
Eureka Naming server will be used to know about the currently running
instances of serviceB.
You can configure Eureka Naming Server and Ribbon client in SpringBoot
very easily.
If the GET service is getting failed, then the fallback method will be
executed.
JPA / Hibernate
You should choose the one from javax.persistence package, because if you
choose the Hibernate one, then in future, if by any chance, you have to
remove Hibernate and use some other JPA implementation, like iBatis, then
you will have to change your code (imports). As you remember, JPA is just a
specification, like an interface. You can plug any of its implementations as
long as you use the imports from javax.persistence package.
- get() method involves a database hit, if the object does not exist in
Session cache and it returns a fully initialized object which may
involve several database calls, whereas load() method returns a
proxy object and it only hit the database if any method other than
getId() is called on the entity object
- load() method results in slightly better performance as it can
return a proxy object, it will only hit the database when a non-
identifier getter method is called, whereas get() method returns a
fully initialized object when it does not exist in Session cache
which may involve multiple database calls based on entity
relationships
- get() method returns null if the object is not found in the cache as
well as the database whereas load() method will throw
ObjectNotFoundException but never return null
- If you are not sure whether the object exists or not, then use get()
as it will return null but if you are sure that the object exists, then
use load() method as it is lazily initialized
Question 177: What is the difference between save(),
saveOrUpdate() and persist() method of Hibernate Session?
Answer: Hibernate Session class provides various methods to save an object
into the database, like save(), saveOrUpdate() and persist()
The difference between save() and saveOrUpdate() method is that save()
method saves the record into the database by INSERT query, generates a
new identifier and returns the Serializable identifier back, while
saveOrUpdate() method either INSERT the record or UPDATE the record if it
already exists, so it involves extra processing to find whether the record
already exists in the table or not.
Similar to save(), persist() method is also used to save the record into the
database table.
The differences between save() and persist() are:
Question 181: How can we see the SQL query that gets
generated by Hibernate?
Answer: If you are using hibernate.cfg.xml file, then use the below property:
<property name="show_sql">true</property>
If you are using Spring Data JPA, then you can set this property in
application.properties file, like:
spring.jpa.show-sql=true
1. Inner join: Inner join selects all records from Table A and Table B,
where the join condition is met.
Syntax:
SELECT Table1.column1, Table1.column2, Table2.column1, …..
FROM Table1
INNER JOIN Table2
On Table1.MatchingColumnName = Table2.MatchColumnName;
(Note: Use either INNER JOIN or JOIN for this operation)
2. Left Join: Left join selects all records from Table A along with
records of Table B for which the join condition is met.
Syntax:
SELECT Table1.column1, Table1.column2, Table2.column1, …..
FROM Table1
LEFT JOIN Table2
On Table1.MatchingColumnName = Table2.MatchColumnName;
3. Right Join: Right join selects all records from Table B along with
records of Table A for which the join condition is met.
Syntax:
SELECT Table1.column1, Table1.column2, Table2.column1, …..
FROM Table1
RIGHT JOIN Table2
On Table1.MatchingColumnName = Table2.MatchColumnName;
4. Full Join: Full join selects all records from Table A and Table B,
regardless of whether the join condition is met or not.
Syntax:
SELECT Table1.column1, Table1.column2, Table2.column1, …..
FROM Table1
FULL JOIN Table2
On Table1.MatchingColumnName = Table2.MatchColumnName;
DML: DML stands for Data Manipulation language. These statements allows
us to manage the data stored in the database. DML commands are not auto-
committed, so they can be rolled back.
Examples of DML commands are: INSERT, UPDATE, DELETE, SELECT
Other than these common questions, you can be asked to write a lot of
queries which mostly contains Joins, so you should also prepare for those
types of database queries.
Question 201: Find first 3 largest numbers in an array
In this question, the interviewer will most probably ask you to not use
sorting and pick the first/last 3 numbers from the array. Instead he will ask
you to use one “for loop” to solve this problem.
Output:
Three largest elements are: 78, 33, 20
Here, the idea is to have 3 numbers and then iterating through the array and
finding where the current element of array fits in.
At first, we check whether the current element is greater than first, if true,
assign the current element to first number by swapping the values of first,
second and third.
When the first condition is not true, then we compare the current element
with second largest number to find whether the current number is second
largest or not, same goes for third condition.
There are many programmatic, puzzle problems that the interviewer can
ask. If you are a beginner, then prepare for programs like Palindrome,
Fibonacci, Array problems, String problems, Linked list programs etc. First,
try to solve them with whatever brute-force solution that comes to your
mind, then try to find its time and space complexity, then try to optimize it.
If you are not able to think of a better solution, no problem, look for the
optimal solution on the internet.
About the Author
Jatin Arora is a Computer Science graduate. He holds an expertise in Java &
SpringBoot. He has worked on a variety of interesting projects across
different domains like Inventory management, DevOps, cloud & financial
domain.