Interview Preparation

Java Interview Questions

Master the most commonly asked interview questions with comprehensive, expert-crafted answers designed to help you succeed.

25
Questions
100%
Expert Answers
Q1
Is Java Platform Independent? If then how?

Yes, Java is platform-independent. This means that Java code can run on any operating system or device that has a Java Virtual Machine (JVM).

How It Works:

  • Java source code (.java files) is compiled by the Java compiler into bytecode (.class files).
  • This bytecode is not platform-specific. Instead, it is an intermediate representation that can be executed on any system with a JVM.
  • Each operating system has its own implementation of the JVM that interprets the bytecode and converts it into machine code suitable for that system.

Compilation & Execution Process:

Source Code (.java)
     ⬇️ Compile
Bytecode (.class)
     ⬇️ Run on
Java Virtual Machine (JVM)
     ⬇️
Machine Code (OS/Hardware specific)

This model enables the famous Java slogan: "Write Once, Run Anywhere".

Q2
What are the top Java Features?

Java is one of the most widely used programming languages due to its powerful features. Here are the top features of Java:

  • Platform Independent: Java code runs on any machine that has the JVM. Compile once, run anywhere.
  • Object-Oriented: Java uses classes and objects, and supports concepts like inheritance, encapsulation, and polymorphism.
  • Simple: Java has a clean, readable syntax similar to C++, but without complex features like pointers.
  • Secure: Java runs inside a virtual machine sandbox and includes built-in security mechanisms.
  • Robust: Java has strong memory management, garbage collection, and error-handling capabilities.
  • Multithreaded: Java supports multiple threads, allowing efficient multitasking and better CPU utilization.
  • High Performance: Bytecode is optimized by Just-In-Time (JIT) compiler for faster execution.
  • Distributed: Java supports RMI and networking features out of the box.
  • Dynamic: Java loads classes dynamically during runtime.
Q3
What is JIT?

JIT stands for Just-In-Time compiler. It is a part of the Java Runtime Environment (JRE) and plays a crucial role in improving the performance of Java applications. When a Java program is executed, the source code is first compiled into bytecode by the Java compiler. This bytecode is then interpreted by the Java Virtual Machine (JVM). However, interpreting bytecode line-by-line can be slow, especially for performance-critical applications.

The JIT compiler solves this problem by compiling the bytecode into native machine code at runtime, just before the code is executed. This process is called Just-In-Time compilation. Once compiled, the native code is stored and reused whenever the method is called again, which greatly improves the execution speed compared to interpreting the bytecode every time.

JIT works in conjunction with the HotSpot engine in the JVM, which identifies frequently used or 'hot' code paths and optimizes them for faster execution. The JIT compiler can perform various optimizations like inlining methods, loop unrolling, and removing dead code, which further enhances performance.

Q4
Difference between JVM, JRE, and JDK.

Here is the difference between JVM, JRE, and JDK in Java:

  • JVM (Java Virtual Machine): The JVM is responsible for executing the Java bytecode. It abstracts the platform-specific details and allows Java code to be run on any device or OS with a JVM.
  • JRE (Java Runtime Environment): JRE provides the environment to run Java applications. It contains the JVM and standard Java class libraries, but it does not include tools for developing Java applications.
  • JDK (Java Development Kit): JDK is the full software development kit used to develop Java programs. It includes the JRE, along with tools like the compiler (javac), debugger, and other development utilities.

Visual Representation:

JDK
 ├── JRE
 │    ├── JVM
 │    └── Core Java Libraries
 └── Development Tools (javac, debugger, etc.)

So, to run Java programs, you need the JRE. To develop them, you need the JDK.

Q5
What is Java String Pool?

The Java String Pool is a special memory region where Java stores String literals. This is done to save memory and improve performance when working with strings.

How It Works:

  • When you create a String using double quotes ("Hello"), Java checks if the string already exists in the pool.
  • If it does, it reuses the same object (reference).
  • If it doesn't, it creates a new string and stores it in the pool.

Code Example:

public class Main {
    public static void main(String[] args) {
        String a = "Hello";
        String b = "Hello";
        String c = new String("Hello");

        System.out.println(a == b); // true
        System.out.println(a == c); // false
    }
}

a == b is true because both point to the same object in the String pool.
But a == c is false because new String() creates a new object in heap memory, not the pool.

Note: String pool helps reduce memory usage when you use many identical string values in your program.

Q6
When a byte datatype is used?

A byte is an 8-bit signed two-complement integer. The minimum value supported by bytes is -128 and 127 is the maximum value. It is used in conditions where we need to save memory and the limit of numbers needed is between -128 to 127.

Q7
Can we declare Pointer in Java?

No, Java doesn't provide the support of Pointer. As Java needed to be more secure because which feature of the pointer is not provided in Java.

Q8
What is the Wrapper class in Java?

In Java, a wrapper class is an object representation of a primitive data type. Java is an object-oriented language, and sometimes it is necessary to use objects instead of primitive types, especially when working with collections like ArrayList or HashMap, which only work with objects. Wrapper classes allow primitive data types to be treated as objects by 'wrapping' them in a class.

Java provides a wrapper class for each of the eight primitive types: int becomes Integer, char becomes Character, boolean becomes Boolean, float becomes Float, and so on. These classes are located in the java.lang package and provide several useful methods to perform operations on the values.

Wrapper classes are essential for features such as autoboxing and unboxing. Autoboxing is the automatic conversion of a primitive type into its corresponding wrapper class when an object is required. Unboxing is the reverse, where the wrapper object is automatically converted back to its primitive type. For example, assigning an int value to an Integer object is autoboxing, and retrieving the int from that object is unboxing.

Q9
What are Packages in Java?

In Java, a package is a namespace that organizes a set of related classes and interfaces. Just like folders in a file system, packages help to avoid name conflicts and to control access. They also make it easier to locate and use the classes, interfaces, and sub-packages that are part of a larger project or Java API.

There are two types of packages in Java: built-in packages and user-defined packages. Built-in packages are provided by the Java Development Kit (JDK) and include common functionality like java.util for data structures, java.io for input/output operations, and java.sql for database access. On the other hand, user-defined packages are created by developers to group their own classes logically, helping improve modularity and maintainability of the code.

Packages also help in controlling access to classes and methods through access modifiers like public, protected, and private. For instance, classes in the same package can access each other's package-private members, which helps in designing encapsulated components without exposing everything to the outside world.

Using packages also supports reusability. Once a package is created, it can be imported and reused in other programs easily. This enables developers to share code across different modules of a large project, or even across multiple projects.

In conclusion, packages in Java are essential for organizing code, avoiding naming conflicts, providing controlled access, and promoting reusability. They are a fundamental part of Java’s architecture and play a significant role in creating scalable and maintainable applications.

Q10
What is the difference between System.out, System.err, and System.in?

System.out: It is a PrintStream that is used for writing characters or can be said it can output the data we want to write on the Command Line Interface console/terminal.

System.out Example:

// Java Program to implement System.out
import java.io.*;
class IQ {
    public static void main(String[] args) {
        System.out.println("This is a System.out message");
    }
}

System.err: It is also a PrintStream but is used to print error messages (standard error).

System.err Example:

// Java program to demonstrate System.err
import java.io.*;
class IQ {
    public static void main(String[] args) {
        System.err.println("This is how we throw error with System.err");
    }
}

Output:

This is how we throw error with System.err

System.in: It is an InputStream used to read input from the terminal Window. We can't use the System.in directly so we use Scanner class for taking input with the system.in.

System.in Example:

// Java Program to demonstrate System.in
import java.util.*;
class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int x = sc.nextInt();
        int y = sc.nextInt();
        System.out.printf("Addition: %d", x + y);
    }
}

Output:

3
4
Addition: 7
Q11
Can Java be said to be the complete object-oriented programming language?

Java is not considered 100% object-oriented. While it supports all major object-oriented features like encapsulation, inheritance, polymorphism, and abstraction, but it still uses primitive data types such as int, float, char, etc., which are not objects.

This violates the object-oriented principle that everything should be an object.

Q12
What is a static variable?
The static keyword is used to share the same variable or method of a given class. Static variables are the variables that once declared then a single copy of the variable is created and shared among all objects at the class level.
Q13
How do you swap two numbers without using a third variable in Java?

You can swap two numbers without using a third variable by using arithmetic operations like addition and subtraction or XOR bitwise operation. Here is an example using addition and subtraction:

Example Code (Addition and Subtraction):

public class SwapNumbers {
    public static void main(String[] args) {
        int a = 5, b = 10;
        System.out.println("Before Swap: a = " + a + ", b = " + b);

        a = a + b;
        b = a - b;
        a = a - b;

        System.out.println("After Swap: a = " + a + ", b = " + b);
    }
}

// Output:
Before Swap: a = 5, b = 10
After Swap: a = 10, b = 5

This works because values are updated mathematically without needing a temporary third variable.

Q14
Are there dynamic arrays in Java?

Java does not have built-in dynamic arrays like Python lists, but it provides a class called ArrayList in the java.util package that acts like a dynamic array. Unlike traditional arrays, ArrayList can grow and shrink in size automatically as elements are added or removed.

Example Code:

import java.util.ArrayList;

public class DynamicArrayExample {
    public static void main(String[] args) {
        ArrayList<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");
        System.out.println("Fruits List: " + fruits);
        fruits.remove("Banana");
        System.out.println("After Removing Banana: " + fruits);
    }
}

// Output:
Fruits List: [Apple, Banana, Orange]
After Removing Banana: [Apple, Orange]

Use ArrayList when you need resizable arrays in Java. It's part of the Java Collection Framework.

Q15
Why is reflection used in Java?

Reflection in Java is a feature that allows a program to inspect or modify its own runtime behavior. It is part of the java.lang.reflect package and is commonly used for:

  • Inspecting classes, methods, fields, and constructors at runtime
  • Instantiating objects dynamically
  • Invoking methods during runtime without knowing their names at compile-time
  • Building frameworks, libraries, and tools (e.g., Spring, JUnit)

Example Code:

import java.lang.reflect.Method;

class Demo {
    public void show() {
        System.out.println("Reflection in Java works!");
    }
}

public class ReflectionExample {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("Demo");
        Object obj = cls.getDeclaredConstructor().newInstance();
        Method method = cls.getMethod("show");
        method.invoke(obj);
    }
}

// Output:
Reflection in Java works!

Note: While powerful, reflection should be used carefully as it may break encapsulation, impact performance, and bypass compile-time checks.

Q16
Does every try block need a catch block?

No, every try block does not need a catch block. The try block can be succeeded by either a catch block, a finally block, or even both. A catch block is needed for catching the exceptions raised in try block. The absence of a catch block after a try block, though syntactically correct, is not much of use practically. The finally block might be needed after try block if some critical operations like closing a file needs to be done.

There are three valid combinations:

  • try + catch
  • try + finally
  • try + catch + finally

Example Code (try with finally only):

public class TryFinallyExample {
    public static void main(String[] args) {
        try {
            System.out.println("Inside try block");
        } finally {
            System.out.println("Inside finally block");
        }
    }
}

// Output:
Inside try block
Inside finally block
Q17
What is the difference between String, StringBuilder, and StringBuffer?

In Java, String, StringBuilder, and StringBuffer are three distinct classes used to work with textual data. While they seem similar, they differ in mutability, performance, and thread safety.

1. String (Immutable):

A String is immutable in Java, which means that once a String object is created, its value cannot be changed. Any operation that seems to change the string (e.g., concat()) actually returns a new String object.

This immutability makes String objects safe for multithreading and allows JVM to optimize memory via the String pool.

2. StringBuilder (Mutable, Not Thread-safe):

StringBuilder is a mutable class, meaning you can change the contents without creating a new object. It provides better performance in single-threaded environments due to the absence of synchronization.

Best use case: When you need to build or modify strings inside a loop or method in a single-threaded application.

3. StringBuffer (Mutable, Thread-safe):

StringBuffer is also mutable like StringBuilder but it is synchronized, which makes it thread-safe. However, synchronization adds overhead, so it's slower than StringBuilder in single-threaded scenarios.

Best use case: When multiple threads are modifying the same string data concurrently.

Java Example:

public class StringTypesDemo {
    public static void main(String[] args) {
        // String: Immutable
        String s = "Hello";
        s.concat(" World");
        System.out.println("String: " + s);

        // StringBuilder: Mutable, not thread-safe
        StringBuilder sb = new StringBuilder("Hello");
        sb.append(" World");
        System.out.println("StringBuilder: " + sb);

        // StringBuffer: Mutable, thread-safe
        StringBuffer sbf = new StringBuffer("Hello");
        sbf.append(" World");
        System.out.println("StringBuffer: " + sbf);
    }
}

// Output:
String: Hello
StringBuilder: Hello World
StringBuffer: Hello World
Q18
Describe the Singleton pattern and provide an example of a thread-safe implementation in Java.

The Singleton pattern is a widely used design pattern in Java that restricts the instantiation of a class to a single object. It ensures that only one instance of the class exists throughout the lifecycle of the application. This is useful when exactly one object is needed to coordinate actions across a system, such as for managing shared resources like database connections, logging, or configuration settings.

To implement the Singleton pattern, the class constructor is made private so that it cannot be instantiated from outside the class. A static method is provided to return the instance, ensuring that only one object is created. In a multithreaded environment, care must be taken to make the Singleton implementation thread-safe. One common approach is to use lazy initialization with double-checked locking, where the instance is created only when needed, and access is synchronized to prevent multiple threads from creating different instances simultaneously.

The following is an example of a thread-safe Singleton implementation in Java using double-checked locking and the volatile keyword:

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {
        System.out.println("Singleton instance created");
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    public void showMessage() {
        System.out.println("Hello from Singleton");
    }
}

public class Main {
    public static void main(String[] args) {
        Singleton obj = Singleton.getInstance();
        obj.showMessage();
    }
}

// Output:
Singleton instance created
Hello from Singleton

This implementation ensures that only one instance of the Singleton class is ever created, even when multiple threads access it concurrently. The volatile keyword prevents memory consistency errors, and the synchronized block ensures that the instantiation is atomic. This pattern promotes efficient resource usage and controlled access, making it a robust solution in concurrent programming scenarios.

Q19
What is the default value stored in Local Variables?

There is no default value stored with local variables. Also, primitive variables and objects don't have any default values.

Q20
What are the super most classes for all the streams?

All the stream classes in Java are broadly categorized into two types:

  • Byte Stream Classes
  • Character Stream Classes

The Byte Stream classes are further divided into:

  • InputStream classes – for reading bytes
  • OutputStream classes – for writing bytes

The Character Stream classes are similarly divided into:

  • Reader classes – for reading characters
  • Writer classes – for writing characters

Supermost classes for each category:

  • java.io.InputStream – Superclass for all byte input streams
  • java.io.OutputStream – Superclass for all byte output streams
  • java.io.Reader – Superclass for all character input streams
  • java.io.Writer – Superclass for all character output streams

These classes form the backbone of Java's I/O (Input/Output) framework. Every file or stream handling class in Java inherits from one of these four abstract superclasses.

Q21
What are the differences between Comparable and Comparator interfaces? When would you use each?

In Java, both Comparable and Comparator are used for sorting objects, but they differ in how and where sorting logic is implemented. The Comparable interface is used when you want to define the default or natural ordering of objects of a class. A class that implements Comparable overrides the compareTo() method, and this logic is used whenever objects of that class need to be sorted using Collections.sort() or Arrays.sort() without any external comparator.

On the other hand, the Comparator interface is used to define custom orderings for objects that may be different from the natural ordering. It is useful when you want to sort the same objects in different ways, or when you cannot modify the original class (such as in third-party libraries). You pass a Comparator implementation to sorting methods like Collections.sort() or Arrays.sort() to control how the sorting should be done.

The following examples demonstrate the difference:

Comparable Example:

public class Student implements Comparable<Student> {
    int rollNo;
    String name;

    public Student(int rollNo, String name) {
        this.rollNo = rollNo;
        this.name = name;
    }

    @Override
    public int compareTo(Student s) {
        return this.rollNo - s.rollNo;
    }
}

// Usage:
List<Student> list = new ArrayList<>();
list.add(new Student(3, "Alice"));
list.add(new Student(1, "Bob"));
Collections.sort(list);

Comparator Example:

class NameComparator implements Comparator<Student> {
    public int compare(Student s1, Student s2) {
        return s1.name.compareTo(s2.name);
    }
}

// Usage:
Collections.sort(list, new NameComparator());
Q22
How many ways you can take input from the console?

In Java, there are several ways to take input from the console. Each approach is suited to different needs and use cases:

  1. Using Scanner class (most common):
    The java.util.Scanner class is widely used to read input from the console. It supports reading strings, integers, floating-point numbers, etc.
    import java.util.Scanner;
    public class InputExample {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            System.out.print("Enter your name: ");
            String name = sc.nextLine();
            System.out.println("Hello, " + name);
        }
    }
  2. Using BufferedReader with InputStreamReader:
    This method is faster and more efficient for reading large input but requires manual type conversion.
    import java.io.*;
    public class InputExample {
        public static void main(String[] args) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            System.out.print("Enter your age: ");
            int age = Integer.parseInt(reader.readLine());
            System.out.println("Age entered: " + age);
        }
    }
  3. Using Console class:
    This method is suitable for secure input (like passwords). It may return null in some IDEs (e.g., Eclipse, IntelliJ) that do not support console.
    import java.io.Console;
    public class InputExample {
        public static void main(String[] args) {
            Console console = System.console();
            if (console != null) {
                String name = console.readLine("Enter your name: ");
                System.out.println("Welcome, " + name);
            } else {
                System.out.println("Console not available");
            }
        }
    }

Summary: Java provides three primary ways to take input from the console: Scanner (most user-friendly), BufferedReader (for faster input), and Console (for secure input). Each has its pros and cons depending on the application context.

Q23
What is the purpose of the synchronized keyword in Java? What are its limitations?

The synchronized keyword in Java is used to ensure that only one thread can execute a particular section of code at a time. It provides a mechanism to achieve thread safety by controlling access to critical sections in a multithreaded environment. By locking an object or class during execution, it helps avoid race conditions, where two or more threads might simultaneously update shared data in an unpredictable way.

Synchronization can be applied in the following ways:

  • If a method is marked as synchronized, the thread holds a lock for that method’s object. This prevents other threads from executing any other synchronized method on the same object.
  • If a static method is marked as synchronized, the lock is on the class object, so it restricts access across all instances of the class.
  • Synchronization can also be applied to a specific block of code within a method using a synchronized block, allowing more fine-grained control over what gets locked.

Below is an example that demonstrates the use of the synchronized keyword:

public class Counter {
    private int count = 0;

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

    public int getCount() {
        return count;
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) counter.increment();
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) counter.increment();
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("Final Count: " + counter.getCount());
    }
}

Limitations of the synchronized keyword:

  • Performance Overhead: Synchronization adds overhead because it requires acquiring and releasing locks, which can degrade performance, especially in high-concurrency applications.
  • Potential Deadlocks: Improper use of synchronized blocks can lead to deadlocks, where two or more threads wait indefinitely for each other to release locks.
  • Lack of Flexibility: The synchronized keyword does not provide advanced locking features such as try-lock, timed lock, or interruptible locks, which are available in more flexible constructs like ReentrantLock.
  • Blocking Nature: When one thread holds the lock, other threads attempting to access the synchronized code are blocked until the lock is released, potentially reducing system throughput.
Q24
How do you iterate through a collection in Java?

In Java, collections such as List, Set, and Map can be iterated in several ways. Iterating through a collection is a fundamental operation and Java provides multiple mechanisms to achieve it depending on the use case. The most common approaches are: using a for-each loop, using an Iterator, and using a for loop with indexing (for lists).

1. Enhanced for-each loop:

This is the most readable and concise way to iterate through collections that implement Iterable, such as ArrayList and HashSet.

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
for (String name : names) {
    System.out.println(name);
}

2. Using Iterator:

The Iterator interface provides a way to iterate over a collection with more control, such as safely removing elements during iteration.

List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");

Iterator<String> it = names.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

3. Using traditional for loop (for List only):

If you need access to indexes or wish to modify elements at specific positions, the traditional for loop is useful.

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
for (int i = 0; i < names.size(); i++) {
    System.out.println(names.get(i));
}
Q25
What are the differences between ArrayList and LinkedList? When would you choose one over the other?

In Java, both ArrayList and LinkedList are implementations of the List interface, but they differ significantly in terms of their internal structure and performance characteristics. ArrayList is backed by a dynamic array, which means it allows for fast random access to elements using an index. This makes it very efficient for reading data or accessing elements in constant time. However, inserting or deleting elements—especially in the middle or beginning of the list—can be inefficient, as it requires shifting subsequent elements to maintain order.

On the other hand, LinkedList is implemented as a doubly linked list. Each element in a LinkedList is stored in a node that contains pointers to both the previous and next elements. This structure makes LinkedList more efficient for insertion and deletion operations, particularly at the beginning or in the middle of the list. However, accessing elements by index is slower compared to ArrayList, because it requires traversal from the head of the list until the desired position is reached.

Another difference lies in memory usage. ArrayList is generally more memory efficient because it only stores the data itself, whereas LinkedList requires additional memory to store the pointers for each node. Due to these differences, ArrayList is typically chosen when the primary operation is accessing or iterating over elements, and when memory efficiency is important. LinkedList, however, is preferred when the application involves frequent insertion and deletion of elements, especially at the start or in the middle of the list.

In conclusion, the choice between ArrayList and LinkedList should be based on the specific performance requirements of the application. If quick access to elements is more critical, ArrayList is ideal. If the program involves frequent structural modifications, LinkedList may be a better fit.

Why Choose Our Question Bank?

Get access to expertly crafted answers and comprehensive preparation materials

Complete Collection

Access all 25 carefully curated questions covering every aspect of Java interviews

Expert Answers

Get detailed, professional answers crafted by industry experts with real-world experience

Instant Access

Start preparing immediately with instant access to all questions and answers after sign-up