OOP Basics in Java: Classes, Objects and the Four Pillars with Runnable Examples
Connect Java's OOP syntax to design decisions with a complete Account example. Trace two objects through validation, overriding and runtime dispatch to exact output.
KnowledgeGate Team
Exam prep & CS education

You can write loops and methods, yet class, new, extends and @Override may still look like disconnected Java syntax. Each keyword changes one object model: define a type, create instances, specialise behaviour and verify an override. Two account objects then move through validation and runtime dispatch to exact output.
OOP basics in Java: the class, object and reference mental model
Oracle's Java Tutorials on object-oriented concepts describe a class as the blueprint from which objects are created, and an object as a bundle of state and behaviour. That source covers JDK 8, so use it for these core definitions, not for claims about current Java releases.
Here, Account declares state (owner and balance) and behaviour (deposit(), withdraw() and applyMonthEnd()). new SavingsAccount("Asha", 10000, 5) creates one object reached through the asha reference. ravi points to a separate CurrentAccount, so their balances are independent.
Term | Meaning here |
|---|---|
Class |
|
Object | One |
Reference |
|
State | Owner, balance, interest percentage or monthly fee |
Behaviour | Deposit, withdraw and month-end processing |
The Coding & Skills category places this Java foundation inside a wider language and DSA learning path.
Java classes and objects: build the base Account abstraction
Start OopDemo.java with an abstract base class:
abstract class Account {
private final String owner;
private int balance;
Account(String owner, int openingBalance) {
if (openingBalance < 0) {
throw new IllegalArgumentException("Opening balance cannot be negative");
}
this.owner = owner;
this.balance = openingBalance;
}
public String getOwner() {
return owner;
}
public int getBalance() {
return balance;
}
public void deposit(int amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
balance += amount;
}
public void withdraw(int amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
if (amount > balance) {
throw new IllegalArgumentException("Insufficient balance");
}
balance -= amount;
}
abstract void applyMonthEnd();
}The class is abstract because every account shares identity and safe balance operations, but no month-end rule fits every account. Its abstract method requires that operation without inventing a universal implementation.

Encapsulation in Java: protect state through methods
Encapsulation matters because it protects an invariant: a balance changes only through checked operations. Client code cannot bypass those checks because balance is private. This deliberately commented line would not compile:
// asha.balance = 999999; // balance has private access in AccountAsha begins at 10000. deposit(2500) makes the balance 10000 + 2500 = 12500. Ravi begins at 8000. withdraw(1500) makes his balance 8000 - 1500 = 6500.
The checks define predictable failures. asha.deposit(-100) throws Amount must be positive. After Ravi reaches 6500, ravi.withdraw(9000) throws Insufficient balance, leaving his state at 6500. Getters allow reading, but no public setter bypasses the rules.
Inheritance and overriding in Java: specialise the shared account type
Now add two subclasses to the same file:
class SavingsAccount extends Account {
private final int interestPercent;
SavingsAccount(String owner, int openingBalance, int interestPercent) {
super(owner, openingBalance);
this.interestPercent = interestPercent;
}
@Override
void applyMonthEnd() {
int interest = getBalance() * interestPercent / 100;
deposit(interest);
}
}
class CurrentAccount extends Account {
private final int monthlyFee;
CurrentAccount(String owner, int openingBalance, int monthlyFee) {
super(owner, openingBalance);
this.monthlyFee = monthlyFee;
}
@Override
void applyMonthEnd() {
withdraw(monthlyFee);
}
}Both subclasses inherit getOwner(), getBalance(), deposit() and withdraw(). They cannot access private balance directly, so they reuse the safe methods.
For Asha, interest is 12500 * 5 / 100 = 625, and the new balance is 12500 + 625 = 13125. For Ravi, the fee produces 6500 - 200 = 6300. Overriding supplies a new implementation with the same signature. Overloading uses the same method name with a different parameter list.
Abstraction and runtime polymorphism: run one loop over two behaviours
Complete the file with the public class:
public class OopDemo {
public static void main(String[] args) {
SavingsAccount asha = new SavingsAccount("Asha", 10000, 5);
CurrentAccount ravi = new CurrentAccount("Ravi", 8000, 200);
asha.deposit(2500);
ravi.withdraw(1500);
Account[] accounts = {asha, ravi};
int total = 0;
for (Account account : accounts) {
account.applyMonthEnd();
System.out.println(account.getOwner() + " -> " + account.getBalance());
total += account.getBalance();
}
System.out.println("Total -> " + total);
}
}The Account type exposes an operation while hiding its algorithm. That is abstraction. The JVM dispatches the call to the actual object's override. That is polymorphism.
Runtime type | Balance before month end | Dispatched rule | Final balance |
|---|---|---|---|
|
| Add |
|
|
| Subtract |
|
The exact output is:
Asha -> 13125
Ravi -> 6300
Total -> 19425The total is 13125 + 6300 = 19425.

OOP basics in Java: common errors and what fixes them
Without super(owner, openingBalance), Java cannot initialise the base state because Account has no zero-argument constructor. Call its available constructor. A subclass also cannot read private balance; it must use getBalance().
Changing applyMonthEnd() to applyMonthEnds() leaves the abstract method unimplemented. Keep @Override to catch that typo, and do not reduce visibility. new Account(...) fails because an abstract class cannot be instantiated.
Inheritance must model an is-a relationship. A SavingsAccount is an Account. An InterestCalculator merely uses account data, so extending Account only to reuse code creates a false model. Prefer composition for a uses-a relationship.
OOP basics in interviews and exams: trace, explain and extend
Trace this check: Account a = new SavingsAccount("Neha", 4000, 10); a.deposit(1000); a.applyMonthEnd(); System.out.println(a.getBalance());. The balance becomes 4000 + 1000 = 5000; runtime dispatch adds 5000 * 10 / 100 = 500; the line prints 5500. The reference type does not change the runtime object. Identify the class, object, reference, inherited method and override.
Then make three changes:
Change Asha's rate from
5to4. Interest becomes12500 * 4 / 100 = 500, Asha ends at13000, and the total is13000 + 6300 = 19300.Add
PremiumAccount, whose month-end rule deposits a fixed300into an opening balance of5000. Its result is5000 + 300 = 5300.Call
withdraw(5301)on that object. ExpectInsufficient balance, with the state unchanged at5300.
For field hiding, compile-time binding and interview traps, use OOPs Concepts in Java: Worked Interview Questions. OopDemo.java instead follows two object states through encapsulation, inheritance, abstraction and runtime dispatch. For a wider preparation map, see Technical Interview: OS, DBMS, CN & OOP Prep.
OOP basics in Java: the short version and next step
A class is a blueprint, an object is an instance, encapsulation guards state, inheritance reuses a valid base, abstraction exposes an operation, and polymorphism selects its runtime implementation. The Account[] loop brings all four pillars together.
If you want a language-first route from fundamentals through OOP, follow Complete Java. If you are preparing for coding assessments across languages and placement practice, Coding For Placements is the broader route.
Type the complete file instead of pasting it. Verify the three output lines, then implement PremiumAccount and reach 5300 without changing the polymorphic loop in OopDemo.
Keep learning

Polymorphism in Java: Overloading, Overriding and Runnable Examples
Learn how Java chooses overloaded methods at compile time and overridden methods at runtime. Trace two complete programs, common traps and practice targets.

Packages and Access Modifiers in Java: Visibility Rules with Runnable Examples
Learn how Java package boundaries change member visibility. Compile a complete example covering public, protected, package-private and private access.

Classes and Objects in Java: Build and Trace Two Objects Step by Step
Build a Java Student class, create two independent objects, and trace fields, methods, aliases, null references, output, and common errors step by step.

Abstraction and Interfaces in Java: Abstract Classes, Contracts and Runnable Examples
Learn when Java needs an abstract class, when it needs an interface, and how both work together in one runnable charge-calculation program with exact outputs.