Top 25 Java Interview Q&A with Practical Test | Try Personalized AI Help

Join our community to see how developers are using Workik AI everyday.

Interview Question & Answers for Senior Developers

Q1. What are the main principles of OOP and how do they relate to Java?

The main principles of Object-Oriented Programming (OOP) are encapsulation, inheritance, polymorphism, and abstraction.
1. Encapsulation: Achieved using access modifiers to restrict access to the internal state of objects.
2. Inheritance: Allows a class to inherit properties and methods from another class using the extends keyword.
3. Polymorphism: Enables a single interface to be used for different data types, achieved through method overriding and overloading.
4. Abstraction: Allows hiding complex implementation details and showing only the necessary features, using abstract classes and interfaces.

Understanding these principles through example:

public class Main {
    public static void main(String[] args) {
        // Encapsulation
        Animal animal = new Animal("Generic Animal", 5);
        System.out.println("Animal: " + animal.getName() + ", Age: " + animal.getAge());
        animal.setName("New Animal");
        System.out.println("Updated Animal: " + animal.getName());

        // Inheritance and Polymorphism (Method Overriding)
        Dog dog = new Dog("Buddy", 3, "Golden Retriever");
        System.out.println("Dog: " + dog.getName() + ", Breed: " + dog.getBreed());
        dog.speak(); // Dog's overridden method
        dog.eat("bones");

        // Abstraction
        Cat cat = new Cat();
        cat.makeSound();
        cat.sleep();
        cat.play();
    }
}


}

Q2. Explain the concept of Java Memory Management and Garbage Collection.

Java Memory Management

Java memory management involves allocating and deallocating memory for objects in a way that optimizes performance and avoids memory leaks.

1. Heap

This is the runtime data area from which memory for all class instances and arrays is allocated. It is divided into:

  • Young Generation : Where new objects are allocated. It consists of:
    • Eden Space : Most new objects are created here.
    • Survivor Spaces (S0 and S1) : Objects that survive garbage collection in Eden are moved here.
  • Old Generation (Tenured Generation) : Holds objects that have been alive for a long time.
  • Permanent Generation (Metaspace) : Stores metadata about classes and methods. Since Java 8, it's been replaced by Metaspace, which grows dynamically.
2. Stack

Each thread has its own stack, storing local variables and partial results. It also plays a part in method invocation and return.

Garbage Collection

Garbage collection (GC) is the process by which Java programs perform automatic memory management. The JVM (Java Virtual Machine) automatically deletes objects that are no longer reachable in order to reclaim memory.

  • Generational Garbage Collection : Based on the observation that most objects die young.
    • Minor GC : Collects objects in the Young Generation. This is a frequent and fast process.
    • Major GC (Full GC) : Collects objects in the Old Generation. This is less frequent but more time-consuming.
  • Garbage Collection Algorithms :
    • Serial GC : Uses a single thread to perform all garbage collection work, suitable for small applications.
    • Parallel GC (Throughput Collector) : Uses multiple threads for garbage collection, improving performance for multi-threaded applications.
    • CMS (Concurrent Mark-Sweep) GC : Aims to minimize pause times by performing most of the garbage collection work concurrently with the application threads.
    • G1 (Garbage First) GC : Divides the heap into regions and prioritizes garbage collection in regions with the most garbage, aiming to balance pause times and throughput.

Q3. What is the difference between == and equals() in Java?

In Java, == and equals() are used to compare objects, but they do so in different ways and for different purposes.
This images tells us about the difference between == and equals() in Java
Understanding these difference through example:

    public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    // Override the equals() method to compare Person objects by name
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        Person person = (Person) obj;
        return name.equals(person.name);
    }

    public static void main(String[] args) {
        // Create two different Person objects with the same name
        Person p1 = new Person("Alice");
        Person p2 = new Person("Alice");

        // Compare using ==
        System.out.println("Using == to compare p1 and p2: " + (p1 == p2)); // false

        // Compare using equals()
        System.out.println("Using equals() to compare p1 and p2: " + p1.equals(p2)); // true

        // Create another reference to p1
        Person p3 = p1;

        // Compare p1 and p3 using ==
        System.out.println("Using == to compare p1 and p3: " + (p1 == p3)); // true

        // Compare p1 and p3 using equals()
        System.out.println("Using equals() to compare p1 and p3: " + p1.equals(p3)); // true
    }
}




Q4. Describe how Java achieves platform independence.

Java achieves platform independence through a combination of its design philosophy, the Java Virtual Machine (JVM), and its bytecode.


Process of Platform Independence
  • Write Once, Compile Once:
    • Developers write Java code once and compile it into bytecode using the Java compiler.
    • The bytecode is platform-independent and can be distributed to any platform.
  • Execute Anywhere:
    • The bytecode can be executed on any device or operating system that has a compatible JVM.
    • The JVM translates the bytecode into machine code appropriate for the underlying hardware and operating system at runtime.


This is a flow chart representing how Java achieves platform independence

  • Bytecode (.class):
    • The compiled bytecode is platform-independent and can be executed on any platform with a compatible JRE.
  • Java Runtime Environment (JRE):
    • Each platform (Windows, Linux, macOS, etc.) has its own JRE.
    • The JRE includes the JVM and the necessary standard libraries to run Java applications.
  • Execute with JRE:
    • The JRE on each platform interprets and executes the Java bytecode.
    • The JVM within the JRE translates the bytecode into machine-specific instructions at runtime, allowing the Java program to run on any platform.
Q5. What is the difference between ArrayList and LinkedList in Java?

The table below shows the difference between ArrayList and LinkedList in Java?

This is the image which shows difference between ArrayList and LinkedList in Java in tabular format

Request question

Please fill in the form below to submit your question.

Q6. Explain the concept of thread synchronization in Java.

Thread synchronization in Java is a mechanism to control the access of multiple threads to shared resources. It ensures that only one thread can access the resource at a time, preventing data inconsistency and race conditions.

Key Components of Thread Synchronization:

  1. Synchronized Methods:
    • Only one thread can execute a synchronized method of an object at a time.
  2. Synchronized Blocks:
    • A synchronized block is used to synchronize a specific section of code within a method.
  3. Intrinsic Locks (Monitors):
    • Every object in Java has an intrinsic lock or monitor. When a thread enters a synchronized method or block, it acquires the lock. It releases the lock when it exits the method or block.
  4. Locking Mechanism:
    • If a thread tries to enter a synchronized method or block while another thread has the lock, it is blocked until the lock is released.
This is a flow chart explaining concept of thread synchronization in Java

Q7. What are Java Streams and how are they used?

Java Streams, introduced in Java 8, are a powerful tool for processing sequences of elements in a functional style. They allow developers to perform complex data manipulations and transformations with concise and readable code. Streams support operations such as filtering, mapping, and reducing, and they can work with collections, arrays, and other data sources.

Key Concepts of Java Streams:

  • Stream Creation:
    • Streams can be created from collections, arrays, or other data sources.
  • Intermediate Operations:
    • These operations transform a stream into another stream and are lazy, meaning they are not executed until a terminal operation is invoked.
  • Terminal Operations:
    • These operations produce a result or a side-effect and trigger the execution of the intermediate operations.

Example: Using Java Streams

Here is an example that combines several stream operations:


import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Filter even numbers, square them, and collect the result into a list
List
 evenSquares = numbers.stream()
.filter(n -> n % 2 == 0)       // Intermediate operation: filter
.map(n -> n * n)               // Intermediate operation: map
.collect(Collectors.toList()); // Terminal operation: collect
System.out.println(evenSquares); // Output: [4, 16, 36, 64, 100]
}
}

Here's a simplified flow chart of Stream Processing

Asimplified flowchart of Stream Processing

Q8. What is the purpose of the volatile keyword in Java?

The volatile keyword in Java ensures visibility and ordering of variable updates across threads. It guarantees that:

  • Visibility:
    • Ensures that changes to a variable are always visible to other threads.
    • When a variable is declared as volatile, any read of that variable will always return the most recent write by any thread.
  • Ordering:
    • Prevents certain types of compiler optimizations that could reorder instructions.
    • The volatile keyword ensures that reads and writes to the variable are done in the order they appear in the code.

However, volatile does not guarantee atomicity for compound actions like incrementing a variable. It is primarily used for flags or state variables accessed by multiple threads.

Q9. Describe the use of try-with-resources in Java.

The use of try-with-resources in Java

  • Automatic Resource Management:
    • Automatically closes resources after the program is finished with them.
    • Resources declared within the try parentheses are automatically closed when the try block exits, regardless of whether the exit is due to normal execution or an exception.
  • Syntax:
    • The try keyword is followed by parentheses that declare one or more resources.
    • Resources must implement the AutoCloseable interface, which includes the close() method.
  • Resource Declaration:
    • Resources can be any object that implements AutoCloseable , such as InputStream , OutputStream , Reader , Writer , Connection , Statement , etc.
  • Automatic Closing:
    • Ensures that each resource is closed at the end of the statement.
    • Reduces the risk of resource leaks by guaranteeing that close() is called on the resource, even if an exception occurs.
  • Exception Handling:
    • Simplifies exception handling by managing exceptions thrown during resource operations.
    • If both the try block and the close() method throw exceptions, the exception from the try block is suppressed, and the exception from the close() method is propagated.
  • Cleaner Code:
    • Eliminates the need for explicit finally blocks to close resources.
    • Results in cleaner, more readable, and maintainable code.
  • Multiple Resources:
    • Allows multiple resources to be declared and managed within a single try-with-resources statement.
    • Resources are closed in the reverse order of their creation.

Q10. Explain the concept of lambda expressions in Java.

Lambda expressions in Java, introduced in Java 8, provide a clear and concise way to represent a method interface using an expression. They are used primarily to define the inline implementation of a functional interface. A functional interface is an interface with a single abstract method, and lambda expressions enable you to treat functionality as a method argument or store it in a variable.

  1. Syntax: (parameters) -> expression or (parameters) -> { statements }
  2. Functional Interface: An interface with a single abstract method, such as Runnable , Callable , Comparator , etc.
  3. Use Cases: Commonly used with the Stream API, collections framework, and event handling.

Example Usage

Sorting a List using Lambda Expression:


import java.util.Arrays;
import java.util.List;

public class SortExampleLambda {
    public static void main(String[] args) {
        List names = Arrays.asList("Peter", "Anna", "Mike", "Xenia");
        names.sort((a, b) -> a.compareTo(b));
        System.out.println(names);
    }
}

Here's a simplified flow chart that illustrates usage of lambda expressions in sorting a list :

A simplified flow chart that illustrates usage of lambda expressions in sorting a list

Request question

Please fill in the form below to submit your question.

Q11. What is the significance of the final keyword in Java?

The final keyword in Java is used to apply restrictions on classes, methods, and variables.

Significance of the final Keyword

  • Final Variables:
    • Immutability: Once a final variable is initialized, its value cannot be changed. This makes the variable a constant.
    • Thread Safety: Final variables ensure thread safety because their values cannot be modified after initialization, preventing data inconsistency in concurrent environments.
    • final int MAX_VALUE = 100;
  • Final Methods:
    • Prevent Method Overriding: A final method cannot be overridden by subclasses. This ensures that the original implementation remains unchanged and provides consistent behavior.
    • Security and Integrity: Final methods can be used to prevent unintended behavior changes by ensuring that critical methods cannot be altered.
    • public final void display() {
         System.out.println("Display method");
      }
  • Final Classes:
    • Prevent Inheritance: A final class cannot be subclassed. This is useful for creating immutable classes and for ensuring that the class’s implementation cannot be extended or altered.
    • Security: Final classes prevent subclassing, which can be important for security-sensitive applications to prevent unauthorized or insecure extensions.
    • public final class Utility {
      // Class implementation
      }

Q12. What are the differences between checked and unchecked exceptions in Java?

Differences between checked and unchecked exceptions in Java are as follows:
This images illustrates differences between checked and unchecked exception in Java in tabular format

Q13. Explain the use of the transient keyword in Java.

The transient keyword in Java is used to indicate that a particular field should not be serialized. Serialization is the process of converting an object's state into a byte stream so that it can be saved to a file or transmitted over a network. When an object is serialized, all non-transient fields are included in the serialized representation.

Lets understand with code:

 
import java.io.*;

class User implements Serializable {
    private static final long serialVersionUID = 1L;

    String username;
    transient String password; // This field will not be serialized

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
}

public class TransientExample {
    public static void main(String[] args) {
        User user = new User("admin", "password123");

        // Serialize the user object
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
            out.writeObject(user);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Deserialize the user object
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.ser"))) {
            User deserializedUser = (User) in.readObject();
            System.out.println("Username: " + deserializedUser.username);
            System.out.println("Password: " + deserializedUser.password); // This will print null
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}


Here's a flow chart that sums it all up

This is a flow chart that explains use of transient keyword in Java

Q14. Describe the Singleton design pattern and how to implement it in Java.

The Singleton design pattern is a creational pattern that ensures a class has only one instance and provides a global point of access to it. The three common implementations in Java are:

  1. Eager Initialization: The instance is created at the time of class loading. This is the simplest method but may not be efficient if the instance is not used.
    public class Singleton {
        private static final Singleton INSTANCE = new Singleton();
    
        private Singleton() {}
    
        public static Singleton getInstance() {
            return INSTANCE;
        }
    }
  2. Lazy Initialization: The instance is created only when it is needed. This method is thread-safe but requires synchronization, which can impact performance.
    public class Singleton {
        private static Singleton instance;
    
        private Singleton() {}
    
        public static synchronized Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
  3. Double-Checked Locking: This method reduces the overhead of calling synchronized methods by using a synchronized block inside the if condition. It ensures thread safety and improves performance.
    public class Singleton {
        private static volatile Singleton instance;
    
        private Singleton() {}
    
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

Q15. What is Java Reflection and how is it used?

Java Reflection is a powerful feature that allows a Java program to inspect and manipulate the internal properties of classes, methods, and fields at runtime. It is part of the java.lang.reflect package.

How to Use Java Reflection

  1. Obtain the Class Object
    • You can obtain the Class object using various methods:
      • Class.forName("className")
      • object.getClass()
      • ClassName.class
  2. Inspect Class Properties
    • Use methods provided by the Class object to get details about the class.
  3. Create Instances
    • Use Constructor objects to create new instances.
  4. Invoke Methods
    • Use Method objects to invoke methods on instances.
  5. Access Fields
    • Use Field objects to read or write fields on instances.

Understand how it is used with below Flow Chart:

This image illustrates a flow chart that explains how Java Reflection is used

Request question

Please fill in the form below to submit your question.

Q16. Explain the concept of Generics in Java.

Generics in Java provide a way to create classes, interfaces, and methods that operate on types specified by the programmer at compile-time. They enhance code reusability, type safety, and readability.

Key Concepts

  1. Type Parameter Syntax:
    • Generic class declaration: class Box<T> { }
    • Generic method declaration: <T> void add(T element) { }
    • Here, T is a type parameter that can be replaced with any class type when the class or method is used.
  2. Bounded Type Parameters:
    • You can restrict the types that can be used as type parameters. For example, class Box<T extends Number> { } means that T can only be a subclass of Number .
  3. Generic Methods:
    • You can define methods with their own type parameters: public <T> void printArray(T[] inputArray) { }
  4. Wildcards:
    • The wildcard ? represents an unknown type. For example, List<?> can hold any type of List .
    • Upper bounded wildcard: List<? extends Number> means a list of objects that are instances of Number or its subclasses.
    • Lower bounded wildcard: List<? super Integer> means a list of objects that are instances of Integer or its superclasses.

Q17. What is the ExecutorService framework in Java?

The ExecutorService framework in Java is part of the java.util.concurrent package. It provides a higher-level replacement for managing and controlling threads. This framework simplifies the process of executing tasks asynchronously, managing a pool of threads, and handling their lifecycle.

Key Features

  1. Thread Management:
    • Automatically manages a pool of worker threads, reducing the need for manual thread creation and management.
    • Reuses threads, which improves performance and resource utilization.
  2. Task Submission:
    • Allows submission of tasks that return results using the submit() method.
    • You can submit Runnable and Callable tasks, and it returns a Future object that represents the result of an asynchronous computation.
  3. Lifecycle Management:
    • Methods to control the lifecycle of the executor:
      • shutdown() : Initiates an orderly shutdown 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.

Flow chart of ExecutorService Usage:

This is a Flow chart that explains the usage of ExecutorService framework in Java

Q18. How does Java handle multiple inheritance?

Java does not support multiple inheritance with classes to avoid complexity and ambiguity. However, it supports multiple inheritance through interfaces. A class can implement multiple interfaces, allowing the use of multiple types.

Lets understand through Flow Chart

This is a flow chart explaining how Java handles multiple inheritance

Q19. Explain the use of Stream API with an example.

The Stream API, introduced in Java 8, provides a powerful way to process sequences of elements in a functional programming style. It allows for operations such as filtering, mapping, and reducing, enabling developers to write more readable and concise code.

Key Concepts

  1. Stream Creation: Streams can be created from collections, arrays, or I/O resources.
  2. Intermediate Operations: Transform a stream into another stream and are lazy (e.g., filter , map ).
  3. Terminal Operations: Produce a result or a side-effect and mark the end of the stream processing (e.g., forEach , collect ).

Example

Below is an example that demonstrates the use of the Stream API to filter, map, and collect elements from a list.


import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamAPIExample {
    public static void main(String[] args) {
        List names = Arrays.asList("John", "Jane", "Jack", "Doe");

        // Filter names that start with 'J', convert to uppercase, and collect the result into a list
        List result = names.stream()
            .filter(name -> name.startsWith("J"))
            .map(String::toUpperCase)
            .collect(Collectors.toList());

        System.out.println(result); // Output: [JOHN, JANE, JACK]
    }
}


     

Explanation

  1. Stream Creation: A stream is created from the list names using names.stream() .
  2. Intermediate Operation (filter): The stream is filtered to include only names that start with 'J'.
  3. Intermediate Operation (map): The filtered names are converted to uppercase.
  4. Terminal Operation (collect): The resulting stream is collected into a list.

Q20. What are annotations in Java and how are they used?

Annotations in Java are a form of metadata that provide data about a program but are not part of the program itself. They have no direct effect on the operation of the code they annotate. Annotations can be used to provide information to the compiler, help with code analysis, or be processed at runtime by frameworks or tools.

How Annotations are Used

  1. Compilation Instructions:
    • @Override : Tells the compiler that a method is intended to override a method in a superclass.
    • @Deprecated : Marks a method or class as deprecated, generating a warning if it is used.
    • @SuppressWarnings : Suppresses specific compiler warnings.
  2. Runtime Processing:
    • Annotations can be accessed and processed at runtime using reflection. This is commonly used in frameworks and libraries for configuration and behavior customization.
  3. Framework Configuration:
    • Many frameworks use annotations to reduce or eliminate the need for XML configuration files. For example, Spring uses annotations for dependency injection and defining beans.
  4. Validation:
    • Annotations are used for validating data, such as in Java's Bean Validation API (JSR 380). These annotations are used on fields to specify validation constraints.
  5. Code Generation:
    • Libraries like Lombok use annotations to generate boilerplate code at compile time, such as getters, setters, and constructors.

Request question

Please fill in the form below to submit your question.

Q21. What is the difference between Comparable and Comparator in Java?

The difference in Comparable and Comparator are as follows:

This image shows difference between Comparable and Comparator in Java

Q17. What is the ExecutorService framework in Java?

Implementing a thread-safe singleton in Java can be achieved in several ways. One of the most common and efficient methods is using the Bill Pugh Singleton Design. However, other methods like double-checked locking and using the enum type are also effective.

  1. Bill Pugh Singleton Design:
    • The singleton instance is created in a static nested class (SingletonHelper).
    • The INSTANCE is created only when getInstance() is called.
    • This approach is lazy-loaded and thread-safe without requiring synchronization.
    public class Singleton {
        private Singleton() {
            // private constructor to prevent instantiation
        }
    
        private static class SingletonHelper {
            // Nested class is referenced after getInstance() is called
            private static final Singleton INSTANCE = new Singleton();
        }
    
        public static Singleton getInstance() {
            return SingletonHelper.INSTANCE;
        }
    }
  2. Double-Checked Locking:
    • The volatile keyword ensures that multiple threads handle the instance variable correctly.
    • The first check ( if (instance == null) ) is not synchronized to improve performance.
    • The second check inside the synchronized block ensures that only one instance is created.
    public class Singleton {
        private static volatile Singleton instance;
    
        private Singleton() {
            // private constructor to prevent instantiation
        }
    
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
  3. Enum Singleton:
    • Java ensures that any enum value is instantiated only once in a Java program.
    • This method is the most straightforward and ensures serialization correctness.
    • Enum singletons are inherently thread-safe.
    public enum Singleton {
        INSTANCE;
    
        public void someMethod() {
            // method implementation
        }
    }

Q23. Explain the concept of Autoboxing and Unboxing in Java.

Autoboxing and Unboxing are two key concepts in Java that allow automatic conversion between primitive data types and their corresponding wrapper classes. This feature simplifies code by enabling developers to work with primitives and objects seamlessly.

Autoboxing

Autoboxing is the automatic conversion that the Java compiler makes between the primitive types and their corresponding object wrapper classes. For instance, converting an int to an Integer , or a double to a Double .

Unboxing

Unboxing is the reverse process where the Java compiler automatically converts an object of a wrapper class to its corresponding primitive type. For instance, converting an Integer to an int , or a Double to a double .

Let's look at a more comprehensive example that demonstrates both autoboxing and unboxing:

import java.util.ArrayList;
import java.util.List;

public class AutoboxingUnboxingExample {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();

        // Autoboxing: Adding primitive int to List of Integer
        for (int i = 0; i < 5; i++) {
            list.add(i);  // int is autoboxed to Integer
        }

        System.out.println("List: " + list);

        // Unboxing: Retrieving Integer from List and assigning to int
        int sum = 0;
        for (Integer num : list) {
            sum += num;  // Integer is unboxed to int
        }

        System.out.println("Sum: " + sum);
    }
}

Q24. What is the Java Optional class and how is it used?

The Optional class in Java, introduced in Java 8, is a container object which may or may not contain a non-null value. It is used to represent optional values and provides a more expressive way to handle the presence or absence of values, thereby avoiding NullPointerException .

Key Methods of Optional

  1. of() : Creates an Optional with a specified non-null value.
  2. ofNullable() : Creates an Optional that may hold a null value.
  3. empty() : Returns an empty Optional .
  4. isPresent() : Returns true if the Optional contains a value, otherwise false.
  5. ifPresent() : Executes a given action if a value is present.
  6. orElse() : Returns the value if present, otherwise returns a default value.
  7. orElseGet() : Returns the value if present, otherwise invokes a Supplier and returns the result.
  8. orElseThrow() : Returns the value if present, otherwise throws an exception.
This is a image of flow chart that explains usage of Optional Class

Q25. What are the differences between HashMap and Hashtable in Java?

The difference between HashMap and Hashtable in Java are as follows:

This is an image of table that illustrates difference between HashMap & HashTable in Java

Request question

Please fill in the form below to submit your question.

Practical Assessment Based Questions & Answer

Q1: Write a Java method to create a fixed thread pool using ExecutorService and submit tasks that print numbers from 1 to 10. Ensure the proper shutdown of the executor service.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 1; i <= 10; i++) {
            final int num = i;
            executor.submit(() -> {
                System.out.println("Number: " + num);
            });
        }

        executor.shutdown();
    }
}

Q2: Implement the Factory design pattern to create different types of vehicles (Car, Bike, Truck).

 
    // Vehicle.java
public interface Vehicle {
    void drive();
}

// Car.java
public class Car implements Vehicle {
    public void drive() {
        System.out.println("Driving a car.");
    }
}

// Bike.java
public class Bike implements Vehicle {
    public void drive() {
        System.out.println("Riding a bike.");
    }
}

// Truck.java
public class Truck implements Vehicle {
    public void drive() {
        System.out.println("Driving a truck.");
    }
}

// VehicleFactory.java
public class VehicleFactory {
    public static Vehicle createVehicle(String type) {
        switch (type) {
            case "Car":
                return new Car();
            case "Bike":
                return new Bike();
            case "Truck":
                return new Truck();
            default:
                throw new IllegalArgumentException("Unknown vehicle type");
        }
    }
}

// Main.java
public class Main {
    public static void main(String[] args) {
        Vehicle car = VehicleFactory.createVehicle("Car");
        car.drive();

        Vehicle bike = VehicleFactory.createVehicle("Bike");
        bike.drive();
    }
}
 

Q3: Identify the performance issue in the following code snippet and provide a solution.

public class PerformanceTest {
    public static void main(String[] args) {
        String result = "";
        for (int i = 0; i < 10000; i++) {
            result += "Number: " + i;
        }
        System.out.println(result);
    }
}

The performance issue is due to the use of string concatenation in a loop, which creates many intermediate String objects. Use StringBuilder instead:


public class PerformanceTest {
    public static void main(String[] args) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < 10000; i++) {
            result.append("Number: ").append(i);
        }
        System.out.println(result.toString());
    }
}

Q4: Write a Java method that uses reflection to print all methods of a given class.

import java.lang.reflect.Method;

public class ReflectionExample {
    public static void printMethods(Class<?> clazz) {
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("Method: " + method.getName());
        }
    }

    public static void main(String[] args) {
        printMethods(String.class);
    }
}

Explanation:

  • The printMethods method uses Java Reflection to get all declared methods of the given class.
  • It iterates through the array of Method objects and prints each method's name.
  • In the main method, printMethods is called with String.class , so it prints all methods of the String class.

Q5: Write a unit test for a method that calculates the sum of two integers using JUnit.


    // Calculator.java
public class Calculator {
    public int sum(int a, int b) {
        return a + b;
    }
}

// CalculatorTest.java
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

public class CalculatorTest {
    @Test
    public void testSum() {
        Calculator calculator = new Calculator();
        assertEquals(5, calculator.sum(2, 3));
    }
}
Q6: Review the following code and suggest improvements.
 public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public void printName() {
        System.out.println("User name: " + name);
    }
}

1. Add null check in the constructor.

2. Mark name as final.

3. Override toString , equals , and hashCode methods.

public class User {
    private final String name;

    public User(String name) {
        if (name == null) {
            throw new IllegalArgumentException("Name cannot be null");
        }
        this.name = name;
    }

    public void printName() {
        System.out.println("User name: " + name);
    }

    @Override
    public String toString() {
        return "User{name='" + name + "'}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return name.equals(user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

Q7: Write a Spring Boot application with a REST endpoint /greet that returns "Hello, World!"


// SpringBootApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootApplication.class, args);
    }
}

// GreetingController.java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {
    @GetMapping("/greet")
    public String greet() {
        return "Hello, World!";
    }
}

Q8: Write a method to fetch all records from a table employees using JDBC.


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class DatabaseExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/yourdatabase";
        String user = "yourusername";
        String password = "yourpassword";

        try (Connection con = DriverManager.getConnection(url, user, password);
             Statement stmt = con.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT * FROM employees")) {

            while (rs.next()) {
                System.out.println("ID: " + rs.getInt("id"));
                System.out.println("Name: " + rs.getString("name"));
                System.out.println("Position: " + rs.getString("position"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 
Q9: You are given the following Java code snippet that implements a simple bubble sort algorithm to sort an array of integers. However, the code does not sort the array correctly. Identify and fix the issue.

public class BubbleSort {
    public static void main(String[] args) {
        int[] numbers = {5, 3, 8, 4, 2};
        bubbleSort(numbers);
        System.out.println(Arrays.toString(numbers));
    }

    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }
}

Issue:

The outer loop runs n times instead of n-1 times, and the inner loop does not account for already sorted elements in the later iterations, making the algorithm inefficient.

Fix:

Adjust the outer loop to run n-1 times and the inner loop to run n-1-i times to avoid unnecessary comparisons.


import java.util.Arrays;

public class BubbleSort {
    public static void main(String[] args) {
        int[] numbers = {5, 3, 8, 4, 2};
        bubbleSort(numbers);
        System.out.println(Arrays.toString(numbers));
    }

    public static void bubbleSort(int[] arr) {
        int n = arr.length;
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < n - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }
}
Q10: You are given the following Java code snippet that removes even numbers from a list. However, the code throws a ConcurrentModificationException. Identify and fix the issue.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class NumberRemover {
    public static void main(String[] args) {
        List numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
        removeEvenNumbers(numbers);
        System.out.println(numbers);
    }

    public static void removeEvenNumbers(List numbers) {
        for (Integer number : numbers) {
            if (number % 2 == 0) {
                numbers.remove(number);
            }
        }
    }
}

Issue:

The code throws a ConcurrentModificationException because it modifies the list while iterating over it using a for-each loop.

Fix:

Use an Iterator to remove elements safely while iterating over the list.


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class NumberRemover {
    public static void main(String[] args) {
        List numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
        removeEvenNumbers(numbers);
        System.out.println(numbers);
    }

    public static void removeEvenNumbers(List numbers) {
        Iterator iterator = numbers.iterator();
        while (iterator.hasNext()) {
            Integer number = iterator.next();
            if (number % 2 == 0) {
                iterator.remove();
            }
        }
    }
}

Request question

Please fill in the form below to submit your question.

Discover the True Potential of AI in Development with Workik

Join developers who are using Workik’s AI assistance everyday for programming

Sign Up Now

Overview of JAVA

What is Java?

What is the history and what are the latest trends in Java development?

List Some Popular Frameworks, Libraries & tools associated with Java.

What are the use cases of Java?

What are the tech roles associated with Java Expertise?

What Pay Package can be expected with experience in Java?