All Java Interview Questions

What is the difference between map() and flatMap() in Java streams?

map is one to one. flatMap is one to many, and it flattens the result.

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

public class Main {
    public static void main(String[] args) {
        List<String> sentences = List.of("java is fun", "streams are great");

        List<String[]> mapped = sentences.stream()
                .map(s -> s.split(" "))
                .toList();
        System.out.println(mapped.size() + " arrays");           // a list of arrays

        List<String> words = sentences.stream()
                .flatMap(s -> Arrays.stream(s.split(" ")))
                .toList();
        System.out.println(words);                                 // one flat list
    }
}
Output

2 arrays
[java, is, fun, streams, are, great]

See Java Streams.

What is a functional interface in Java?

A functional interface has one abstract method (it can have default and static methods too). The annotation @FunctionalInterface asks the compiler to check this rule.

import java.util.function.Function;
import java.util.function.Predicate;

@FunctionalInterface
interface Validator {
    boolean validate(String text);
}

public class Main {
    public static void main(String[] args) {
        Validator notEmpty = t -> !t.isBlank();
        Predicate<Integer> positive = n -> n > 0;
        Function<String, Integer> length = String::length;

        System.out.println(notEmpty.validate("hi") + " " + notEmpty.validate("  "));
        System.out.println(positive.test(-5));
        System.out.println(length.apply("functional"));
    }
}
Output

true false
false
10

See Java Lambda Expressions.

What are the ways to create a thread in Java?

import java.util.concurrent.*;

public class Main {
    static class MyThread extends Thread {
        public void run() { System.out.println("1. extends Thread"); }
    }

    public static void main(String[] args) throws Exception {
        MyThread t1 = new MyThread();
        t1.start();
        t1.join();

        Thread t2 = new Thread(() -> System.out.println("2. Runnable lambda"));
        t2.start();
        t2.join();

        try (ExecutorService pool = Executors.newFixedThreadPool(2)) {
            Future<String> f = pool.submit(() -> "3. Callable returns a value");
            System.out.println(f.get());
        }

        Thread t4 = Thread.ofVirtual().start(() -> System.out.println("4. virtual thread"));
        t4.join();
    }
}
Output

1. extends Thread
2. Runnable lambda
3. Callable returns a value
4. virtual thread

WayReturns a value?Note
Extend ThreadNoUses your only parent class
RunnableNoThe usual simple way
Callable + ExecutorServiceYes (Future)Best for real programs
Virtual threadSame as aboveLight, for many waiting tasks (Java 21+)

See Java Threads.

What is the difference between synchronized and volatile in Java?

Pointsynchronizedvolatile
Applies toMethods and blocksVariables
Mutual exclusionYes (one thread at a time)No
Visibility of changesYesYes
Atomic count++Yes, inside a synchronized blockNo
CostHigher (locking)Low

Use volatile for a simple flag which one thread writes and others read. Use synchronized, a Lock or AtomicInteger when several steps must be done together.

public class Main {
    static volatile boolean running = true;

    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            long loops = 0;
            while (running) {
                loops++;
            }
            System.out.println("Worker stopped");
        });
        worker.start();
        Thread.sleep(200);
        running = false;                // the worker sees this change because of volatile
        worker.join();
    }
}
Output

Worker stopped

What is a deadlock in Java and how to avoid it?

Thread A locks lock1 and waits for lock2. Thread B locks lock2 and waits for lock1. Both wait for ever.

public class Main {
    static final Object lock1 = new Object();
    static final Object lock2 = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread a = new Thread(() -> {
            synchronized (lock1) {
                pause();
                synchronized (lock2) { System.out.println("A done"); }
            }
        });
        Thread b = new Thread(() -> {
            synchronized (lock2) {
                pause();
                synchronized (lock1) { System.out.println("B done"); }
            }
        });
        a.setDaemon(true);
        b.setDaemon(true);
        a.start();
        b.start();

        a.join(1500);
        b.join(1500);
        System.out.println("A alive: " + a.isAlive() + ", B alive: " + b.isAlive());
    }

    static void pause() {
        try { Thread.sleep(300); } catch (InterruptedException e) { }
    }
}
Output

A alive: true, B alive: true

Both threads are still alive after waiting, so they are in a deadlock. How to avoid it:

  • Always take the locks in the same order in all threads.
  • Use tryLock(timeout) of ReentrantLock and give up after some time.
  • Keep the locked code small and do not call unknown code inside it.
  • Prefer high level tools (ExecutorService, concurrent collections).

How to create a thread safe singleton in Java?

A singleton allows only one object of a class. The two simple and safe ways are:

public class Main {
    // 1. Holder idiom: lazy and thread safe, because class loading is thread safe
    static class Config {
        private Config() { System.out.println("Config created"); }

        private static class Holder {
            static final Config INSTANCE = new Config();
        }

        static Config getInstance() { return Holder.INSTANCE; }
    }

    // 2. Enum singleton: also safe against reflection and serialization
    enum Registry {
        INSTANCE;
        int counter;
    }

    public static void main(String[] args) {
        System.out.println("Start");
        Config a = Config.getInstance();
        Config b = Config.getInstance();
        System.out.println(a == b);

        Registry.INSTANCE.counter++;
        System.out.println(Registry.INSTANCE.counter);
    }
}
Output

Start
Config created
true
1

The object is created when it is used the first time, and only once. Other common ways are double checked locking with a volatile field, but the two above are simpler.

What is new in Java 25?

Java 25 (September 2025) is an LTS release, so it gets updates for many years. Some of the final features:

  • Compact source files and instance main methods (JEP 512): small programs without the class and public static, plus IO.println.
  • Module import declarations (JEP 511): import module java.base; imports all the public packages of a module.
  • Flexible constructor bodies (JEP 513): statements before super(...), for checks.
  • Scoped values (JEP 506): a safer way to share data between threads than thread locals.
  • Compact object headers (JEP 519) and other performance work.

We can test the first three on a real Java 25:

import module java.base;

void main() {
    List<String> names = new ArrayList<>(List.of("Riya", "Aman"));      // List and ArrayList without a normal import
    IO.println(names);
    IO.println(new Child(-5).value());
}

class Parent {
    final int value;
    Parent(int value) { this.value = value; }
    int value() { return value; }
}

class Child extends Parent {
    Child(int raw) {
        int fixed = Math.max(raw, 0);       // a statement before super(...), allowed in Java 25
        super(fixed);
    }
}
Output

[Riya, Aman]
0

Java 27 is newer, but it is not an LTS. See the chapter Java Introduction for the versions.

Which Java version should I learn? What are the LTS versions?

Java has a new version every 6 months (in March and September). Every 2 years, one of them is an LTS (long term support) release, which gets updates for many years.

LTS versionReleased
Java 8March 2014
Java 11September 2018
Java 17September 2021
Java 21September 2023
Java 25September 2025

Learn the newest LTS (Java 25), because almost everything of Java 8, 11, 17 and 21 works in it, and you also learn the modern features. In a job you may meet Java 8, 11 or 17 code, so the older features are in this tutorial too.

Terminal

$ java -version 2>&1 | head -1
openjdk version "25.0.5" 2026-10-20 LTS

Java vs Python: which language should you learn first?

Both are among the most popular languages. In the TIOBE index of November 2026 Python is first (20.9%) and Java is third (8.9%), and in the PYPL index of Google tutorial searches the two are on the first and the second place.

PointPythonJava
StartVery easy, short syntaxMore code to write, stricter rules
TypingDynamic (types checked when running)Static (types checked by the compiler)
SpeedSlower for heavy workFaster (JIT compiler)
Strong inData, AI, scripts, automationBig back ends, Android, banking, large teams
RunInterpreterCompile to bytecode, run on the JVM

If you want to start fast, begin with Python. If you want to learn how programs are built with types and classes, or you plan an enterprise or Android career, Java is a great choice. After one, the other becomes easy.

What is the difference between throw and throws in Java?

Pointthrowthrows
KindStatement inside a methodKeyword in the method declaration
UseCreate and send an exception objectWarn that the method can throw exceptions
Followed byOne exception objectOne or many exception types
import java.io.IOException;

public class Main {
    static void check(int age) throws IOException {                // throws: declaration
        if (age < 0) {
            throw new IOException("Age can not be negative");       // throw: action
        }
        System.out.println("Age ok");
    }

    public static void main(String[] args) {
        try {
            check(10);
            check(-1);
        } catch (IOException e) {
            System.out.println("Caught: " + e.getMessage());
        }
    }
}
Output

Age ok
Caught: Age can not be negative

See Java Exceptions.

What are annotations in Java?

An annotation adds information (metadata) to classes, methods and fields. It does not change the code by itself. The compiler or a framework reads it. Well known ones: @Override, @Deprecated, @FunctionalInterface, @SuppressWarnings. Frameworks like Spring use many.

You can make your own, and read it with reflection:

import java.lang.annotation.*;
import java.lang.reflect.Method;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Run {
    String value() default "no name";
}

public class Main {
    @Run("first")
    public void alpha() { System.out.println("alpha called"); }

    public void beta() { }

    @Run
    public void gamma() { System.out.println("gamma called"); }

    public static void main(String[] args) throws Exception {
        Main obj = new Main();
        for (String name : new String[]{"alpha", "beta", "gamma"}) {
            Method m = Main.class.getMethod(name);
            Run run = m.getAnnotation(Run.class);
            if (run != null) {
                System.out.println("Running " + run.value());
                m.invoke(obj);
            } else {
                System.out.println(name + " has no @Run");
            }
        }
    }
}
Output

Running first
alpha called
beta has no @Run
Running no name
gamma called

What are the best practices for exception handling in Java?

  • Catch specific exceptions, not just Exception or Throwable.
  • Never swallow an exception with an empty catch. At least log it.
  • Use try with resources to close files, sockets and connections.
  • Keep the cause when you wrap an exception: throw new MyException("message", e).
  • Add useful details to the message: which value, which file.
  • Do not use exceptions for normal flow, like ending a loop. They are slow and unclear.
  • Throw early, catch late: check arguments at the start, and handle the error where you can do something about it.
  • Use unchecked exceptions for programming errors, and checked ones only when the caller can recover.
public class Main {
    static class ConfigException extends RuntimeException {
        ConfigException(String message, Throwable cause) { super(message, cause); }
    }

    static int readPort(String text) {
        try {
            return Integer.parseInt(text);
        } catch (NumberFormatException e) {
            throw new ConfigException("Invalid port value '" + text + "'", e);    // keeps the cause
        }
    }

    public static void main(String[] args) {
        try {
            readPort("80x");
        } catch (ConfigException e) {
            System.out.println(e.getMessage());
            System.out.println("Cause: " + e.getCause());
        }
    }
}
Output

Invalid port value '80x'
Cause: java.lang.NumberFormatException: For input string: "80x"

See Java Exceptions.

What is reflection in Java?

Reflection (the package java.lang.reflect) lets code look at classes, methods and fields at run time, and call them, even if the names were not known when the code was written.

import java.lang.reflect.Method;
import java.util.Arrays;

public class Main {
    public static class Greeter {
        public String hello(String name) { return "Hello, " + name; }
        public int twice(int n) { return n * 2; }
    }

    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("Main$Greeter");
        System.out.println(c.getSimpleName());

        Object obj = c.getDeclaredConstructor().newInstance();
        Method hello = c.getMethod("hello", String.class);
        System.out.println(hello.invoke(obj, "Riya"));

        String[] names = Arrays.stream(c.getDeclaredMethods()).map(Method::getName).sorted().toArray(String[]::new);
        System.out.println(Arrays.toString(names));
    }
}
Output

Greeter
Hello, Riya
[hello, twice]

  • It is powerful but slower, and it breaks the safety checks of the compiler. Use it for frameworks and tools, not for normal code.
  • Modern Java also limits reflection on the internals of the JDK (modules).