OOP Concepts
Object-Oriented Programming (OOP) is the design philosophy that Java was built around — instead of writing a long list of instructions, you model your program as a collection of objects that know things (data) and can do things (behavior). Once it clicks, it transforms how you think about every program you write.
Why OOP?
Before OOP, most programs were written procedurally — a sequence of functions acting on shared data. That approach works fine for small programs, but it falls apart fast as projects grow: data gets modified from anywhere, dependencies become tangled, and adding a feature risks breaking something else.
OOP solves this by bundling data and the code that operates on it into a single unit called a class. Think of a class as a blueprint and an object as a real thing built from that blueprint — just like a house plan versus the actual house.
Here is the simplest possible Java object in action:
public class Dog {
String name; // data (field)
int age;
void bark() { // behavior (method)
System.out.println(name + " says: Woof!");
}
}
public class Main {
public static void main(String[] args) {
Dog d = new Dog(); // create an object from the blueprint
d.name = "Rex";
d.age = 3;
d.bark();
}
}
Output:
Rex says: Woof!
The Four Pillars of OOP
Java’s OOP model rests on four pillars. Master these and you understand the language’s design at a fundamental level.
1. Encapsulation
Encapsulation means hiding the internal details of an object and exposing only what the outside world needs. You achieve this in Java by marking fields private and providing public getter/setter methods.
public class BankAccount {
private double balance; // hidden from outside
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) balance += amount; // validation lives here
}
}
Because nothing outside BankAccount can touch balance directly, you control exactly how it changes. See Encapsulation for the full treatment, and Access Modifiers for the private/protected/public rules.
2. Inheritance
Inheritance lets one class reuse and extend the fields and methods of another. The existing class is the superclass (parent) and the new class is the subclass (child).
public class Animal {
String name;
public void eat() {
System.out.println(name + " is eating.");
}
}
public class Cat extends Animal {
public void purr() {
System.out.println(name + " is purring.");
}
}
public class Main {
public static void main(String[] args) {
Cat c = new Cat();
c.name = "Whiskers";
c.eat(); // inherited from Animal
c.purr(); // defined in Cat
}
}
Output:
Whiskers is eating.
Whiskers is purring.
Cat gets eat() for free — you wrote it once in Animal. Explore this further at Inheritance and Types of Inheritance.
Note: Java supports single inheritance for classes (one parent only) but allows a class to implement multiple interfaces, giving you the flexibility of multiple inheritance without the diamond-problem ambiguity.
3. Polymorphism
Polymorphism means “many forms” — the same method name can behave differently depending on the object it is called on. Java supports two kinds:
| Kind | When resolved | Mechanism |
|---|---|---|
| Compile-time (static) | At compile time | Method overloading |
| Runtime (dynamic) | At runtime | Method overriding + reference type |
public class Shape {
public void draw() {
System.out.println("Drawing a shape");
}
}
public class Circle extends Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
public class Main {
public static void main(String[] args) {
Shape s = new Circle(); // Shape reference, Circle object
s.draw(); // runtime decides: Circle.draw()
}
}
Output:
Drawing a circle
The magic is that s is declared as Shape, but the JVM calls Circle’s version at runtime — that is dynamic dispatch. Dive deeper at Polymorphism, Method Overloading, and Method Overriding.
4. Abstraction
Abstraction means showing only what is relevant and hiding the complexity. A TV remote is a perfect analogy: you press “Volume Up” without caring about the electronics inside.
In Java, you achieve abstraction through abstract classes and interfaces.
public abstract class Vehicle {
abstract void startEngine(); // subclasses must implement this
public void stop() { // shared behavior already implemented
System.out.println("Vehicle stopped.");
}
}
public class Car extends Vehicle {
@Override
public void startEngine() {
System.out.println("Car engine started with a key.");
}
}
The caller only needs to know startEngine() exists — not how it works for each vehicle type.
OOP vs Procedural: A Quick Comparison
| Aspect | Procedural | Object-Oriented |
|---|---|---|
| Focus | Functions/procedures | Objects |
| Data | Shared, global | Encapsulated inside objects |
| Reuse | Copy-paste or function calls | Inheritance & composition |
| Maintainability | Gets harder at scale | Stays manageable |
| Example languages | C, Pascal | Java, C++, Python |
Under the Hood
When you write Dog d = new Dog(), Java does several things at the JVM level:
- Class loading — the ClassLoader reads
Dog.classfrom the classpath and loads the bytecode into the method area of the JVM. - Memory allocation —
newtriggers the JVM to allocate space on the heap for theDogobject. Fields get default values (nullforString,0forint). - Constructor call — the constructor runs to initialize the object.
- Reference storage — the variable
don the stack holds a reference (essentially a pointer) to the heap object, not the object itself.
This reference-based model is why assigning Dog d2 = d gives you two variables pointing to the same object — changing d2.name also changes what you see through d. For the same reason, Java passes object references by value (see Call by Value).
Virtual method calls (the heart of runtime polymorphism) are implemented through a vtable — a per-class table of method pointers. When you call s.draw() on a Shape reference, the JVM looks up the vtable of the actual object (Circle) and dispatches to the right method. Learn more at vtable & Dynamic Dispatch.
Tip: Understanding heap vs stack and reference semantics will save you hours of debugging subtle bugs — especially when objects are passed to methods or stored in collections.
In This Section
These pages cover the foundational building blocks you need before moving on to inheritance and polymorphism:
- Naming Conventions — the Java community’s agreed rules for naming classes, methods, variables, and constants so your code is immediately readable.
- Classes & Objects — how to define a class, create objects with
new, and understand the relationship between the two. - Methods — how to declare and call methods, pass arguments, return values, and understand method signatures.
- Constructors — special methods that initialize objects when they are created, including constructor chaining and overloading.
- static Keyword — how
staticfields and methods belong to the class itself rather than any individual object. - this Keyword — how
thisrefers to the current object instance inside a method or constructor. - Object vs Class — clarifying the difference between a class (blueprint) and an object (instance), with practical examples.
Related Topics
- Inheritance — extend classes to build an “IS-A” relationship and reuse code cleanly.
- Polymorphism — write flexible code that works with objects of many related types.
- Encapsulation — protect your data by hiding fields and controlling access through methods.
- Abstract Class — define partial implementations that subclasses are required to complete.
- Interfaces — define contracts that any class can implement, enabling powerful abstraction.
- vtable & Dynamic Dispatch — understand exactly how the JVM resolves method calls at runtime.