Course topics

By WebNest Studio Engineering Team

Java - Core Tutorial

OOPs Concepts in Java

Object-Oriented Programming (OOP) is a way of designing software around "objects" — self-contained units that bundle data (fields) with the behavior (methods) that operates on that data. Java was built as an object-oriented language from the ground up, so almost everything you write, aside from the eight primitive types, is an object or a reference to one.

The motivation behind OOP is managing complexity. Procedural code tends to scatter data and the functions that act on it across a program, which becomes hard to maintain as the codebase grows. OOP keeps related data and behavior together inside classes, which makes large systems easier to reason about, extend, and test in isolation.

Why it matters

The four pillars are not exam vocabulary; they are the reasons large Java codebases stay manageable. Encapsulation keeps invalid data out, abstraction hides detail you do not need, inheritance removes duplication, and polymorphism lets new types slot in without changing existing code. Almost every Java interview opens with a question about them.

Watch: Java OOPs Concepts: The Overall View

Prefer to watch? This video gives a big-picture overview of object-oriented programming in Java: classes, objects and the four pillars.

Watch on YouTube

The Four Pillars of OOP

Every object-oriented feature in Java ultimately supports one of four core principles. Understanding these four ideas up front makes every later topic in this module — classes, inheritance, overriding, and initializer blocks — click into place faster.

  • Encapsulation — bundling data and the methods that operate on it inside a class, and restricting direct access to that data using access modifiers (typically private fields with public getters/setters). This protects an object's internal state from invalid changes.
  • Inheritance — allowing one class (a subclass) to acquire the fields and methods of another class (a superclass) using the extends keyword, enabling code reuse and a natural "is-a" relationship between types.
  • Polymorphism — allowing the same method call to behave differently depending on the actual object it is invoked on (runtime/dynamic polymorphism via overriding) or depending on the arguments passed (compile-time polymorphism via overloading).
  • Abstraction — hiding internal implementation details and exposing only the essential features of an object, typically through abstract classes and interfaces, so callers depend on "what" an object does, not "how" it does it.

Object and Class as the Foundation

All four pillars are built on top of two foundational concepts: a class, which is a blueprint describing what fields and methods a category of objects will have, and an object, which is an actual instance created from that blueprint at runtime, occupying its own memory on the heap. Nothing else in OOP makes sense without this class/object distinction, which is why it is the very next topic.

OOP vs Procedural Programming

In procedural programming, a program is a sequence of functions that pass data around. In object-oriented programming, a program is a set of objects that each look after their own data and talk to each other through methods. The difference shows up as a program grows.

  • Organisation — procedural code groups by steps; OOP groups by things (a Customer, an Order, a Payment), each with its own data and methods.
  • Data safety — procedural data is often shared and changed from anywhere; OOP keeps fields private and changes them only through methods that can validate input.
  • Reuse — procedural code reuses functions; OOP also reuses whole types through inheritance and composition.
  • Change — adding a new kind of thing in OOP usually means adding a new class, not editing every function that handles the old kinds.

How the Four Pillars Work Together

The pillars are rarely used one at a time. In the second example below, Shape is abstract, so callers only know that every shape has an area (abstraction). Its name field is private and read through a getter (encapsulation). Circle and Rectangle extend Shape and reuse its name handling (inheritance). Finally, the loop calls shape.area() on each element of a Shape[] array, and Java runs the right version for each object at runtime (polymorphism). Adding a Triangle later would mean writing one new class; the loop would not change.

Why Java Enforces OOP Structure

Unlike some multi-paradigm languages, Java requires almost all code to live inside a class — there are no free-floating functions outside a class the way there are in C or Python. This is a deliberate design decision: it forces every piece of behavior to have an owner (a class), which keeps large codebases organized and makes dependencies explicit rather than implicit.

Examples

A tiny class demonstrating encapsulation and abstraction together

Java
public class BankAccount {
    private double balance; // encapsulated: not directly accessible from outside

    public BankAccount(double initialBalance) {
        balance = initialBalance;
    }

    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    public double getBalance() { // controlled access to private data
        return balance;
    }

    public static void main(String[] args) {
        BankAccount account = new BankAccount(500.0);
        account.deposit(150.0);
        System.out.println("Balance: " + account.getBalance());
    }
}
Output
Balance: 650.0

All four pillars in one program: abstraction, encapsulation, inheritance and polymorphism

Java
public class ShapeDemo {
    public static void main(String[] args) {
        Shape[] shapes = { new Circle(2), new Rectangle(3, 4) };
        for (Shape shape : shapes) {
            // Polymorphism: the same call runs Circle's or Rectangle's area()
            System.out.printf("%s area: %.2f%n", shape.getName(), shape.area());
        }
    }
}

// Abstraction: callers know every Shape has an area, not how it is calculated
abstract class Shape {
    private final String name; // Encapsulation: private, read through a getter

    Shape(String name) {
        this.name = name;
    }

    String getName() {
        return name;
    }

    abstract double area();
}

// Inheritance: Circle and Rectangle reuse Shape's name handling
class Circle extends Shape {
    private final double radius;

    Circle(double radius) {
        super("Circle");
        this.radius = radius;
    }

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

class Rectangle extends Shape {
    private final double width;
    private final double height;

    Rectangle(double width, double height) {
        super("Rectangle");
        this.width = width;
        this.height = height;
    }

    @Override
    double area() {
        return width * height;
    }
}
Output
Circle area: 12.57
Rectangle area: 12.00

Common Mistakes

  • Treating OOP as just "using classes" — without encapsulation, inheritance, polymorphism, and abstraction actually being applied, a class-based program is not meaningfully object-oriented.
  • Confusing overloading (compile-time polymorphism) with overriding (runtime polymorphism) as if they were the same mechanism.
  • Making every field public instead of private, which defeats encapsulation and allows any code to corrupt an object's internal state.

Exercise

Write a Thermostat that protects its own data. The target temperature must stay between 16 and 30; a request outside that range is ignored.

Expected output: 22, then 22 again after an attempt to set it to 40.

Java · starter code
class Thermostat {
    // TODO: a private field for the target temperature, starting at 20
    // TODO: setTarget(int value) that ignores values below 16 or above 30
    // TODO: getTarget()
}

public class ThermostatDemo {
    public static void main(String[] args) {
        Thermostat thermostat = new Thermostat();
        thermostat.setTarget(22);
        System.out.println(thermostat.getTarget());
        thermostat.setTarget(40);
        System.out.println(thermostat.getTarget());
    }
}
Show hints
  • Make the field private so the only way to change it is through setTarget.
  • Inside the setter, assign the value only when it is within the allowed range.
Show solution
Java · solution
class Thermostat {
    private int target = 20;

    void setTarget(int value) {
        if (value >= 16 && value <= 30) {
            target = value;
        }
    }

    int getTarget() {
        return target;
    }
}

public class ThermostatDemo {
    public static void main(String[] args) {
        Thermostat thermostat = new Thermostat();
        thermostat.setTarget(22);
        System.out.println(thermostat.getTarget()); // 22
        thermostat.setTarget(40);
        System.out.println(thermostat.getTarget()); // 22
    }
}

Quick quiz

1. Keeping fields private and exposing them only through methods that enforce rules is an example of which principle?

  • A. Inheritance
  • B. Polymorphism
  • C. Encapsulation
  • D. Overloading
Show answer

C. Encapsulation bundles data with the methods that control it and hides the data from outside code.

2. The same call, shape.area(), runs different code depending on whether the object is a Circle or a Square. Which principle is this?

  • A. Encapsulation
  • B. Abstraction
  • C. Aggregation
  • D. Polymorphism
Show answer

D. Polymorphism means one interface with many implementations, chosen by the actual type of the object at runtime.

3. What is the relationship between a class and an object?

  • A. A class is a blueprint; an object is an instance created from it
  • B. They are the same thing
  • C. An object is a blueprint for a class
  • D. A class is stored on the heap, an object on the stack
Show answer

A. A class defines fields and methods. Each object created with new is a separate instance with its own field values.

Interview questions

What are the four pillars of object-oriented programming?

Show answer

Encapsulation: keeping data and the methods that operate on it together and restricting direct access. Abstraction: exposing what an object does while hiding how. Inheritance: a class reusing and extending the fields and methods of another. Polymorphism: one method call behaving differently depending on the object's actual type.

What is the difference between abstraction and encapsulation?

Show answer

Abstraction is about design: deciding what to expose so callers can use something without knowing its internals, typically through abstract classes and interfaces. Encapsulation is about implementation: hiding the data inside a class with access modifiers and controlling it through methods. Abstraction hides complexity; encapsulation hides and protects state.

Key Points to Remember

  • The four pillars of OOP are encapsulation, inheritance, polymorphism, and abstraction.
  • A class is a blueprint; an object is a runtime instance created from that blueprint.
  • Java requires almost all code to live inside a class, enforcing object-oriented structure by design.
  • The pillars work together: an abstract type, private state, subclasses and one polymorphic call site is the typical shape of OOP code.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering