Insights into Company
← Java

OOP Principles

Understand the idea. Compare it. Explain it. Test yourself.

Question 1 of 11

What is abstraction?

Solution and explanation

What it is

Abstraction means showing only the essential features of something and hiding the implementation details. The caller knows what an object does, not how it does it.

Analogy: when you drive a car you use the steering, accelerator and brake. You do not need to know about fuel injection or the ABS algorithm. When you pay with UPI you tap "Pay"; the bank-to-bank NPCI messaging is hidden.

Achieved in Java with

  1. Abstract classes (0–100% abstraction)
  2. Interfaces (full abstraction of behaviour)
public interface NotificationSender {          // WHAT: send a notification
    void send(String to, String message);
}

public class EmailSender implements NotificationSender {   // HOW: SMTP
    public void send(String to, String message) { /* SMTP connection, MIME, retries… */ }
}

public class SmsSender implements NotificationSender {     // HOW: SMS gateway API
    public void send(String to, String message) { /* HTTP call to gateway */ }
}

public class InterviewScheduler {
    private final NotificationSender sender;                 // depends on the abstraction only
    public InterviewScheduler(NotificationSender sender) { this.sender = sender; }

    public void schedule(String candidate) {
        sender.send(candidate, "Your interview is on Monday 10 AM");
    }
}

InterviewScheduler works with email, SMS or WhatsApp senders without changing a line.

Benefits

  • Reduces complexity for callers.
  • Implementations can change or be swapped (loose coupling).
  • Enables testing with fake/mock implementations.

Abstraction vs encapsulation

AbstractionEncapsulation
Hides complexity (design level)Hides data (implementation level)
Focuses on what an object doesFocuses on how state is protected
Interfaces, abstract classesprivate fields + public methods
Practice explaining this
Question 2 of 11

What is encapsulation?

Solution and explanation

What it is

Encapsulation is bundling data (fields) and the methods that operate on it into one class, and restricting direct access to the data. Fields are private; controlled access is given through public methods that enforce the rules.

Analogy: an ATM. You cannot open the cash box directly; you interact through a PIN and menu, and the machine enforces limits.

public class Candidate {
    private String email;
    private int experienceYears;
    private final List<String> skills = new ArrayList<>();

    public String getEmail() { return email; }

    public void setEmail(String email) {
        if (email == null || !email.matches("^[\\w.+-]+@[\\w-]+\\.[\\w.]+$"))
            throw new IllegalArgumentException("Invalid email");
        this.email = email.toLowerCase();
    }

    public void setExperienceYears(int years) {
        if (years < 0 || years > 50) throw new IllegalArgumentException("Experience must be 0–50");
        this.experienceYears = years;
    }

    public void addSkill(String skill) { skills.add(skill.trim()); }

    public List<String> getSkills() { return List.copyOf(skills); }   // read-only copy, internals stay protected
}

Benefits

  1. Data integrity: invalid values are rejected in one place.
  2. Flexibility: internal representation can change without affecting callers.
  3. Control: read-only (getter only) or write-only properties.
  4. Easier maintenance and debugging.
  5. Security: sensitive data (passwords) never exposed directly.

Levels of encapsulation

  • Field level: private fields.
  • Class level: package-private helper classes.
  • Module level: only exported packages are visible (Java 9 modules).

Note: getters and setters for every field is not automatically good encapsulation. Prefer meaningful behaviour (account.withdraw(500)) over exposing raw setters (account.setBalance(...)).

Practice explaining this
Question 3 of 11

What is inheritance?

Solution and explanation

What it is

Inheritance lets a class (subclass/child) acquire the fields and methods of another class (superclass/parent) using extends. The child reuses the parent's code and can add new members or override behaviour. It models an IS-A relationship: a Car IS-A Vehicle.

public class Employee {
    protected final String name;
    protected final double baseSalary;

    public Employee(String name, double baseSalary) { this.name = name; this.baseSalary = baseSalary; }

    public double monthlyPay() { return baseSalary; }
    public String badge() { return "EMP-" + name; }
}

public class Manager extends Employee {
    private final double teamBonus;

    public Manager(String name, double baseSalary, double teamBonus) {
        super(name, baseSalary);
        this.teamBonus = teamBonus;
    }

    @Override
    public double monthlyPay() { return super.monthlyPay() + teamBonus; }   // extends behaviour

    public void approveLeave(String employee) { System.out.println(name + " approved leave for " + employee); }
}

Manager m = new Manager("Priya", 150000, 25000);
System.out.println(m.monthlyPay());   // 175000.0
System.out.println(m.badge());        // EMP-Priya (inherited)

Types

TypeExampleSupported with classes?
SingleB extends AYes
MultilevelC extends B extends AYes
HierarchicalB and C both extend AYes
MultipleC extends A, BNo (diamond problem); possible with interfaces
HybridCombinationOnly through interfaces

What is not inherited

  • Constructors (called with super() instead).
  • Private members are not accessible (they exist in the object but the child cannot use them directly).
  • Static members are accessible but belong to the parent class (hidden, not overridden).

Why no multiple inheritance of classes?

If two parents had the same method, which one would the child get? (diamond problem). Java avoids this for classes; for interfaces with conflicting default methods, the class must override and choose.

Cautions

Inheritance couples the child tightly to the parent. Use it only for a true IS-A relationship; otherwise prefer composition.

Practice explaining this
Question 4 of 11

What is polymorphism?

Solution and explanation

What it is

Polymorphism ("many forms") means the same method call can behave differently depending on the object or the arguments. One interface, many implementations.

Analogy: a "pay" button in an app. The same action works with UPI, credit card, net banking or wallet, each doing it differently.

1. Compile-time polymorphism (static): method overloading

The compiler chooses the method from the argument types.

class Printer {
    void print(int n)      { System.out.println("int " + n); }
    void print(String s)   { System.out.println("text " + s); }
    void print(double d)   { System.out.println("decimal " + d); }
}

2. Run-time polymorphism (dynamic): method overriding

The JVM chooses the method from the actual object type at run time (dynamic method dispatch).

abstract class Payment {
    abstract double fee(double amount);
}
class UpiPayment extends Payment  { double fee(double a) { return 0; } }
class CardPayment extends Payment { double fee(double a) { return a * 0.018; } }
class EmiPayment extends Payment  { double fee(double a) { return a * 0.03 + 199; } }

List<Payment> methods = List.of(new UpiPayment(), new CardPayment(), new EmiPayment());
for (Payment p : methods) {
    System.out.println(p.getClass().getSimpleName() + " fee: " + p.fee(10000));
}
UpiPayment fee: 0.0
CardPayment fee: 180.0
EmiPayment fee: 499.0

The loop is written once against Payment; adding a new payment type needs no change to it (open/closed principle).

Upcasting and downcasting

Payment p = new CardPayment();          // upcasting: automatic
if (p instanceof CardPayment card) {    // pattern matching (Java 16)
    // use card-specific methods
}
Compile-timeRun-time
MechanismOverloadingOverriding
Decided byCompiler, using reference/argument typesJVM, using the actual object
Also calledStatic bindingDynamic binding / late binding

Note: fields, static methods and private methods are not polymorphic; only overridable instance methods are.

Practice explaining this
Question 5 of 11

What is method overloading?

Solution and explanation

What it is

Method overloading is defining several methods with the same name in the same class (or inherited), but with different parameter lists. The compiler picks the right one based on the arguments, so it is compile-time polymorphism.

public class SearchService {
    public List<Job> search(String keyword) { return search(keyword, "All India"); }
    public List<Job> search(String keyword, String city) { return search(keyword, city, 0); }
    public List<Job> search(String keyword, String city, int minExperience) { /* query */ return List.of(); }
    public List<Job> search(List<String> skills) { /* match by skills */ return List.of(); }
}

Valid ways to overload

  • Different number of parameters: add(int a, int b) and add(int a, int b, int c)
  • Different types: add(int, int) and add(double, double)
  • Different order of types: log(String, int) and log(int, String)

Not overloading

  • Only the return type differs: int total() vs double total() is a compile error.
  • Only parameter names differ.
  • Only access modifiers or thrown exceptions differ.

How the compiler resolves a call (in order)

  1. Exact match.
  2. Widening primitive conversion (int → long → float → double).
  3. Autoboxing/unboxing (int → Integer).
  4. Varargs.
void show(long x)    { System.out.println("long"); }
void show(Integer x) { System.out.println("Integer"); }
void show(int... x)  { System.out.println("varargs"); }

show(5);   // prints "long": widening beats boxing, boxing beats varargs

Ambiguity

void f(Integer a, int b) { }
void f(int a, Integer b) { }
f(1, 2);   // compile error: ambiguous

JDK examples: println (int, double, String, Object…), Math.max, String.valueOf, Arrays.sort.

Practice explaining this
Question 6 of 11

What is method overriding?

Solution and explanation

What it is

Method overriding is when a subclass provides its own implementation of an instance method already defined in its parent, with the same name, parameters and a compatible return type. The version that runs is chosen at run time from the actual object type (runtime polymorphism).

class Notification {
    void send(String msg) { System.out.println("Generic: " + msg); }
}

class EmailNotification extends Notification {
    @Override
    void send(String msg) { System.out.println("Email: " + msg); }
}

class WhatsAppNotification extends Notification {
    @Override
    void send(String msg) { System.out.println("WhatsApp: " + msg); }
}

Notification n = new WhatsAppNotification();
n.send("Offer letter released");   // WhatsApp: Offer letter released

Rules

  1. Same method name and parameter list.
  2. Return type: same, or a subtype (covariant return).
  3. Access cannot be more restrictive (protected → public is OK, public → protected is not).
  4. Cannot throw new or broader checked exceptions (fewer, narrower or unchecked are fine).
  5. final, static and private methods cannot be overridden.
  6. Constructors cannot be overridden.
  7. Use @Override: the compiler then catches typos such as sendd or wrong parameters.

Calling the parent version

@Override
void send(String msg) {
    super.send(msg);            // keep parent behaviour
    auditLog.record(msg);       // and add more
}

Overloading vs overriding

OverloadingOverriding
WhereSame class (or inherited)Parent and child
ParametersMust differMust be the same
Return typeCan differSame or covariant
BindingCompile timeRun time
static/private/final methodsCan be overloadedCannot be overridden

Commonly overridden: toString(), equals(), hashCode(), compareTo(), run().

Practice explaining this
Question 7 of 11

What is an interface?

Solution and explanation

What it is

An interface is a reference type that defines a contract: a set of methods a class promises to provide. A class implements the interface and must give bodies for its abstract methods. Interfaces enable abstraction, loose coupling and multiple inheritance of type.

public interface Shippable {
    double MAX_WEIGHT_KG = 30;                       // implicitly public static final

    double weightKg();                               // abstract (implicitly public abstract)

    default boolean needsHeavyCourier() {            // default method (Java 8)
        return weightKg() > MAX_WEIGHT_KG;
    }

    static Shippable of(double kg) { return () -> kg; }   // static method (Java 8)

    private void log(String s) { System.out.println(s); } // private method (Java 9)
}

public class Refrigerator implements Shippable, Comparable<Refrigerator> {   // a class can implement many
    private final double kg;
    public Refrigerator(double kg) { this.kg = kg; }
    public double weightKg() { return kg; }
    public int compareTo(Refrigerator o) { return Double.compare(kg, o.kg); }
}

Shippable item = new Refrigerator(55);
System.out.println(item.needsHeavyCourier());   // true

What an interface can contain

MemberSinceNotes
Abstract methodsJava 1Public by default
ConstantsJava 1public static final
Default methodsJava 8Body provided; implementing classes may override
Static methodsJava 8Called with the interface name
Private methodsJava 9Share code between default methods

It cannot have instance fields or constructors.

Special kinds

  • Functional interface: exactly one abstract method, usable with lambdas (Runnable, Comparator, Function, Predicate). Mark with @FunctionalInterface.
  • Marker interface: no methods, just tags a class (Serializable, Cloneable).
  • Sealed interface (Java 17): restricts which classes may implement it.

Why interfaces matter

Spring injects beans by interface (PaymentGateway), so implementations can be swapped (Razorpay, Stripe, a mock in tests) without changing the code that uses them.

Practice explaining this
Question 8 of 11

What is the difference between an abstract class and an interface?

Solution and explanation

Comparison

FeatureAbstract classInterface
Keywordabstract class, used with extendsinterface, used with implements
Multiple inheritanceA class extends only oneA class implements many
MethodsAbstract and concreteAbstract, default, static, private
FieldsAny: instance, static, final or notOnly public static final constants
ConstructorsYes (called by subclasses)No
Access modifiers on methodsAnyPublic (private allowed for helpers)
StateCan hold stateCannot hold instance state
RelationshipIS-A with shared base implementationCAN-DO capability / contract
Speed of adding methodsAdding a concrete method is safeAdding an abstract method breaks implementers; default methods avoid that

When to use which

Abstract class: closely related classes that share state and code.

public abstract class BaseRepository<T> {
    protected final DataSource dataSource;                  // shared state
    protected BaseRepository(DataSource ds) { this.dataSource = ds; }
    public abstract T findById(long id);
    protected Connection connection() throws SQLException { return dataSource.getConnection(); }   // shared code
}

Interface: a capability that unrelated classes can have, or an API you want to keep swappable.

public interface Exportable { byte[] toPdf(); }
class Invoice implements Exportable { … }
class Resume implements Exportable { … }       // Invoice and Resume are unrelated

Both together

A common design: an interface for the contract, an abstract class with a skeletal implementation, and concrete classes. The JDK does this with List → AbstractList → ArrayList.

Since Java 8 the gap has narrowed (default methods), but interfaces still cannot hold instance state or constructors.

Practice explaining this
Question 9 of 11

What is an IS-A relationship?

Solution and explanation

What it is

IS-A is the relationship expressed by inheritance (extends) or interface implementation (implements). If B extends A, then B IS-A A, and a B object can be used anywhere an A is expected.

class Vehicle { void start() { System.out.println("Starting"); } }
class Car extends Vehicle { }                    // Car IS-A Vehicle
class ElectricCar extends Car implements Chargeable { public void charge() { } }   // ElectricCar IS-A Car, Vehicle and Chargeable

Vehicle v = new ElectricCar();                   // valid because of IS-A
System.out.println(v instanceof Car);            // true
System.out.println(v instanceof Chargeable);     // true

Test for IS-A: Liskov Substitution Principle

A subclass should be usable wherever the parent is used without breaking expectations.

  • A Savings Account IS-A Bank Account: good inheritance.
  • A Square IS-A Rectangle? Mathematically yes, but if Rectangle.setWidth() changes only width, a Square breaks that expectation. Classic example of where IS-A in English does not mean inheritance in code.

Characteristics

  • Expressed with extends and implements.
  • Tight coupling: changes in the parent affect all children.
  • Enables polymorphism.
  • Checked with instanceof.

IS-A vs HAS-A preview

IS-AHAS-A
InheritanceComposition / aggregation
class Car extends Vehicleclass Car { private Engine engine; }
Fixed at compile timeCan change at run time
Tight couplingLoose coupling
Practice explaining this
Question 10 of 11

What is a HAS-A relationship?

Solution and explanation

What it is

HAS-A means one class contains a reference to another class as a field: a Car HAS-A Engine, an Order HAS-A list of Items. It is implemented with composition or aggregation, not inheritance.

public class Engine {
    private final int cc;
    public Engine(int cc) { this.cc = cc; }
    public void start() { System.out.println(cc + "cc engine started"); }
}

public class Car {
    private final Engine engine;                 // Car HAS-A Engine
    private final String model;

    public Car(String model, Engine engine) {
        this.model = model;
        this.engine = engine;
    }

    public void drive() {
        engine.start();                          // delegation
        System.out.println(model + " is moving");
    }
}

new Car("Creta", new Engine(1497)).drive();

Two forms

Composition (strong HAS-A)Aggregation (weak HAS-A)
LifetimePart cannot exist without the wholePart exists independently
OwnershipWhole creates and owns the partWhole only refers to the part
ExampleHouse HAS rooms; delete house, rooms go tooDepartment HAS employees; employees exist if the department closes
Code hintPart created inside the classPart passed in from outside and shared
class House {
    private final List<Room> rooms = List.of(new Room("Hall"), new Room("Kitchen"));   // composition
}
class Department {
    private final List<Employee> employees;                                            // aggregation
    Department(List<Employee> employees) { this.employees = employees; }
}

Benefits

  • Loose coupling: the Engine can be replaced (petrol, diesel, electric) through an interface.
  • Reuse without inheriting unwanted behaviour.
  • Easier testing: inject a mock part.

This is exactly how Spring dependency injection works: an OrderService HAS-A PaymentGateway.

Practice explaining this

Showing questions 1–10 of 11