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
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.
Inheritance
In Simple Terms
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
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
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 Class | Interface |
|---|---|
| Can have constructors | Cannot have constructors |
| Can have instance variables | Only static final constants |
| Methods can be any modifier | Methods are public by default |
| Single inheritance only | Multiple interfaces implementable |
| Use for 'IS-A' relationship | Use for 'CAN-DO' capability |
| ~30-80% implementation | 0% 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.