What Is Object-Oriented Programming?
At its core, OOP organizes code around objects rather than functions and logic. An object bundles together data (fields) and the behavior that operates on that data (methods). A class is the blueprint from which objects are created.
public class Car {
// Data (fields)
String brand;
int speed;
// Behavior (method)
void accelerate() {
speed += 10;
}
}
// Creating an object from the class
Car myCar = new Car();
myCar.brand = "Toyota";
myCar.accelerate();
This bundling of data and behavior is what makes code modular, reusable, and easier to maintain as it grows. Everything below builds on this idea.
1. Encapsulation: Protecting Your Data
Encapsulation means keeping an object's internal state private and exposing controlled access through methods. Instead of letting outside code modify fields directly, you hide them and provide getters and setters that can enforce rules.
public class BankAccount {
private double balance; // hidden from outside access
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) { // rule enforced here
balance += amount;
}
}
}
Here, no external code can set balance to a negative number directly, because the field is private and the only way in is through deposit(), which validates the input. This is the practical value of encapsulation: it protects the integrity of your data and lets you change the internal implementation without breaking code that uses the class.
2. Inheritance: Reusing and Extending Code
Inheritance lets a class acquire the fields and methods of another class, so you can build on existing code instead of duplicating it. The class being inherited from is the superclass; the one inheriting is the subclass.
public class Animal {
void eat() {
System.out.println("This animal eats food.");
}
}
public class Dog extends Animal {
void bark() {
System.out.println("The dog barks.");
}
}
Dog myDog = new Dog();
myDog.eat(); // inherited from Animal
myDog.bark(); // defined in Dog
Dog automatically gains the eat() method from Animal without rewriting it. Inheritance models "is-a" relationships — a Dog is an Animal — and helps keep shared behavior in one place. A word of caution: overusing inheritance leads to rigid, tangled hierarchies, so use it when there's a genuine "is-a" relationship, not just to share a little code.
3. Polymorphism: One Interface, Many Forms
Polymorphism means the same method call can behave differently depending on the object it's acting on. The most common form in Java is method overriding, where a subclass provides its own version of a method defined in its superclass.
public class Animal {
void makeSound() {
System.out.println("Some generic sound");
}
}
public class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Meow");
}
}
public class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Woof");
}
}
// Same type reference, different behavior
Animal a1 = new Cat();
Animal a2 = new Dog();
a1.makeSound(); // Meow
a2.makeSound(); // Woof
Even though a1 and a2 are both declared as Animal, each calls its own version of makeSound(). This is powerful because it lets you write code that works with the general type (Animal) while each object behaves according to its actual type — the key to flexible, extensible design.
4. Abstraction: Hiding Complexity
Abstraction means exposing only the essential features of something while hiding the underlying complexity. In Java, this is achieved with abstract classes and interfaces, which define what a class should do without dictating how.
public interface PaymentMethod {
void pay(double amount); // what, not how
}
public class CreditCard implements PaymentMethod {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " by credit card");
}
}
public class PayPal implements PaymentMethod {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " via PayPal");
}
}
Code that uses PaymentMethod doesn't need to know the details of how each payment type works — it just calls pay(). This separation lets you add new payment methods later without touching the existing code that relies on the interface.
How the Four Principles Work Together
In real Java applications, these principles aren't used in isolation — they reinforce one another. Encapsulation protects an object's data; inheritance and abstraction structure relationships between classes; and polymorphism lets those related classes be used interchangeably. Together they're what make well-designed Java code flexible, maintainable, and resistant to the kind of tangled dependencies that make software hard to change.
If you want a deeper, structured walkthrough of these ideas with more examples, this breakdown of OOP concepts in Java covers each principle step by step as part of a broader Java Core curriculum — useful for locking in the fundamentals rather than just memorizing definitions.
Final Thoughts
The four principles of OOP aren't just interview trivia — they're the design vocabulary of everyday Java development. Encapsulation keeps your data safe, inheritance reduces duplication, polymorphism enables flexibility, and abstraction manages complexity. Understand not just what each one is but when to reach for it, and you'll write Java that's cleaner, more maintainable, and far easier to extend as your projects grow.
