🎤 Top 82 Interview Questions
These are the most commonly asked Core Java interview questions — tap to reveal the answer.
The JVM (Java Virtual Machine) runs Java bytecode — it's a virtual computer. JRE = JVM + libraries, needed to run a program. JDK = JRE + compiler and tools, needed to write and compile code.
Java code is compiled once into bytecode (a .class file), which can then run on any OS through that OS's JVM — without being rewritten.
The bytecode is universal; only the JVM is OS-specific. Each OS has its own JVM that understands the same bytecode — that's what makes the code portable.
Primitive types (int, char, boolean, etc.) store the value directly and have a fixed size. Reference types (String, Array, Object) store a memory address that points to the actual object.
int is a primitive type. Integer is a wrapper class that lets you use an int as an object (e.g. in Collections) — this process is called autoboxing.
int=0, double=0.0, boolean=false, char=(empty), and object references (String etc) = null. Local variables have no default value — they must be initialized before use.
'==' compares references (memory addresses) — whether two objects are the exact same one in memory. '.equals()' compares content/value — whether the text of two Strings is the same, for example.
To take keyboard input from the user — e.g. Scanner sc = new Scanner(System.in); sc.nextInt();
A class is a blueprint/template that defines what fields (data) and methods (behavior) an object will have.
An object is a real instance of a class — it holds its own values and is created in memory when you use the 'new' keyword.
Encapsulation, Abstraction, Inheritance, and Polymorphism.
Keeping data (fields) private and exposing access only through getters/setters. It keeps data safe and prevents it from being changed incorrectly from outside.
While driving a car you only use the steering wheel and pedals — you don't need to worry about the engine's internal workings. The complex part is hidden; only the necessary interface is shown.
Code reusability — you write common properties/behavior once in a parent class, and child classes can reuse it without rewriting it.
No, a class can extend only one class (to avoid the diamond problem). But a class can implement multiple interfaces.
Same method name in the same class, but with different parameters (number/type) — like add(int,int) and add(int,int,int). This is compile-time polymorphism.
A child class redefines a parent class's method with the same signature, in its own way. This is run-time polymorphism.
Overloading happens within the same class (parameters differ), decided at compile-time. Overriding happens between parent-child (signature stays the same), decided at run-time.
A special method with the same name as the class, automatically called when an object is created (with 'new'), so initial values can be set.
Having multiple constructors in the same class, with different parameters — so an object can be created in different ways.
If you don't write any constructor yourself, Java automatically provides an empty (no-argument) one.
To refer to the current object — e.g. when a constructor parameter and a field share the same name, you write this.name = name.
It's used from inside a child class to refer to/call the parent class's constructor, method, or field.
A static member belongs to the class, not to any single object — all objects share it. You can access it via ClassName.member even without creating an object.
Because a static method isn't tied to any object — it's class-level, so 'this' (which refers to the current object) has no meaning there.
A final variable's value can't be changed (a constant). A final method can't be overridden. A final class can't be extended.
A class you can't directly instantiate with 'new'. It can have some complete methods and some abstract ones (without a body) — child classes must complete the abstract methods.
A full contract that (traditionally) contains only method signatures, no implementation. A class follows an interface using the 'implements' keyword.
Use an Interface when you need a common contract across completely unrelated classes. Use an Abstract class when related classes need to share some common code.
default and static methods were added, which can have a body — before that, only abstract methods were allowed in interfaces.
An unexpected problem/error that occurs while a program is running, like divide by zero or an invalid array index — if not handled, it crashes the program.
Checked exceptions are checked at compile-time and must be handled (e.g. IOException). Unchecked exceptions occur at run-time and handling them is optional (e.g. NullPointerException).
You put risky code in the try block. If an error occurs, the matching catch block handles it. The finally block always runs, whether an error occurred or not.
throw is used inside a method to manually raise an exception. throws, in the method signature, declares which exceptions the method might produce.
When you try to call a method or access a field on a null reference (one that has no object).
In very rare cases — for instance if the JVM itself crashes, or System.exit() is called inside the try block, only then is finally skipped.
By extending the Exception class (or RuntimeException) to make your own — e.g. class InsufficientBalanceException extends Exception.
A resizable (dynamic size) list that implements the List interface — it maintains order, allows duplicates, and supports index-based access.
An Array has a fixed size and can store primitive types too. ArrayList can grow/shrink dynamically, but only stores objects.
It implements the Set interface, storing only unique values (duplicates are automatically ignored), with no guaranteed order.
It stores key-value pairs. It calculates each key's hashcode to decide where the value is stored, which is why lookup by key is very fast.
List is ordered and allows duplicates. Set holds only unique values. Map stores data as key-value pairs.
ArrayList is array-based, so random access (by index) is fast. LinkedList is a chain of nodes — insert/delete in the middle is fast, but random access is slow.
An object that lets you traverse the elements of a Collection (List, Set, etc.) one by one, using the hasNext() and next() methods.
An independent flow/path of execution within a program — a Java program runs in at least one thread (the main thread), and extra threads can be created to do work in parallel.
By extending the Thread class, or by implementing the Runnable interface — in both you write a run() method and call start() to run the thread.
start() creates a new thread and executes run() inside it (in parallel). Calling run() directly doesn't create a new thread — it just runs like a normal method call (on the same thread).
When multiple threads try to modify the same data at once, synchronized lets only one thread into that code block/method at a time — preventing data corruption.
For security, thread-safety and performance — Strings with the same value can only safely share space in the String Pool if they can't be changed. This also saves memory.
String is immutable. StringBuilder is mutable but not thread-safe (fast, best for single-threaded use). StringBuffer is also mutable and is thread-safe (synchronized, a bit slower).
Widening automatically converts a smaller type into a bigger one (e.g. int->double), with no loss. Narrowing requires manually casting a bigger type into a smaller one (e.g. double->int), and data (the decimal part) can be lost.
Automatically converting a primitive type (like int) into its wrapper class object (Integer) — e.g. to store a primitive in a Collection.
A method gets a copy of a variable's value — the original variable is never changed. For objects, it gets a copy of the reference, so changes made inside the object are visible outside, but reassigning the reference itself is not.
In recursion, a method calls itself. The base case is the condition where recursion stops — without one, you get a StackOverflowError (infinite calls).
If you don't put a break after a case, control flows into the next case too, even if it doesn't match — this is called fall-through.
int[][] grid = new int[3][4]; creates a grid of 3 rows and 4 columns, accessed as grid[row][col].
So HashMap/HashSet work correctly — if two objects are equal by equals(), their hashCode() must also match, otherwise Collections can behave unexpectedly.
Something like ClassName@hashcode (e.g. Point@1b6d3586) — which is why it's usually overridden to give a readable format.
A static nested class can be created without an outer object (it's independent). A non-static Inner class needs an object of the outer class, and can access the outer's instance members.
When you need a class only once, right away (like an event listener) — you define it inline, without giving it a name.
Type safety — only predefined constants are allowed, so invalid/random values are rejected right at compile-time.
Type safety and code reusability — one class/method can work with many types, and the compiler immediately flags an error if you pass the wrong type.
If you don't write any modifier, the member is accessible only within the same package, not from outside it.
A group/folder of related classes — it organizes code and avoids naming conflicts (e.g. java.util, java.io).
Comparable (the compareTo method) is defined inside the class and describes its "natural ordering". Comparator (the compare method) is written outside the class and lets you define custom/multiple orderings.
First-In-First-Out — the element added first is the first one removed, like a ticket line.
A small, nameless function that immediately implements a functional interface (one with a single abstract method) — like (a, b) -> a + b.
It lets you chain operations like filter, map, and sort on collections without writing manual loops, resulting in shorter, more readable code.
An interface with exactly one abstract method — like Runnable or Comparator. These are the target types for lambda expressions.
It automatically closes a resource (like a file or connection) once the try block finishes, whether an error occurred or not — no need to manually call close().
To read a file quickly, line by line — it does internal buffering, which gives better performance than repeatedly hitting the disk.
Catching multiple exception types in a single catch block using the '|' operator, like catch (IOException | SQLException e) { } — it reduces code duplication.
To organize specific error types in large projects — e.g. a base AppException with specific exceptions like PaymentException and LoginException, which can be caught individually or together.
New, Runnable, Running, Waiting/Blocked, and Terminated.
When two (or more) threads keep waiting for a resource the other one holds, and neither can move forward — the program gets stuck forever.
A thread pool manager that reuses ready threads instead of you manually creating/managing them — it gives better resource management.
Stack stores method calls and local variables (fast, auto-cleaned). Heap stores objects (created with 'new'), and they stay there until no reference is using them.
When no reference to that object remains (from any variable) — it then becomes "unreachable" and eligible for GC.
Only once, the first time the class is loaded — even before any object is created — used to initialize static variables.
Converting an object into a stream of bytes (e.g. to save to a file or send over a network) — the class must implement the Serializable interface. Converting it back into an object is called deserialization.
Types like int, char, String, and enum — since Java 7+, String can also be used in a switch.
Something like a chessboard or a spreadsheet — you need both rows and columns, like a seating chart or a tic-tac-toe board.