Object-oriented programming in Java rests on four ideas: encapsulation (hide data behind methods), inheritance (reuse a parent class), polymorphism (one call, different behaviour) and abstraction (expose what, hide how). Every test framework uses all four, so they come up in nearly every Java and SDET interview.

Encapsulation

In Simple Terms

Encapsulation = Medicine Capsule

A capsule hides the medicine inside. You don't touch the medicine directly — you take the whole capsule. In OOP, encapsulation means hiding data (fields) inside a class and only exposing it through controlled methods (getters/setters).

The Basics

public class BankAccount {
    // private = hidden from outside world
    private double balance;
    private String accountNumber;

    public BankAccount(String accountNumber, double initialBalance) {
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
    }

    // GETTER — controlled read access
    public double getBalance() {
        return balance;
    }

    // SETTER — controlled write access with validation
    public void deposit(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive!");
        }
        balance += amount;
    }

    public boolean withdraw(double amount) {
        if (amount > balance) {
            System.out.println("Insufficient funds!");
            return false;
        }
        balance -= amount;
        return true;
    }
}

BankAccount acc = new BankAccount("ACC001", 1000.0);
// acc.balance = -500;  // COMPILE ERROR! Cannot access private field
acc.deposit(500);        // OK — validated access
System.out.println(acc.getBalance());  // 1500.0

Interview Questions

What is encapsulation and why is it important?

Encapsulation bundles data (fields) and methods into a class and restricts direct field access using access modifiers. Benefits: data hiding, validation before modification, ability to change internal implementation without breaking external code.

Is Java 100% object-oriented?

No. Java has primitive types (int, double, etc.) which are NOT objects. A truly OO language would treat everything as objects. Java is 'mostly' OO. Languages like Smalltalk are considered pure OO.

Advertisement

Inheritance

In Simple Terms

Inheritance = Family traits

You inherited your parent's eye color and hair color, but you also have your own personality. In Java, a child class INHERITS fields and methods from parent class, but can also have its own additions and can OVERRIDE parent behaviors.

The Basics

// PARENT CLASS (Superclass)
public class Animal {
    String name;
    int age;

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

    public void eat() {
        System.out.println(name + " is eating");
    }

    public void makeSound() {
        System.out.println(name + " makes a sound");
    }
}

// CHILD CLASS (Subclass) — extends parent
public class Dog extends Animal {
    String breed;

    public Dog(String name, int age, String breed) {
        super(name, age);  // MUST call parent constructor first!
        this.breed = breed;
    }

    @Override  // Overriding parent's makeSound
    public void makeSound() {
        System.out.println(name + " says: Woof!");
    }

    // Dog-specific method
    public void fetch() {
        System.out.println(name + " fetches the ball!");
    }
}

Dog dog = new Dog("Max", 3, "Labrador");
dog.eat();         // Inherited from Animal
dog.makeSound();   // Overridden: Max says: Woof!
dog.fetch();       // Dog-only method

Deep Dive: super keyword & Constructor Chain

// Constructor call order (always parent → child):
class A {
    A() { System.out.println("A constructor"); }
}
class B extends A {
    B() {
        super();  // implicit if not written!
        System.out.println("B constructor");
    }
}
class C extends B {
    C() {
        super();  // implicit
        System.out.println("C constructor");
    }
}

new C();
// Output:
// A constructor
// B constructor
// C constructor

// super.method() calls parent version:
class Dog extends Animal {
    @Override
    public void makeSound() {
        super.makeSound();  // calls Animal's makeSound
        System.out.println(name + " also barks!");
    }
}

Interview Questions

Does Java support multiple inheritance?

Not with classes — to avoid the Diamond Problem (ambiguity when two parent classes have same method). But Java supports multiple inheritance via INTERFACES. A class can implement multiple interfaces.

What is the Diamond Problem?

If class C extends both A and B, and A and B both have method foo(), which version should C inherit? Java avoids this by not allowing multiple class inheritance. With interfaces (Java 8+), if conflict occurs, the implementing class MUST override the method.

Can we override private or static methods?

No. Private methods are not visible to child classes — you can define same-named method but it's not overriding. Static methods belong to the class, not objects — they can be HIDDEN (method hiding) but not overridden. @Override annotation on static causes compile error.

Polymorphism

In Simple Terms

Polymorphism = One remote, many devices

You have ONE 'play' button on a remote. Press it on a TV → plays video. Press it on a music player → plays audio. Press it on a game → starts the game. Same command, different behaviors. That's polymorphism!

Two Types

1. Compile-time Polymorphism (Method Overloading)

public class Calculator {
    // Same method name, different parameters
    public int add(int a, int b) {
        return a + b;
    }
    public double add(double a, double b) {
        return a + b;
    }
    public int add(int a, int b, int c) {
        return a + b + c;
    }
    // Note: return type alone CANNOT distinguish overloads!
}

Calculator calc = new Calculator();
calc.add(5, 3);        // calls int version
calc.add(5.0, 3.0);    // calls double version
calc.add(1, 2, 3);     // calls 3-param version
// Resolved at COMPILE TIME (static dispatch)

2. Runtime Polymorphism (Method Overriding)

Animal animal;

// Reference type: Animal   Object type: Dog
animal = new Dog("Max", 3, "Lab");
animal.makeSound();  // Woof! (Dog's version) — runtime decision

// Reference type: Animal   Object type: Cat
animal = new Cat("Whiskers", 2);
animal.makeSound();  // Meow! (Cat's version) — runtime decision

// Polymorphic array
Animal[] animals = {
    new Dog("Rex", 4, "German Shepherd"),
    new Cat("Luna", 1),
    new Dog("Buddy", 2, "Poodle")
};

for (Animal a : animals) {
    a.makeSound();  // Each calls its OWN version!
}

Deep Dive: Virtual Table (vtable)

// How Java resolves overridden methods at runtime:

// Every class has a vtable (method dispatch table)
// Dog's vtable:
// makeSound → Dog.makeSound  (overridden)
// eat       → Animal.eat     (inherited)
// fetch     → Dog.fetch      (Dog-only)

// When you call animal.makeSound():
// 1. JVM looks at actual object type (Dog, not Animal)
// 2. Finds Dog's vtable
// 3. Finds makeSound → Dog.makeSound
// 4. Executes Dog's version

// This is 'dynamic dispatch' or 'late binding'

// upcasting (implicit)
Animal a = new Dog("Max", 3, "Lab");  // OK

// downcasting (explicit, risky)
Dog d = (Dog) a;  // OK if 'a' actually IS a Dog
Dog d2 = (Dog) new Cat("...", 1);  // ClassCastException at runtime!

// Safe downcasting with instanceof
if (a instanceof Dog) {
    Dog dog = (Dog) a;
    dog.fetch();
}

// Pattern matching (Java 16+)
if (a instanceof Dog dog) {
    dog.fetch();  // auto-cast!
}

Interview Questions

What is the difference between method overloading and overriding?

Overloading: same method name, different parameters, same class, resolved at compile time. Overriding: same method signature, different class (parent/child), resolved at runtime via dynamic dispatch.

Can we override a method and make it more restrictive?

No. You cannot reduce the visibility of an overridden method. If parent has public method, child cannot make it protected or private. But you CAN increase visibility (protected → public is OK).

What is covariant return type?

Java 5+ allows overriding method to return a subtype of the parent return type. Example: parent returns Animal, child can return Dog (which extends Animal). This is covariant return type and is valid overriding.

Abstraction — Abstract Classes & Interfaces

In Simple Terms

Abstraction = Restaurant Menu vs Kitchen

When you order 'Pasta', you don't need to know HOW it's made in the kitchen. You just know WHAT you'll get. Abstract classes and interfaces define WHAT should happen, without specifying HOW. Each subclass/implementor decides the HOW.

Abstract Class

// Abstract class: cannot be instantiated directly
public abstract class Shape {
    String color;

    public Shape(String color) {
        this.color = color;
    }

    // Abstract method: subclass MUST implement
    public abstract double area();
    public abstract double perimeter();

    // Concrete method: shared by all shapes
    public void display() {
        System.out.println("Shape: " + color +
            ", Area: " + area());
    }
}

public class Circle extends Shape {
    double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }

    @Override
    public double perimeter() {
        return 2 * Math.PI * radius;
    }
}

// Shape s = new Shape("..");  // ERROR: cannot instantiate abstract
Shape circle = new Circle("Red", 5.0);
circle.display();  // Shape: Red, Area: 78.53...

Interfaces

// Interface: pure contract (all methods abstract by default)
public interface Flyable {
    // implicitly: public static final
    double MAX_ALTITUDE = 40000;  // constant

    // implicitly: public abstract
    void fly();
    void land();

    // Default method (Java 8+) — has implementation
    default void describe() {
        System.out.println("I can fly up to " + MAX_ALTITUDE + "ft!");
    }

    // Static method (Java 8+)
    static String getType() {
        return "Flyable entity";
    }
}

public interface Swimmable {
    void swim();
}

// Class implements MULTIPLE interfaces
public class Duck extends Animal implements Flyable, Swimmable {
    public Duck(String name) { super(name, 2); }

    @Override
    public void fly() { System.out.println(name + " flies!"); }

    @Override
    public void land() { System.out.println(name + " lands!"); }

    @Override
    public void swim() { System.out.println(name + " swims!"); }

    @Override
    public void makeSound() { System.out.println("Quack!"); }
}

Deep Dive: Abstract Class vs Interface

Abstract ClassInterface
Can have constructorsCannot have constructors
Can have instance variablesOnly static final constants
Methods can be any modifierMethods are public by default
Single inheritance onlyMultiple interfaces implementable
Use for 'IS-A' relationshipUse for 'CAN-DO' capability
~30-80% implementation0% implementation (before Java 8)
Example: Vehicle (is-a)Example: Serializable, Runnable

Interview Questions

Can an interface have a constructor?

No. Interfaces cannot be instantiated, so constructors make no sense. However, from Java 8, interfaces can have default and static methods with implementations.

What happens if a class implements two interfaces with same default method?

Compile error! The class MUST override the conflicting default method to resolve ambiguity. It can then call a specific interface's version using: InterfaceA.super.method().

Can an abstract class implement an interface?

Yes, and it can leave some interface methods unimplemented (they become abstract in the class). The first concrete subclass must then implement them.