Java Interview Questions

What is Java?

Java is a high-level, object-oriented programming language developed by Sun Microsystems (now owned by Oracle) in 1995. It is widely used to develop desktop applications, mobile applications, web applications, and enterprise applications.

What are the features of Java?

Java is a popular high-level programming language that is widely used for developing a variety of applications, from web-based applications to mobile apps and desktop software. Here are some of the key features of Java:

  1. Object-Oriented Programming (OOP): Java is an object-oriented programming language, which means that it is based on the concept of objects. It allows developers to create modular and reusable code, which can be easily maintained and extended.
  2. Platform-Independent: One of the main features of Java is that it is platform-independent. This means that Java code can run on any machine that has a Java Virtual Machine (JVM) installed, regardless of the underlying operating system.
  3. Robust and Secure: Java is designed to be robust and secure, with built-in features for error handling, memory management, and security. It also includes a robust set of APIs and libraries that make it easy to develop secure and reliable applications.
  4. Multi-threaded: Java supports multi-threading, which allows multiple threads of execution to run concurrently within the same program. This makes it possible to write programs that can take advantage of multiple processors and cores, resulting in faster and more efficient code.
  5. Garbage Collection: Java includes a garbage collector that automatically frees up memory that is no longer being used by the program. This makes it easier to write code that is memory-efficient and reduces the risk of memory leaks and crashes.
  6. Rich API and Library Support: Java includes a large number of APIs and libraries, which provide developers with access to a wide range of functionality, including network programming, database connectivity, and user interface development.
  7. Easy to Learn and Use: Java is a relatively easy language to learn and use, with a simple syntax and a wide range of learning resources available online. It also has a large and active developer community, which provides support and resources for developers of all skill levels.

What is the difference between JDK, JRE, and JVM?

JDK, JRE, and JVM are all components of the Java platform, but they serve different purposes. Here’s a brief explanation of each:

JDK (Java Development Kit): The JDK is a software development kit that includes everything needed to develop Java applications, including the Java compiler, Java libraries, and tools for creating and debugging Java code. It also includes the JRE (see below).

JRE (Java Runtime Environment): The JRE is a software package that allows Java programs to run on a computer. It includes the Java Virtual Machine (JVM) and the Java class libraries, but it does not include the Java compiler or other development tools.

JVM (Java Virtual Machine): The JVM is a software component that provides a runtime environment for Java programs. It executes compiled Java code and provides a platform-independent abstraction layer between the Java program and the underlying hardware and operating system.

In summary, the JDK is used for developing Java applications, the JRE is used for running Java applications, and the JVM is the component that actually executes the Java code on a computer.

What is a class in Java?

A class is a blueprint or template for creating objects. It defines the attributes and methods that an object will have.

What is an object in Java?

An object is an instance of a class. It has state and behavior. The state of an object is represented by its attributes or properties, and the behavior is represented by its methods.

What is the difference between a class and an object in Java?

In Java, a class is a blueprint or a template for creating objects. It defines the attributes and behaviors of an object. On the other hand, an object is an instance of a class. When a class is instantiated, it creates an object in memory that has the attributes and behaviors defined by the class.

Here are some differences between a class and an object in Java:

Definition: A class is a template or a blueprint that defines the properties and behaviors of objects of that type, whereas an object is an instance of a class that has the properties and behaviors defined by the class.

Number: A class can be used to create multiple objects, whereas an object is a single instance of a class.

Memory allocation: A class is a logical entity and does not occupy any memory space, whereas an object is a physical entity that occupies memory space.

Access to attributes and methods: Class attributes and methods are shared by all objects of that class, whereas object attributes and methods are unique to that particular object.

Inheritance: Classes can inherit attributes and methods from other classes, whereas objects cannot inherit attributes or methods.

In summary, a class is a blueprint or a template for creating objects, whereas an object is a specific instance of a class with its own set of properties and behaviors.

What is the difference between static and non-static methods in Java?

In Java, a method is a block of code that performs a specific task. Methods can be either static or non-static (also called instance methods). Here are some differences between static and non-static methods in Java:

Accessing a method: A static method can be accessed using the class name, whereas a non-static method can be accessed using an object of the class.

Use of instance variables: A static method cannot access instance variables (i.e., variables defined in the class but not marked as static), whereas a non-static method can access both static and instance variables.

Memory allocation: A static method is loaded into memory when the class is loaded, whereas a non-static method is loaded into memory when an object of the class is created.

Overriding: A static method cannot be overridden in a subclass, whereas a non-static method can be overridden in a subclass.

Polymorphism: Static methods are not polymorphic, whereas non-static methods are polymorphic (i.e., a subclass can override the method defined in the superclass).

In summary, the main difference between static and non-static methods in Java is that a static method belongs to a class and can be accessed using the class name, whereas a non-static method belongs to an object of the class and can only be accessed using an object of the class.

What is the difference between abstract class and interface in Java?

In Java, both abstract classes and interfaces are used to define abstract behavior, but they have some differences in terms of their implementation and usage. Here are some key differences:

Implementation: An abstract class can have both abstract and non-abstract methods, whereas an interface can only have abstract methods (prior to Java 8). Starting from Java 8, interfaces can also have default and static methods.

Instantiation: An abstract class cannot be instantiated directly, but must be subclassed and implemented, whereas an interface cannot be instantiated at all. Instead, it is implemented by a class that provides the implementation for all its abstract methods.

Inheritance: A class can only extend one abstract class, but can implement multiple interfaces. This is because a class is a more specific type than an interface, and can only inherit from one class at a time. On the other hand, interfaces are more general and can be implemented by multiple classes.

Access modifiers: Abstract classes can have any access modifier (public, protected, or default), whereas interface methods are always public by default.

Purpose: Abstract classes are used to define common behavior for a family of classes, whereas interfaces are used to define a set of methods that must be implemented by any class that implements the interface, regardless of its relationship to other classes.

In summary, abstract classes and interfaces in Java are both used to define abstract behavior, but they have different implementations, instantiation rules, inheritance rules, access modifiers, and purposes.

What is inheritance in Java?

In Java, inheritance is a mechanism that allows a new class (called the subclass) to be based on an existing class (called the superclass), inheriting all of its properties and behaviors. This means that the subclass can reuse the code of the superclass, avoiding the need to write the same code again. Inheritance is one of the key concepts of object-oriented programming, and is implemented in Java using the “extends” keyword.

Here are some key points about inheritance in Java:

Inheritance allows a subclass to inherit all of the properties and behaviors of the superclass, including fields, methods, and nested classes.

The subclass can also add new fields, methods, and nested classes that are specific to the subclass, and can override the methods of the superclass to provide a different implementation.

In Java, a class can only inherit from one superclass at a time, but can implement multiple interfaces.

The access level of a superclass member determines whether it can be accessed by the subclass. If a member is declared as public or protected, it can be accessed by the subclass. If it is declared as private, it cannot be accessed by the subclass.

Inheritance creates an “is-a” relationship between the subclass and the superclass, indicating that the subclass is a specific type of the superclass.

In summary, inheritance in Java is a powerful mechanism that allows a subclass to reuse the code of a superclass, while also adding its own specific behavior.

What is polymorphism in Java?

Polymorphism is a key concept in object-oriented programming that refers to the ability of an object to take on many forms. In Java, polymorphism is implemented through method overriding and method overloading.

Method overriding is a mechanism that allows a subclass to provide its own implementation of a method that is already defined in its superclass. When a subclass overrides a method, it provides a new implementation that is specific to the subclass, but with the same name and signature as the method in the superclass. When the method is called on an object of the subclass, the new implementation in the subclass is executed.

Method overloading, on the other hand, allows a class to have multiple methods with the same name, but with different parameter lists. When a method is called with different parameters, the appropriate method is selected based on the number, type, and order of the parameters. This allows a class to provide multiple methods with the same name, but with different behavior depending on the parameters.

Here are some key points about polymorphism in Java:

Polymorphism allows a single method name to be used for different implementations, depending on the type of object that the method is called on.

Polymorphism is achieved through method overriding and method overloading.

Method overriding is used to provide a new implementation of a method that is already defined in a superclass.

Method overloading is used to provide multiple methods with the same name, but with different parameter lists.

Polymorphism is one of the key features of object-oriented programming, allowing objects to be treated generically, without having to know their specific type.

In summary, polymorphism in Java is a powerful mechanism that allows different objects to be treated generically, while still providing specific behavior based on the type of the object.

What is encapsulation in Java?

Encapsulation is a key concept in object-oriented programming that refers to the practice of hiding the internal details of an object and providing a public interface for interacting with the object. In Java, encapsulation is implemented using access modifiers (public, private, protected, and default), which control the visibility of fields, methods, and nested classes.

Encapsulation has several benefits:

It allows the internal details of an object to be hidden from other parts of the program, reducing the complexity of the code and making it easier to maintain.

It allows the behavior of an object to be controlled, ensuring that it is used in a consistent and correct manner.

It enables changes to the implementation of an object to be made without affecting other parts of the program, as long as the public interface remains the same.

It promotes code reusability by providing a simple and consistent way of interacting with objects.

In Java, encapsulation is implemented using access modifiers, which determine the visibility of fields, methods, and nested classes. Here are the four access modifiers and their meanings:

Public: A public field, method, or nested class can be accessed from anywhere in the program.

Private: A private field, method, or nested class can only be accessed from within the same class.

Protected: A protected field, method, or nested class can be accessed from within the same class, as well as from any subclass and any other class in the same package.

Default: A default (also known as package-private) field, method, or nested class can be accessed from within the same class and any other class in the same package.

By using these access modifiers, Java developers can control the visibility of the internal details of an object, and provide a public interface for interacting with the object. This helps to promote good design and maintainable code.

What is the final keyword in Java?

In Java, the final keyword is used to declare a constant variable or to indicate that a class or method cannot be overridden.

When used with a variable, the final keyword indicates that the variable can only be assigned a value once and cannot be changed later. This creates a constant variable that cannot be modified during the execution of the program. For example:

final int MAX_SIZE = 10;

In this example, MAX_SIZE is a constant variable whose value cannot be changed during program execution.

When used with a class, the final keyword indicates that the class cannot be subclassed. When used with a method, it indicates that the method cannot be overridden by a subclass. This is useful in situations where you want to ensure that a certain behavior or implementation cannot be changed.

For example:

javaCopy codepublic final class MyClass {
    // class implementation
}

public class MySubClass extends MyClass { // this will result in a compilation error
    // subclass implementation
}

public class AnotherClass {
    public final void myMethod() {
        // method implementation
    }
}

public class MySubClass extends AnotherClass {
    public void myMethod() { // this will result in a compilation error
        // overridden method implementation
    }
}

In both examples, the final keyword prevents further modification of the class or method.

What is the difference between checked and unchecked exceptions in Java?

In Java, exceptions can be classified as checked or unchecked based on their handling requirements.

Checked exceptions are exceptions that are checked by the compiler at compile-time. These are exceptions that can occur during the execution of a program and the compiler requires the programmer to handle them explicitly by using try-catch blocks or by declaring them in the method signature using the throws keyword. This is done to ensure that the exception is properly handled, and to avoid the possibility of the program crashing unexpectedly. Examples of checked exceptions include IOException, SQLException, and ClassNotFoundException.

Unchecked exceptions are exceptions that are not checked by the compiler at compile-time. These are exceptions that occur due to programming errors such as null pointer exceptions, array index out of bounds exceptions, or arithmetic exceptions. These exceptions do not need to be explicitly handled by the programmer, but they should still be handled in some way to prevent the program from crashing unexpectedly. Unchecked exceptions are also known as runtime exceptions, and they are not required to be declared in the method signature. Examples of unchecked exceptions include NullPointerException, ArrayIndexOutOfBoundsException, and ArithmeticException.

The main difference between checked and unchecked exceptions is that checked exceptions are required to be handled explicitly by the programmer, while unchecked exceptions are not. This means that if a method throws a checked exception, the calling code must handle it or declare it in the method signature. On the other hand, if a method throws an unchecked exception, the calling code is not required to handle it explicitly, but it is still good practice to do so.

What is a thread in Java?

In Java, a thread is a lightweight unit of execution that can run concurrently with other threads. Threads allow multiple tasks to be executed simultaneously within a single program, and they are useful for performing tasks that can be run in parallel or that require a degree of responsiveness or interactivity.

In Java, threads are implemented using the Thread class, which provides a set of methods for creating and managing threads. To create a new thread, you can either extend the Thread class and override its run() method, or you can implement the Runnable interface and pass an instance of the class to a new Thread object. For example:


// Extending the Thread class
public class MyThread extends Thread {
public void run() {
// Thread implementation
}
}

// Implementing the Runnable interface
public class MyRunnable implements Runnable {
public void run() {
// Thread implementation
}
}

// Creating and starting a new thread
Thread thread1 = new MyThread();
thread1.start();

Thread thread2 = new Thread(new MyRunnable());
thread2.start();


When a new thread is started, it begins executing its run() method in a separate call stack. The thread can be put to sleep using the sleep() method, and it can be interrupted using the interrupt() method. In addition, threads can communicate with each other and share data using synchronized blocks and methods.

Java provides several built-in classes for working with threads, including the ThreadLocal class for creating thread-local variables, the Executor framework for managing thread pools, and the Semaphore and CountDownLatch classes for synchronizing thread execution.

In summary, a thread in Java is a unit of execution that runs concurrently with other threads and allows multiple tasks to be executed simultaneously within a single program. Threads are created using the Thread class or the Runnable interface, and they can be managed and synchronized using a variety of built-in classes and frameworks.

What is synchronization in Java?

In Java, synchronization refers to the coordination of multiple threads to ensure that they do not interfere with each other when accessing shared resources or data. When multiple threads access the same resource simultaneously, there is a risk of data corruption or inconsistent results, which can cause unexpected behavior in the program. Synchronization helps to prevent these problems by ensuring that only one thread can access the shared resource at a time.

In Java, synchronization can be achieved using the synchronized keyword, which can be applied to methods or code blocks. When a method or block is synchronized, a lock is acquired on the object or class that the method or block is associated with. This lock ensures that only one thread can execute the synchronized code at a time, and other threads must wait until the lock is released before they can execute the code. For example:


public class MyClass {
private int count = 0;

public synchronized void increment() {
    count++;
}

public synchronized int getCount() {
    return count;
}

}


In this example, the increment() and getCount() methods are synchronized, which means that only one thread can execute them at a time. This ensures that the count variable is accessed and modified atomically, and prevents any data corruption that could occur if multiple threads were to modify it simultaneously.

In addition to using the synchronized keyword, Java provides several other synchronization constructs, such as locks, semaphores, and barriers, which can be used to coordinate the execution of multiple threads.

It’s important to note that synchronization can cause performance overheads, as threads may have to wait for access to shared resources. Therefore, it’s important to use synchronization judiciously and only when necessary, and to consider alternative approaches, such as using thread-safe data structures or using immutable data, where possible.

What is the difference between wait and sleep in Java?

In Java, both wait() and sleep() methods are used for pausing the execution of a program, but they have some differences in their functionality.

The main differences between wait() and sleep() methods in Java are:

wait() is a method of Object class whereas sleep() is a method of Thread class.
wait() method is used to wait for a particular condition to occur, while sleep() method is used to pause the execution of the current thread for a specified amount of time.
wait() method releases the lock on the object on which it is called, while sleep() method does not release any lock.
wait() method is used in synchronization and multithreading, while sleep() method is used for time-based operations.
Here is an example to illustrate the differences:


synchronized void methodA() {
// Some code here
wait(); // Wait for some other thread to notify this thread
// Some more code here
}

void methodB() {
// Some code here
try {
Thread.sleep(1000); // Pause this thread for 1 second
} catch(InterruptedException e) {
// Handle the exception here
}
// Some more code here
}
In the above example, methodA() uses wait() to wait for some other thread to notify it, while methodB() uses sleep() to pause the execution of the current thread for 1 second.

What is a singleton class in Java?

In Java, a Singleton class is a class that can have only one instance created at any given time. This pattern is used to ensure that there is only one instance of the class throughout the entire program.

To create a Singleton class, we typically follow these steps:

Make the constructor of the class private, so that no other class can create an instance of it.
Create a static variable of the class type, which will hold the only instance of the class.
Create a static method that will return the instance of the class. This method should create the instance if it has not been created before, and return the existing instance if it has already been created.
Here is an example of a Singleton class in Java:


public class MySingleton {
private static MySingleton instance = null;

private MySingleton() {
// Private constructor to prevent instantiation from outside
}

public static MySingleton getInstance() {
if(instance == null) {
instance = new MySingleton();
}
return instance;
}

public void showMessage() {
System.out.println(“Hello World!”);
}
}
In the above example, the constructor of MySingleton class is private, so no other class can create an instance of it. The getInstance() method is a static method that returns the instance of the class. If the instance has not been created before, it creates a new instance, otherwise it returns the existing instance. The showMessage() method is a public method that can be called on the Singleton instance to perform some action.

What is the use of the volatile keyword in Java?

In Java, the volatile keyword is used to indicate that a variable’s value may be modified by multiple threads, and that changes to the variable should be immediately visible to other threads.

When a variable is declared as volatile, its value is always read from and written to main memory, rather than being cached in a thread’s local memory. This ensures that any change made to the variable by one thread is immediately visible to all other threads that access that variable.

Here are some common use cases of the volatile keyword:

Synchronization: If a variable is shared among multiple threads and its value is modified by one thread and read by another, it is necessary to use synchronization to ensure that the changes are properly propagated. By declaring the variable as volatile, we can avoid the need for explicit synchronization.

Double-checked locking: The volatile keyword can also be used in the double-checked locking idiom, which is a technique for lazy initialization of objects. In this pattern, a volatile variable is used to ensure that the object is properly initialized before it is returned.

Performance optimization: In some cases, using volatile variables can provide performance benefits over other synchronization mechanisms, since it avoids the overhead of acquiring and releasing locks.

It is important to note that while the volatile keyword provides some guarantees around thread-safety and visibility, it does not provide atomicity guarantees. If multiple threads are modifying the same variable, additional synchronization mechanisms such as locks or atomic variables should be used to ensure that the changes are properly synchronized.

What is the difference between an array and an ArrayList in Java?

In Java, both arrays and ArrayLists are used to store a collection of elements, but they have some differences in their functionality.

Size: Arrays have a fixed size, which is determined at the time of creation, while ArrayLists can dynamically grow or shrink in size as elements are added or removed.

Type: Arrays can only store elements of a single data type, while ArrayLists can store elements of any data type.

Memory allocation: In Java, arrays are allocated as a contiguous block of memory, while ArrayLists are implemented as a resizable array and dynamically allocate memory as needed.

Methods: Arrays have a limited set of methods available, such as length to get the size, while ArrayLists provide a rich set of methods for adding, removing, and manipulating elements.

Here is an example to illustrate the differences:
// Creating an array of integers
int[] arr = new int[5];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;

// Creating an ArrayList of integers
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);

// Accessing elements
int x = arr[2]; // Accessing the third element of the array
int y = list.get(2); // Accessing the third element of the ArrayList

// Adding elements
arr[5] = 6; // This will result in an ArrayIndexOutOfBoundsException
list.add(6); // This will add a sixth element to the ArrayList

// Removing elements
arr[2] = 0; // This will set the third element of the array to 0
list.remove(2); // This will remove the third element of the ArrayList


In the above example, we created an array of integers and an ArrayList of integers, and then demonstrated how to access, add, and remove elements from both data structures. Note that arrays have a fixed size, and attempting to add or remove elements beyond the size of the array will result in an exception, while ArrayLists can dynamically grow or shrink in size.

What is a HashMap in Java?

In Java, HashMap is a class that implements the Map interface and provides a way to store and manipulate key-value pairs. It uses a hash table to store the data and provides constant-time performance for basic operations such as put, get and remove.

The HashMap class allows you to store key-value pairs of any non-primitive data type. Each key is unique and maps to a corresponding value. The keys are used to retrieve the corresponding values from the HashMap.

Here is an example of how to use a HashMap:


HashMap map = new HashMap();

// Adding elements to the map
map.put(“John”, 25);
map.put(“Mary”, 30);
map.put(“David”, 35);

// Accessing elements in the map
int age = map.get(“Mary”); // age = 30

// Updating an element in the map
map.put(“David”, 40);

// Removing an element from the map
map.remove(“John”);

// Iterating over the elements in the map
for(Map.Entry entry : map.entrySet()) {
String key = entry.getKey();
int value = entry.getValue();
System.out.println(key + ” = ” + value);
}
In the above example, we created a HashMap that maps strings to integers. We added three key-value pairs to the map, accessed the value associated with the key “Mary”, updated the value associated with the key “David”, removed the key-value pair associated with the key “John”, and iterated over the remaining elements in the map.

What is the difference between a HashSet and a TreeSet in Java?

In Java, both HashSet and TreeSet are used to store a collection of unique elements, but they have some differences in their functionality.

  1. Ordering: HashSet does not maintain any order of the elements, while TreeSet maintains the natural ordering of the elements or a custom ordering specified by the user.
  2. Implementation: HashSet is implemented using a hash table, while TreeSet is implemented using a self-balancing binary search tree (specifically, a Red-Black tree).
  3. Performance: HashSet provides faster access and retrieval of elements as it is implemented using hashing, while TreeSet provides slower access and retrieval times as it needs to traverse the binary search tree.
  4. Null elements: HashSet allows null elements, while TreeSet does not allow null elements.

Here is an example to illustrate the differences:

// Creating a HashSet and adding elements
HashSet<String> set1 = new HashSet<String>();
set1.add("apple");
set1.add("banana");
set1.add("orange");

// Creating a TreeSet and adding elements
TreeSet<String> set2 = new TreeSet<String>();
set2.add("apple");
set2.add("banana");
set2.add("orange");

// Iterating over the elements in the sets
for (String element : set1) {
    System.out.println(element);
}

for (String element : set2) {
    System.out.println(element);
}

In the above example, we created a HashSet and a TreeSet, added the same elements to both sets, and then iterated over the elements in each set. Note that the HashSet did not maintain any order, while the TreeSet maintained the natural ordering of the elements (in this case, alphabetical order).