0% found this document useful (0 votes)
31 views22 pages

Java QandA

The document discusses various concepts in Java, including the immutability of Strings, the workings of the intern() method, and the differences between String, StringBuffer, and StringBuilder. It also covers Object-Oriented Programming principles, exception handling, and serialization, along with the distinctions between checked and unchecked exceptions. Additionally, it explains the use of wrapper classes, marker interfaces, and the significance of the final, finalize, and finally keywords.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
31 views22 pages

Java QandA

The document discusses various concepts in Java, including the immutability of Strings, the workings of the intern() method, and the differences between String, StringBuffer, and StringBuilder. It also covers Object-Oriented Programming principles, exception handling, and serialization, along with the distinctions between checked and unchecked exceptions. Additionally, it explains the use of wrapper classes, marker interfaces, and the significance of the final, finalize, and finally keywords.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 22

1. Why are Strings immutable in Java?

Strings are immutable in Java for several reasons, primarily for security,
performance, and thread-safety. Immutable objects cannot be altered once they are
created, which means that when a string is passed around in a program, it remains
unchanged. This is particularly important when strings are used as keys in hash
maps or for managing sensitive data. In addition, string immutability allows the
JVM to optimize memory usage through a technique called string interning, where the
same string value is shared across multiple variables, reducing memory overhead.
Immutability also ensures that strings can safely be used in multi-threaded
environments without the need for synchronization.

2. How intern() works in Java?


The intern() method in Java is used to ensure that a string is stored in the string
pool, which is a special area of memory where the JVM stores unique string
literals. When you call intern() on a string, it checks if an identical string
already exists in the pool. If it does, the reference to the existing string is
returned; if not, the string is added to the pool, and its reference is returned.
This mechanism ensures that there is only one copy of each distinct string in
memory, which can save a significant amount of memory if many strings are used
repeatedly. For example, calling intern() on a string with a value of "hello"
ensures that there is only one "hello" string object shared across the entire JVM
instance.

3. How many objects are created in Strings using string literals and the new
operator?
When using string literals in Java (e.g., String s = "hello";), only one object is
created for the string value, and that string is placed in the string pool. If
another string with the same value is declared, it will refer to the same object in
the pool. On the other hand, when you use the new operator (e.g., String s = new
String("hello");), two objects are created: one in the string pool (if not already
present) and another on the heap as a new object. This means that the string object
created using the new operator is distinct from the one in the string pool, even
though the values are the same.

4. How String Constant Pool works?


The String Constant Pool (also known as the string pool) is a special memory area
where string literals are stored. The primary purpose of the pool is to store only
one copy of each distinct string value to save memory. When a string literal is
created, Java checks the pool to see if an identical string already exists. If it
does, the reference to the existing string in the pool is returned. If it doesn't,
a new string is added to the pool. This mechanism improves performance and memory
usage by reusing string objects. For example, when you write String s = "hello";,
it ensures that if any other part of the code uses the same string literal "hello",
they will reference the same object in the pool rather than creating a new one each
time.

5. Difference between equals and == operator in Java?


The == operator in Java is used to compare references (memory addresses) of two
objects, whereas the equals() method is used to compare the values of the objects.
For primitive types like int or char, == compares the actual values. For objects,
== checks if the two references point to the exact same memory location, which
means the objects are the same instance. On the other hand, equals() is meant to
compare the actual content or logical equivalence of two objects. For example, new
String("hello") == new String("hello") would return false because they are
different objects in memory, but new String("hello").equals(new String("hello"))
would return true because the values of both strings are the same.

6. Difference between String, StringBuffer, and StringBuilder?


String is immutable, meaning once a string object is created, it cannot be
modified. Every time you modify a string, a new object is created in memory. This
makes it less efficient for frequent modifications.
StringBuffer is mutable, which means you can modify the contents of the string
without creating new objects. It is synchronized, making it thread-safe, but this
synchronization can introduce performance overhead in multi-threaded scenarios.
StringBuilder is also mutable and works similarly to StringBuffer, but it is not
synchronized, making it more efficient than StringBuffer in single-threaded
environments. For performance-sensitive applications where thread safety is not a
concern, StringBuilder is usually preferred over StringBuffer.
7. Why is a wrapper class required?
Wrapper classes in Java (such as Integer, Double, Character, etc.) are used to wrap
primitive data types (e.g., int, double, char) in an object. Java is an object-
oriented language, and primitive data types are not objects. However, many Java
libraries and APIs, especially in collections (like ArrayList or HashMap), require
objects instead of primitives. Wrapper classes allow primitive types to be treated
as objects. Additionally, wrapper classes provide utility methods for conversion,
parsing, and other operations that cannot be performed directly on primitive types.
For example, Integer.parseInt("123") is used to convert a string into an int, and
Integer provides the intValue() method to convert it back to a primitive.

8. Methods of Object class?


The Object class is the root class of all Java classes, and it provides several
methods that are inherited by all objects:

toString(): Returns a string representation of the object. By default, it returns


the object's class name followed by the memory address, but it can be overridden to
provide a more meaningful representation.
equals(Object obj): Compares the current object with another object for equality.
By default, it checks for reference equality, but it can be overridden to check for
value equality.
hashCode(): Returns an integer hash code value for the object, used by collections
like HashMap. If two objects are considered equal, they must have the same hash
code.
getClass(): Returns the runtime class of the object.
clone(): Creates and returns a copy of the object. This method is part of the
Cloneable interface and can be overridden for custom cloning behavior.
notify(), notifyAll(), wait(): These methods are used for inter-thread
communication.
9. Does Java give importance to primitive data types?
Yes, Java gives considerable importance to primitive data types because they are
fundamental to the language's performance and simplicity. Java uses primitive types
for basic operations and high-performance scenarios because they are faster and
consume less memory than objects. They include types like int, char, float, double,
long, short, byte, boolean, and void. While Java also uses wrapper classes for
these primitives in object-oriented contexts (such as in collections), primitive
types are still widely used for basic data manipulation and computational tasks due
to their efficiency.

10. Is Java pass-by-value or pass-by-reference?


Java is strictly pass-by-value, but it can sometimes seem like pass-by-reference,
especially when dealing with objects. In Java, when you pass a primitive type (like
int or double) to a method, the actual value is passed. Any changes made to the
parameter inside the method will not affect the original variable. However, when
passing an object, the reference (memory address) to the object is passed by value,
meaning that the method can modify the state of the object. However, the reference
itself cannot be changed to point to a different object. This is why, in some
cases, it seems like Java is passing by reference when working with objects, but
technically, it's always passing the value of the reference.
11. Types of OOPS (Object-Oriented Programming Concepts)
The four main principles of OOP are:

Encapsulation: This is the concept of bundling data (variables) and methods that
operate on the data into a single unit, a class. It restricts direct access to some
of the object's components and protects the object's internal state from unintended
modifications. Encapsulation is achieved using access modifiers like private,
protected, and public.
Abstraction: Abstraction involves hiding the complex implementation details and
exposing only the necessary parts of an object. This can be achieved using abstract
classes and interfaces, which define the methods without providing implementation
details.
Inheritance: Inheritance allows one class to inherit properties and methods from
another class. It promotes code reusability, enabling one class to use the fields
and methods of another class. The class inheriting the properties is called the
subclass, and the class being inherited from is the superclass.
Polymorphism: Polymorphism allows objects of different classes to be treated as
objects of a common superclass. It enables one method to behave differently based
on the object it is acting upon, either via method overriding or method
overloading.
12. Composition vs Aggregation vs Association
Composition: In composition, one class contains references to objects of another
class, and the contained objects' lifetime is controlled by the parent class. If
the parent class is destroyed, so are the objects contained within it. Composition
represents a "has-a" relationship. For example, a Car class that contains an Engine
class would be an example of composition.
Aggregation: Aggregation is a more relaxed form of association where one object
contains references to another object, but the lifetime of the contained objects is
independent of the parent object. This is also known as a "whole-part"
relationship, but the child object can exist independently. For example, a
Department containing Employee objects can exist independently.
Association: Association represents a relationship where one object is related to
another object. It is a broad term and can include both composition and
aggregation. Association can be unidirectional or bidirectional.
13. Function Overloading vs Overriding
Function Overloading: Overloading occurs when multiple methods in a class have the
same name but differ in the number or type of parameters. The return type can be
the same or different, but the method signature must differ. This allows the
programmer to provide different behaviors for the same method name based on its
input.
Function Overriding: Overriding occurs when a subclass provides a specific
implementation for a method already defined in its superclass. The method signature
(name, return type, and parameters) must remain the same in both the parent and
child classes. Overriding is used to define new behaviors for inherited methods.
14. Difference between Abstract class and Interface
Abstract Class: An abstract class can have both abstract (without implementation)
and concrete (with implementation) methods. It allows you to define default
behaviors while also forcing subclasses to implement certain methods. An abstract
class can also have member variables, constructors, and any other standard class
functionality.
Interface: An interface is purely a contract, meaning it can only declare abstract
methods (although in Java 8, it can also contain default and static methods with
implementation). Interfaces cannot have constructors or member variables (except
static final variables). Classes implement interfaces rather than extend them.
15. Can private method or static methods be overridden in Java?
Private Methods: Private methods cannot be overridden because they are not visible
to subclasses. They are bound to the class in which they are defined and cannot be
accessed outside of that class.
Static Methods: Static methods can be redefined in subclasses (a form of hiding),
but they cannot be overridden in the true sense of polymorphism. Overriding
requires dynamic dispatch, which only applies to instance methods, not static
methods.
16. Can the main() method be overloaded?
Yes, the main() method can be overloaded. The standard main() method signature is
public static void main(String[] args), but you can define other main() methods
with different parameter lists, such as public static void main(int[] args) or
public static void main(String[] args, int number). However, the JVM will always
call the main method with the standard signature when starting a Java application.

17. Can Abstract class have a main method?


Yes, an abstract class can have a main() method. Although an abstract class cannot
be instantiated directly, the main() method is just a static method and can be
called from any other class, including abstract classes. The main() method in an
abstract class can serve as an entry point for running some common code for all
subclasses.

18. What is Serialization and Deserialization?


Serialization is the process of converting an object's state into a byte stream so
that it can be easily saved to a file, sent over a network, or stored in a
database. Serialization is achieved in Java using the Serializable interface.
Deserialization is the reverse process, where a byte stream is converted back into
an object. Deserialization reconstructs the object with the same state it had
during serialization.
19. Use of transient keyword?
The transient keyword is used in Java to indicate that a particular field should
not be serialized. If a field is marked as transient, it will not be included in
the serialized representation of an object. This is useful for fields that are
either temporary or sensitive (such as passwords) and should not be saved or
transferred.

20. Is it possible to serialize a class if its superclass is not serializable?


Yes, it is possible to serialize a class if its superclass is not serializable, as
long as the class itself implements the Serializable interface. However, if the
superclass contains non-serializable fields, those fields will not be serialized
unless they are explicitly marked as transient.

21. Can Uninitialized non-serialized, non-transient fields still be tolerated?


Yes, uninitialized non-serializable, non-transient fields can be tolerated during
serialization. These fields are not serialized, and their default values will be
used upon deserialization. For instance, if a field is of type int, it will be
initialized to 0 during deserialization.

22. What is Marker Interface?


A Marker Interface is an interface with no methods or fields. Its primary purpose
is to signal to the JVM or other tools that a class implementing this interface has
special properties or behavior. For example, Serializable and Cloneable are marker
interfaces in Java. By implementing these interfaces, a class indicates that it can
be serialized or cloned, respectively.

23. What is Shallow Copy and Deep Copy?


Shallow Copy: A shallow copy creates a new object, but the elements of the original
object are not copied deeply. Instead, it copies references to the original
objects, meaning changes to mutable objects in the copied object will also affect
the original.
Deep Copy: A deep copy creates a new object and also copies the objects that the
original object references. This means that the original and copied objects are
completely independent of each other.
24. Difference between Error and Exception?
Error: Errors are abnormal conditions that usually occur in the JVM or hardware and
are generally beyond the control of the application. Examples include
OutOfMemoryError or StackOverflowError.
Exception: Exceptions are conditions that an application can catch and handle. They
are used to handle runtime issues such as invalid user input or failed file
operations. Exceptions can be caught and dealt with using try-catch blocks.
25. Checked vs Unchecked Exception?
Checked Exception: A checked exception is an exception that the compiler forces you
to handle either by catching it in a try-catch block or by declaring it in the
method signature using the throws keyword. Examples include IOException and
SQLException.
Unchecked Exception: Unchecked exceptions are not checked at compile time. They
extend RuntimeException and can be thrown during the program's execution without
being explicitly caught or declared. Examples include NullPointerException and
ArrayIndexOutOfBoundsException.

26. Create Custom Exception in Java


In Java, creating a custom exception involves extending one of the existing
exception classes, typically Exception or RuntimeException. A custom exception
allows you to define specialized error-handling behavior tailored to your
application's needs. Here's an example of creating a custom exception:

java
Copy
public class InvalidAgeException extends Exception {
public InvalidAgeException(String message) {
super(message);
}
}
You can throw this exception in your code like this:

java
Copy
if (age < 18) {
throw new InvalidAgeException("Age must be 18 or older.");
}
27. What is Runtime Exception?
A RuntimeException in Java is a type of unchecked exception that occurs during the
program's execution. These exceptions are not checked by the compiler, meaning the
programmer is not required to handle them explicitly. They typically represent
programming bugs, such as NullPointerException, ArrayIndexOutOfBoundsException, and
IllegalArgumentException. These exceptions can be avoided through proper validation
and code design, but they don’t need to be declared in the method signature or
caught in a try-catch block.

28. How does JVM handle Exception?


The Java Virtual Machine (JVM) handles exceptions by looking at the exception
hierarchy and checking whether the exception can be caught by any catch block. If
an exception is thrown:

The JVM starts searching for a matching catch block starting from the current
method.
If no matching catch block is found, the exception is propagated to the calling
method.
If the exception reaches the main() method or any method without a catch block, the
JVM terminates the program, and the exception details are printed to the standard
error stream.
29. Difference between Final, Finalize, and Finally
final: The final keyword can be applied to variables, methods, and classes. A final
variable’s value cannot be changed once assigned, a final method cannot be
overridden, and a final class cannot be subclassed.
finalize(): The finalize() method is a method in the Object class that is called by
the garbage collector before an object is destroyed. It gives an object a chance to
clean up resources, like closing file streams, before it is garbage collected.
finally: The finally block is used to execute code that must run regardless of
whether an exception is thrown or not. It follows a try-catch block and is used for
cleanup tasks, like closing file resources or releasing database connections.
30. Super class of all exceptions
The superclass of all exceptions in Java is the Throwable class. All exceptions and
errors in Java either directly or indirectly inherit from Throwable. The two main
subclasses of Throwable are Exception (for exceptions that a program can catch and
handle) and Error (for serious issues that typically cannot be handled by programs,
such as OutOfMemoryError).

31. Is Throwable an interface?


No, Throwable is not an interface. It is a class in Java, and all errors and
exceptions inherit from it. Throwable is the superclass for both Exception and
Error.

32. When Finally block doesn’t get executed?


The finally block will not execute if:

The JVM exits or crashes during the execution of the try or catch blocks (for
example, due to a System.exit() call or an unrecoverable Error).
The thread executing the finally block is interrupted before it finishes its
execution.
A fatal exception (like OutOfMemoryError) occurs that causes the JVM to terminate
unexpectedly.
33. Can subclass throw higher checked exception than base class?
No, a subclass cannot throw a checked exception that is broader or more general
than the checked exceptions declared by the base class. If a superclass method
declares a specific checked exception (like IOException), the subclass method
cannot throw a more general exception (like Exception) without causing a
compilation error. However, it can throw a subclass of the original exception.

34. Can we throw an unchecked exception in child class if parent class doesn’t
throw any exception?
Yes, in Java, you can throw unchecked exceptions (RuntimeException or its
subclasses) in a method of the subclass, even if the parent class doesn’t declare
or throw any exceptions. Unchecked exceptions do not require a throws declaration
in the method signature.

35. Difference between throw and throws()


throw: The throw keyword is used to explicitly throw an exception in Java. You can
throw both checked and unchecked exceptions using this keyword. For example:
java
Copy
throw new IllegalArgumentException("Invalid argument");
throws: The throws keyword is used in a method signature to declare that a method
might throw an exception. It is followed by the exception type(s) that the method
might throw. For example:
java
Copy
public void readFile() throws IOException {
// method implementation
}
36. Usage of Enum in Java
Enum is a special class in Java that represents a group of constants (unchangeable
variables). Enums provide type safety, meaning you can ensure that only valid
predefined values are used. They can have methods, constructors, and fields, and
they provide a way to define sets of related constants. For example:

java
Copy
enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}
Enums are particularly useful for representing fixed sets of constants like days of
the week, months, and states in a state machine.

37. How does Garbage Collection in Java work?


Garbage Collection (GC) in Java is the process of automatically freeing up memory
by removing objects that are no longer in use. The JVM’s garbage collector
identifies objects that are no longer reachable from any references in the program
and reclaims their memory. The garbage collector typically works in the background,
running when the system is under memory pressure or when triggered explicitly. It
uses various algorithms, such as mark-and-sweep and generational GC, to efficiently
manage memory. There is no guarantee when garbage collection will occur, but it's
an important feature for memory management in Java.

38. Array vs ArrayList


Array: An array is a fixed-size data structure that can store elements of the same
type. Once an array is created, its size cannot be changed. Arrays are faster and
use less memory because of their fixed nature.
ArrayList: An ArrayList is a resizable array implementation of the List interface
in Java. It allows dynamic resizing, meaning elements can be added or removed at
runtime. ArrayList uses more memory than arrays because of this flexibility, but it
provides better usability when dealing with dynamic data.
39. ArrayList vs LinkedList – When to use which collection?
ArrayList: It is backed by an array, making it efficient for random access
(constant time complexity O(1)) but inefficient for insertions and deletions (which
can take O(n) time). Use an ArrayList when you need fast access and minimal
modification to the collection.
LinkedList: It is a doubly linked list implementation, so insertion and deletion
are efficient (constant time complexity O(1)), but random access takes linear time
(O(n)). Use LinkedList when you need frequent insertions and deletions.
40. Fail-Safe vs Fail-Fast Iterators
Fail-Fast: These iterators immediately throw a ConcurrentModificationException if
the collection is modified while being iterated over. This helps prevent
inconsistent behavior when collections are modified during iteration.
Fail-Safe: Fail-safe iterators, such as those provided by CopyOnWriteArrayList,
allow modification of the collection while iterating without throwing an exception.
These iterators work on a copy of the collection, so modifications don’t affect the
iteration.
41. What is ConcurrentModificationException?
ConcurrentModificationException is thrown when an object is modified concurrently
while it is being iterated. This typically happens when an element is added or
removed from a collection while another thread is iterating over it, or when an
iterator is used to modify the collection while it's being iterated in a single-
threaded environment.

42. Internal working of HashMap


A HashMap stores key-value pairs and uses hashing to quickly access elements.
Internally, it uses an array of buckets, each bucket containing a linked list or a
tree (if the list becomes too long). The key is hashed to generate an index, and
the corresponding bucket is checked for the key. If the key is not found, a new
entry is added; if the key is found, the value is updated. HashMap provides O(1)
time complexity for insertion, deletion, and retrieval, on average.
43. Java 8 changes to HashMap
In Java 8, several improvements were made to HashMap. One of the most notable
changes is the introduction of balanced trees. If a HashMap bucket's linked list
becomes too long (i.e., the number of elements in a bucket exceeds a threshold),
the list is transformed into a balanced Red-Black Tree, improving the time
complexity for operations from O(n) to O(log n) in the worst case. Additionally,
Java 8 introduced the forEach, removeIf, and replaceAll methods for more
functional-style operations on Map collections.

44. Why HashMap contains null key?


In Java, HashMap allows one null key. This is because HashMap uses the hashCode()
method to determine the position of keys in the internal array, and null does not
have a hashCode(). When a null key is inserted, the HashMap handles it specially,
storing it at a designated bucket (usually the first one). However, it is important
to note that a TreeMap does not allow null keys, as it relies on natural ordering.

45. Is it mandatory to have key immutable in HashMap?


No, it is not mandatory for keys in a HashMap to be immutable. However, it is
highly recommended to use immutable objects as keys, because if a key object is
modified after being inserted into the map, it may alter its hash code. This could
lead to inconsistencies in the map, as the HashMap relies on the hash code to
locate the key. Immutable objects ensure that the hash code remains consistent
during the lifetime of the map entry.

46. Why override equals() and hashCode() method?


It is essential to override both equals() and hashCode() methods when working with
custom objects as keys in a HashMap. The hashCode() method is used to compute the
bucket location, and the equals() method is used to compare keys within the same
bucket. If both methods are not properly overridden, you may face issues such as
incorrect retrieval, updates, or deletions of elements, as the HashMap may fail to
correctly identify an object.

47. HashSet vs LinkedHashSet vs TreeSet


HashSet: A HashSet is an implementation of the Set interface backed by a hash
table. It does not guarantee any specific order of elements. It is the fastest in
terms of operations like add, remove, and contains because it uses hashing.
LinkedHashSet: This implementation is similar to HashSet, but it maintains the
order of elements based on the order in which they were inserted. The order is
maintained using a doubly-linked list.
TreeSet: A TreeSet implements the Set interface and uses a Red-Black tree to store
elements. It maintains elements in a sorted order according to their natural
ordering or by a comparator provided during creation. The operations add, remove,
and contains are O(log n) in TreeSet.
48. What is the Internal Data Structure in TreeMap? How the elements are sorted?
Internally, a TreeMap uses a Red-Black Tree, which is a self-balancing binary
search tree. The elements are stored in sorted order based on the natural ordering
of the keys (if they implement Comparable) or according to a custom Comparator
provided at the time of creation. This ensures that the keys are always sorted, and
operations such as insertion, deletion, and retrieval are done in O(log n) time
complexity.

49. HashMap vs ConcurrentHashMap


HashMap is not thread-safe, which means if multiple threads try to modify the
HashMap concurrently, it may lead to inconsistent states or data corruption.
ConcurrentHashMap, on the other hand, is designed to be thread-safe. It allows
multiple threads to read and write concurrently without locking the entire map. It
divides the map into segments (or buckets), and each segment is independently
locked, allowing for better concurrency. For thread safety, ConcurrentHashMap uses
fine-grained locks, rather than locking the entire map, making it more efficient
for concurrent access.
50. Comparable vs Comparator
Comparable: The Comparable interface is used to define a natural ordering for
objects of a class. It requires the implementation of the compareTo() method. This
interface is used when the comparison logic is intrinsic to the class, such as
comparing Integer objects by value or String objects alphabetically.
Comparator: The Comparator interface is used to define an external comparison logic
for objects. It requires the implementation of the compare() method and can be used
when you need to sort objects in a manner that is not their natural ordering or to
provide multiple different sorting strategies for a class.
51. What is Blocking Queue?
A BlockingQueue is a type of queue in Java where operations like take() (removal of
an element) and put() (insertion of an element) block the calling thread if the
queue is empty or full, respectively. This behavior makes it useful in scenarios
such as producer-consumer problems, where a producer thread may wait for space to
insert elements, and a consumer thread may wait for elements to consume. Examples
of BlockingQueue implementations include ArrayBlockingQueue and
LinkedBlockingQueue.

52. What is Vector? When to use it?


A Vector is a dynamic array-like data structure in Java that implements the List
interface. Unlike an ArrayList, which grows dynamically, a Vector doubles its size
when more space is needed. It is thread-safe because all of its methods are
synchronized, but this comes at the cost of performance. Vector should be used when
thread safety is required and when the application does not need to worry about
synchronization mechanisms at a higher level.

53. MultiThreading vs MultiProcessing vs MultiProgramming vs MultiTasking


Multithreading refers to the ability of a CPU or a single core to execute multiple
threads concurrently. Threads are the smallest unit of execution within a process.
Multiprocessing refers to the simultaneous use of more than one CPU or core to
execute multiple processes concurrently.
Multiprogramming is an older concept where multiple programs are loaded into
memory, and the CPU switches between them based on availability, giving the
illusion of simultaneous execution.
Multitasking is the concurrent execution of multiple tasks or processes, whether on
a single processor (via context switching) or on multiple processors. It can be
cooperative or preemptive.
54. Life Cycle of a Thread
The life cycle of a thread in Java involves several states:

New: A thread is created but not yet started.


Runnable: A thread is ready to run but is waiting for CPU time.
Blocked: A thread is blocked due to waiting for a resource or lock.
Waiting: A thread is waiting indefinitely for another thread to perform a
particular action (e.g., waiting for input).
Timed Waiting: A thread is waiting for a specified period (e.g., using sleep() or
join()).
Terminated: A thread has completed its execution or has been killed.
55. Extends vs Runnable
Extending the Thread class: You can create a thread by subclassing the Thread class
and overriding its run() method. This approach is simpler but limits you to only
one inheritance path (since Java does not support multiple inheritance).
Implementing the Runnable interface: By implementing the Runnable interface and
overriding its run() method, you can create a thread. This approach is preferred as
it allows you to extend another class, making it more flexible.
56. yield() vs sleep() vs join()
yield(): The yield() method suggests to the thread scheduler that the current
thread is willing to yield its current use of CPU resources. However, it is only a
suggestion, and the thread might continue running if the scheduler doesn't choose
to allow a context switch.
sleep(): The sleep() method makes the current thread sleep for a specified amount
of time, releasing the CPU for other threads. Unlike yield(), sleep() guarantees
that the thread will not execute during its sleep time.
join(): The join() method allows one thread to wait for the completion of another.
When t.join() is called, the calling thread is paused until thread t finishes
execution.

57. wait() vs sleep()?


sleep(): The sleep() method is used to pause the execution of the current thread
for a specified amount of time (milliseconds or nanoseconds). This method is static
and does not release any locks or monitors that the thread might be holding when it
sleeps. It simply suspends the execution of the thread for the given duration.
wait(): The wait() method is used in inter-thread communication. A thread invokes
wait() on an object to release the lock it holds and go into the waiting state
until another thread calls notify() or notifyAll() on the same object. The
difference is that wait() can only be called within a synchronized block or method,
as it needs to release the object’s lock.
58. Why is join() method used?
The join() method is used to ensure that one thread waits for the completion of
another thread. When a thread calls join() on another thread, the calling thread is
paused until the thread it called join() on finishes execution. For example, if
thread A calls join() on thread B, thread A will wait until thread B completes
before continuing its execution.

59. Can we override start() method in Thread?


No, the start() method cannot be overridden in Java. The start() method is
responsible for invoking the run() method in a new thread of execution. Overriding
the start() method can cause unexpected behavior and confusion, as start() is
tightly coupled with thread creation and control. Instead, you should override the
run() method to define the code that will execute in the new thread.

60. Can we override run() method?


Yes, the run() method can be overridden in a custom Thread class. The run() method
contains the code that will execute in a new thread. If you extend the Thread
class, you should override the run() method with the code you want to execute in
the new thread. Alternatively, if you implement the Runnable interface, the run()
method should be implemented.

61. Can we start the thread twice?


No, you cannot start a thread twice. Once a thread has been started using the
start() method, it cannot be restarted. Attempting to call start() on a thread that
has already been started will result in an IllegalThreadStateException. If you want
to run the same task multiple times, you should create a new instance of the thread
each time.

62. What is IllegalThreadStateException?


IllegalThreadStateException is thrown when an operation is attempted on a thread
that is not allowed in its current state. A common example is calling start() on a
thread that has already been started or trying to start a thread after it has
finished executing.

63. What happens if run() method is called without start()?


If the run() method is called directly without calling start(), the thread will not
execute in a separate thread of execution. Instead, it will be executed as a normal
method call in the current thread, just like any other method. This defeats the
purpose of threading because it does not create a new thread.

64. Why do we use ThreadPool?


A ThreadPool is used to manage a group of worker threads, avoiding the overhead of
creating and destroying threads frequently. Thread pools can reuse threads from a
pool to perform multiple tasks, which is much more efficient than creating a new
thread each time a task needs to be executed. Thread pools also help with
controlling the number of threads running concurrently, making it easier to manage
resources and prevent overloading.

65. What is Race Condition?


A race condition occurs when two or more threads attempt to update shared data or
resources concurrently, leading to unpredictable and undesirable results. This
happens when proper synchronization mechanisms (such as locks or atomic operations)
are not applied to shared resources. For example, if two threads simultaneously
modify a variable without synchronization, the final value might be incorrect due
to lost updates or inconsistent state.

66. What is Synchronisation? Types of Synchronisation?


Synchronization in Java is a mechanism to control access to shared resources by
multiple threads. It ensures that only one thread can access a resource at a time,
preventing data corruption and inconsistent results. The two primary types of
synchronization are:

Method-Level Synchronization: This is achieved by using the synchronized keyword on


the method. Only one thread can execute the synchronized method at any time on a
particular object.
Block-Level Synchronization: This is done using synchronized blocks inside methods.
A thread can acquire the lock for a specific block of code, allowing more granular
control over synchronization.
67. Object Level Locking vs Class Level Locking?
Object Level Locking: When a synchronized method or block is applied to an instance
method, the lock is on the instance (object) of the class. This means only one
thread can access a particular instance method of an object at a time.
Class Level Locking: When synchronization is applied to a static method, the lock
is on the class itself (not on a specific instance). This means that only one
thread can access a static synchronized method for the entire class, regardless of
the number of object instances.
68. If there are two synchronized methods m1 and m2 in a class, can two different
threads t1 and t2 call different methods (m1, m2) respectively on the same object
of class c at the same time?
No, if both m1 and m2 are synchronized instance methods in the same class, two
different threads cannot call them at the same time on the same object. This is
because the synchronized methods require a lock on the object, and only one thread
can hold the lock at a time. Therefore, if one thread is executing m1, the other
thread will be blocked from executing m2 until the first thread releases the lock.

69. If a class has a synchronized method and a non-synchronized method, can


multiple threads execute the non-synchronized method?
Yes, multiple threads can execute non-synchronized methods simultaneously. The
synchronization mechanism only affects synchronized methods, and non-synchronized
methods can be accessed by any number of threads concurrently without any locking
mechanism, unless there is another form of external synchronization.

70. Can two threads call two different static synchronized methods of the same
class?
No, two threads cannot call two different static synchronized methods of the same
class at the same time. This is because the lock on a static synchronized method is
at the class level, not at the instance level. When one thread acquires the lock on
a static synchronized method, no other thread can access any other static
synchronized method of the same class.

71. Does static synchronized methods block non-synchronized methods?


No, static synchronized methods do not block non-synchronized methods. Static
synchronized methods lock the class object, whereas non-synchronized methods are
not locked, meaning threads can execute them concurrently, even if other static
synchronized methods are executing.

72. Can Constructors be synchronized?


No, constructors cannot be synchronized in Java. This is because a constructor is
not a method, and synchronization applies to methods. Moreover, synchronizing
constructors would be problematic, as a constructor is invoked when an object is
being created, and it is difficult to guarantee that synchronization will work
correctly during object construction.

73. What is DeadLock?


Deadlock is a situation in concurrent programming where two or more threads are
blocked indefinitely because each thread is holding a resource and waiting for the
other to release the resource. This typically occurs when two or more threads
acquire locks on multiple objects in a way that forms a circular dependency.

74. What is Inter-thread communication? Explain wait(), notify() and notifyAll()?


Inter-thread communication allows threads to cooperate by communicating and
synchronizing with each other. The three primary methods for inter-thread
communication are:

wait(): Causes the current thread to release the lock and enter the waiting state
until another thread sends a signal (via notify() or notifyAll()).
notify(): Wakes up a single thread that is waiting on the lock. If multiple threads
are waiting, one of them will be chosen to resume execution.
notifyAll(): Wakes up all threads that are waiting on the lock, allowing all of
them to compete for the lock.
75. What is IllegalMonitorStateException?
IllegalMonitorStateException is thrown if a thread tries to call wait(), notify(),
or notifyAll() without holding the monitor (lock) of the object. These methods must
be called within a synchronized block or method.

76. Which class does wait(), notify(), and notifyAll() method belong?
The methods wait(), notify(), and notifyAll() belong to the Object class in Java.
These methods are used for inter-thread communication, and since all Java classes
inherit from the Object class, they can be used on any object in Java. However,
they must be called from within a synchronized block or method to ensure that the
thread has acquired the monitor (lock) of the object.

77. Explain a few Thread class methods. Is sleep() a method in Thread class or
Object class?
Thread.sleep(long millis): This static method makes the current thread sleep for
the specified number of milliseconds, effectively pausing its execution
temporarily. It is a static method in the Thread class.
Thread.start(): This method is used to start the thread, which in turn calls the
run() method of the thread. It transitions the thread from the new state to the
runnable state.
Thread.join(): This method makes the current thread wait until the thread it is
called on completes its execution.
Thread.isAlive(): This method checks whether the thread is alive or has finished
executing.
The sleep() method is part of the Thread class, not the Object class.
78. Producer-Consumer Problem in Java?
The Producer-Consumer problem is a classic example of multi-threaded
synchronization. It involves two types of threads: Producers, which produce items
and place them in a shared buffer, and Consumers, which consume those items from
the buffer. The challenge lies in synchronizing the operations to avoid issues like
buffer overflow (when the producer tries to add items to a full buffer) or
underflow (when the consumer tries to consume from an empty buffer). This problem
can be solved using mechanisms like wait(), notify(), and notifyAll() for inter-
thread communication.

79. Volatile vs Synchronized?


Volatile: The volatile keyword ensures that changes to a variable are visible to
all threads. It guarantees that when one thread changes the value of a volatile
variable, other threads will immediately see the updated value. It is useful for
flags or state indicators.
Synchronized: Synchronization ensures that only one thread can access a particular
block of code or method at a time, thus preventing issues with shared resources. It
is used for ensuring mutual exclusion and preventing race conditions, especially in
cases where multiple threads modify shared data.
80. What are Atomic variables?
Atomic variables are variables that are updated atomically, meaning that their
update happens as a single, indivisible operation, without interference from other
threads. Java provides atomic classes like AtomicInteger, AtomicLong, etc., in the
java.util.concurrent.atomic package. These classes allow for safe, lock-free
manipulation of variables, providing a simpler alternative to synchronization.

81. Runnable vs Callable?


Runnable: The Runnable interface represents a task that can be executed by a
thread. It has a single method run(), which contains the code to be executed. It
cannot return a result or throw checked exceptions.
Callable: The Callable interface is similar to Runnable, but it can return a result
or throw an exception. It is used when the task has a return value. The call()
method returns a result, and it can throw exceptions, which makes it more versatile
than Runnable.
82. What is Future Object?
A Future object represents the result of an asynchronous computation. It provides
methods to check if the computation is complete, to retrieve the result of the
computation, or to cancel the task. Future is returned by the submit() method of an
ExecutorService, allowing you to track the execution of a task and obtain the
result once it is done.

83. What is CompletableFuture?


A CompletableFuture is an extension of Future that allows you to write non-blocking
asynchronous code. It provides methods for handling the result of asynchronous
computations, chaining actions to run when the result is ready, and combining
multiple asynchronous tasks. It also supports handling exceptions, and it is part
of the java.util.concurrent package in Java 8 and beyond.

84. Use of Done(), IsCancelled(), and Cancel() method of Future Object?


done(): The done() method indicates whether the task has been completed, either by
finishing execution or by being canceled.
isCancelled(): This method returns true if the task was canceled before it
completed.
cancel(): The cancel() method attempts to cancel the execution of the task. If the
task is already completed, the method has no effect.
85. Explain ThreadLocal class.
The ThreadLocal class provides thread-local variables, which means that each thread
accessing the variable gets its own independent copy. The value stored in a
ThreadLocal variable is isolated to the thread that set it, which is useful when
you want to avoid sharing variables across threads. This is commonly used for
things like session data or database connections where each thread requires a
separate copy.

86. What is CountDownLatch?


A CountDownLatch is a synchronization tool that allows one or more threads to wait
until a set of operations in other threads have completed. The latch is initialized
with a count, and each thread performing a task calls the countDown() method to
decrease the count. Other threads can call await() to block until the count reaches
zero, indicating that all tasks are finished.

87. What is CyclicBarrier?


A CyclicBarrier is a synchronization aid that allows a set of threads to wait for
each other to reach a common barrier point. Once all threads have reached the
barrier, they can proceed together. Unlike CountDownLatch, a CyclicBarrier can be
reused after the barrier is tripped, making it "cyclic" in nature.

88. What is Reentrant Lock?


A ReentrantLock is a type of lock that allows the same thread to acquire it
multiple times without causing a deadlock. It is part of the
java.util.concurrent.locks package. Reentrant locks provide advanced lock
mechanisms like fairness, interruptibility, and the ability to try to acquire the
lock with a timeout. It is called "reentrant" because the thread that holds the
lock can re-enter the lock (re-lock) without any issues.

89. ExecutorService.submit() vs Executor.execute()?


submit(): The submit() method is used to submit a task for execution and returns a
Future object, which can be used to check the task's status or retrieve the result
once the task is completed. It can accept Runnable, Callable, and return values.
execute(): The execute() method is used to submit a task for execution, but it does
not return a result or a Future object. It is a simpler form for submitting tasks
that do not require a result or exception handling.
90. Different types of ThreadExecutor Services?
FixedThreadPool: A pool with a fixed number of threads that reuse existing threads
for executing tasks. If all threads are busy, additional tasks wait in a queue.
CachedThreadPool: A pool that creates new threads as needed, but will reuse
previously constructed threads when they are available. Ideal for handling many
short-lived asynchronous tasks.
SingleThreadExecutor: A pool that ensures that only one thread will execute tasks
sequentially. It is useful for tasks that must be executed in a specific order.
ScheduledThreadPoolExecutor: A pool designed for scheduling tasks with a fixed rate
or with a delay, useful for recurring tasks or delayed executions.
91. Explain how FixedThreadPool executor works?
A FixedThreadPool executor creates a fixed number of threads, which are reused to
execute submitted tasks. If all threads are busy, additional tasks are placed in a
queue until a thread becomes available. This executor is useful when you know the
maximum number of concurrent threads required and want to limit the number of
threads to avoid resource exhaustion.

92. Interface8 changes


Java 8 introduced several significant changes in interfaces:

Default Methods: Java 8 allows interfaces to have method implementations using the
default keyword. This enables backward compatibility by allowing new methods to be
added to interfaces without breaking existing implementations.
Static Methods: Interfaces in Java 8 can also have static methods, which can be
called using the interface name. These methods are not inherited by implementing
classes.
Functional Interfaces: Java 8 encourages the use of functional interfaces, which
have a single abstract method. This supports lambda expressions and makes it easier
to pass behavior as parameters.
93. What is Functional Interface? Why do we need it?
A Functional Interface is an interface with just one abstract method. Functional
interfaces are essential for enabling lambda expressions and method references in
Java. They provide a way to pass behavior around, making code more concise and
readable. Examples include Runnable, Comparator, and Callable. The
@FunctionalInterface annotation is optional but helps ensure that the interface
follows the rules of a functional interface.

94. Difference between Collection and Stream


Collection: A Collection is a group of objects, and it's part of the Java
Collections Framework. It provides various methods to store, manipulate, and
retrieve objects. Collections are usually used for data storage.
Stream: A Stream is an abstraction that represents a sequence of elements
supporting parallel and sequential aggregation operations. Streams allow you to
perform functional-style operations (such as map, filter, reduce) on a collection
of elements. Streams are not data structures but are used to process data from
collections in a more functional and declarative manner.
95. What is Terminal Operator vs Intermediate operators?
Intermediate Operators: These are operations that transform a stream into another
stream. They are lazy and do not execute until a terminal operation is invoked.
Examples include map, filter, distinct, etc.
Terminal Operators: These are operations that produce a result or a side-effect and
terminate the stream pipeline. Examples include collect, forEach, reduce, etc. Once
a terminal operator is invoked, the stream is consumed, and it can no longer be
used.
96. What is Optional?
An Optional is a container object used to represent the presence or absence of a
value. Instead of returning null, a method can return an Optional, allowing the
caller to explicitly handle the case when a value is missing. It helps avoid
NullPointerExceptions and provides methods like isPresent(), ifPresent(), orElse(),
and map() to safely deal with optional values.

97. FlatMap vs Map


map(): The map() function is used to transform each element of the stream using the
provided function. It produces a stream of the transformed elements, where each
input element maps to one output element.
flatMap(): The flatMap() function is similar to map(), but it flattens the
resulting streams into a single stream. This is useful when the transformation
produces multiple elements for each input element (like a list of lists that you
want to flatten).
98. Difference between Parallel Sort vs Sort
Sort: Traditional sorting algorithms work on a single thread and sort the elements
sequentially. The Arrays.sort() method is an example of this.
Parallel Sort: In parallel sorting, the collection is divided into multiple parts
and sorted in parallel across multiple threads. This method can improve performance
on large datasets by taking advantage of multiple CPU cores. Java provides
Arrays.parallelSort() to achieve this.
99. Difference between Predicate vs BiPredicate?
Predicate: A Predicate is a functional interface that takes a single argument and
returns a boolean value. It is used for evaluating conditions. It has a method
test(T t).
BiPredicate: A BiPredicate is a functional interface that takes two arguments and
returns a boolean value. It is used when the condition involves two inputs. It has
a method test(T t, U u).
100. How Diamond problem is solved in Java 8?
The Diamond Problem occurs when two classes have a common ancestor, and a subclass
inherits from both classes. In Java, interfaces can have default methods, and if a
class implements multiple interfaces with conflicting default methods, the compiler
will throw an error. Java 8 resolves this by allowing classes to provide their own
implementations of conflicting default methods. This approach provides a resolution
by explicitly overriding the conflicting methods.

101. Difference between JDK, JRE, and JVM


JDK (Java Development Kit): JDK is a complete software development kit for Java
developers. It includes the JRE, compilers, debuggers, and other tools necessary to
develop Java applications.
JRE (Java Runtime Environment): JRE provides the libraries, Java Virtual Machine
(JVM), and other components to run Java applications. It does not include
development tools like compilers.
JVM (Java Virtual Machine): JVM is an abstract computing machine that enables Java
programs to run on any device or operating system. It interprets compiled Java
bytecode and executes it on the host machine.
102. What is Immutable class?
An Immutable class is a class whose instances cannot be changed once they are
created. The fields of an immutable class are final, and no setter methods are
provided. Common examples in Java include the String class, where the state of the
object cannot be modified after it is created. Immutable classes are thread-safe
and can be shared freely among multiple threads without synchronization.

103. What are SOLID principles?


The SOLID principles are a set of five design principles that help software
developers create more understandable, flexible, and maintainable systems. They
include:

S: Single Responsibility Principle (SRP) – A class should have one reason to


change, meaning it should have only one job or responsibility.
O: Open/Closed Principle (OCP) – Software entities (classes, modules, functions)
should be open for extension but closed for modification.
L: Liskov Substitution Principle (LSP) – Objects of a superclass should be
replaceable with objects of a subclass without affecting the functionality of the
program.
I: Interface Segregation Principle (ISP) – Clients should not be forced to depend
on interfaces they do not use.
D: Dependency Inversion Principle (DIP) – High-level modules should not depend on
low-level modules. Both should depend on abstractions.
104. Difference between ClassNotFound vs NoClassDefError
ClassNotFoundException: This exception occurs when the JVM cannot find a class at
runtime, even though it was available at compile time. This is typically due to a
missing class or an incorrect classpath.
NoClassDefFoundError: This error occurs when a class was available at compile time
but is no longer available at runtime. It could happen if the class was removed or
if the classpath changed.
105. What is Singleton Design Pattern? Explain ThreadSafe Singleton and Bill Pugh
Singleton?
Singleton Design Pattern: This is a design pattern that ensures a class has only
one instance and provides a global point of access to that instance. It prevents
the creation of multiple instances of the class.
Thread-Safe Singleton: In this approach, synchronization is used to ensure that
only one thread can create the instance. Common implementations include double-
checked locking or using synchronized keyword.
Bill Pugh Singleton: This approach uses the Bill Pugh Singleton Design that
leverages a static inner helper class to implement a thread-safe Singleton. The
instance is created when the helper class is loaded, which guarantees lazy
initialization without synchronization overhead.
106. How to break Singleton?
Singleton patterns can be broken using techniques like:
Reflection: By using reflection, we can create a new instance of the Singleton
class even if it has a private constructor.
Serialization and Deserialization: A Singleton instance can be broken if the class
is serializable. Upon deserialization, a new instance of the Singleton class can be
created.
Cloning: If the clone() method is overridden, it can create a new instance of the
Singleton class.

107. Explain few features in each Java versions starting from Java 8
Java 8:

Lambda Expressions: Introduced to provide a clear and concise way to express


instances of single-method interfaces (functional interfaces).
Stream API: Introduced to enable functional-style operations on streams of data,
allowing for more concise code and easier parallelization.
Default Methods: Interfaces can now have method implementations, which helps in
adding methods to interfaces without breaking existing implementations.
Optional Class: Provides a container object to avoid NullPointerExceptions by
representing optional values in a more explicit way.
New Date and Time API: Replaced the old Date and Calendar classes with java.time
for better date and time handling.
Java 9:

Module System: Introduced the Java Platform Module System (JPMS), which helps
modularize the codebase for better encapsulation and separation of concerns.
JShell: Java introduced an interactive command-line tool that allows developers to
quickly test Java code snippets without needing a complete program.
Private Methods in Interfaces: Java 9 allows private methods in interfaces, helping
in code reuse inside interfaces.
Immutable Collections: Introduced convenience methods to create immutable
collections, which are useful for data that should not change.
Java 10:

Local-Variable Type Inference: Introduced var keyword to simplify variable


declarations by allowing the compiler to infer the type of local variables.
Garbage Collection Improvements: Added the G1 Garbage Collector as the default
collector for better performance and low-latency garbage collection.
Java 11:

Removed Java EE and CORBA Modules: Java 11 removed older modules related to Java EE
and CORBA, making the JDK leaner and more focused.
New String Methods: Added several new utility methods in the String class, such as
isBlank(), lines(), and strip().
HTTP Client API: Introduced a new HTTP Client API that supports HTTP/2 and
asynchronous requests, making HTTP communications easier.
Java 12:

Switch Expressions: Introduced as a preview feature, allowing for more flexible and
concise switch statements.
JVM Constants API: Java 12 added an API to model nominal descriptors for class file
constants to make code easier to understand.
Java 13:

Text Blocks (Preview): Java introduced a new way to write multi-line string
literals, making it easier to work with JSON, SQL queries, and other multiline
content.
Dynamic CDS Archives: This feature improves the class-data-sharing process,
speeding up application startup.
Java 14:

Records (Preview): Introduced records as a new type to model immutable data with
less boilerplate code.
Helpful NullPointerExceptions: Enhanced NullPointerException to provide more
specific information about which variable was null.
Java 15:

Hidden Classes: Introduced hidden classes to help implement dynamic languages more
efficiently.
Text Blocks: Text Blocks became a standard feature to simplify working with multi-
line strings.
Java 16:

JEP 394 - Strongly Encapsulate JDK Internals: Made it difficult for code outside
the JDK to use internal JDK APIs.
JEP 395 - Records: Records were finalized as a feature in Java 16, improving data
encapsulation.
Java 17 (Long-Term Support):

Sealed Classes: Introduced sealed classes to restrict which classes or interfaces


can extend or implement them.
Foreign Function and Memory API (Incubator): Allows Java programs to interact with
native code and memory more easily.

1. Singleton Pattern
The Singleton Pattern ensures that a class has only one instance and provides a
global point of access to that instance. This is useful when you want to limit the
number of instances of a class to just one, such as a database connection or a
configuration manager.

Key Characteristics:
Ensures a single instance of a class.
Provides a global access point to that instance.
Example:
java
Copy
public class Singleton {
// private static instance of the class
private static Singleton instance;

// private constructor to prevent instantiation


private Singleton() {}

// public method to provide access to the instance


public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // creating the instance lazily
}
}
}
return instance;
}
}
Explanation:
The Singleton class has a private static variable instance that holds the single
instance of the class.
The constructor is private to prevent instantiation from outside the class.
The getInstance() method checks if the instance is null. If so, it creates the
instance, ensuring that only one instance of the class exists (using double-checked
locking for thread safety).
2. Factory Method Pattern
The Factory Method Pattern defines an interface for creating an object, but it
allows subclasses to alter the type of objects that will be created. This is useful
when you want to delegate the instantiation of objects to subclasses or factories
rather than directly creating objects using the new keyword.

Key Characteristics:
Defines an interface for creating an object.
Subclasses decide which class to instantiate.
Example:
java
Copy
// Abstract product
public interface Product {
void create();
}

// Concrete product 1
public class ConcreteProduct1 implements Product {
@Override
public void create() {
System.out.println("Product 1 created");
}
}

// Concrete product 2
public class ConcreteProduct2 implements Product {
@Override
public void create() {
System.out.println("Product 2 created");
}
}

// Creator
public abstract class Creator {
public abstract Product factoryMethod();
}

// Concrete Creator 1
public class ConcreteCreator1 extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProduct1();
}
}

// Concrete Creator 2
public class ConcreteCreator2 extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProduct2();
}
}
Explanation:
The Creator class defines the factory method factoryMethod() that is responsible
for creating Product objects.
The ConcreteCreator1 and ConcreteCreator2 classes override the factoryMethod() to
instantiate different types of Product objects (like ConcreteProduct1 and
ConcreteProduct2).
This pattern allows for flexibility in creating products without knowing their
exact type.
3. Builder Pattern
The Builder Pattern is used to construct complex objects by separating the
construction process from the actual representation. It’s useful when an object
needs to be created with many optional parameters or requires multiple steps to
create.

Key Characteristics:
Separates the construction of a complex object from its representation.
Allows for more readable and flexible object construction.
Example:
java
Copy
// Product class
public class Product {
private String part1;
private String part2;
private String part3;

// Setters for the parts


public void setPart1(String part1) {
this.part1 = part1;
}

public void setPart2(String part2) {


this.part2 = part2;
}

public void setPart3(String part3) {


this.part3 = part3;
}

@Override
public String toString() {
return "Product [part1=" + part1 + ", part2=" + part2 + ", part3=" + part3
+ "]";
}
}

// Builder class
public class ProductBuilder {
private Product product;

public ProductBuilder() {
product = new Product();
}

public ProductBuilder buildPart1(String part1) {


product.setPart1(part1);
return this;
}

public ProductBuilder buildPart2(String part2) {


product.setPart2(part2);
return this;
}

public ProductBuilder buildPart3(String part3) {


product.setPart3(part3);
return this;
}

public Product build() {


return product;
}
}
Explanation:
The Product class represents the complex object that is being created.
The ProductBuilder class allows you to build the Product step by step using method
chaining.
This pattern is helpful for creating complex objects where setting each part might
involve multiple optional parameters.
Usage:

java
Copy
Product product = new ProductBuilder()
.buildPart1("Part1")
.buildPart2("Part2")
.buildPart3("Part3")
.build();
System.out.println(product);
4. Abstract Factory Pattern
The Abstract Factory Pattern provides an interface for creating families of related
or dependent objects without specifying their concrete classes. This pattern is
particularly useful when the system should be independent of how its products are
created or composed.

Key Characteristics:
Provides an interface for creating families of related or dependent objects.
Ensures that the system remains independent from how the objects are created.
Example:
java
Copy
// Abstract Product A
public interface ProductA {
void doSomethingA();
}

// Abstract Product B
public interface ProductB {
void doSomethingB();
}

// Concrete Product A1
public class ConcreteProductA1 implements ProductA {
@Override
public void doSomethingA() {
System.out.println("ProductA1 action");
}
}

// Concrete Product B1
public class ConcreteProductB1 implements ProductB {
@Override
public void doSomethingB() {
System.out.println("ProductB1 action");
}
}

// Abstract Factory
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}

// Concrete Factory 1
public class ConcreteFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA1();
}

@Override
public ProductB createProductB() {
return new ConcreteProductB1();
}
}
Explanation:
The abstract factory AbstractFactory defines methods for creating related products
(ProductA and ProductB).
The ConcreteFactory1 implements the abstract factory and creates instances of
concrete products (ConcreteProductA1 and ConcreteProductB1).
The Abstract Factory pattern is useful when you have different families of related
objects that need to be created together.
Usage:

java
Copy
AbstractFactory factory = new ConcreteFactory1();
ProductA productA = factory.createProductA();
ProductB productB = factory.createProductB();
productA.doSomethingA();
productB.doSomethingB();
Summary:
Singleton Pattern ensures a class has only one instance and provides a global
access point to that instance.
Factory Method Pattern allows subclasses to decide which type of object to
instantiate via an abstract method.
Builder Pattern separates the construction of a complex object from its
representation, making it more flexible and readable.
Abstract Factory Pattern provides an interface to create families of related
objects, ensuring that the system is independent of how objects are created.
These patterns help in creating maintainable, flexible, and extensible code. Let me
know if you need more details on any of these patterns!

You might also like