Java OOP

Su Suriya Ravichandran Updated 14 Nov 2026
7 min read ·Lesson 41 of 133

Object-oriented programming (OOP) is a way of structuring programs around objects: units that bundle data together with the operations allowed on that data. Java was designed as an object-oriented language. Nearly all Java code lives inside classes, and the standard library, frameworks such as Spring, and Android are all built from objects collaborating with each other. This lesson explains the ideas; the lessons that follow cover each mechanism in detail.

The Problem OOP Solves

Consider a bank program written only with variables and static methods:

public class Bank {
    public static void main(String[] args) {
        String owner1 = "Ada";
        double balance1 = 500;

        String owner2 = "Linus";
        double balance2 = 1200;

        balance1 = deposit(balance1, 200);
        balance2 = withdraw(balance2, 2000);   // nothing stops an overdraft
        balance1 = -999;                       // nothing stops invalid data
    }

    static double deposit(double balance, double amount) { return balance + amount; }
    static double withdraw(double balance, double amount) { return balance - amount; }
}

Problems appear quickly:

  • Related data (owner1, balance1) is held in separate variables, connected only by naming. Adding a third account means more variables.
  • Any code can change any balance to anything, including invalid values.
  • The rules (no overdrafts, no negative deposits) must be remembered and repeated wherever balances change.

OOP groups the data and the rules into a single unit:

public class Account {
    private final String owner;
    private double balance;

    public Account(String owner, double openingBalance) {
        this.owner = owner;
        this.balance = openingBalance;
    }

    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("Deposit must be positive");
        balance += amount;
    }

    public void withdraw(double amount) {
        if (amount > balance) throw new IllegalStateException("Insufficient funds");
        balance -= amount;
    }

    public double getBalance() { return balance; }
    public String getOwner() { return owner; }
}
Account a = new Account("Ada", 500);
Account b = new Account("Linus", 1200);

a.deposit(200);
b.withdraw(2000);     // throws IllegalStateException: the rule is enforced
// a.balance = -999;  // compile error: balance is private

Each Account object carries its own owner and balance. The only way to change a balance is through methods that enforce the rules. A thousand accounts need no more code than one.

Classes and Objects

A class is a blueprint: it defines what data an object holds (fields) and what it can do (methods). An object is a concrete instance built from that blueprint, with its own values.

Class: Account                    Objects (instances)
┌─────────────────────┐           ┌──────────────────────┐
│ fields:             │   new     │ owner   = "Ada"      │
│   owner             │ ───────►  │ balance = 700.0      │
│   balance           │           └──────────────────────┘
│ methods:            │           ┌──────────────────────┐
│   deposit()         │ ───────►  │ owner   = "Linus"    │
│   withdraw()        │           │ balance = 1200.0     │
│   getBalance()      │           └──────────────────────┘
└─────────────────────┘

One class, many objects. Every object has the same structure and behavior, but its own state. Calling a.deposit(200) changes Ada's balance and leaves Linus's untouched.

Every object has three characteristics:

CharacteristicMeaningIn the example
StateThe current values of its fieldsbalance = 700.0
BehaviorWhat it can do: its methodsdeposit, withdraw
IdentityIt is a distinct object, even if another has identical stateTwo accounts both holding 500 are still two accounts

The Four Principles of OOP

1. Encapsulation

Keep an object's data private and expose only controlled operations. The Account class makes balance private, so the only way to change it is through deposit and withdraw, which validate every change. The object is responsible for keeping itself in a valid state.

Encapsulation also hides implementation details. If Account later stores the balance in paise as a long instead of rupees as a double, code that uses getBalance() does not need to change.

2. Inheritance

A class can extend another, inheriting its fields and methods and adding or changing behavior:

public class SavingsAccount extends Account {
    private final double interestRate;

    public SavingsAccount(String owner, double openingBalance, double interestRate) {
        super(owner, openingBalance);
        this.interestRate = interestRate;
    }

    public void addMonthlyInterest() {
        deposit(getBalance() * interestRate / 12);
    }
}

SavingsAccount is an Account: it has an owner, a balance, and the deposit and withdraw rules, without repeating that code. It adds interest. Inheritance models "is-a" relationships and lets shared behavior live in one place.

3. Polymorphism

"Many forms": code written for a general type works with any of its specific types, and each object responds in its own way.

public abstract class Shape {
    public abstract double area();
}

public class Circle extends Shape {
    private final double r;
    public Circle(double r) { this.r = r; }
    public double area() { return Math.PI * r * r; }
}

public class Rectangle extends Shape {
    private final double w, h;
    public Rectangle(double w, double h) { this.w = w; this.h = h; }
    public double area() { return w * h; }
}
List<Shape> shapes = List.of(new Circle(1), new Rectangle(2, 3));
double total = 0;
for (Shape s : shapes) {
    total += s.area();            // each shape computes its own area
}
System.out.printf("%.2f%n", total);   // 9.14

The loop does not know or care which kind of shape it has. Adding a Triangle class requires no change to the loop. This is what makes object-oriented systems extensible.

4. Abstraction

Expose what an object does and hide how. Shape above declares that every shape has an area(), without saying how it is computed. Interfaces take this further:

public interface PaymentGateway {
    boolean charge(String customerId, double amount);
}

The rest of the application calls gateway.charge(...). Whether the implementation uses Razorpay, Stripe, or a test stub is hidden behind the interface and can be swapped without touching the calling code.

PrincipleCore questionJava mechanism
EncapsulationWho can change this data?private fields, public methods
InheritanceWhat does this type share with a more general type?extends, super
PolymorphismCan one piece of code handle many types?Overriding, interfaces, dynamic dispatch
AbstractionWhat is essential, and what can be hidden?abstract classes, interface

Composition: Objects Containing Objects

Inheritance is not the only way to reuse code. Often a class is better built by containing other objects, a "has-a" relationship:

public class Order {
    private final Customer customer;          // an Order has a Customer
    private final List<LineItem> items;       // and has LineItems
    private final PaymentGateway gateway;     // and uses a PaymentGateway

    ...
}

Composition is more flexible than inheritance, because the parts can be changed independently and replaced at runtime. A common design guideline is to prefer composition and use inheritance only for genuine "is-a" relationships.

Objects Collaborating

An object-oriented program runs by objects calling methods on each other. Each object does its own part:

Cart cart = new Cart();
cart.add(new Product("Keyboard", 2499), 1);
cart.add(new Product("Mouse", 799), 2);

Order order = checkout.placeOrder(customer, cart);   // Checkout asks Cart for its total,
                                                     // asks PaymentGateway to charge,
                                                     // creates an Order
emailService.sendConfirmation(order);

No single piece of code knows every detail. Cart knows how to total its items; PaymentGateway knows how to charge a card; EmailService knows how to send mail. This division of responsibility is what keeps large applications understandable.

OOP and the Rest of Java

Even code that does not look object-oriented uses objects constantly:

String name = "ada lovelace";
String title = name.toUpperCase();        // calling a method on a String object
List<String> list = new ArrayList<>();    // creating an object
list.add(title);                          // calling a method on it

String, ArrayList, Scanner, LocalDate, and HttpClient are all classes, and every value you have used of those types is an object. The static methods used in earlier lessons (static void greet(...)) were a stepping stone. From here on, most methods you write will be instance methods that operate on an object's own state.

Java's Object Model in Brief

  • Every class implicitly extends java.lang.Object, which provides toString(), equals(), hashCode(), and getClass().
  • A class extends exactly one parent class but can implement any number of interfaces.
  • Objects are always created with new (or a factory method that uses it) and live on the heap; variables hold references to them.
  • Objects are destroyed automatically by the garbage collector once nothing references them.
  • Primitive types (int, double, boolean, ...) are the exception: they are values, not objects.

The Lessons Ahead

LessonTopic
Classes, ObjectsDefining classes and creating instances
Class Methods, Constructors, Constructor OverloadBehavior and initialization
this, static, finalKey modifiers and references
Encapsulation, Inheritance, Polymorphism, AbstractionThe four principles in depth
Access Modifiers, superVisibility and parent-class access
Method Overload, Method OverrideTwo kinds of same-named methods
instanceof, Nested/Inner/Anonymous ClassesType tests and class nesting
Enums, RecordsSpecialized class types for constants and data

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Java OOP and get answers drawn from it.

Signed-in readers only.