Before Java was a language, Object-Oriented Programming was a paradigm — a way of thinking about software. Instead of writing a long list of instructions (procedural), you model your program as a collection of objects that talk to each other.
Think of it this way: the real world is full of objects — a Car, a BankAccount, a Student. Each object has properties (what it has) and behaviours (what it does). OOP lets us mirror this natural structure in code.
The Four Pillars
Everything in Java OOP circles back to these four pillars. Memorise them — every interviewer will ask about them.
Abstraction · Encapsulation · Inheritance · Polymorphism — "A Every Individual Person"
Why OOP? (Exam Favourite)
- Modularity — Code is divided into self-contained objects, making it manageable.
- Reusability — Write once (parent class), use many times (child classes).
- Maintainability — Change one object without affecting others.
- Security — Data hiding prevents unauthorised access.
- Real-world modelling — Naturally maps to how we think.
Defining a Class
A class is declared using the class keyword. It contains fields (data/attributes) and methods (behaviour).
public class Student {
// Fields (instance variables)
private String name;
private int age;
private double gpa;
// Method (behaviour)
public void study() {
System.out.println(name + " is studying!");
}
public void introduce() {
System.out.println("Hi, I am " + name + " and I'm " + age);
}
}Java
Creating Objects
An object is an instance of a class, created using the new keyword. Each object gets its own copy of the instance variables.
public class Main {
public static void main(String[] args) {
// Creating objects (instances) of Student class
Student s1 = new Student();
Student s2 = new Student();
// s1 and s2 are SEPARATE objects — they don't share data
s1.name = "Rahul"; // Only possible if field is public
s2.name = "Priya";
s1.study(); // Output: Rahul is studying!
s2.study(); // Output: Priya is studying!
}
}Java
Instance vs Class Members
| Aspect | Instance Member | Class (Static) Member |
|---|---|---|
| Belongs to | Each individual object | The class itself |
| Keyword | (none) | static |
| Access | object.field | ClassName.field |
| Copies | One per object | One shared copy |
A: A class is a user-defined data type — a template or blueprint. An object is an instance of that class created at runtime using
new. The class defines structure; the object holds actual data. Multiple objects can be created from one class, each with its own state.
The new Keyword — What Actually Happens
When you write new Student(), Java does three things:
- Allocates memory in the heap for the new object.
- Initialises instance variables to default values (
0,null,false). - Calls the constructor to set up the object.
A constructor is a special method that is automatically called when an object is created with new. Its job is to initialise the object.
- Same name as the class.
- No return type — not even
void. - Called automatically when
newis used. - Can be overloaded (multiple constructors with different parameters).
Types of Constructors
1. Default Constructor
If you write no constructor, Java automatically provides a no-argument constructor that sets all fields to default values. As soon as you define any constructor, Java stops providing this.
public class Car {
String brand;
int speed;
// Java provides this implicitly if you write nothing:
// public Car() { } — fields get: brand=null, speed=0
}Java
2. Parameterised Constructor
Accepts arguments so you can initialise an object with specific values at creation time.
public class Car {
private String brand;
private int speed;
// Parameterised Constructor
public Car(String brand, int speed) {
this.brand = brand; // 'this' differentiates field from parameter
this.speed = speed;
}
public void display() {
System.out.println(brand + " goes " + speed + " km/h");
}
}
// Usage:
Car c1 = new Car("Toyota", 180); // brand="Toyota", speed=180
Car c2 = new Car("BMW", 250); // brand="BMW", speed=250Java
3. Constructor Overloading
Multiple constructors with different parameter lists in the same class. Java picks the right one based on the arguments.
public class Rectangle {
private double length, width;
// No-arg constructor — creates a unit square
public Rectangle() {
this.length = 1.0;
this.width = 1.0;
}
// One-arg — creates a square of given size
public Rectangle(double side) {
this.length = side;
this.width = side;
}
// Two-arg — creates a rectangle
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
public double area() { return length * width; }
}
// All valid:
Rectangle r1 = new Rectangle(); // 1×1
Rectangle r2 = new Rectangle(5.0); // 5×5
Rectangle r3 = new Rectangle(4.0, 6.0); // 4×6Java
4. Copy Constructor
Creates a new object as a copy of an existing one. Java doesn't provide one automatically like C++ — you write it manually.
public Car(Car other) {
this.brand = other.brand;
this.speed = other.speed;
}
Car original = new Car("Honda", 150);
Car copy = new Car(original); // separate object, same dataJava
A: Yes! A private constructor prevents object creation from outside the class. This is the foundation of the Singleton design pattern, where only one instance of a class is allowed.
Access Modifiers
| Modifier | Same Class | Same Package | Subclass | Everywhere |
|---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ |
default | ✅ | ✅ | ❌ | ❌ |
protected | ✅ | ✅ | ✅ | ❌ |
public | ✅ | ✅ | ✅ | ✅ |
Implementing Encapsulation
public class BankAccount {
// Step 1: Make fields PRIVATE
private String owner;
private double balance;
public BankAccount(String owner, double initialBalance) {
this.owner = owner;
this.balance = initialBalance;
}
// Step 2: Provide PUBLIC getters (read access)
public String getOwner() { return owner; }
public double getBalance() { return balance; }
// Step 3: Provide PUBLIC setters (write access) WITH VALIDATION
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
} else {
System.out.println("Deposit amount must be positive!");
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
} else {
System.out.println("Invalid withdrawal!");
}
}
}Java
balance = -5000 directly. All changes go through deposit() and withdraw() which have validation logic. This is data protection in action.
Why Encapsulation Matters
- Data hiding — Internal state is safe from accidental corruption.
- Controlled access — You can make a field read-only (getter, no setter).
- Validation — Setters can enforce rules before changing data.
- Maintainability — You can change the internal implementation without breaking external code.
int, char, double, etc.) that are not objects. A purely OOP language would have everything as an object (like Smalltalk). However, Java provides wrapper classes (Integer, Character, etc.) for all primitives.
Inheritance allows a class (child/subclass) to acquire properties and methods of another class (parent/superclass). It's Java's primary mechanism for code reuse.
extends to inherit. Java supports single inheritance only (one parent class) — but multiple levels (multilevel inheritance) are allowed.
Basic Inheritance
// Parent (Superclass)
public class Animal {
protected String name;
protected 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 breathe() {
System.out.println(name + " is breathing.");
}
}
// Child (Subclass) — inherits from Animal
public class Dog extends Animal {
private String breed;
public Dog(String name, int age, String breed) {
super(name, age); // Call parent constructor FIRST
this.breed = breed;
}
// Dog's own behaviour
public void bark() {
System.out.println(name + " says: Woof!");
}
}
// Dog inherits eat() and breathe() from Animal
Dog d = new Dog("Bruno", 3, "Labrador");
d.eat(); // Inherited from Animal → "Bruno is eating."
d.bark(); // Dog's own method → "Bruno says: Woof!"Java
Types of Inheritance in Java
| Type | Description | Java Support |
|---|---|---|
| Single | One parent, one child | ✅ Yes |
| Multilevel | A → B → C (chain) | ✅ Yes |
| Hierarchical | One parent, many children | ✅ Yes |
| Multiple | One child, many parents | ❌ Not via classes |
| Hybrid | Combination of above | ✅ Via interfaces only |
A: To avoid the Diamond Problem. If class C inherits from both A and B, and both A and B have a method
display(), which version does C inherit? Java eliminates this ambiguity by disallowing multiple class inheritance. However, multiple inheritance is possible through interfaces (covered in Chapter 07).
Method Overriding
A child class can redefine a method from the parent class. The child's version replaces the parent's when called on a child object.
public class Animal {
public void makeSound() {
System.out.println("Some generic animal sound");
}
}
public class Dog extends Animal {
@Override // Good practice — compiler checks you're actually overriding
public void makeSound() {
System.out.println("Woof!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
Animal a = new Dog(); // Polymorphism — covered next chapter
a.makeSound(); // Output: "Woof!" — Dog's version runsJava
- Method name, return type, and parameters must match exactly.
- Access modifier of child method cannot be more restrictive than parent's.
- Use
@Overrideannotation — it helps catch errors. privateandfinalmethods cannot be overridden.staticmethods are not overridden — they are hidden.
The word comes from Greek: poly (many) + morphos (forms). In Java, polymorphism means that the same method call can behave differently depending on the object it's acting on.
Two Types
Method Overloading (Compile-time)
Same method name, different parameters in the same class.
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) { // different param types
return a + b;
}
public int add(int a, int b, int c) { // different param count
return a + b + c;
}
}
Calculator calc = new Calculator();
calc.add(2, 3); // Calls first: int version → 5
calc.add(2.5, 3.5); // Calls second: double version → 6.0
calc.add(1, 2, 3); // Calls third: three-arg version → 6Java
Runtime Polymorphism — The Magic
This is where it gets powerful. A parent class reference can hold a child class object. The method called is determined at runtime based on the actual object type.
class Shape {
public void draw() {
System.out.println("Drawing a shape");
}
}
class Circle extends Shape {
@Override
public void draw() { System.out.println("Drawing a circle ○"); }
}
class Triangle extends Shape {
@Override
public void draw() { System.out.println("Drawing a triangle △"); }
}
// Runtime polymorphism in action:
Shape s;
s = new Circle(); // Shape ref → Circle object
s.draw(); // "Drawing a circle ○" — Circle's version!
s = new Triangle(); // Shape ref → Triangle object
s.draw(); // "Drawing a triangle △" — Triangle's version!
// POWER: Process any shape uniformly
Shape[] shapes = { new Circle(), new Triangle(), new Circle() };
for (Shape shape : shapes) {
shape.draw(); // Correct version called each time — automatically!
}Java
Shape s = new Circle();Downcasting: Parent → Child reference (manual, use
instanceof to check first).Circle c = (Circle) s; — only safe if s actually holds a Circle.
| Feature | Overloading | Overriding |
|---|---|---|
| Where | Same class | Parent-child classes |
| Parameters | Must differ | Must be same |
| Resolved at | Compile time | Runtime |
| Return type | Can differ | Must be same (or covariant) |
| Inheritance | Not required | Required |
Abstraction is the process of hiding complex implementation details and showing only the essential features of an object. Think about driving a car — you know how to use the steering wheel and pedals, but you don't need to understand the combustion engine internals.
In Java, abstraction is achieved via abstract classes and interfaces.
Abstract Classes
- Declared with
abstractkeyword. - Can have both abstract methods (no body) and concrete methods (with body).
- Cannot be instantiated — you cannot do
new AbstractClass(). - A subclass must implement ALL abstract methods, or itself be declared abstract.
abstract class Vehicle {
protected String model;
protected int year;
public Vehicle(String model, int year) {
this.model = model;
this.year = year;
}
// Abstract method — NO body, subclass MUST implement
public abstract void fuelUp();
public abstract double calculateRange();
// Concrete method — has body, inherited as-is
public void displayInfo() {
System.out.println(year + " " + model);
}
}
class ElectricCar extends Vehicle {
private double batteryCapacity;
public ElectricCar(String model, int year, double battery) {
super(model, year);
this.batteryCapacity = battery;
}
@Override
public void fuelUp() {
System.out.println("Charging battery...");
}
@Override
public double calculateRange() {
return batteryCapacity * 6.5; // 6.5 km per kWh
}
}
// Vehicle v = new Vehicle(...); ← ERROR! Cannot instantiate abstract class
Vehicle v = new ElectricCar("Tesla Model 3", 2024, 82.0);
v.fuelUp(); // "Charging battery..."
v.displayInfo(); // "2024 Tesla Model 3"Java
Animal class.
An interface defines a contract — a set of method signatures that a class promises to implement. Think of it as a job description: "Anyone who takes this role MUST be able to do these things."
Defining an Interface
// By default, interface methods are public and abstract
public interface Drawable {
void draw(); // implicitly public abstract
double getArea();
}
public interface Serializable {
void serialize();
}
// A class can implement MULTIPLE interfaces — solves multiple inheritance!
public class Circle implements Drawable, Serializable {
private double radius;
public Circle(double radius) { this.radius = radius; }
@Override
public void draw() {
System.out.println("Drawing circle with radius: " + radius);
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
@Override
public void serialize() {
System.out.println("Serializing circle data...");
}
}Java
Abstract Class vs Interface — The Big Comparison
| Feature | Abstract Class | Interface |
|---|---|---|
| Keyword | abstract class | interface |
| Instantiation | Cannot | Cannot |
| Methods | Abstract + Concrete | Abstract (+ default/static in Java 8+) |
| Fields | Any type | Only public static final (constants) |
| Constructor | Yes | No |
| Multiple | Only single extends | Class can implement many |
| Use when | Shared code + identity | Shared contract across unrelated classes |
Java 8+ Interface Features
public interface Logger {
// Abstract method (must implement)
void log(String message);
// Default method — has a body, optional to override
default void logError(String message) {
log("ERROR: " + message);
}
// Static method — called via interface name, not object
static Logger getConsoleLogger() {
return msg -> System.out.println("[LOG] " + msg);
}
}Java
Runnable, Comparator, Callable.
Trainable).
The static Keyword
static members belong to the class, not to individual objects. There's exactly one copy, shared by all instances.
public class Counter {
private static int count = 0; // Shared by ALL Counter objects
private int id; // Each object has its OWN id
public Counter() {
count++; // Every time a Counter is created, count grows
this.id = count;
}
public static int getCount() { return count; }
public int getId() { return id; }
}
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();
System.out.println(Counter.getCount()); // 3 — accessed via class nameJava
static method cannot use this or access non-static (instance) fields directly. Why? Because static methods run without any object — there's no "this" to refer to.
Static Blocks
public class Config {
public static final String DB_URL;
static { // Runs once when class is first loaded
DB_URL = "jdbc:mysql://localhost:3306/mydb";
System.out.println("Config class loaded.");
}
}Java
The final Keyword
| Applied to | Effect |
|---|---|
final variable | Value cannot be changed (constant) |
final method | Cannot be overridden in a subclass |
final class | Cannot be subclassed (no child classes) |
// final variable — must be initialised once
final double PI = 3.14159;
// PI = 3.0; ← COMPILE ERROR
// final class — String is a classic example
public final class ImmutablePoint {
private final double x, y; // Both fields are final too
public ImmutablePoint(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() { return x; }
public double getY() { return y; }
}
// class MyPoint extends ImmutablePoint {} ← COMPILE ERRORJava
String is a final class for security (you can't subclass String and change its behaviour), thread safety (immutable objects are inherently thread-safe), and caching (the JVM's String Pool relies on strings being immutable).
The this Keyword
this is a reference to the current object — the object on which the method is being called.
public class Employee {
private String name;
private double salary;
// Use 1: Distinguish field from parameter with same name
public Employee(String name, double salary) {
this.name = name; // this.name = field, name = parameter
this.salary = salary;
}
// Use 2: Call another method of the same object
public void printDetails() {
this.validate(); // 'this.' is optional here, but makes intent clear
System.out.println(name + ": ₹" + salary);
}
// Use 3: Constructor chaining — call another constructor
public Employee(String name) {
this(name, 30000.0); // Calls the 2-param constructor above
}
private void validate() { /* validation logic */ }
}Java
The super Keyword
super refers to the parent class. It lets you access parent class members that have been hidden or overridden.
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void describe() {
System.out.println("I am an animal named " + name);
}
}
public class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // Use 1: Call parent constructor
this.breed = breed;
}
@Override
public void describe() {
super.describe(); // Use 2: Call parent's version of the method
System.out.println("I am a " + breed + " dog!");
}
}
Dog d = new Dog("Bruno", "Labrador");
d.describe();
// Output:
// I am an animal named Bruno
// I am a Labrador dog!Java
super() in a constructor, it must be the very first statement. Same applies to this(). You cannot have both in the same constructor.
These are genuine questions asked at companies. Practice saying these answers out loud.
super() which triggers the abstract class constructor; (2) the constructor initialises inherited fields.final class cannot be subclassed (extended). String, Integer, and all wrapper classes are final. String is final for security, immutability, and to enable String Pool caching.== compares references (memory addresses) — whether two variables point to the same object. .equals() compares content (logical equality). For Strings: s1 == s2 may be false even if they contain the same text, but s1.equals(s2) will be true.this(). To call a parent constructor, use super(). The call must be the first statement. This avoids code duplication and ensures proper initialisation order.extends. HAS-A is composition/aggregation: Car HAS-A Engine. Modelled by having an object of another class as a field. Prefer composition over inheritance when the relationship is HAS-A — it's more flexible.Serializable, Cloneable, Remote. The JVM or framework checks if a class implements these to grant special behaviour (e.g., serialisation permissions).java.lang.Object. It provides fundamental methods like equals(), hashCode(), toString(), clone(), getClass(). This is why you can call .toString() on any Java object.Animal parameter works with Dog, Cat, Bird — any subclass. When you add a new animal, existing code doesn't change. This is the Open/Closed Principle — open for extension, closed for modification.extends. Example: interface C extends A, B { }. A class implementing C must implement all methods from A, B, and C. This is how Java achieves multiple inheritance through interfaces.Keywords Summary
| Keyword | Purpose | Example |
|---|---|---|
class | Define a class | class Dog { } |
new | Create object | new Dog() |
extends | Inherit from class | class Dog extends Animal |
implements | Implement interface | class Dog implements Trainable |
abstract | Abstract class/method | abstract void move() |
interface | Define interface | interface Flyable { } |
super | Reference parent | super.eat() |
this | Reference current object | this.name = name |
static | Class-level member | static int count |
final | Constant/no-override/no-extend | final double PI = 3.14 |
instanceof | Type check | if (obj instanceof Dog) |
@Override | Annotation for method override | @Override void eat() |
Access Modifiers Quick View
| Modifier | Class | Package | Subclass | World |
|---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ |
default | ✅ | ✅ | ❌ | ❌ |
protected | ✅ | ✅ | ✅ | ❌ |
public | ✅ | ✅ | ✅ | ✅ |
The Four Pillars — One-Line Definitions
🧬 Inheritance =
extends = child gets parent's properties = code reuse🎭 Polymorphism = overloading (compile-time) + overriding (runtime) = one interface, many forms
🎨 Abstraction =
abstract class + interfaces = hide HOW, show WHAT
Abstract Class vs Interface — Quick
If classes need a common capability/contract regardless of relationship → Interface
If you need multiple inheritance → Interface
Overloading vs Overriding — Quick
| Overloading | Overriding | |
|---|---|---|
| Type | Compile-time polymorphism | Runtime polymorphism |
| Class | Same class | Parent + Child |
| Params | Must differ | Must match |
@Override | Not used | Recommended |
Common Exam Traps
- Constructors are not inherited — but they are chained via
super(). - You cannot override private methods — they aren't visible to the child.
- Static methods are hidden, not overridden.
- Interfaces cannot have instance fields — only
public static finalconstants. - Abstract classes can have constructors — called by subclass via
super(). finalmethod can be inherited, just not overridden.- Java passes object references by value — not actual objects.
Every OOP concept serves one goal: manageable, reusable, secure, flexible software. When an interviewer asks "why use X", your answer should connect back to one of these four outcomes. Show that you understand the purpose, not just the syntax.