Free lesson · Java Basic To Advance
Introduction to OOP
The Mental Model That Defines Java
Modules 1 through 6 covered Java's syntax — the mechanics of variables, control flow, arrays, strings, and methods. You can write working Java programs with that material. But Java isn't fundamentally about syntax. It's about objects.
Java is an object-oriented language by design, by convention, and by ecosystem. The standard library is built on classes. Frameworks like Spring and Hibernate are built on classes. Every Java codebase you'll work on professionally is built on classes. Understanding object-oriented programming isn't optional for a Java career — it's the whole point.
I'm Brandon Carter. Principal SDE at AWS, currently on the S3 team working on cross-region replication. Before that I was at Stripe building distributed payment systems handling 100M+ transactions per day. Before that, four years at IBM writing enterprise Java in the late 2000s. I've conducted over 300 interviews as an Amazon Bar Raiser, and OOP is where I make most of my hire decisions for senior engineers. Olena handed me the narration baton for this module because OOP is where I've spent most of my career.
Module 7 covers the OOP foundations: what classes and objects are, how they're constructed, how data and behavior get organized, and the mechanisms (access modifiers, static members, special methods) that make OOP work in Java. Modules 8 through 11 build on this — inheritance, polymorphism, interfaces, abstract classes. But everything depends on the foundations in this module.
This Topic introduces OOP at a conceptual level — the mental model, the four pillars, and Java's specific take on it. The remaining 13 Topics in this module make those concepts concrete with Java syntax.
What "Object-Oriented" Means
The core idea of OOP is straightforward: organize code around data and the operations on that data, packaged together as objects.
In procedural programming (C, classic shell scripts, simple functional code), you have functions operating on passive data. The data is one thing; the operations are another:
// Procedural style
struct User { char name[50]; int age; };
void birthday(struct User* u) { u->age++; }
In object-oriented programming, you bundle the data and operations into objects. The data is part of the object; the operations are methods on the object:
// Object-oriented style
class User {
private String name;
private int age;
public void birthday() {
age++;
}
}
This bundling has consequences. Code organizes itself around concepts (User, Order, Payment) rather than around functions (validateUser, processOrder, calculateTax). Behavior lives with its data. Related code clusters together.
For small programs, this is overhead — you write more declarations to do the same work. For large programs, this is structure — the codebase becomes navigable and changes become localized.
At AWS, our codebases are millions of lines of Java. The class structure is what makes them tractable. Without OOP, that scale of code would be unmaintainable.
Classes and Objects: The Fundamental Distinction
A class is a blueprint. It defines what fields an object has, what methods are available, and what constraints apply.
An object (also called an instance) is a specific concrete thing built from a class — actual data in memory with the structure the class describes.
The relationship is like a cookie cutter (class) and a cookie (object). The cutter defines the shape; the cookies are concrete instances of that shape. You can make many cookies from one cutter, and each cookie is independent of the others.
// Class — the blueprint
public class User {
private String name;
private int age;
}
// Objects — instances of the blueprint
User alice = new User(); // one instance
User bob = new User(); // another instance, independent of alice
User charlie = new User(); // a third instance
Each User object has its own name field, its own age field. Changing alice.age doesn't affect bob.age. The class defines the shape; the objects are the concrete data.
This distinction matters constantly. Interview questions like "What's the difference between a class and an object?" are common at junior levels because getting it wrong signals a missing foundation.
Topic 7.2 covers classes and objects in detail.
Interactive animation: One User class, two objects - each holds its own name field, and all of them share one copy of the methods.
The Four Pillars of OOP
Object-oriented programming traditionally rests on four concepts. Every Java OOP interview at some level tests these by name.
Encapsulation
Encapsulation is bundling data and methods together AND hiding internal details from outside code.
The "bundling" part is what makes a class a class. The "hiding" part is what makes it useful — outside code can use the object without knowing or depending on how it's implemented internally.
public class BankAccount {
private double balance; // hidden — can't be accessed from outside
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public double getBalance() {
return balance;
}
}
The balance field is private — outside code can't read or write it directly. The only way to modify it is through deposit() (or withdraw(), if defined). The class controls access, which means it can enforce invariants — "balance can never become negative" or "deposits must be positive."
Without encapsulation, any code anywhere could modify the balance directly, including setting it to negative or NaN. With encapsulation, the class enforces its own correctness.
Topic 7.6 covers encapsulation in depth.
Interactive animation: A public balance anyone can set to -5000, beside a private balance that only a validating withdraw can change.
Inheritance
Inheritance lets one class extend another, inheriting its fields and methods while adding new ones or overriding existing ones.
public class Animal {
public void breathe() {
System.out.println("Breathing...");
}
}
public class Dog extends Animal {
public void bark() {
System.out.println("Woof!");
}
}
Dog rex = new Dog();
rex.breathe(); // inherited from Animal
rex.bark(); // defined in Dog
Dog is-a Animal. Every Dog can do everything an Animal can, plus more. This relationship — the "is-a" relationship — is the conceptual basis for inheritance.
Inheritance enables code reuse (don't rewrite breathe() for every animal) and polymorphism (treating Dog as Animal in some contexts). It also enables some real problems when used badly — we'll cover the pitfalls in Module 8.
Module 8 covers inheritance in detail. Module 7 stays focused on single-class concerns.
Polymorphism
Polymorphism means "many forms" — the ability for the same code to behave differently based on the actual object's type.
Animal a = new Dog();
a.speak(); // calls Dog's speak() if it overrides Animal's
Animal b = new Cat();
b.speak(); // calls Cat's speak()
The same line of code — a.speak() — produces different behavior depending on whether a is actually a Dog or a Cat. The JVM dispatches the call at runtime to the appropriate concrete method.
Polymorphism is what makes OOP powerful for large systems. You write code in terms of abstract types (Animal) and the right concrete behavior fires automatically based on the actual object. New animal types can be added without changing existing code.
We cover polymorphism in detail in Module 8 (inheritance and overriding) and Module 9 (interfaces).
Abstraction
Abstraction is exposing essential features while hiding implementation details.
In Java, abstraction shows up through:
- Abstract classes — classes that define structure but leave details to subclasses
- Interfaces — pure abstractions specifying what without saying how
- Method signatures — the public contract, separate from the body
public interface PaymentProcessor {
void processPayment(double amount);
}
// Implementations
public class StripeProcessor implements PaymentProcessor { ... }
public class PayPalProcessor implements PaymentProcessor { ... }
Code that uses a PaymentProcessor doesn't know whether it's Stripe or PayPal. It calls processPayment() — that's the abstraction. The concrete implementation can change without affecting the caller.
We cover abstraction in detail in Module 10 (abstract classes) and Module 11 (interfaces).
graph TD
A[Object-Oriented Programming] --> B[Encapsulation]
A --> C[Inheritance]
A --> D[Polymorphism]
A --> E[Abstraction]
B --> B1[Bundle data + methods<br/>Hide internal state<br/>Module 7]
C --> C1[Reuse via extends<br/>is-a relationships<br/>Module 8]
D --> D1[Same code, different behavior<br/>Dispatch by runtime type<br/>Modules 8-11]
E --> E1[Hide how, expose what<br/>Interfaces + abstract classes<br/>Modules 10-11]
style A fill:#7c3aed,color:#fff
style B fill:#10b981,color:#fff
style C fill:#3b82f6,color:#fff
style D fill:#f59e0b,color:#fff
style E fill:#06b6d4,color:#fff
This diagram shows the four pillars of object-oriented programming. Encapsulation bundles data and methods together and hides internal state — that's the focus of Module 7. Inheritance enables code reuse through is-a relationships, covered in Module 8. Polymorphism allows the same code to behave differently based on the runtime type of an object — covered across Modules 8 through 11. Abstraction hides implementation details and exposes only essential features, primarily through interfaces and abstract classes in Modules 10 and 11. These four concepts together form the conceptual foundation of OOP. Java implements all four with specific syntax features that the remaining topics in this module and the next four modules cover in detail.
Why Java Chose OOP
Java was designed in the early 1990s as a response to the perceived complexity of C++. Sun's engineers wanted a language that was:
- Simpler than C++ — no multiple inheritance, no operator overloading, no manual memory management
- Safer than C++ — no pointer arithmetic, no buffer overflows, strict type checking
- Cross-platform — write once, run anywhere via the JVM
The OOP model was chosen because it was proven (Smalltalk and C++ had demonstrated its scalability) and because it matched how Sun's engineers thought about large systems. By 1995 when Java launched publicly, OOP was the dominant paradigm for enterprise software.
The choice wasn't universal. Modern languages like Go and Rust pull back from heavy OOP in favor of composition and traits. Functional programming has resurged via Scala, Kotlin, and modern Java itself (lambdas, streams). But Java's core remains object-oriented, and that orientation shapes how every Java codebase is structured.
For interview prep, this matters because OOP fluency is the baseline expectation for Java engineers. You can be a good engineer without fluent OOP — but not a good Java engineer.
What This Module Covers
Module 7 establishes the foundation:
- Topic 7.2 — Classes and Objects: declaration syntax, creating instances, the heap
- Topic 7.3 — Fields and Methods: state and behavior in classes
- Topic 7.4 — Constructors: how objects get initialized
- Topic 7.5 — Access Modifiers: public, private, protected, package-private
- Topic 7.6 — Encapsulation: the principle and its enforcement
- Topic 7.7 — Static Members: class-level state and behavior
- Topic 7.8 — The
thisKeyword: referring to the current object - Topic 7.9 — Object Class and Methods: equals, hashCode, toString
- Topic 7.10 — Inner Classes: nested class types
- Topic 7.11 — Anonymous Classes: classes without names
- Topic 7.12 — Final in OOP: immutability and prevention of inheritance
- Topic 7.13 — Records: modern data carriers (Java 14+)
- Topic 7.14 — Signature Interview Lens
After Module 7, you'll be able to design classes with proper encapsulation, write constructors that maintain invariants, use static members appropriately, and override Object methods correctly. Modules 8 through 11 then add inheritance, polymorphism, abstract classes, and interfaces — the relationships between classes.
A Real Production Scenario
At Stripe, I worked on the payment-method abstraction layer. We supported credit cards, ACH transfers, SEPA debits, wire transfers — dozens of payment types, each with different validation rules, processing flows, settlement timing, and failure modes.
We could have written this procedurally: one giant processPayment() function with a switch statement on the type. We didn't. We modeled each payment type as a class hierarchy:
- An abstract
PaymentMethoddefining the interface (validate, charge, refund, status) - A
Cardsubclass with credit-card-specific logic - An
ACHsubclass with ACH-specific logic - Each with their own state, their own methods, their own invariants
This wasn't OOP for its own sake. It was OOP because the problem genuinely had this structure — payment methods ARE different things with shared abstract behavior. The class hierarchy made the code mirror the domain, which made it easier to extend (adding a new payment type didn't require touching existing code) and easier to reason about (each class was self-contained).
Bad OOP design produces brittle inheritance hierarchies, leaky abstractions, and god classes. Good OOP design produces code that mirrors the problem domain and resists change. The difference is judgment, which is what this module and the next four modules build.
Common Pitfalls in OOP Thinking
Some patterns I see in candidate code that signal weak OOP understanding:
Treating classes as namespaces. Putting unrelated functions in the same class just to group them, with no shared state and no real connection between them. This is procedural code dressed in OOP clothing.
Public fields everywhere. Skipping encapsulation by making all fields public, then accessing them directly from outside code. The class becomes a struct with methods, not a real object.
Inheritance for code reuse alone. Extending a class just to get some method, even if the "is-a" relationship doesn't apply. Composition is usually better in these cases.
God classes. Classes that do too much — 1000 lines, 20 methods, handling multiple responsibilities. Should be split into multiple smaller classes with clear responsibilities.
Anemic domain models. Classes that have only fields and getters/setters, with all the logic living in service classes. The data has no behavior — defeating the bundling principle.
I'll flag specific examples of each in later topics. For now, just be aware that "uses classes" isn't the same as "uses OOP well."
Why This Matters in Interviews
Interview: OOP questions appear in every Java interview, often as the gating filter at the senior level. The four pillars by name. Class vs object. Encapsulation rationale. Inheritance vs composition trade-offs. These come up over and over because they're the foundation everything else builds on.
A candidate who can explain encapsulation precisely AND show me a class that demonstrates it AND tell me when to break it (yes, sometimes the right answer is public fields) — that's the senior signal. A candidate who recites the four pillars but can't apply them — that's junior.
Topic 7.14 covers the Interview Lens for OOP foundations.
Try It
Exercise: Before moving on:
- Pick a domain you know well (a game, an app, a system you've worked with). Identify 3-5 nouns that would be classes. Identify 3-5 verbs that would be methods.
- For each class, list what fields it would have (state) and what methods it would have (behavior).
- Identify is-a relationships: which classes inherit from which? Identify has-a relationships: which classes contain instances of which?
- Identify where you'd want encapsulation — what data should be private? What should be the public interface?
This is conceptual modeling — what good OOP design looks like before code. The remaining topics in Module 7 will give you the Java syntax to implement this; the modeling itself is the senior-engineer skill.