I still remember one interview where I was feeling unusually confident.
DSA? Done.
SQL? Manageable.
Basic Java? Smooth.
Then the interviewer smiled and asked:
"Can you explain the HAS-A relationship in OOP?"
And my brain instantly opened 47 Chrome tabs internally.
Because in my mind, OOP relationships were basically:
- Dog is an Animal
- Car is a Vehicle
- Manager is an Employee
That's it. I knew inheritance. I knew extends. I knew "IS-A".
I thought I was powerful.
Then the interviewer continued:
"What's the difference between Aggregation and Composition?"
At that moment, even the ceiling fan started judging me. 😅
That interview taught me something important:
Knowing syntax is not the same as understanding software design.
And honestly, class relationships are one of the most underrated parts of OOP. Most beginners learn classes, objects, constructors, and inheritance — but skip the actual relationships between objects. Yet this is exactly what companies ask in interviews because it shows whether you can design real systems instead of just writing random classes.
What We'll Cover
In this article, we'll deeply understand:
| Relationship | Short Meaning |
|---|---|
| Inheritance | IS-A |
| Association | Knows-A |
| Aggregation | HAS-A (weak) |
| Composition | HAS-A (strong) |
| Dependency | USES-A (temporary) |
With simple explanations, real-world examples, Java code, and interview-level understanding. Let's begin.
Why Class Relationships Matter
Imagine building a food delivery app.
You'll have: Users, Orders, Restaurants, Payments, DeliveryPartners
The real question is — how are these objects connected?
Because software engineering is not just "making classes" — it's designing relationships between classes. That's the heart of OOP.
1. Inheritance — The Famous "IS-A" Relationship
This is usually the first relationship everyone learns.
Inheritance means: One class becomes a specialized version of another class.
Real-life examples:
- Dog is an Animal
- Car is a Vehicle
- Manager is an Employee
Java Example
class Animal {
void eat() {
System.out.println("Animal eats food");
}
}
class Dog extends Animal {
void bark() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Dog d = new Dog();
d.eat(); // inherited from Animal
d.bark(); // Dog's own method
}
}
Output:
Animal eats food
Dog barks
What happened here?
Dog inherited the eat() method from Animal using the extends keyword. This means:
-
Doggets all methods and properties ofAnimal -
Dogcan also have its own methods likebark() -
Dogbecomes a type ofAnimal— so anywhereAnimalis expected,Dogcan be used too
This creates a hierarchy:
Animal
↑
Dog
Why use Inheritance?
Without inheritance, every animal class would repeat the same eat() logic:
class Dog { void eat() {} }
class Cat { void eat() {} }
class Cow { void eat() {} }
// Lots of repeated code!
Inheritance solves this through code reusability — write once, inherit everywhere.
⚠️ Advanced Insight
Inheritance creates tight coupling. If the parent class changes heavily, child classes may break unexpectedly. That's why modern software engineering often says:
"Favor Composition over Inheritance."
We'll understand this later.
2. Association — Classes That Simply Know Each Other
Association is much more common in real applications.
Association means: Two objects are connected or interact with each other, but neither owns the other. Both can exist independently.
Real-life examples:
- Teacher teaches Student
- Doctor treats Patient
- Customer uses Bank
Java Example
class Teacher {
String name;
Teacher(String name) {
this.name = name;
}
}
class Student {
String name;
Teacher teacher; // Student "knows" a Teacher
Student(String name, Teacher teacher) {
this.name = name;
this.teacher = teacher;
}
void study() {
System.out.println(name + " studies under " + teacher.name);
}
}
public class Main {
public static void main(String[] args) {
Teacher t1 = new Teacher("Sharma Sir");
Student s1 = new Student("Yukti", t1);
s1.study();
}
}
Output:
Yukti studies under Sharma Sir
Key Understanding
The Student holds a reference to a Teacher — it knows the teacher. But:
- A
Teachercan exist without anyStudent - A
Studentcan exist without aTeacher
So this is a weak, general relationship:
Student ---- Teacher
Types of Association
| Type | Example |
|---|---|
| One-to-One | One Person ↔ One Passport |
| One-to-Many | One Teacher → Many Students |
| Many-to-Many | Many Students ↔ Many Courses |
This becomes extremely important when you design databases later.
3. Aggregation — Weak HAS-A Relationship
Now we enter the famous HAS-A world.
Aggregation means: One object contains another object, but the contained object can still exist independently even if the container is destroyed.
Real-life examples:
- Department has Teachers → If department is closed, teachers still exist
- Team has Players → If team is disbanded, players still exist
- Library has Books → If library burns down... well, hopefully the books survive 📚
Java Example
class Teacher {
String name;
Teacher(String name) {
this.name = name;
}
}
class Department {
String deptName;
Teacher teacher; // Department HAS-A Teacher (weakly)
Department(String deptName, Teacher teacher) {
this.deptName = deptName;
this.teacher = teacher; // Teacher passed from OUTSIDE
}
void display() {
System.out.println(teacher.name + " works in " + deptName);
}
}
public class Main {
public static void main(String[] args) {
Teacher t1 = new Teacher("Yukti"); // Created independently
Department d1 = new Department("Computer Science", t1);
d1.display();
// Even if d1 is removed, t1 (the Teacher) still exists
}
}
Important Observation
Notice this line:
Teacher t1 = new Teacher("Yukti"); // Created OUTSIDE Department
The Teacher object was created separately and then passed into Department. This means Department uses the teacher but does not own or control its lifecycle.
That's the core of aggregation — the child object is an independent entity.
UML Representation:
Department ◇---- Teacher
(hollow diamond = weak ownership)
4. Composition — Strong HAS-A Relationship
This is where interviewers get dangerous. 😅
Composition means: One object completely owns another object. If the parent is destroyed, the child is destroyed too. They share the same lifecycle.
Real-life examples:
- Human has Heart → A heart without a human (in this context) can't function on its own
- House has Rooms → Demolish the house, the rooms are gone
- Car has Engine → Scrap the car, the engine goes with it
Java Example
class Heart {
void beat() {
System.out.println("Heart is beating");
}
}
class Human {
private Heart heart; // Human HAS-A Heart (strongly)
Human() {
heart = new Heart(); // Heart is created INSIDE Human
}
void live() {
heart.beat();
System.out.println("Human is alive");
}
}
public class Main {
public static void main(String[] args) {
Human h = new Human();
h.live();
// When h is destroyed, the Heart inside it is also gone
}
}
Output:
Heart is beating
Human is alive
Critical Difference from Aggregation
Inside Human:
heart = new Heart(); // Created INSIDE the parent
The parent creates the child. The Heart object doesn't exist outside of Human. This means:
- Strong ownership ✅
- Same lifecycle — they live and die together ✅
- The child has no independent existence ✅
UML Representation:
Human ◆---- Heart
(filled diamond = strong ownership)
🆚 Aggregation vs Composition (Interview Favorite!)
This question appears in almost every OOP interview. Let's simplify it permanently.
| Feature | Aggregation | Composition |
|---|---|---|
| Ownership | Weak | Strong |
| Child's independence | Can exist independently | Cannot exist independently |
| Object creation | Created separately, passed in | Created inside the parent |
| Lifecycle | Different lifecycles | Same lifecycle |
| UML symbol | Hollow diamond ◇ | Filled diamond ◆ |
| Example | Department → Teacher | Human → Heart |
🧠 Easy Memory Trick
Just ask yourself one question:
"Can the child object exist without the parent?"
- YES → Aggregation
- NO → Composition
That's it. One question settles the debate every time.
5. Dependency — Temporary Relationship (The Weakest Link)
Dependency is the most casual relationship of all.
Dependency means: One object temporarily uses another object — usually through a method parameter, local variable, or a short-lived interaction. There's no permanent connection stored between them.
Real-life examples:
- Customer uses ATM (temporarily)
- Computer uses Printer (when needed)
- User uses Internet (session-based)
Java Example
class Printer {
void printDocument() {
System.out.println("Printing document...");
}
}
class Computer {
// Printer is NOT stored as a field — only used temporarily
void print(Printer p) {
p.printDocument(); // Uses Printer just for this method call
}
}
public class Main {
public static void main(String[] args) {
Printer p1 = new Printer();
Computer c1 = new Computer();
c1.print(p1); // Temporary use — no permanent relationship
}
}
Output:
Printing document...
Key Observation
Computer does not store a Printer as a class field. It simply receives it as a method parameter and uses it for that one call. Once the method is done, the connection is gone.
If Printer changes tomorrow, Computer might need a small update — that's what makes this a dependency — it knows about Printer, but only barely.
🗺️ The Big Picture
| Relationship | Meaning | Strength | Lifetime |
|---|---|---|---|
| Inheritance | IS-A | Strong | Permanent |
| Association | Knows-A | Weak | Can be permanent |
| Aggregation | HAS-A (weak) | Moderate | Independent |
| Composition | HAS-A (strong) | Strong | Shared |
| Dependency | USES-A | Weakest | Temporary |
Real-World Example: Food Delivery App 🍕
Let's connect everything using a real app scenario.
Inheritance — IS-A
User
├── Customer
└── DeliveryPartner
Both are specialized types of User.
Association — Knows-A
Customer ↔ Restaurant
A customer interacts with restaurants, but both exist independently.
Aggregation — Weak HAS-A
Team ◇---- DeliveryPartner
A delivery team has partners, but if the team is dissolved, the partners (employees) still exist.
Composition — Strong HAS-A
Order ◆---- OrderItem
An Order owns its OrderItems. Delete the order, and the items disappear with it — they have no meaning on their own.
Dependency — USES-A
PaymentService - - -> UPI_API
PaymentService calls the UPI API temporarily to process a payment. No permanent link stored.
The Biggest Lesson
Earlier I thought OOP was just: classes, constructors, inheritance, getters/setters.
Now I realize:
OOP is mostly about relationships and responsibilities.
Good software design is really about answering:
- Which object should own what?
- Which objects should depend on each other?
- How tightly should they be connected?
That's real engineering.
One Final Industry Truth: Composition > Inheritance
Modern software development heavily prefers Composition over Inheritance.
Why? Because inheritance can become rigid and tightly coupled over time. Composition gives:
- ✅ Flexibility — swap out behaviors easily
- ✅ Modularity — each piece is independent
- ✅ Cleaner architecture — easier to reason about
- ✅ Easier testing — mock individual components
That's why modern frameworks and design patterns (like Strategy, Decorator, Dependency Injection) lean on composition everywhere.
Final Thoughts
The funny thing is — that interview where I got destroyed by the HAS-A question?
It helped me more than many easy interviews ever did.
Because it forced me to stop memorizing syntax and start understanding design.
Once class relationships truly click, OOP stops feeling theoretical and exam-oriented. It starts feeling like actual software engineering.
If you're learning Java or preparing for interviews, mastering these relationships will massively improve:
- Your code design instincts
- Your project structure
- Your interview answers
- Your understanding of real-world systems
And most importantly — you'll never again stare at the ceiling fan after hearing:
"Can you explain composition vs aggregation?" 😭
Found this helpful? Drop a ❤️ and share it with someone who's just starting their OOP journey!
Top comments (0)