Interview

15 Oracle Java Interview Questions and Answers

Prepare for your next technical interview with our comprehensive guide on Oracle Java, featuring curated questions to enhance your understanding and skills.

Oracle Java remains a cornerstone in the world of programming, known for its robustness, portability, and extensive community support. Java’s versatility allows it to be used in a variety of applications, from enterprise-level backend systems to Android app development. Its strong object-oriented principles and comprehensive standard libraries make it a preferred choice for developers and organizations alike.

This article offers a curated selection of interview questions designed to test your understanding and proficiency in Java. By working through these questions, you will gain a deeper insight into key concepts and be better prepared to demonstrate your expertise in any technical interview setting.

Oracle Java Interview Questions and Answers

1. Explain the concept of Java Virtual Machine (JVM) and its role in Java development.

The Java Virtual Machine (JVM) is an abstract computing machine that provides a runtime environment for executing Java bytecode. It is part of the Java Runtime Environment (JRE) and converts bytecode into machine-specific code, enabling Java’s platform independence. The JVM handles class loading, bytecode verification, execution, memory management, and security.

2. How does garbage collection work in Java?

Garbage collection in Java is the process by which the JVM automatically identifies and discards objects that are no longer needed, freeing up memory resources. This helps prevent memory leaks and optimize application performance. Java uses several garbage collection algorithms, including Serial, Parallel, Concurrent Mark-Sweep (CMS), and G1, each suited for different application needs. The process involves marking, normal deletion, and sometimes compaction to reduce fragmentation.

3. Implement a singleton pattern in Java.

The singleton pattern restricts a class to a single instance, useful for coordinating actions across a system. In Java, it typically involves a private constructor, a static method to access the instance, and a private static variable to hold it.

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

4. What are the different types of polymorphism in Java? Provide examples.

Polymorphism in Java allows an object to take on many forms, using one interface for a general class of actions. The two main types are:

  • Compile-time (Static) Polymorphism: Achieved through method overloading, allowing multiple methods in the same class with the same name but different parameters.
  • Runtime (Dynamic) Polymorphism: Achieved through method overriding, allowing a subclass to provide a specific implementation of a method defined in its superclass.

Examples:

// Compile-time Polymorphism
class MathOperations {
    int add(int a, int b) { return a + b; }
    double add(double a, double b) { return a + b; }
}

// Runtime Polymorphism
class Animal {
    void sound() { System.out.println("Animal makes a sound"); }
}
class Dog extends Animal {
    @Override
    void sound() { System.out.println("Dog barks"); }
}

5. Create a simple producer-consumer problem using threads.

The producer-consumer problem involves producers generating data and placing it into a buffer, while consumers take data from the buffer. Proper synchronization ensures producers don’t add to a full buffer and consumers don’t remove from an empty one. This can be implemented using threads and synchronization mechanisms like wait() and notify().

import java.util.LinkedList;
import java.util.Queue;

class ProducerConsumer {
    private final Queue<Integer> buffer = new LinkedList<>();
    private final int capacity = 5;

    public void produce() throws InterruptedException {
        int value = 0;
        while (true) {
            synchronized (this) {
                while (buffer.size() == capacity) {
                    wait();
                }
                buffer.add(value++);
                System.out.println("Produced " + value);
                notify();
                Thread.sleep(1000);
            }
        }
    }

    public void consume() throws InterruptedException {
        while (true) {
            synchronized (this) {
                while (buffer.isEmpty()) {
                    wait();
                }
                int value = buffer.poll();
                System.out.println("Consumed " + value);
                notify();
                Thread.sleep(1000);
            }
        }
    }

    public static void main(String[] args) {
        ProducerConsumer pc = new ProducerConsumer();

        Thread producerThread = new Thread(() -> {
            try {
                pc.produce();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        Thread consumerThread = new Thread(() -> {
            try {
                pc.consume();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producerThread.start();
        consumerThread.start();
    }
}

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

The volatile keyword in Java marks a variable as stored in main memory, ensuring visibility of changes across threads. This is important in multi-threaded environments where multiple threads might access and modify the same variable.

Example:

public class VolatileExample extends Thread {
    private volatile boolean running = true;

    public void run() {
        while (running) {
            // Thread keeps running
        }
        System.out.println("Thread stopped.");
    }

    public void stopRunning() {
        running = false;
    }

    public static void main(String[] args) throws InterruptedException {
        VolatileExample thread = new VolatileExample();
        thread.start();
        Thread.sleep(1000);
        thread.stopRunning();
        thread.join();
    }
}

7. Explain the concept of Java Reflection API and provide a use case.

The Java Reflection API allows inspection and manipulation of classes, methods, and fields at runtime. This is useful for scenarios requiring flexibility, such as frameworks that perform dependency injection.

Example:

import java.lang.reflect.Method;

public class ReflectionExample {
    public void sayHello() {
        System.out.println("Hello, world!");
    }

    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("ReflectionExample");
            Object instance = clazz.getDeclaredConstructor().newInstance();
            Method method = clazz.getMethod("sayHello");
            method.invoke(instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

8. Write a program to implement a basic LRU cache.

An LRU (Least Recently Used) cache discards the least recently used items first when it reaches capacity. It can be implemented using a doubly linked list and a hash map.

import java.util.*;

class LRUCache<K, V> {
    private final int capacity;
    private final Map<K, V> map;
    private final LinkedList<K> list;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.map = new HashMap<>();
        this.list = new LinkedList<>();
    }

    public V get(K key) {
        if (!map.containsKey(key)) {
            return null;
        }
        list.remove(key);
        list.addFirst(key);
        return map.get(key);
    }

    public void put(K key, V value) {
        if (map.containsKey(key)) {
            list.remove(key);
        } else if (map.size() == capacity) {
            K leastUsedKey = list.removeLast();
            map.remove(leastUsedKey);
        }
        list.addFirst(key);
        map.put(key, value);
    }
}

9. What are lambda expressions and how are they used in Java?

Lambda expressions in Java provide a concise way to represent one method interface using an expression, enabling functionality as a method argument. They consist of parameters, an arrow token, and a body.

Example:

// Traditional way using an anonymous class
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, world!");
    }
};

// Using a lambda expression
Runnable r2 = () -> System.out.println("Hello, world!");

Lambda expressions are often used with functional interfaces, such as those in the java.util.function package.

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> filteredNames = names.stream()
                                  .filter(name -> name.startsWith("A"))
                                  .collect(Collectors.toList());

10. Explain the concept of Java Streams and provide an example of their usage.

Java Streams, part of the java.util.stream package, allow for functional-style operations on streams of elements. They support intermediate and terminal operations, enabling transformations like map-reduce.

Example:

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

public class StreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

        List<String> filteredNames = names.stream()
                                          .filter(name -> name.startsWith("A"))
                                          .collect(Collectors.toList());

        System.out.println(filteredNames); // Output: [Alice]
    }
}

11. What is the difference between synchronized and Lock in Java?

In Java, synchronized and Lock handle synchronization in multithreaded environments. Synchronized is a keyword providing a simple way to ensure exclusive access to code blocks or methods. Lock is an interface offering more flexibility and control, such as timed or interruptible lock acquisition.

Example:

// Using synchronized
public class SynchronizedExample {
    public synchronized void synchronizedMethod() {
        // critical section
    }
}

// Using Lock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private final Lock lock = new ReentrantLock();

    public void lockMethod() {
        lock.lock();
        try {
            // critical section
        } finally {
            lock.unlock();
        }
    }
}

12. Explain the Java Memory Model and its importance in concurrent programming.

The Java Memory Model (JMM) defines how the JVM interacts with memory, particularly in concurrent programming. It ensures atomicity, visibility, and ordering of operations, providing a framework for understanding thread interactions with memory.

13. Describe the use and creation of annotations in Java.

Annotations in Java provide metadata for code and can be applied to various elements. They can be used by the compiler or at runtime to influence program behavior. Custom annotations are created using the @interface keyword.

Example:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

// Define a custom annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyCustomAnnotation {
    String value();
}

// Use the custom annotation
public class MyClass {
    @MyCustomAnnotation(value = "Example")
    public void myMethod() {
        System.out.println("This is a method with a custom annotation.");
    }
}

14. Discuss the concurrency utilities available in java.util.concurrent.

The java.util.concurrent package provides concurrency utilities, including the Executor Framework, concurrent collections, synchronizers, locks, and atomic variables. These utilities simplify concurrent application development.

Example:

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

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

        for (int i = 0; i < 5; i++) {
            executor.submit(() -> {
                System.out.println("Task executed by: " + Thread.currentThread().getName());
            });
        }

        executor.shutdown();
    }
}

15. Write a method to serialize and deserialize an object in Java.

Serialization and deserialization in Java use the Serializable interface and ObjectOutputStream/ObjectInputStream classes. The Serializable interface signals that an object can be serialized.

Example:

import java.io.*;

class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

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

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

public class SerializationExample {
    public static void main(String[] args) {
        Person person = new Person("John Doe", 30);

        // Serialization
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            oos.writeObject(person);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Deserialization
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person deserializedPerson = (Person) ois.readObject();
            System.out.println(deserializedPerson);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
Previous

10 Perl Scripting Interview Questions and Answers

Back to Interview
Next

10 Data Pipelines Interview Questions and Answers