As one of the most widely used programming languages in enterprise software development, Java powers everything from web applications and microservices to Android apps and financial systems. Recruiters must identify developers skilled in Java syntax, OOP principles, data structures, and frameworks to build scalable, secure, and maintainable applications.
This resource, "100+ Java Interview Questions and Answers," is tailored for recruiters to simplify the evaluation process. It covers topics from Java fundamentals to advanced concepts, including collections, multithreading, design patterns, JVM internals, and frameworks like Spring and Hibernate.
Whether hiring for Backend Developers, Full-Stack Engineers, or Software Engineers, this guide enables you to assess a candidate’s:
- Core Java Knowledge: Understanding of data types, operators, control flow, classes, objects, inheritance, polymorphism, encapsulation, and abstraction.
- Advanced Skills: Expertise in collections framework, generics, exception handling, multithreading, synchronization, lambda expressions, streams API, and Java 8+ features.
- Real-World Proficiency: Ability to design scalable applications, use design patterns (Singleton, Factory, Observer, etc.), optimize memory and performance, and integrate with frameworks like Spring Boot, Hibernate, or build REST APIs.
For a streamlined assessment process, consider platforms like WeCP, which allow you to:
✅ Create customized Java assessments aligned to your project requirements and role seniority.
✅ Include hands-on coding tasks, such as implementing algorithms, designing OOP solutions, or building REST APIs with Spring Boot.
✅ Proctor assessments remotely with AI-powered integrity monitoring.
✅ Leverage automated grading to evaluate code correctness, performance, and adherence to Java best practices.
Save time, improve technical screening, and confidently hire Java professionals who can build robust, secure, and high-performance applications from day one.
Core Java Interview Questions
Beginner Level Questions
- What is Java?
- What are the main features of Java?
- Explain the difference between JDK, JRE, and JVM.
- What is the significance of the main method in Java?
- What is the difference between a class and an object in Java?
- What is an instance variable?
- What is a constructor in Java?
- What is the difference between == and equals() in Java?
- What is an array in Java, and how is it declared?
- Explain the concept of encapsulation in Java.
- What are the access modifiers in Java?
- What is method overloading?
- What is method overriding?
- What is the difference between final, finally, and finalize() in Java?
- What is the difference between ArrayList and LinkedList in Java?
- How does a for-each loop work in Java?
- What is the default value of an uninitialized variable in Java?
- What is a static variable? How is it different from an instance variable?
- What is a static method? Can you call a static method without creating an object?
- What is the difference between String, StringBuilder, and StringBuffer in Java?
- What is a package in Java?
- What is the use of the super keyword in Java?
- What is the difference between public, private, protected, and default access modifiers?
- What is an interface in Java?
- What is an abstract class in Java, and how is it different from an interface?
- What is polymorphism in Java?
- What is inheritance in Java?
- What is the difference between this and super in Java?
- What is the difference between throw and throws in Java?
- What is exception handling in Java?
- What is the try-catch block in Java?
- What is the difference between checked and unchecked exceptions?
- What are some common types of exceptions in Java?
- What is a NullPointerException?
- What is a ClassNotFoundException?
- What is the difference between String and StringBuilder in Java?
- What is a Java thread? How do you create a thread?
- What is the purpose of the wait() and notify() methods in Java?
- What is the purpose of the synchronized keyword in Java?
- What is a deadlock in Java?
Intermediate Level Questions
- What are the differences between ArrayList and Vector?
- What is the Collections framework in Java? Name some commonly used classes.
- What is the HashMap class in Java? How is it different from TreeMap?
- What is the difference between HashMap and Hashtable?
- How does garbage collection work in Java?
- What are weak references in Java?
- What is the role of the clone() method in Java?
- What is the difference between shallow copy and deep copy?
- What is a Singleton class? How can you create one in Java?
- What is the transient keyword in Java?
- What is the purpose of the volatile keyword in Java?
- What are the advantages of using the final keyword with classes, methods, and variables?
- What are Lambda expressions in Java? Provide an example.
- What are Functional interfaces in Java?
- What is the difference between Runnable and Callable in Java?
- What is the difference between ArrayList and LinkedList in terms of performance?
- What is the difference between == and .equals() in terms of object comparison?
- How does HashSet work in Java? What are its benefits?
- What is the Iterator interface in Java, and how is it used?
- What is the Comparable interface in Java?
- What is the Comparator interface, and how is it different from Comparable?
- What is the difference between String and StringBuffer in terms of immutability?
- What are the different types of threads in Java?
- What is a thread pool in Java, and how does it work?
- What is the ExecutorService in Java?
- How does synchronization work in Java?
- What is the difference between wait() and sleep() methods in Java?
- What is the join() method in Java threads?
- What is the difference between final, finally, and finalize in Java?
- What is a Stack and Queue in Java? Explain their differences.
- What is the difference between HashMap and LinkedHashMap in Java?
- What is a soft reference in Java?
- What is the difference between ArrayList and Vector in terms of performance?
- What are generics in Java, and why are they used?
- How do you implement a thread-safe collection in Java?
- What is the try-with-resources statement in Java?
- What is the significance of the enum type in Java?
- What is the difference between a List, Set, and Map in Java?
- What are the different types of exceptions in Java? Explain them with examples.
- What is reflection in Java?
Experienced Level Questions
- Explain the Java memory model (JMM) and how synchronization affects it.
- What are the design patterns you have used in Java, and explain one in detail?
- How does Java’s garbage collection work? Explain the generational garbage collection approach.
- What is the difference between synchronized block and synchronized method in Java?
- Explain how the volatile keyword ensures thread safety in Java.
- How do you prevent deadlock in Java?
- What are the differences between ExecutorService and ForkJoinPool?
- What are Functional Programming concepts in Java 8? Explain some of the key features.
- How do you implement custom serializable classes in Java?
- Explain the Java Reflection API and how you use it.
- What is the java.nio package, and how does it improve I/O operations over java.io?
- How does Java’s Stream API` work? Provide examples of its usage.
- What is a lazy initialization in Java, and how do you implement it?
- How do you handle memory leaks in Java applications?
- What are the different ways to handle concurrency in Java?
- What is a cyclic barrier in Java, and where would you use it?
- Explain the difference between ThreadLocal and synchronized keyword in Java.
- How do you implement thread-safe singleton class in Java?
- What are immutable objects in Java? Explain how to create them.
- What is Java NIO (New I/O), and what are its advantages over classic I/O?
- What are annotations in Java? How do you create custom annotations?
- How do you implement producer-consumer problem in Java?
- What is the observer design pattern, and how is it implemented in Java?
- What are Java’s concurrent collections? Provide examples.
- What is the purpose of Java’s Future and Callable interfaces?
- How does Java 8 handle default methods in interfaces?
- Explain the Fork/Join Framework in Java and when to use it.
- How would you optimize performance in a multithreaded Java application?
- How does the Java ClassLoader work?
- What is the difference between final, finally, and finalize in detail?
- What is the Proxy Design Pattern in Java, and how is it implemented?
- How does Spring Framework use Java reflection?
- Explain how the Composite Design Pattern works in Java.
- What is a hash code in Java, and how is it used in collections?
- How does Java’s HashMap` handle collisions?
- What is dependency injection in Java, and how is it used in frameworks like Spring?
- What is the difference between deep cloning and shallow cloning in Java?
- What are the best practices to handle large-scale enterprise Java applications?
- How do you improve the memory footprint of a Java application?
- Explain the concept of Reactive Programming in Java.
Core Java Interview Questions and Answers
Beginners Questions with Answers
1. What is Java?
Java is a high-level, object-oriented, and platform-independent programming language developed by Sun Microsystems (now owned by Oracle Corporation) in 1995. It was designed with the principle of "Write Once, Run Anywhere" (WORA), meaning that once a program is written, it can run on any platform without modification, thanks to the Java Virtual Machine (JVM). Java is widely used for building enterprise-level applications, mobile apps (especially Android), web applications, and large systems.
Java is compiled into bytecode that is executed by the JVM, making it platform-independent. This contrasts with languages like C or C++, which are compiled into machine code and are platform-specific. Java's syntax is similar to C++, which makes it easier for developers with a C/C++ background to learn and use Java.
Key characteristics of Java:
- Object-Oriented: Everything in Java is an object (except primitive types).
- Platform-Independent: Java code can be executed on any machine that has the JVM.
- Distributed Computing: Java has built-in networking capabilities that make it easy to work with distributed systems.
- Security: Java has a robust security model that helps in creating virus-free, tamper-free systems.
2. What are the main features of Java?
The main features of Java are:
- Platform Independence: Java follows the WORA (Write Once, Run Anywhere) philosophy. Java programs are compiled into bytecode, which can be run on any platform with a Java Virtual Machine (JVM).
- Object-Oriented Programming (OOP): Java is an object-oriented language, which means it organizes software design around data (objects) and methods (functions). Key OOP principles in Java include encapsulation, inheritance, polymorphism, and abstraction.
- Multithreading: Java has built-in support for multithreading, allowing developers to create applications that can perform multiple tasks simultaneously. This is useful for creating interactive and high-performance applications.
- Automatic Garbage Collection: Java provides automatic memory management through garbage collection. This means that memory is automatically reclaimed when objects are no longer in use, reducing memory leaks and making Java programs more efficient.
- Rich API: Java has a vast standard library (API) that includes everything from basic data structures (e.g., lists and sets) to advanced networking, database connectivity (JDBC), and graphical user interface (GUI) development.
- Security: Java provides a strong security model through its sandbox approach, where applications are executed in a controlled environment, restricting access to system resources.
- High Performance: Java's performance is enhanced by JIT (Just-In-Time) compilation, which compiles bytecode to machine code at runtime, improving execution speed.
- Distributed Computing Support: Java provides built-in support for developing distributed applications via technologies like RMI (Remote Method Invocation), JNDI (Java Naming and Directory Interface), and Web Services.
3. Explain the difference between JDK, JRE, and JVM.
Java Development Kit (JDK), Java Runtime Environment (JRE), and Java Virtual Machine (JVM) are integral components of the Java environment, but they serve different purposes:
- JVM (Java Virtual Machine):
- The JVM is an abstract machine that provides the environment in which Java bytecode can be executed. It is responsible for converting Java bytecode into machine-specific code so that it can run on any platform.
- JVM is platform-specific. Different operating systems (Windows, Linux, etc.) have their own JVM implementations.
- It performs tasks such as garbage collection, memory management, and code execution.
- JRE (Java Runtime Environment):
- The JRE is a subset of the JDK that provides the environment for running Java applications. It includes the JVM and libraries required to run Java programs, but it does not include development tools (like compilers).
- JRE is what you need if you only want to run Java programs, not develop them.
- JDK (Java Development Kit):
- The JDK is a superset of the JRE and includes everything needed for developing Java applications. This includes the JRE (for running Java programs), as well as development tools like the Java compiler (javac), debugger (jdb), and documentation tools.
- The JDK is needed for both developing and running Java applications.
4. What is the significance of the main method in Java?
In Java, the main method is the entry point for any standalone Java application. When you run a Java program, the JVM looks for this method to start execution.
The signature of the main method is:
public static void main(String[] args)
Here's a breakdown of the key components:
- public: The method is public so that it can be accessed by the JVM from anywhere.
- static: The static keyword allows the JVM to call the method without creating an instance of the class.
- void: This means the method does not return any value.
- String[] args: This parameter allows command-line arguments to be passed to the program. It is an array of String values.
The main method is necessary because it tells the JVM where to begin the execution of a Java program. Without the main method, the JVM would have no starting point to launch the program.
5. What is the difference between a class and an object in Java?
In Java:
- Class:
- A class is a blueprint or template for creating objects. It defines the properties (fields) and behaviors (methods) that the objects of that class will have.
- A class does not consume memory on its own. It defines the structure and functionality but does not represent an actual instance.
- For example, Car could be a class, with properties like color and speed, and behaviors like accelerate() and brake().
- Object:
- An object is an instance of a class. It represents a real-world entity and occupies memory. Objects are created using the new keyword and can have values assigned to their fields and can invoke methods defined in their class.
- For example, if Car is the class, then myCar is an object of the Car class.
In essence, a class is like a blueprint, and an object is an instance of that blueprint.
6. What is an instance variable?
An instance variable is a variable declared inside a class but outside any method, constructor, or block. Each object created from the class has its own copy of instance variables, and these variables represent the state of the object.
For example:
public class Car {
// Instance variable
String color;
public Car(String color) {
this.color = color; // Assigning value to instance variable
}
}
In this example, color is an instance variable, and each object of the Car class will have its own value for the color property.
- Instance variables are non-static and belong to individual objects.
- They are initialized to default values if not explicitly initialized (e.g., null for objects, 0 for integers).
7. What is a constructor in Java?
A constructor in Java is a special method used to initialize objects when they are created. It has the same name as the class and does not have a return type. Constructors are called automatically when an object of a class is instantiated.
- Default constructor: If no constructor is explicitly defined, Java provides a default constructor that initializes object fields to default values.
- Parameterized constructor: A constructor that accepts arguments to initialize an object with specific values.
For example:
public class Car {
String model;
// Constructor
public Car(String model) {
this.model = model; // Initializing instance variable
}
}
In this example, Car has a constructor that accepts a model parameter to initialize the model instance variable.
8. What is the difference between == and equals() in Java?
In Java:
- == Operator:
- The == operator compares references (memory addresses) for object references and values for primitive data types.
- For objects, it checks whether two references point to the same memory location, not if the objects are equivalent in content.
- equals() Method:
- The equals() method is used to compare the content of two objects (i.e., whether they are logically equivalent).
- The equals() method must be overridden in a class to define what it means for two objects to be equal based on their content.
For example:
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2); // false (compares references)
System.out.println(str1.equals(str2)); // true (compares content)
9. What is an array in Java, and how is it declared?
An array in Java is an object that holds a fixed-size collection of elements of the same type. Arrays are used when you need to store multiple values in a single variable.
Declaration and initialization:
// Declaring an array
int[] arr = new int[5]; // Array of 5 integers
// Initializing an array with values
int[] arr2 = {1, 2, 3, 4, 5}; // Array with initial values
- Arrays have a fixed size, which means that once the size is specified, it cannot be changed. You can access individual elements using an index, where the first element is at index 0.
For example:
arr[0] = 10; // Assigning value to the first element
System.out.println(arr[0]); // Output: 10
10. Explain the concept of encapsulation in Java.
Encapsulation is one of the four fundamental OOP principles. It refers to the bundling of data (variables) and methods that operate on the data into a single unit, or class, and restricting access to some of the object's components. This is done by making the instance variables private and providing public methods (getters and setters) to access or modify them.
- Benefits of Encapsulation:
- Data hiding: By keeping data private, the class's internal state is hidden from outside interference, ensuring it is only accessed and modified through well-defined methods.
- Control over data: You can add logic in the getter and setter methods to control how data is accessed or modified.
- Maintainability: Encapsulation helps in maintaining and modifying code since changes in the internal workings of a class do not affect other parts of the program.
Example:
public class Employee {
private String name; // private variable
// Public getter
public String getName() {
return name;
}
// Public setter
public void setName(String name) {
this.name = name;
}
}
In this example, the name variable is encapsulated, and access to it is controlled through the getName() and setName() methods.
11. What are the access modifiers in Java?
In Java, access modifiers are keywords used to define the visibility or scope of classes, methods, and variables. There are four primary access modifiers in Java:
- public:
- When a class, method, or variable is declared public, it is accessible from any other class or package in the application.
- Example: A public method can be called from anywhere.
public class MyClass {
public int x;
}
- private:
- When a method or variable is declared private, it is only accessible within the same class where it is defined. It is not accessible from outside the class or package, ensuring data encapsulation.
- Example: A private variable or method cannot be accessed directly by other classes.
public class MyClass {
private int x;
- protected:
- The protected modifier makes the method or variable accessible within the same package and by subclasses (even if they are in different packages).
- Example: A protected variable can be accessed by subclasses but not by other classes in the same package unless they are subclasses.
public class MyClass {
protected int x;
}
- Default (Package-Private):
- When no access modifier is specified, the method or variable has package-private access, meaning it is accessible only within classes in the same package.
- Example: A class or variable with no modifier is visible only to classes in the same package.
class MyClass {
int x;
}
Each access modifier provides different levels of accessibility, helping manage encapsulation and the structure of the codebase.
12. What is method overloading?
Method overloading in Java refers to the ability to define multiple methods with the same name but with different parameter lists (different types, number of parameters, or both). Overloading allows a class to perform a similar action in different ways, improving code readability and usability.
- Rules of method overloading:
- The method name must be the same.
- The parameter list must differ in type, number, or both.
- Overloaded methods can have different return types, but return type alone is not enough to distinguish overloaded methods.
Example:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
In this example, the add method is overloaded to handle both int and double types.
13. What is method overriding?
Method overriding occurs when a subclass provides its own specific implementation of a method that is already defined in its superclass. The method in the subclass must have the same name, return type, and parameters as the method in the superclass.
- Key points of method overriding:
- It allows a subclass to modify or extend the functionality of a method inherited from the superclass.
- The method must be marked with the @Override annotation in the subclass to indicate that it is overriding a method from the superclass.
- The method in the subclass can call the superclass method using the super keyword.
Example:
class Animal {
public void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void sound() {
System.out.println("Dog barks");
}
}
In this example, Dog overrides the sound method of the Animal class. When sound() is called on a Dog object, it prints "Dog barks" instead of the default "Animal makes a sound".
14. What is the difference between final, finally, and finalize() in Java?
In Java, final, finally, and finalize() are distinct concepts, each serving different purposes:
- final:
- Used to define constants, prevent method overriding, and prevent inheritance.
- Can be applied to variables, methods, and classes:
- final variable: The value cannot be changed once initialized.
- final method: The method cannot be overridden by subclasses.
- final class: The class cannot be subclassed.
Example:
final int x = 10; // Cannot change the value of x
- finally:
- A block of code that follows a try-catch block and will always execute, regardless of whether an exception is thrown or not. It's commonly used for cleanup operations (like closing files or releasing resources).
Syntax:
try {
// code that may throw exception
} catch (Exception e) {
// handle exception
} finally {
// code that will always run, even if exception occurs
}
- finalize():
- A method in the Object class that is called by the garbage collector just before an object is destroyed. It gives objects a chance to clean up resources before they are garbage collected. However, its use is discouraged in modern Java programming in favor of using try-with-resources and explicit resource management.
Example:
@Override
protected void finalize() throws Throwable {
System.out.println("Object is being garbage collected");
}
15. What is the difference between ArrayList and LinkedList in Java?
ArrayList and LinkedList are both List implementations in Java, but they have different performance characteristics and internal implementations.
- ArrayList:
- Internally, ArrayList uses a dynamic array to store elements.
- It allows fast random access (O(1)) for retrieving elements via an index.
- Inserting or removing elements in the middle or at the beginning of the list is slower (O(n)) because shifting elements is required.
- It is better for scenarios where frequent random access is required.
- LinkedList:
- Internally, LinkedList uses a doubly linked list to store elements, where each element contains references to both the next and previous elements.
- It allows fast insertions and deletions at both ends of the list (O(1)).
- Random access (getting an element by index) is slower (O(n)) because it has to traverse the list.
- It is better for scenarios where you need to frequently add or remove elements from the beginning or middle.
16. How does a for-each loop work in Java?
The for-each loop (also called the enhanced for loop) in Java provides a simpler way to iterate over arrays and collections without needing an index variable. It is used to iterate over elements of a collection (such as arrays, lists, or sets).
The syntax for a for-each loop:
for (Type element : collection) {
// Code to execute with the element
}
- Type: The type of elements in the collection (e.g., int, String).
- element: The variable that will hold the current element in each iteration.
- collection: The array or collection you are iterating over.
Example:
int[] numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
System.out.println(num); // Prints each element of the array
}
In this example, the loop iterates through the numbers array, and num takes each value from the array in turn.
17. What is the default value of an uninitialized variable in Java?
In Java, uninitialized instance variables (variables defined inside a class but outside any method or constructor) are automatically assigned default values by the Java compiler, depending on their data type. Local variables, however, must be explicitly initialized before use.
The default values are:
- Numeric types (byte, short, int, long, float, double): 0 or 0.0
- Boolean: false
- Character: \u0000 (null character)
- Object references (String, Arrays, etc.): null
Example:
public class MyClass {
int x; // Default value is 0
boolean flag; // Default value is false
String name; // Default value is null
}
18. What is a static variable? How is it different from an instance variable?
A static variable is a variable that belongs to the class rather than to instances (objects) of the class. It is shared by all instances of the class, and its value is common across all objects.
- Static variable:
- Declared with the static keyword.
- It has the same value across all instances of the class.
- It's initialized only once when the class is loaded.
- Instance variable:
- Belongs to each individual object (instance) of the class.
- Each object has its own copy of instance variables.
Example:
public class MyClass {
static int staticVar = 10; // Shared by all instances
int instanceVar = 20; // Unique to each instance
}
19. What is a static method? Can you call a static method without creating an object?
A static method is a method that belongs to the class rather than to instances of the class. You can call a static method without creating an object of the class. Static methods can access only static variables and other static methods within the class.
- Key points:
- Static methods are called using the class name, not through an object.
- They cannot access instance variables or instance methods directly.
Example:
class MyClass {
static void myStaticMethod() {
System.out.println("This is a static method.");
}
}
public class Test {
public static void main(String[] args) {
MyClass.myStaticMethod(); // Called without an object
}
}
20. What is the difference between String, StringBuilder, and StringBuffer in Java?
In Java, String, StringBuilder, and StringBuffer are used to represent and manipulate sequences of characters. However, they differ in terms of mutability and performance:
- String:
- Immutable: Once a String object is created, its value cannot be changed.
- Any operation that modifies a String results in the creation of a new String object.
- Less efficient for frequent modifications (like concatenation) due to the overhead of creating new objects.
- StringBuilder:
- Mutable: StringBuilder objects can be modified after creation without creating new objects.
- It is more efficient than String for string manipulation (like concatenation).
- Thread-unsafe (not synchronized), which means it is faster in single-threaded environments.
- StringBuffer:
- Mutable: Similar to StringBuilder, but it is thread-safe.
- It is synchronized, which makes it slower than StringBuilder in single-threaded scenarios but suitable for multi-threaded environments.
Example of StringBuilder vs StringBuffer:
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
StringBuffer sbf = new StringBuffer("Hello");
sbf.append(" World");
In this example, both StringBuilder and StringBuffer are mutable, but StringBuffer is synchronized, making it slower in environments with no concurrency.
21. What is a package in Java?
A package in Java is a namespace that organizes a set of related classes and interfaces. It helps to avoid naming conflicts and to manage large codebases by grouping similar classes. Java packages can also help with access control and provide a structured way of organizing files within a project.
Types of packages:
- Built-in packages: These are predefined packages provided by the Java API. For example, java.util (for utility classes like ArrayList, HashMap), java.io (for input/output operations), etc.
- User-defined packages: These are packages created by the developer to organize their own classes.
Syntax to declare a package:
package mypackage;
public class MyClass {
// Class code here
}
To use classes from a package:
import mypackage.MyClass; // Importing the class from the package
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass(); // Using the class from the package
}
}
22. What is the use of the super keyword in Java?
The super keyword in Java is used to refer to the superclass (parent class) of the current object. It is typically used in two main ways:
- Accessing superclass methods:
- If a method is overridden in the subclass, you can use super.method() to call the superclass version of the method.
- Accessing superclass constructors:
- The super() keyword can be used to invoke a constructor of the superclass. It must be the first statement in the subclass constructor.
Examples:
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
void sound() {
super.sound(); // Calls the superclass method
System.out.println("Dog barks");
}
}
Calling a superclass constructor:
class Animal {
Animal() {
System.out.println("Animal constructor");
}
}
class Dog extends Animal {
Dog() {
super(); // Calls the Animal constructor
System.out.println("Dog constructor");
}
}
23. What is the difference between public, private, protected, and default access modifiers?
In Java, access modifiers determine the visibility and accessibility of classes, methods, and variables. The four access modifiers are:
- public:
- The member is accessible from anywhere in the program, both inside and outside the class, and from any package.
Example:
public int x;
- private:
- The member is accessible only within the same class. It cannot be accessed from other classes, even if they are in the same package.
Example:
private int x;
- protected:
- The member is accessible within the same class, same package, and subclasses (even if the subclass is in a different package).
Example:
protected int x;
- Default (Package-Private):
- When no access modifier is specified, the member has package-private access. It is only accessible within classes that belong to the same package.
Example:
int x; // Default access modifier
24. What is an interface in Java?
An interface in Java is a reference type, similar to a class, but it is a collection of abstract methods (methods without a body). It cannot contain instance variables or constructors, and all methods in an interface are implicitly public and abstract (until Java 8, which introduced default methods). Interfaces are used to specify a set of methods that a class must implement.
Key points about interfaces:
- Interfaces define abstract behavior that classes can implement.
- A class can implement multiple interfaces, allowing Java to support multiple inheritance of behavior.
Syntax:
interface Animal {
void sound(); // Abstract method
}
class Dog implements Animal {
@Override
public void sound() {
System.out.println("Barks");
}
}
25. What is an abstract class in Java, and how is it different from an interface?
An abstract class in Java is a class that cannot be instantiated on its own and may contain both abstract methods (methods without a body) and concrete methods (methods with an implementation). An abstract class is used to represent a common base class for other classes that share common behavior.
Differences between abstract class and interface:
- Abstract class:
- Can have both abstract and concrete methods.
- Can have instance variables.
- Can have constructors.
- A class can inherit only one abstract class (single inheritance).
- Interface:
- Can only have abstract methods (before Java 8).
- Cannot have instance variables (only constants).
- Cannot have constructors.
- A class can implement multiple interfaces (supports multiple inheritance).
Example of abstract class:
abstract class Animal {
abstract void sound(); // Abstract method
void breathe() { // Concrete method
System.out.println("Breathing");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Barks");
}
}
26. What is polymorphism in Java?
Polymorphism in Java is the ability of an object to take on many forms. Specifically, it allows one interface to be used for a general class of actions. The specific action is determined at runtime. There are two types of polymorphism in Java:
- Compile-time polymorphism (Method Overloading):
- Occurs when multiple methods have the same name but different parameters.
Example:
class Printer {
void print(int a) {
System.out.println("Printing integer: " + a);
}
void print(String s) {
System.out.println("Printing string: " + s);
}
}
- Runtime polymorphism (Method Overriding):
- Occurs when a subclass provides a specific implementation of a method that is already defined in its superclass. The method to be called is determined at runtime based on the object type.
Example:
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
27. What is inheritance in Java?
Inheritance is one of the four pillars of object-oriented programming (OOP) in Java. It allows a new class (child or subclass) to inherit the properties and methods of an existing class (parent or superclass). The subclass can then add additional features or modify the inherited behavior.
Key benefits of inheritance:
- Reusability: The subclass reuses the code from the parent class.
- Extensibility: The subclass can extend the functionality of the parent class.
Syntax:
class Animal {
void eat() {
System.out.println("Eating");
}
}
class Dog extends Animal {
void bark() {
System.out.println("Barking");
}
}
In this example, Dog inherits the eat() method from the Animal class.
28. What is the difference between this and super in Java?
Both this and super are keywords used to refer to the current object and its superclass, respectively.
- this:
- Refers to the current object (the object whose method or constructor is being invoked).
- It can be used to call instance variables, methods, and constructors within the current class.
Example:
class Dog {
int age;
Dog(int age) {
this.age = age; // Refers to the current object's age
}
}
- super:
- Refers to the parent class of the current object.
- It is used to access superclass methods and constructors.
Example:java
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
super.sound(); // Calls the superclass method
System.out.println("Dog barks");
}
}
29. What is the difference between throw and throws in Java?
Both throw and throws are related to exception handling but are used in different contexts.
- throw:
- Used to explicitly throw an exception from a method or a block of code.
- You can throw both checked and unchecked exceptions.
Example:
throw new ArithmeticException("Cannot divide by zero");
- throws:
- Used in a method declaration to declare that a method may throw one or more exceptions.
- It is used to indicate that the method does not handle the exception itself, and it will be handled by the calling method.
Example:
public void myMethod() throws IOException {
// Code that may throw an IOException
}
30. What is exception handling in Java?
Exception handling in Java is a mechanism to handle runtime errors (exceptions) so that the normal flow of the program can be maintained. Java provides a robust mechanism to catch and handle exceptions using the try, catch, finally, and throw keywords.
Main components:
- try block: Contains the code that might throw an exception.
- catch block: Catches the exception and handles it.
- finally block: Executes code after the try and catch blocks, regardless of whether an exception is thrown or not.
- throw keyword: Used to throw an exception manually.
- throws keyword: Declares the exceptions a method might throw.
Example:
try {
int result = 10 / 0; // This will cause an ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Error: " + e.getMessage());
} finally {
System.out.println("This will always execute.");
}
In this example, the catch block handles the exception, and the finally block executes no matter what.
31. What is the try-catch block in Java?
The try-catch block in Java is used to handle exceptions in a controlled way. The try block contains code that may potentially throw an exception, and the catch block contains code that handles the exception if one is thrown.
Structure:
- try block: The code that might throw an exception is placed here.
- catch block: This block catches and handles the exception, allowing the program to continue execution without crashing.
- finally block (optional): This block will always execute, regardless of whether an exception occurred or not, making it suitable for resource cleanup (like closing files or database connections).
Syntax:
try {
// Code that may throw an exception
} catch (ExceptionType e) {
// Code to handle the exception
} finally {
// Optional block, will execute whether or not an exception occurs
}
Example:
try {
int result = 10 / 0; // This will throw an ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Error: Division by zero");
} finally {
System.out.println("This will always execute.");
}
In this example, the catch block handles the ArithmeticException, and the finally block executes no matter what.
32. What is the difference between checked and unchecked exceptions?
In Java, exceptions are categorized into two types based on whether or not they are checked at compile-time.
- Checked exceptions:
- These are exceptions that are checked at compile time. The programmer is forced to handle these exceptions explicitly (using try-catch or by declaring them with throws).
- Examples include IOException, SQLException, ClassNotFoundException.
- Compiler enforces handling: If the code throws a checked exception but does not handle it, the compiler will generate an error.
- Unchecked exceptions:
- These are exceptions that occur at runtime and are not checked at compile-time. These include subclasses of RuntimeException and Error.
- Examples include NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException.
- Not mandatory to handle: The compiler does not require you to handle unchecked exceptions, but they should still be handled in some cases to ensure program stability.
Example:
// Checked exception
try {
FileReader file = new FileReader("test.txt");
} catch (IOException e) {
System.out.println("File not found.");
}
// Unchecked exception
int[] arr = new int[5];
arr[10] = 10; // ArrayIndexOutOfBoundsException
33. What are some common types of exceptions in Java?
Java provides a variety of exception classes. Here are some common ones:
ArithmeticException: Thrown when an exceptional arithmetic condition has occurred (e.g., division by zero).
int result = 10 / 0; // Throws ArithmeticException
NullPointerException: Thrown when trying to access an object or invoke a method on a null object.
String s = null;
s.length(); // Throws NullPointerException
ArrayIndexOutOfBoundsException: Thrown when accessing an array with an invalid index.
int[] arr = new int[5];
arr[10] = 10; // Throws ArrayIndexOutOfBoundsException
IOException: Thrown when there are issues with input/output operations (e.g., file not found).
FileInputStream fis = new FileInputStream("file.txt"); // Throws IOException
ClassNotFoundException: Thrown when trying to load a class via its name but the class is not found.
Class.forName("com.example.MyClass"); // Throws ClassNotFoundException
SQLException: Thrown when a database error occurs.
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db");
IllegalArgumentException: Thrown when an illegal or inappropriate argument is passed to a method.
int number = Integer.parseInt("abc"); // Throws IllegalArgumentException
34. What is a NullPointerException?
A NullPointerException (NPE) occurs when your code attempts to use a reference that points to null (an uninitialized object). This exception is a runtime exception, and it indicates that your code is trying to access methods or fields of an object that doesn't exist (i.e., is null).
Common causes:
- Dereferencing null when trying to call a method on a null object.
- Accessing an element in an array that is null.
- Modifying or accessing a null collection.
Example:
String str = null;
System.out.println(str.length()); // Throws NullPointerException
35. What is a ClassNotFoundException?
A ClassNotFoundException is a checked exception that occurs when Java tries to load a class by name using methods like Class.forName() or ClassLoader.loadClass(), but it cannot find the class.
Common causes:
- The class file is not in the classpath.
- The class file is missing or misplaced.
- The class was not properly compiled or packaged.
Example:
try {
Class.forName("com.example.MyClass"); // Throws ClassNotFoundException if class is not found
} catch (ClassNotFoundException e) {
System.out.println("Class not found.");
}
36. What is the difference between String and StringBuilder in Java?
In Java, String and StringBuilder are both used to represent sequences of characters, but they differ significantly in terms of mutability and performance.
- String:
- Immutable: Once a String object is created, its value cannot be changed. Any operation that modifies a string results in the creation of a new String object.
- Less efficient for concatenation or frequent modifications because each modification creates a new object.
- StringBuilder:
- Mutable: StringBuilder objects can be modified without creating new objects.
- More efficient than String for concatenating strings in loops or when frequent modifications are required.
Example:
String str = "Hello";
str = str + " World"; // A new String object is created
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // The StringBuilder is modified without creating a new object
37. What is a Java thread? How do you create a thread?
A Java thread is a lightweight process that allows concurrent execution of tasks in a program. It is part of the Java concurrency model and allows a program to execute multiple tasks in parallel, improving performance in multi-core processors.
There are two ways to create a thread in Java:
- By extending the Thread class:
- You can create a new thread by subclassing the Thread class and overriding its run() method.
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running");
}
}
public class Main {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // Starts the thread and calls the run() method
}
}
- By implementing the Runnable interface:
- You can also create a thread by implementing the Runnable interface and passing it to a Thread object.
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable thread is running");
}
}
public class Main {
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start(); // Starts the thread
}
}
38. What is the purpose of the wait() and notify() methods in Java?
The wait() and notify() methods are part of the Object class and are used for inter-thread communication in Java, typically when using multiple threads that need to communicate or synchronize their actions.
- wait():
- Causes the current thread to release the lock it holds and enter the waiting state. The thread will stay in the waiting state until another thread sends a signal (via notify() or notifyAll()).
- Must be called inside a synchronized block or method.
- notify():
- Wakes up one thread that is waiting on the object's monitor (lock). If multiple threads are waiting, one of them is chosen randomly to resume.
- Must also be called inside a synchronized block or method.
Example:
class Shared {
synchronized void printNumber() {
try {
wait(); // Thread waits for a signal
System.out.println("Number is printed");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized void notifyNumber() {
notify(); // Wakes up the waiting thread
}
}
39. What is the purpose of the synchronized keyword in Java?
The synchronized keyword in Java is used to control access to a block of code or an object by multiple threads. It ensures that only one thread at a time can access the synchronized code or resource, preventing race conditions and ensuring thread safety.
- Method-level synchronization: If a method is declared as synchronized, the thread holds the object’s monitor (lock) while executing that method, preventing other threads from executing synchronized methods on the same object.
- Block-level synchronization: You can also synchronize specific blocks of code within a method, providing more fine-grained control over thread synchronization.
Example:
class Counter {
private int count = 0;
synchronized void increment() {
count++; // Only one thread can access this method at a time
}
synchronized int getCount() {
return count;
}
}
40. What is a deadlock in Java?
A deadlock in Java occurs when two or more threads are blocked forever, waiting for each other to release locks on resources. This can happen when each thread holds a lock on one resource and is waiting for a lock on another resource that the other thread is holding.
Deadlock condition:
- Mutual exclusion: At least one resource is held in a non-shareable mode (i.e., only one thread can use the resource at a time).
- Hold and wait: A thread holding at least one resource is waiting to acquire additional resources that are currently being held by other threads.
- No preemption: Resources cannot be forcibly taken from threads holding them; they must be released voluntarily.
- Circular wait: A circular chain of threads exists, where each thread is waiting for a resource held by the next thread in the chain.
Example of deadlock:
class A {
synchronized void methodA(B b) {
b.last();
}
synchronized void last() {}
}
class B {
synchronized void methodB(A a) {
a.last();
}
synchronized void last() {}
}
public class DeadlockExample {
public static void main(String[] args) {
A a = new A();
B b = new B();
new Thread(() -> a.methodA(b)).start();
new Thread(() -> b.methodB(a)).start();
}
}
In this example, two threads are in a circular wait, causing a deadlock situation.
Intermediate Questions with Answers
1. What are the differences between ArrayList and Vector?
Both ArrayList and Vector are part of the Java Collections Framework and implement the List interface, but they have several differences, mainly in terms of synchronization, growth policy, and usage.
Feature
ArrayList
Vector
Synchronization
Not synchronized (not thread-safe)
Synchronized (thread-safe)
Growth Policy
Grows by 50% when capacity is exceeded
Grows by doubling the size when capacity is exceeded
Performance
Faster in most cases due to lack of synchronization
Slower due to synchronization overhead
Legacy
Introduced in Java 1.2 (part of Java Collections Framework)
Introduced in Java 1.0 (legacy class)
Method
More modern methods like ensureCapacity()
Older methods like addElement() and removeElement()
Capacity Increment
The default size increases by 50%
The default size increases by doubling
Example:
ArrayList<Integer> list = new ArrayList<>();
Vector<Integer> vector = new Vector<>();
2. What is the Collections framework in Java? Name some commonly used classes.
The Collections Framework in Java is a unified architecture for storing and manipulating a group of objects. It provides a set of interfaces, implementations (classes), and algorithms that allow developers to work with collections of objects (lists, sets, maps, etc.) in an efficient manner.
Key components:
- Interfaces: These define the behavior of collections.
- List: Ordered collection (e.g., ArrayList, LinkedList).
- Set: Collection that does not allow duplicates (e.g., HashSet, LinkedHashSet).
- Map: Collection of key-value pairs (e.g., HashMap, TreeMap).
- Queue: Collection designed for holding elements prior to processing (e.g., LinkedList, PriorityQueue).
- Implementations: These are the actual classes that implement the collection interfaces.
- ArrayList: A resizable array-based list.
- LinkedList: A doubly linked list.
- HashMap: A hash table-based map implementation.
- HashSet: A set implementation based on a hash table.
- TreeSet: A set implementation that maintains order.
- PriorityQueue: A queue implementation based on priority.
- Algorithms: These are methods that operate on collections (e.g., sorting, shuffling, etc.).
- Example: Collections.sort(list) to sort a list.
3. What is the HashMap class in Java? How is it different from TreeMap?
The HashMap and TreeMap classes are both implementations of the Map interface in Java, but they have key differences:
Feature
HashMap
TreeMap
Ordering
Does not guarantee any order for elements
Elements are stored in sorted order based on natural ordering of keys (or a custom comparator)
Performance
Offers constant-time complexity (O(1)) for get and put operations, on average
Slower due to maintaining a sorted order (O(log n) for get, put, and remove)
Null Keys/Values
Allows one null key and multiple null values
Does not allow null keys, but allows null values
Underlying Data Structure
Hash table-based implementation
Red-Black tree-based implementation
Example:
HashMap<Integer, String> hashMap = new HashMap<>();
TreeMap<Integer, String> treeMap = new TreeMap<>();
4. What is the difference between HashMap and Hashtable?
HashMap and Hashtable are both used for storing key-value pairs in Java, but they have several key differences:
Feature
HashMap
Hashtable
Synchronization
Not synchronized (not thread-safe)
Synchronized (thread-safe)
Null Keys/Values
Allows one null key and multiple null values
Does not allow null keys or values
Performance
Faster because it's not synchronized
Slower due to synchronization overhead
Legacy
Introduced in Java 1.2 (part of Collections Framework)
Introduced in Java 1.0 (legacy class)
Example:
HashMap<Integer, String> hashMap = new HashMap<>();
Hashtable<Integer, String> hashtable = new Hashtable<>();
5. How does garbage collection work in Java?
Garbage collection in Java is an automatic memory management process that reclaims memory occupied by objects that are no longer in use, helping to prevent memory leaks and improve performance.
Key aspects:
- Heap Memory: Objects in Java are stored in the heap memory, and garbage collection operates on this part of memory.
- Reachability: An object becomes eligible for garbage collection when it is no longer reachable by any thread, meaning there are no references pointing to it.
- Garbage Collector (GC): The Java Virtual Machine (JVM) automatically runs the garbage collector to identify and remove unreachable objects. The GC runs in the background and can be triggered when the JVM needs more memory.
- Generational Garbage Collection: Objects are classified into generations (young, old, permanent), and garbage collection strategies vary based on object age.
Example:
- Java developers do not need to manually manage memory with delete or free like in C/C++. The JVM's garbage collector takes care of it.
6. What are weak references in Java?
A weak reference in Java is a reference that does not prevent its referent (the object being referenced) from being garbage collected. Weak references are useful in situations where you want to refer to an object without preventing it from being reclaimed by the garbage collector.
Java provides the WeakReference class in the java.lang.ref package to create weak references.
Use case: Weak references are often used in caching, where you want to allow the JVM to reclaim memory if the cache is not used for a while.
Example:
WeakReference<MyObject> weakRef = new WeakReference<>(new MyObject());
7. What is the role of the clone() method in Java?
The clone() method in Java is used to create a shallow copy of an object. The clone() method is defined in the Object class and must be overridden in the class to allow cloning.
Shallow Copy: If an object contains references to other objects, the cloned object will reference the same objects rather than creating new instances of those objects.
- The object being cloned must implement the Cloneable interface to avoid throwing a CloneNotSupportedException.
Example:
class MyClass implements Cloneable {
int x;
MyClass(int x) { this.x = x; }
@Override
public MyClass clone() throws CloneNotSupportedException {
return (MyClass) super.clone();
}
}
8. What is the difference between shallow copy and deep copy?
A shallow copy and a deep copy both involve copying an object, but they differ in how they handle nested objects.
- Shallow Copy: A shallow copy creates a new object, but it copies references to the nested objects. This means that if the original object contains references to other objects, the shallow copy will share the same references to those objects.
- Example: The nested objects are not cloned; they are shared.
- Deep Copy: A deep copy creates a completely independent object, including new copies of all objects referenced by the original object, thus ensuring that changes to the original object's nested objects do not affect the copy.
- Example: The nested objects are cloned, and the new object is fully independent.
Example:
class MyClass {
int[] arr;
MyClass(int[] arr) {
this.arr = arr;
}
}
// Shallow copy
MyClass obj1 = new MyClass(new int[] {1, 2, 3});
MyClass obj2 = new MyClass(obj1.arr); // Shared reference to the same array
// Deep copy
int[] newArr = new int[obj1.arr.length];
System.arraycopy(obj1.arr, 0, newArr, 0, obj1.arr.length);
MyClass obj2 = new MyClass(newArr); // Independent copy of the array
9. What is a Singleton class? How can you create one in Java?
A Singleton class in Java is a class that can have only one instance during the lifetime of the application. The Singleton pattern is used to ensure that a class has only one instance and provides a global point of access to that instance.
To create a Singleton class:
- Make the constructor private to prevent instantiation from outside the class.
- Provide a static method to get the instance of the class.
- Optionally, use the Bill Pugh Singleton Design or Double-Checked Locking to ensure thread safety and performance.
Example (basic):
public class Singleton {
private static Singleton instance;
private Singleton() {} // Private constructor
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
10. What is the transient keyword in Java?
The transient keyword in Java is used to indicate that a specific field in a class should not be serialized. When an object is serialized (converted into a byte stream), fields marked with transient are excluded from the serialization process.
This is useful for fields that contain sensitive or non-serializable data (e.g., database connections, file handles).
Example:
class Employee implements Serializable {
String name;
transient int salary; // The salary field will not be serialized
}
In this example, when the Employee object is serialized, the salary field will not be included in the serialized output.
11. What is the purpose of the volatile keyword in Java?
The volatile keyword in Java is used to mark a variable as being shared between multiple threads. When a variable is declared as volatile, it ensures that any thread reading the value of the variable will always get the most recent value, and the value will be updated immediately for all threads. This is essential in multi-threaded programming where one thread modifies a variable and other threads need to see the changes immediately.
Key Points:
- Ensures visibility: Changes made to a volatile variable by one thread are visible to other threads.
- Prevents caching: The value of a volatile variable is never cached in registers or processor caches.
- Does not guarantee atomicity: While it ensures visibility, it does not ensure atomicity. For example, incrementing a volatile variable is not atomic.
Example:
class VolatileExample {
private volatile boolean flag = false;
public void toggleFlag() {
flag = !flag; // Changes made to 'flag' will be visible to all threads
}
public boolean getFlag() {
return flag; // Always reads the latest value
}
}
In this example, the volatile keyword ensures that changes to the flag variable are immediately visible across all threads.
12. What are the advantages of using the final keyword with classes, methods, and variables?
The final keyword in Java is used to declare constants, prevent method overriding, and prevent inheritance. Its use provides several advantages:
- final with variables: When applied to a variable, the variable's value cannot be changed once it is assigned. It ensures immutability.
- Example: final int MAX_SIZE = 100;
- Advantage: Guarantees that the value will remain constant, providing consistency and safety in the code.
- final with methods: When applied to a method, the method cannot be overridden by subclasses.
Example:
java
Copy code
public final void doSomething() { ... }
- Advantage: Ensures that the behavior of the method remains unchanged in subclasses, which is useful for maintaining the integrity of core functionality.
- final with classes: When applied to a class, it prevents the class from being subclassed.
Example:
public final class MyClass { ... }
- Advantage: Prevents subclassing, ensuring that the class cannot be extended and that its implementation cannot be altered.
13. What are Lambda expressions in Java? Provide an example.
Lambda expressions in Java are a way to provide clear and concise syntax for writing anonymous methods (implementations of functional interfaces). Introduced in Java 8, lambda expressions enable you to pass functionality as arguments to methods or to create a function that can be treated as an object.
Syntax:
(parameters) -> expression
Key Advantages:
- Provides a functional style of programming.
- Makes code more concise and readable.
- Allows you to treat functionality as an argument to methods, making it easier to work with APIs like Streams and functional interfaces.
Example:
interface Greeting {
void sayHello(String name);
}
public class LambdaExample {
public static void main(String[] args) {
// Lambda expression
Greeting greeting = (name) -> System.out.println("Hello, " + name);
greeting.sayHello("John"); // Output: Hello, John
}
}
In this example, we define a Greeting interface and implement the sayHello method using a lambda expression.
14. What are Functional interfaces in Java?
A functional interface in Java is an interface that has exactly one abstract method and can have any number of default or static methods. Functional interfaces serve as the basis for lambda expressions and method references.
Key Characteristics:
- Single abstract method (SAM): Functional interfaces contain only one abstract method.
- Can be annotated with the @FunctionalInterface annotation, which is optional but recommended as it helps with clarity and ensures that the interface meets the requirements of a functional interface.
Common examples of functional interfaces:
- Runnable: Represents a task that can be executed by a thread.
- Comparator: Used to compare two objects.
- Predicate: Represents a boolean-valued function of one argument.
Example:
@FunctionalInterface
interface Calculator {
int add(int a, int b); // Single abstract method
}
public class FunctionalInterfaceExample {
public static void main(String[] args) {
Calculator calc = (a, b) -> a + b; // Lambda expression
System.out.println(calc.add(5, 3)); // Output: 8
}
}
15. What is the difference between Runnable and Callable in Java?
Both Runnable and Callable are functional interfaces used to represent tasks that can be executed by multiple threads, but they have significant differences:
- Return Value:
- Runnable: The run() method does not return a result; its return type is void.
- Callable: The call() method returns a result and may throw a checked exception.
- Exception Handling:
- Runnable: It cannot throw checked exceptions.
- Callable: It can throw checked exceptions, making it more versatile for tasks that might require exception handling.
- Use in ExecutorService:
- Runnable is typically used with ExecutorService.submit(Runnable task).
- Callable is used with ExecutorService.submit(Callable task), and the result of the submit() call can be obtained as a Future object.
Example:
Runnable runnableTask = () -> System.out.println("Runnable task executed");
Callable<Integer> callableTask = () -> {
return 42; // Return value
};
16. What is the difference between ArrayList and LinkedList in terms of performance?
Both ArrayList and LinkedList are implementations of the List interface, but they have different performance characteristics depending on the operation being performed.
Operation
ArrayList
LinkedList
Access time (get)
O(1): Direct access to elements via index
O(n): Must traverse the list from the head
Insertion (at end)
O(1) (amortized)
O(1) (constant time if no resizing)
Insertion (at index)
O(n): Requires shifting elements
O(n): Traversal of list is needed
Deletion (at index)
O(n): Requires shifting elements
O(n): Traversal of list is needed
Memory Usage
More compact (uses less memory per element)
Requires more memory due to storing pointers
- ArrayList is generally better when you need fast random access to elements (like get() operations).
- LinkedList performs better when you frequently add or remove elements from the beginning or middle of the list (like add() or remove() operations).
17. What is the difference between == and .equals() in terms of object comparison?
- == (Reference Comparison):
- The == operator compares memory addresses or references of two objects, not their contents.
- It checks if both references point to the same object in memory.
- .equals() (Content Comparison):
- The .equals() method is intended for comparing the content of two objects, not their references.
- It is defined in the Object class, but many classes, such as String and Integer, override it to provide meaningful content-based comparisons.
Example:
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2); // false: different objects
System.out.println(str1.equals(str2)); // true: same content
18. How does HashSet work in Java? What are its benefits?
A HashSet in Java is a Set implementation that does not allow duplicate elements and is backed by a hash table. It provides constant-time performance for the basic operations (add(), remove(), and contains()) as long as the hash function disperses the elements properly among the buckets.
Key Features:
- No duplicates: Ensures that no two elements in the set are equal, according to the .equals() method.
- Unordered: The elements are not stored in any particular order.
- Efficiency: HashSet provides constant-time performance for most operations due to the use of hashing.
Example:
HashSet<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
set.add("apple"); // Duplicate will be ignored
System.out.println(set); // Output: [apple, banana]
19. What is the Iterator interface in Java, and how is it used?
The Iterator interface provides a way to iterate over a collection in Java. It is part of the java.util package and is implemented by most collection classes like ArrayList, HashSet, etc.
Key Methods:
- hasNext(): Returns true if there are more elements to iterate over.
- next(): Returns the next element in the iteration.
- remove(): Removes the last element returned by the iterator.
Example:
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
20. What is the Comparable interface in Java?
The Comparable interface is used to define the natural ordering of objects. It is part of the java.lang package and is implemented by a class to allow objects of that class to be compared with each other.
Key Method:
- compareTo(T o): This method compares the current object (this) with the specified object (o) and returns:
- Negative value if this is less than o.
- Zero if this is equal to o.
- Positive value if this is greater than o.
Example:
class Person implements Comparable<Person> {
String name;
Person(String name) {
this.name = name;
}
@Override
public int compareTo(Person other) {
return this.name.compareTo(other.name); // Compare by name alphabetically
}
}
21. What is the Comparator interface, and how is it different from Comparable?
The Comparator interface is used to define multiple ways of comparing two objects, allowing you to define custom sorting logic. It provides more flexibility than the Comparable interface, as it allows for sorting in various ways and doesn't require modifying the classes being compared.
Key Differences:
- Comparable:
- Defines the natural ordering of objects.
- A class that implements Comparable must override the compareTo() method to define how two objects should be compared.
- Only one sorting order can be defined for objects.
- The compareTo() method returns:
- A negative value if this is less than the other object.
- Zero if this is equal to the other object.
- A positive value if this is greater than the other object.
- Comparator:
- Allows for multiple sorting criteria.
- It doesn't modify the class being compared, making it more flexible for comparing different objects or multiple fields (e.g., sorting by name, age, etc.).
- The compare() method in Comparator can define custom comparison logic and is passed to methods like Collections.sort() to sort elements.
Example:
// Using Comparable (Natural Ordering)
class Person implements Comparable<Person> {
String name;
Person(String name) {
this.name = name;
}
@Override
public int compareTo(Person other) {
return this.name.compareTo(other.name);
}
}
// Using Comparator (Custom Ordering)
class AgeComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) {
return Integer.compare(p1.age, p2.age); // Compare by age
}
}
22. What is the difference between String and StringBuffer in terms of immutability?
- String:
- Immutable: Once a String object is created, its value cannot be modified. Any operation that seems to modify the string (e.g., concatenation) creates a new String object.
Example:
String str = "Hello";
str = str + " World"; // A new String object is created, 'str' now points to a new object
- StringBuffer:
- Mutable: StringBuffer objects are mutable, meaning you can modify the contents of the string without creating new objects.
- It is more efficient when performing operations like appending, inserting, or deleting strings, as it does not create new objects for each modification.
Example:
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World"); // Modifies the same StringBuffer object
Conclusion:
- String is immutable, which ensures that its value cannot change once created.
- StringBuffer is mutable, meaning its value can be modified in-place.
23. What are the different types of threads in Java?
In Java, there are primarily two types of threads:
- User Threads:
- These are the threads that the application developer creates. These threads perform the main work of the program.
- The JVM continues to run until all user threads have finished execution.
- Daemon Threads:
- These are background threads that perform system-level or housekeeping tasks (e.g., garbage collection).
- They do not prevent the JVM from exiting, even if they are still running. The JVM terminates as soon as all user threads finish, even if daemon threads are still executing.
Example:
Thread userThread = new Thread(() -> System.out.println("User thread"));
Thread daemonThread = new Thread(() -> System.out.println("Daemon thread"));
daemonThread.setDaemon(true); // Marking the thread as a daemon thread
24. What is a thread pool in Java, and how does it work?
A thread pool is a collection of worker threads used to perform tasks concurrently. Instead of creating a new thread for every task, a thread pool reuses a pool of existing threads to execute tasks, improving performance and resource management.
- How it works:
- Task Submission: When a task is submitted to the thread pool, it assigns it to an available worker thread.
- Thread Reuse: Once a thread completes a task, it returns to the pool to handle new tasks.
- Size Management: The thread pool can be configured with a fixed size, or it can grow dynamically (depending on the type of pool used, like CachedThreadPool).
Advantages:
- Reduces the overhead of creating new threads repeatedly.
- Helps manage system resources efficiently by limiting the number of concurrent threads.
Example:
ExecutorService executor = Executors.newFixedThreadPool(4); // Pool of 4 threads
executor.submit(() -> System.out.println("Task executed"));
executor.shutdown();
25. What is the ExecutorService in Java?
The ExecutorService is a higher-level interface in Java that manages thread execution. It provides methods for managing a pool of worker threads and executing tasks asynchronously.
Key Methods:
- submit(): Submits a task for execution and returns a Future object, which can be used to retrieve the result or handle exceptions.
- invokeAll(): Submits a collection of tasks and waits for all to complete.
- shutdown(): Initiates an orderly shutdown of the executor, where previously submitted tasks are executed, but no new tasks will be accepted.
- shutdownNow(): Attempts to stop all actively executing tasks and halts the processing of waiting tasks.
Example:
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(() -> System.out.println("Task executed"));
executor.shutdown();
26. How does synchronization work in Java?
Synchronization in Java is used to control access to shared resources by multiple threads, ensuring that only one thread can access a resource at a time. This prevents data corruption and ensures thread safety.
- Synchronized Methods: A method is synchronized by using the synchronized keyword. This ensures that only one thread can execute that method on a given object.
- Synchronized Blocks: A more granular form of synchronization that allows synchronizing only a specific block of code, instead of the entire method.
Example:
class Counter {
private int count = 0;
// Synchronized method
public synchronized void increment() {
count++;
}
// Synchronized block
public void decrement() {
synchronized (this) {
count--;
}
}
}
Key Points:
- Synchronized methods or blocks allow only one thread to execute the critical section at a time.
- It helps avoid issues like race conditions when accessing shared resources.
27. What is the difference between wait() and sleep() methods in Java?
- wait():
- Defined in Object class.
- Used for inter-thread communication.
- A thread must be inside a synchronized block or method to call wait().
- It releases the lock and causes the thread to wait until another thread calls notify() or notifyAll() on the same object.
- Throws InterruptedException.
- sleep():
- Defined in Thread class.
- Used to pause the current thread for a specified amount of time.
- Does not release the lock on the object.
- It is used to introduce a delay or pause in the execution of the thread.
- Throws InterruptedException.
Example:
// wait()
synchronized (obj) {
obj.wait();
}
// sleep()
Thread.sleep(1000); // Sleep for 1 second
28. What is the join() method in Java threads?
The join() method allows one thread to wait for another thread to finish executing before continuing. If thread A calls join() on thread B, thread A will pause until thread B completes its execution.
- Use Case: It's commonly used when you need to wait for one or more threads to finish before continuing with the main program logic.
Example:
Thread t1 = new Thread(() -> System.out.println("Thread 1"));
Thread t2 = new Thread(() -> System.out.println("Thread 2"));
t1.start();
t2.start();
t1.join(); // Main thread waits for t1 to finish
t2.join(); // Main thread waits for t2 to finish
System.out.println("All threads have finished");
29. What is the difference between final, finally, and finalize in Java?
- final:
- A keyword used to define constants, prevent method overriding, or prevent inheritance.
- It can be applied to variables (making them constants), methods (preventing overriding), and classes (preventing subclassing).
Example:
final int MAX_SIZE = 100;
- finally:
- A block used in exception handling to ensure that certain actions (like closing resources) are always executed, regardless of whether an exception occurs or not.
Example:
try {
// Code that may throw an exception
} finally {
// Code that will always run
}
- finalize():
- A method in the Object class, called by the garbage collector before an object is destroyed. It is used to perform cleanup actions (e.g., releasing resources).
Example:
@Override
protected void finalize() throws Throwable {
// Cleanup code
}
30. What is a Stack and Queue in Java? Explain their differences.
- Stack:
- A LIFO (Last In, First Out) data structure, where the last element added is the first one to be removed.
- It supports operations like push() (to add an element) and pop() (to remove the top element).
Example:
Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.pop(); // Removes the top element (10)
- Queue:
- A FIFO (First In, First Out) data structure, where the first element added is the first one to be removed.
- It supports operations like offer() (to add) and poll() (to remove from the front).
Example:
Queue<Integer> queue = new LinkedList<>();
queue.offer(10);
queue.poll(); // Removes the front element (10)
Differences:
- Order: Stack uses LIFO, Queue uses FIFO.
- Use Cases: Stack is used in scenarios like undo operations, parsing expressions, etc. Queue is used in scenarios like task scheduling or handling requests in order.
31. What is the difference between HashMap and LinkedHashMap in Java?
Both HashMap and LinkedHashMap implement the Map interface and store key-value pairs, but they have a key difference regarding order.
- HashMap:
- Does not maintain any order of elements.
- The elements are stored in a random order based on the hash code of the keys.
- Performance: It provides O(1) time complexity for get() and put() operations in most cases, but the order of elements is unpredictable.
- LinkedHashMap:
- Maintains insertion order or access order (if specified).
- The order of elements is based on the order in which they were inserted, or on access if the access order is enabled (true).
- Performance: Slightly slower than HashMap due to the added overhead of maintaining the order, but still provides O(1) time complexity for get() and put() in most cases.
Example:
HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put("A", 1);
hashMap.put("B", 2);
hashMap.put("C", 3); // No guaranteed order
LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("A", 1);
linkedHashMap.put("B", 2);
linkedHashMap.put("C", 3); // Maintains insertion order
32. What is a soft reference in Java?
A soft reference is a type of reference that allows the object it refers to be garbage collected only if the JVM runs low on memory. Soft references are used for memory-sensitive caching, where objects should be kept around as long as there is enough memory, but can be discarded if memory is required.
- Soft references are created using the SoftReference class.
- Objects referenced by soft references are eligible for garbage collection when the JVM needs memory.
Example:
SoftReference<String> softRef = new SoftReference<>(new String("Hello"));
String str = softRef.get(); // Retrieves the object if it's not garbage collected
33. What is the difference between ArrayList and Vector in terms of performance?
Both ArrayList and Vector are dynamic arrays in Java, but they have significant differences:
- ArrayList:
- Not synchronized, which makes it faster for single-threaded applications.
- Automatically grows in size as elements are added (usually doubles in size).
- Performance: Faster than Vector because it doesn't have the synchronization overhead.
- Vector:
- Synchronized, meaning it is thread-safe and can be used in multi-threaded environments without external synchronization.
- Performance: Slower than ArrayList due to synchronization overhead.
- Grows by 100% (double the size) when the array is full.
Key Points:
- Thread Safety: Vector is thread-safe, while ArrayList is not.
- Growth Behavior: ArrayList grows by 50%, whereas Vector grows by 100%.
34. What are generics in Java, and why are they used?
Generics in Java are a feature introduced in Java 5 that allows you to define classes, interfaces, and methods with type parameters. Generics enable type safety and eliminate the need for casting by providing compile-time checking of types.
- Why Use Generics?:
- Type Safety: Generics enforce stronger type checks at compile time, preventing ClassCastException at runtime.
- Code Reusability: Generics allow you to write more flexible and reusable code.
- Avoids Casting: With generics, the need for explicit casting is removed.
Example:
// Without generics
List list = new ArrayList();
list.add("String");
String s = (String) list.get(0); // Casting needed
// With generics
List<String> list = new ArrayList<>();
list.add("String");
String s = list.get(0); // No casting needed
35. How do you implement a thread-safe collection in Java?
In Java, thread-safe collections are collections that can be used by multiple threads concurrently without causing data corruption or inconsistency.
There are several ways to implement thread-safe collections:
- Using synchronized collections:
- Java provides a utility class Collections which wraps non-thread-safe collections in synchronized versions.
- Collections.synchronizedList(), Collections.synchronizedMap(), etc.
Example:
List<String> list = Collections.synchronizedList(new ArrayList<>());
- Using concurrent collections:
- The java.util.concurrent package provides thread-safe collections such as CopyOnWriteArrayList, ConcurrentHashMap, BlockingQueue, etc.
Example:
Map<String, Integer> map = new ConcurrentHashMap<>();
- Manual Synchronization:
- You can manually synchronize the collection operations using the synchronized keyword.
Example:
synchronized (list) {
list.add("element");
}
36. What is the try-with-resources statement in Java?
The try-with-resources statement, introduced in Java 7, ensures that resources (like files, sockets, or database connections) are automatically closed when they are no longer needed, preventing resource leaks.
- Resources must implement the AutoCloseable interface (which includes Closeable for I/O resources).
- The close() method is automatically called on the resources when the try block finishes execution (either normally or due to an exception).
Example:
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
// 'reader' is automatically closed here
37. What is the significance of the enum type in Java?
An enum in Java is a special data type that allows for defining a collection of constant values. Enums are more powerful than simple constants because they can have fields, methods, and constructors.
- Advantages:
- Type Safety: Enums are type-safe, which means you can't assign invalid values.
- Improved Readability: Enums make the code more readable and meaningful, especially when dealing with sets of predefined values (e.g., days of the week, months).
- Object-Oriented: Enums can have methods, fields, and constructors.
Example:
enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY;
}
Day today = Day.MONDAY;
You can also define methods inside an enum:
enum Color {
RED("FF0000"), GREEN("00FF00"), BLUE("0000FF");
private String hexCode;
Color(String hexCode) {
this.hexCode = hexCode;
}
public String getHexCode() {
return this.hexCode;
}
}
38. What is the difference between a List, Set, and Map in Java?
- List:
- A List is an ordered collection of elements that may contain duplicates. The elements are indexed and can be accessed by their index.
- Implementations: ArrayList, LinkedList, Vector, etc.
Example:
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
- Set:
- A Set is an unordered collection of unique elements (no duplicates).
- Implementations: HashSet, LinkedHashSet, TreeSet, etc.
Example:
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Apple"); // Duplicate, won't be added
- Map:
- A Map is a collection of key-value pairs. Each key is unique, and each key maps to a single value.
- Implementations: HashMap, LinkedHashMap, TreeMap, etc.
Example:
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 1);
map.put("Banana", 2);
39. What are the different types of exceptions in Java? Explain them with examples.
In Java, exceptions are divided into two categories:
- Checked Exceptions:
- These exceptions are checked at compile time. The programmer is required to handle them using try-catch blocks or by declaring the exception with throws.
- Example: IOException, SQLException.
try {
FileReader file = new FileReader("file.txt");
} catch (IOException e) {
e.printStackTrace();
}
- Unchecked Exceptions:
- These exceptions are not checked at compile time. They inherit from RuntimeException and occur during runtime (e.g., logic errors, invalid inputs).
- Example: NullPointerException, ArrayIndexOutOfBoundsException.
String str = null;
System.out.println(str.length()); // Throws NullPointerException
- Errors:
- These are serious issues that usually cannot be handled by the program, such as OutOfMemoryError or StackOverflowError.
40. What is reflection in Java?
Reflection in Java is the ability to inspect and modify the properties, methods, and constructors of classes, interfaces, and objects at runtime. It is part of the java.lang.reflect package.
- Reflection can be used to:
- Examine or modify the behavior of objects at runtime.
- Create objects dynamically.
- Access private members.
Example:
Class<?> clazz = Class.forName("java.util.ArrayList");
Method method = clazz.getMethod("add", Object.class);
method.invoke(new ArrayList<>(), "Hello Reflection");
Reflection is powerful, but it should be used cautiously due to performance overhead and potential security risks.
Experienced Questions with Answers
1. Explain the Java Memory Model (JMM) and how synchronization affects it.
The Java Memory Model (JMM) defines how threads interact with memory and how changes made by one thread are visible to other threads. It provides rules for:
- Visibility: Ensures that changes made by one thread to variables are visible to other threads.
- Ordering: Defines the order in which actions (like reading/writing variables) are executed, especially with respect to threads.
The JMM guarantees that:
- Every thread has its own local working memory (cached copy of variables), and it must be synchronized to make sure these changes are visible across threads.
- The main memory (shared memory) is the only place where the actual variables reside.
Impact of Synchronization on JMM:
- Synchronization (synchronized keyword, volatile keyword, Lock mechanisms) ensures visibility and ordering. When you synchronize a block of code or a method, the JVM ensures that changes made in one thread are visible to others, and that operations within synchronized blocks happen in a specific order.
- Visibility: Without synchronization, there is no guarantee that one thread will see the changes made by another thread. The synchronization ensures that once a thread finishes executing a synchronized block, its changes to shared variables are visible to other threads.
- Happens-before relationship: Synchronization establishes a happens-before relationship between the threads, meaning that the actions within a synchronized block will be performed before any subsequent actions in another thread.
Example:
class SharedData {
private int x = 0;
// Synchronized method ensures visibility of changes to x
public synchronized void increment() {
x++;
}
public int getX() {
return x;
}
}
2. What are the design patterns you have used in Java, and explain one in detail?
Some commonly used design patterns in Java are:
- Singleton: Ensures that a class has only one instance and provides a global point of access.
- Factory Method: Defines an interface for creating objects, but allows subclasses to alter the type of objects that will be created.
- Observer: Defines a dependency between objects where when one object changes state, all dependent objects are notified and updated automatically.
- Decorator: Allows behavior to be added to individual objects, without affecting the behavior of other objects from the same class.
- Strategy: Defines a family of algorithms and makes them interchangeable, allowing the algorithm to be selected at runtime.
Singleton Pattern in Detail:
The Singleton pattern ensures that a class has only one instance and provides a global access point to that instance. It is used when you need to control access to shared resources, like database connections or configuration settings.
- Eager initialization: The instance is created at the time of class loading.
- Lazy initialization: The instance is created when it is first needed.
Example (Thread-safe Singleton using Bill Pugh Singleton Design):
public class Singleton {
private Singleton() {}
private static class SingletonHelper {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
In this example, the inner SingletonHelper class is loaded only when getInstance() is called, ensuring thread-safety without the overhead of synchronization.
3. How does Java’s garbage collection work? Explain the generational garbage collection approach.
Garbage Collection (GC) in Java is the process of automatically reclaiming memory by removing objects that are no longer in use, freeing up resources.
Java uses a generational garbage collection approach, which divides objects into generations based on their age. The main idea is that young objects are more likely to be discarded than older ones.
Generational Garbage Collection:
The heap memory is divided into three main regions:
- Young Generation: Where all new objects are allocated. This includes:
- Eden Space: Newly created objects.
- Survivor Space (S0 and S1): Objects that survive the minor garbage collection are moved to the survivor spaces.
- Old Generation (Tenured Generation): Objects that have survived multiple GC cycles in the young generation are moved here. These objects have a longer lifespan.
- Permanent Generation (Metaspace in newer versions): Stores metadata for the classes and methods in the JVM.
Types of Garbage Collections:
- Minor GC: Occurs in the Young Generation, often triggered when the Eden space is full.
- Major GC (Full GC): Occurs in the Old Generation, triggered when the Old Generation is full. This is more costly and impacts performance.
Example:
When the JVM detects that the Eden Space is full, it triggers a minor GC. During this process, objects that are still in use are promoted to Survivor spaces or Old Generation, while discarded objects are reclaimed.
4. What is the difference between synchronized block and synchronized method in Java?
Both synchronized blocks and synchronized methods are used to ensure that only one thread accesses a resource at a time, but there are key differences:
- Synchronized Method:
- Locks the entire method on the object or class level (depending on whether it's an instance method or static method).
- If the method is instance-level, it locks on the instance of the object.
- If the method is static, it locks on the class object itself.
Example:
public synchronized void increment() {
// method is synchronized on the instance
}
- Synchronized Block:
- Provides more granular control by allowing synchronization on specific code blocks instead of the entire method.
- You can synchronize blocks of code on any object, not just the instance or class.
Example:
public void increment() {
synchronized(this) {
// only this block is synchronized
}
}
- Difference:
- Synchronized Method locks the entire method and might lead to performance overhead if the method contains non-critical code.
- Synchronized Block allows finer control, synchronizing only the critical section of the code.
5. Explain how the volatile keyword ensures thread safety in Java.
The volatile keyword in Java is used to indicate that a variable’s value is always read from and written to the main memory rather than from thread-local caches. This ensures that the value of a volatile variable is visible to all threads.
- Visibility: Without volatile, threads may cache variables locally, and changes made by one thread might not be visible to others immediately. volatile ensures that any write to the variable is immediately visible to all threads.
- No Caching: Variables declared as volatile are not cached in registers or thread-local caches.
- Happens-before: The write to a volatile variable in one thread happens-before any subsequent reads of that variable by another thread.
Example:
private volatile boolean flag = false;
public void thread1() {
flag = true; // Write to volatile variable
}
public void thread2() {
if (flag) {
// This code will see the updated value of 'flag'
}
}
Without volatile, there’s no guarantee that flag will be updated in one thread and visible to another.
6. How do you prevent deadlock in Java?
Deadlock occurs when two or more threads are blocked forever because they are waiting for each other to release resources.
To prevent deadlock:
- Lock ordering: Always acquire locks in the same order. If every thread acquires locks in a consistent order, deadlock is avoided.
- Timeouts: Use timeouts when trying to acquire locks, so that threads don't wait forever.
- Avoid nested locks: Try to avoid acquiring multiple locks in the first place. If it’s unavoidable, make sure to follow lock ordering.
- Use higher-level concurrency utilities: Use classes like ReentrantLock that have built-in deadlock prevention features like tryLock().
Example:
class A {
synchronized void method1(B b) {
b.last();
}
}
class B {
synchronized void method2(A a) {
a.last();
}
}
To avoid deadlock, acquire locks in the same order across all threads.
7. What are the differences between ExecutorService and ForkJoinPool?
- ExecutorService:
- A higher-level replacement for Thread management, allowing better control over thread execution.
- It provides methods for managing a pool of threads and executing Runnable or Callable tasks asynchronously.
- It supports task scheduling, graceful shutdown, and features like submit(), invokeAll(), etc.
- ForkJoinPool:
- A special kind of ExecutorService designed for parallelism, especially for divide-and-conquer tasks.
- It is optimized for tasks that can be split into smaller tasks (forked) and later joined together.
- Uses work-stealing to balance the load between threads.
Differences:
- ExecutorService is more general-purpose for asynchronous task execution, while ForkJoinPool is optimized for parallel tasks that can be recursively split into smaller sub-tasks.