Skip to content

Core Java OOP — 30 Interview Questions (Answers & Examples)

Cover the answer, then say it aloud in your own words; bold text = interviewer keywords worth dropping naturally.


Q1. What is Object-Oriented Programming (OOP)?

OOP is a programming paradigm that models software as a collection of interacting objects, where each object bundles together data (state) and behaviour (methods). Instead of writing one long procedure, you break the problem into self-contained entities that talk to each other. Analogy: think of building a car from parts — the engine, wheels, and brakes are separate objects, each knowing its own data and what it can do, and they collaborate to make the car run. Java is object-oriented at its core: everything of substance lives inside a class.

class Car {
    int speed;                 // state (data)
    void accelerate() {        // behaviour (method)
        speed += 10;
    }
}

Q2. What are the main principles of OOP in Java?

The four pillars of OOP are Encapsulation, Abstraction, Inheritance, and Polymorphism. Encapsulation hides data, Abstraction hides implementation, Inheritance enables reuse, and Polymorphism enables one interface with many forms. Analogy: driving a car — you use the pedals and wheel (abstraction), the internals are sealed under the hood (encapsulation), a sports car is-a car (inheritance), and pressing "brake" behaves differently per car type (polymorphism). Master these four and every other OOP concept follows.

// Encapsulation: private data + getters
// Abstraction:   interface / abstract class
// Inheritance:   class Dog extends Animal
// Polymorphism:  Animal a = new Dog(); a.sound();

Q3. What is a class and how is it different from an object?

A class is a blueprint or template that defines fields and methods; an object is a concrete instance of that class created at runtime with the new keyword. The class exists once as a definition; you can create many objects from it, each with its own state. Analogy: a class is the architectural blueprint of a house, while each actual house built from it is an object — same design, different addresses and paint colours. A class consumes no memory for state until you instantiate it into objects.

class House { String colour; }           // blueprint
House h1 = new House();  h1.colour = "red";   // object 1
House h2 = new House();  h2.colour = "blue";  // object 2

Q4. What is encapsulation in Java and how is it achieved?

Encapsulation is the bundling of data and the methods that operate on it into a single unit, while hiding the internal state behind private fields exposed only through public getters and setters — this is data hiding. It protects an object's integrity by preventing outside code from putting it into an invalid state. Analogy: a bank account — you cannot reach in and set the balance directly; you must go through deposit() and withdraw(), which enforce rules. Achieved by: private fields + public getter/setter methods with validation.

class BankAccount {
    private double balance;                 // hidden data
    public void deposit(double amt) {
        if (amt > 0) balance += amt;        // controlled access
    }
    public double getBalance() { return balance; }
}

Q5. What is abstraction and how do you implement it in Java?

Abstraction is hiding implementation details and exposing only the essential behaviour — showing WHAT an object does, not HOW it does it — implemented in Java via abstract classes and interfaces. It lets callers program against a contract without caring about the underlying code. Analogy: a TV remote — you press the power button and the TV turns on; the circuitry behind that button is abstracted away from you. You define the essential operations and leave the concrete implementation to subclasses.

abstract class Shape {
    abstract double area();     // WHAT, not HOW
}
class Circle extends Shape {
    double r;
    double area() { return 3.14 * r * r; }   // HOW lives here
}

Q6. What is the difference between abstraction and encapsulation?

Abstraction hides complexity (design level, "what"); encapsulation hides data (implementation level, "how"). They are complementary but distinct.

Aspect Abstraction Encapsulation
Hides Implementation / complexity Data / internal state
Focus Design level — exposes WHAT it does, hides HOW Access level — hides the DATA / internal state
Achieved by abstract class, interface private fields + getters/setters
Goal Reduce complexity for the caller Protect integrity of data
Analogy Remote button (what happens) Bank account balance (protected)

Remember: Abstraction = hide the HOW (implementation), expose the WHAT; Encapsulation = hide the DATA (private fields behind getters/setters).


Q7. What is inheritance in Java?

Inheritance is a mechanism where a subclass acquires the fields and methods of a superclass using the extends keyword, establishing an "is-a" relationship and promoting code reuse. The child gets everything reusable from the parent and can add or override behaviour. Analogy: a Dog is-an Animal — it inherits eating and breathing from Animal, and adds barking of its own. This avoids duplicating common code across related classes.

class Animal { void eat() { System.out.println("eating"); } }
class Dog extends Animal {              // Dog is-a Animal
    void bark() { System.out.println("woof"); }
}
new Dog().eat();   // inherited

Q8. What is the difference between single and multiple inheritance?

Single inheritance means a class extends exactly one superclass; multiple inheritance means inheriting from more than one superclass — which Java forbids for classes but allows for interfaces.

Aspect Single Inheritance Multiple Inheritance
Parents One superclass Two or more superclasses
Java classes Supported (extends One) Not supported (compile error)
Java interfaces N/A Supported (implements A, B)
Risk None Diamond / ambiguity problem
Example Dog extends Animal class C implements A, B
class Dog extends Animal { }                 // single (classes)
class C implements Flyer, Swimmer { }        // multiple (interfaces)

Q9. Why does Java not support multiple inheritance with classes?

Java disallows multiple class inheritance to avoid the "diamond problem" — the ambiguity that arises when a class inherits the same method from two parents and the compiler cannot decide which version to use. It keeps the language simpler and the type hierarchy unambiguous. Analogy: if you inherited a "greet" trait from two parents who greet differently, no one could tell whose greeting is yours. Java solves the need for multiple types through interfaces instead.

// class A { void greet(){} }
// class B { void greet(){} }
// class C extends A, B {}  // ILLEGAL — which greet()?  Compile error

Remember: Java blocks multiple class inheritance to prevent the diamond/ambiguity problem — use interfaces instead.


Q10. How does Java achieve multiple inheritance using interfaces?

A class can implement any number of interfaces, so it inherits multiple type contracts (and default methods) without inheriting conflicting state — this gives multiple inheritance of type. Because interfaces traditionally carried no instance fields and only abstract methods, there was no state to conflict. Analogy: a smartphone is-a Camera and is-a Phone and is-a MusicPlayer all at once, fulfilling several contracts. Since Java 8, interfaces can also carry default methods, and conflicts are resolved explicitly.

interface Camera { void snap(); }
interface Phone  { void call(); }
class Smartphone implements Camera, Phone {
    public void snap() { }
    public void call() { }
}

Q11. What is polymorphism in Java?

Polymorphism means "many forms" — the ability of a single interface, name, or reference to behave differently depending on the actual object or arguments involved. It comes in two flavours: compile-time (overloading) and runtime (overriding). Analogy: the word "draw" means one thing to an artist and another to a gunslinger — same name, different behaviour based on context. In Java, a superclass reference can point to any subclass object and invoke the right overridden method at runtime.

Animal a = new Dog();
a.sound();          // runs Dog's sound() — runtime polymorphism

Q12. What is the difference between compile-time and runtime polymorphism?

Compile-time polymorphism is resolved by the compiler via method overloading; runtime polymorphism is resolved by the JVM via method overriding and dynamic dispatch.

Aspect Compile-time (Static) Runtime (Dynamic)
Achieved by Method overloading Method overriding
Bound at Compile time Runtime
Basis Method signature (params) Actual object type
Also called Static / early binding Dynamic / late binding
Example add(int,int) vs add(double,double) Animal a = new Dog(); a.sound()
void add(int a,int b){}      void add(double a,double b){}  // overload (compile)
class Dog extends Animal { void sound(){} }                 // override (runtime)

Q13. What is method overloading?

Method overloading is defining multiple methods with the same name but different parameter lists (type, number, or order) within the same class — a form of compile-time/static polymorphism. The compiler picks the correct method based on the arguments you pass. Analogy: a "print" function that behaves sensibly whether you hand it a number, a string, or an array — same name, tailored versions. Return type alone does not distinguish overloads.

class Printer {
    void print(int i)    { }
    void print(String s) { }        // same name, different params
    void print(int a, int b) { }
}

Remember: Overloading = same name, different parameters, resolved at compile time (static polymorphism).


Q14. What is method overriding?

Method overriding is when a subclass provides its own implementation of a method already defined in its superclass, using the exact same signature — a form of runtime/dynamic polymorphism. The JVM decides at runtime which version to call based on the actual object. Analogy: every Animal has a generic sound(), but a Dog overrides it to bark and a Cat to meow. Use @Override to let the compiler catch signature mistakes.

class Animal { void sound(){ System.out.println("..."); } }
class Dog extends Animal {
    @Override void sound(){ System.out.println("woof"); }
}

Remember: Overriding = same signature, subclass redefines superclass method, resolved at runtime (dynamic polymorphism).


Q15. Can you override a private or static method in Java?

No — you cannot override a private method (it is not inherited, so it is invisible to the subclass) nor a static method (statics belong to the class, not the instance, so redeclaring one is method hiding, not overriding). A private method defined again in a subclass is simply a brand-new unrelated method. A static method redefined in a subclass hides the parent's version, resolved by the reference type at compile time, not by dynamic dispatch. Analogy: a private room in a house isn't passed to your heirs; a static "company policy" is looked up by which company's sign is on the door, not by the person standing there.

class Parent { static void info(){ System.out.println("Parent"); } }
class Child extends Parent {
    static void info(){ System.out.println("Child"); }   // HIDING, not overriding
}
Parent p = new Child();  p.info();   // prints "Parent" (reference type wins)

Q16. What is the use of the super keyword?

super refers to the immediate parent class and is used to call the parent's constructor (super(...)), invoke an overridden parent method (super.method()), or access a parent field hidden by the child. It lets a subclass reuse and extend parent behaviour rather than replace it entirely. Analogy: a Dog that first does the generic Animal eat() and then adds its own step — it defers part of the work to its "parent". super() must be the first statement in a constructor.

class Animal { Animal(){ System.out.println("Animal built"); } }
class Dog extends Animal {
    Dog(){ super(); System.out.println("Dog built"); }  // call parent ctor
}

Q17. What is the this keyword in Java?

this is a reference to the current object; it disambiguates instance fields from parameters of the same name, passes the current object as an argument, or invokes another constructor of the same class via this(...). It always points to the object on which the method was called. Analogy: when you say "I'll handle it myself," "myself" is this — the very object speaking. It is most commonly seen in setters where the parameter shadows the field.

class Car {
    int speed;
    Car(int speed){ this.speed = speed; }   // this.speed = field, speed = param
}

Q18. What is an abstract class and when would you use it?

An abstract class is a class declared with the abstract keyword that cannot be instantiated and may contain both abstract methods (no body) and concrete methods (with body), serving as a partial template for subclasses. Use it when related classes share common code and state but must each supply some specialised behaviour. Analogy: "Vehicle" is abstract — you never drive a generic "vehicle," but Car and Bike extend it, sharing startEngine() while each defining its own wheels(). It sits between a full concrete class and a pure interface.

abstract class Vehicle {
    void start(){ System.out.println("starting"); }  // shared concrete
    abstract int wheels();                            // subclass must define
}
class Bike extends Vehicle { int wheels(){ return 2; } }

Q19. What is an interface and how is it different from an abstract class?

An interface is a pure contract of method signatures (abstract by default, plus default/static methods since Java 8) that a class agrees to fulfil; it differs from an abstract class in inheritance model, state, and constructors.

Aspect Interface Abstract Class
Multiple inheritance Yes (implements A, B) No (single extends)
Fields public static final constants only Instance fields allowed
Methods Abstract + default/static (Java 8+) Abstract + concrete
Constructor None Has constructors
Use when Unrelated classes share a contract Related classes share code + state
interface Flyer { void fly(); default void land(){} }
abstract class Bird { abstract void fly(); void breathe(){} }

Q20. Can a class implement multiple interfaces?

Yes — a Java class can implement any number of interfaces simultaneously, which is how Java delivers multiple inheritance of type safely. The class must provide (or inherit as default) an implementation for every method declared across those interfaces. Analogy: a smartphone implements Camera, Phone, and GPS contracts at once, so it can be treated as any of them. This is a cornerstone of flexible, decoupled design.

interface Drawable { void draw(); }
interface Clickable { void click(); }
class Button implements Drawable, Clickable {
    public void draw()  { }
    public void click() { }
}

Q21. What is the diamond problem in inheritance? How does Java handle it?

The diamond problem occurs when a class inherits the same method from two paths that converge on a common ancestor, making the inherited version ambiguous. Java sidesteps it for classes by banning multiple class inheritance entirely. For interfaces with conflicting default methods (possible since Java 8), Java forces the implementing class to resolve the conflict explicitly by overriding the method, optionally choosing a parent's version with Interface.super.method(). Analogy: two parents give you a differently-shaped "greet" gene — Java makes you decide which to express rather than guessing.

interface A { default void hi(){ System.out.println("A"); } }
interface B { default void hi(){ System.out.println("B"); } }
class C implements A, B {
    public void hi(){ A.super.hi(); }   // resolve explicitly
}

Q22. What is the difference between == and .equals() in object comparison?

== compares references (whether two variables point to the same object in memory), while .equals() compares logical/content equality as defined by the class.

Aspect == .equals()
Compares Reference (memory address) Content / logical equality
Type Operator Method (overridable)
Primitives Compares values N/A (not usable)
Default (Object) Behaves like == unless overridden
String example May be false for equal text true for equal text
String a = new String("hi");
String b = new String("hi");
System.out.println(a == b);        // false — different objects
System.out.println(a.equals(b));   // true  — same content

Q23. What is constructor overloading?

Constructor overloading is defining multiple constructors in the same class with different parameter lists, giving callers several ways to create and initialise an object. The compiler selects the matching constructor based on the arguments supplied. Analogy: ordering a coffee — you can create it with no options (default), with just a size, or with size plus milk plus sugar; each "recipe" is a different constructor. Constructors can chain to each other with this(...) to avoid repetition.

class Coffee {
    Coffee(){ this("medium"); }              // no-arg delegates
    Coffee(String size){ }                    // one-arg
    Coffee(String size, boolean milk){ }      // two-arg
}

Q24. Can a constructor be inherited in Java?

No — constructors are not inherited by subclasses, because a constructor's name must match its own class, and a subclass is a different class. However, a subclass constructor implicitly or explicitly calls a superclass constructor via super(...) to initialise the inherited state. Analogy: your child doesn't inherit your name-signing signature, but building them still relies on your "construction steps" running first. If the parent has no no-arg constructor, the child must call super(args) explicitly.

class Animal { Animal(String name){ } }
class Dog extends Animal {
    Dog(){ super("Rex"); }   // must call parent ctor; not inherited
}

Q25. What is object slicing in Java?

Java has no object slicing — it is a C++ concept, and it simply does not apply to Java because Java objects are always accessed through references, never copied by value. In C++, assigning a derived object to a base-type value variable "slices off" the derived-only members, keeping only the base part. In Java, when you assign Animal a = new Dog();, a still refers to the full Dog object on the heap, and overridden methods still dispatch to Dog. So the extra fields and behaviour are never lost. Analogy: in C++ you'd photocopy only the top half of a document; in Java you always hold a pointer to the whole document.

Animal a = new Dog();   // 'a' references the FULL Dog object — no slicing
a.sound();              // still calls Dog.sound() at runtime

Q26. What are access modifiers and how do they relate to encapsulation?

Access modifiers — private, default (package-private), protected, and public — control the visibility of classes, fields, and methods, and they are the primary tool that enforces encapsulation. By marking fields private and exposing controlled public methods, you hide internal state and guard it against invalid access. Analogy: a building's access levels — private office (owner only), staff-only floor (package), members' lounge (protected/subclass), and public lobby (anyone). Choosing the tightest modifier that still works is a best practice.

class Account {
    private double balance;         // private: hidden (encapsulation)
    protected int id;               // subclasses + package
    public String owner;            // everyone
}
Modifier Same class Same package Subclass Everywhere
private Yes No No No
default Yes Yes No No
protected Yes Yes Yes No
public Yes Yes Yes Yes

Q27. What are the key differences between class and structure (though Java doesn't have struct)?

Java has no struct; it uses classes for everything — but conceptually a class is a reference type with behaviour, encapsulation, and inheritance, whereas a struct (in languages like C/C++) is typically a lightweight value type meant mainly to group data.

Aspect Class (Java) Struct (C/C++)
Availability Yes Not in Java
Type Reference type (heap) Value type (stack, by default)
Members Data + methods Mainly data (limited in C)
Inheritance Supported Not (in C)
Access default package-private / configurable public
Encapsulation Full (private + getters) Minimal

Java's closest modern analogue to a lightweight data struct is a record (Java 16+), which is still a reference type but concise for pure data.

record Point(int x, int y) { }   // Java's compact "data-only" type

Q28. How are interfaces used in real-world OOP design?

Interfaces define contracts that decouple what is needed from who provides it, enabling polymorphism, plug-and-play implementations, testability, and dependency injection. Real systems code to interfaces (e.g., List, Comparator, Repository) so any conforming implementation can be swapped in without touching callers. Analogy: a wall power socket is an interface — any appliance with the right plug works, regardless of who made it. This underpins frameworks like Spring, mocking in tests, and the "program to an interface, not an implementation" principle.

interface PaymentGateway { void pay(double amt); }
class Stripe implements PaymentGateway { public void pay(double a){} }
class Paypal implements PaymentGateway { public void pay(double a){} }
PaymentGateway g = new Stripe();   // swap freely, callers unchanged

Q29. How do you achieve loose coupling in OOP?

Loose coupling means components depend on abstractions (interfaces) rather than concrete classes, so changes in one part ripple minimally into others — achieved via interfaces, dependency injection, encapsulation, and the "program to an interface" principle. The goal is that objects know as little as possible about each other's internals. Analogy: a laptop and a USB device only agree on the USB port shape; either can be replaced without redesigning the other. Tightly coupled code, by contrast, hard-wires concrete types and breaks easily.

class OrderService {
    private final PaymentGateway gateway;                 // depend on abstraction
    OrderService(PaymentGateway gateway){ this.gateway = gateway; }  // injected
    void checkout(double amt){ gateway.pay(amt); }
}

A design pattern is a proven, reusable solution to a commonly recurring design problem, expressed in terms of classes and objects — and it is built directly on OOP principles like encapsulation, abstraction, inheritance, and polymorphism. Patterns (Singleton, Factory, Observer, Strategy, etc.) give teams a shared vocabulary and battle-tested structure. Analogy: architectural blueprints for common needs — you don't reinvent how to design a staircase; you apply a known pattern. For example, the Strategy pattern leans on polymorphism, while Factory leans on abstraction to hide object creation.

// Factory pattern — abstraction hides the concrete class chosen
interface Shape { void draw(); }
class ShapeFactory {
    Shape create(String type){
        return type.equals("circle") ? new Circle() : new Square();
    }
}