Advanced Programming with JavaUnit 217 min read
OOP in Java: Classes, Inheritance, Polymorphism & Abstraction
Unit 2 of Advanced Programming with Java covers the core principles of object-oriented programming (OOP) in Java, including classes, objects, inheritance, polymorphism, encapsulation, abstraction, and interfaces. This note explains how to design OOP systems, implement them in Java, and analyze their efficiency and real
TAKEAWAYS:
- Classes and objects are the building blocks of OOP, where a class defines a blueprint and objects are instances of that blueprint.
- Inheritance allows code reuse by creating a hierarchy of classes (parent-child relationships) and supports method overriding.
- Polymorphism enables one interface to represent different underlying forms (e.g., method overloading and overriding).
- Encapsulation hides internal data and exposes only necessary methods via access modifiers (
private,public,protected). - Abstraction defines abstract classes and interfaces to enforce structure without implementation details.
- Composition and aggregation are relationships where objects are combined to form larger systems.
1. Classes and Objects: The Foundation of OOP
A class is a blueprint for creating objects, while an object is an instance of a class. Classes define attributes (data members) and methods (functions) that operate on those attributes.
Key Concepts:
- Constructors: Special methods used to initialize objects.
- Access Modifiers: Control visibility of class members (
public,private,protected, default). thisKeyword: Refers to the current object’s instance variables.
Example: A BankAccount Class
public class BankAccount {
private String accountNumber;
private double balance;
// Constructor
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
// Method to deposit money
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposited: $" + amount);
}
}
// Method to withdraw money
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("Withdrawn: $" + amount);
}
}
// Getter for balance
public double getBalance() {
return balance;
}
}
Trace: Creating and Using a BankAccount Object
| Step | Code Executed | State After Execution |
|---|---|---|
| 1 | BankAccount acc = new BankAccount("12345", 1000.0); |
acc.accountNumber = "12345", acc.balance = 1000.0 |
| 2 | acc.deposit(500.0); |
acc.balance = 1500.0 |
| 3 | acc.withdraw(200.0); |
acc.balance = 1300.0 |
Real-World Analogy: Bank Account in Nabil Bank
- Class:
BankAccount(blueprint for all accounts). - Object: A specific account (e.g., "Account 12345 with balance ₹1300").
- Methods:
deposit()andwithdraw()simulate real transactions.
2. Inheritance: Code Reusability and Hierarchy
Inheritance allows a subclass (child) to inherit properties and methods from a superclass (parent). It promotes DRY (Don’t Repeat Yourself) programming.
Types of Inheritance in Java:
| Type | Description | Example |
|---|---|---|
| Single | One subclass inherits from one superclass. | Animal → Dog |
| Multilevel | A subclass inherits from another subclass. | Vehicle → Car → SportsCar |
| Hierarchical | Multiple subclasses inherit from one superclass. | Shape → Circle, Square |
| Multiple | Not supported in Java (use interfaces instead). | - |
Example: Vehicle and Car Classes
class Vehicle {
private String model;
public Vehicle(String model) {
this.model = model;
}
public void displayModel() {
System.out.println("Model: " + model);
}
}
class Car extends Vehicle {
private int numDoors;
public Car(String model, int numDoors) {
super(model); // Calls Vehicle constructor
this.numDoors = numDoors;
}
public void displayDoors() {
System.out.println("Number of doors: " + numDoors);
}
}
Trace: Creating a Car Object
| Step | Code Executed | State After Execution |
|---|---|---|
| 1 | Car myCar = new Car("Toyota", 4); |
myCar.model = "Toyota", myCar.numDoors = 4 |
| 2 | myCar.displayModel(); |
Output: "Model: Toyota" |
| 3 | myCar.displayDoors(); |
Output: "Number of doors: 4" |
Real-World Example: Daraz’s Order Processing System
- Superclass:
Order(containsorderId,customer,totalAmount). - Subclass:
FoodOrder(extendsOrderand addsdeliveryTime,restaurant). - Benefit: Reuses common order logic while adding food-specific features.
3. Polymorphism: One Interface, Multiple Forms
Polymorphism allows methods to behave differently based on the object. Two types:
- Compile-time (Method Overloading): Same method name, different parameters.
- Run-time (Method Overriding): Subclass provides a specific implementation of a superclass method.
classDiagram
class Shape {
<<abstract>>
+area() double
}
class Circle {
-radius: double
+area() double
}
class Rectangle {
-length: double
-width: double
+area() double
}
Shape <|-- Circle
Shape <|-- Rectangle
note for Circle "Overrides area() to return πr²"
note for Rectangle "Overrides area() to return l × w"
note for Shape "Abstract base class"
note for Circle "Concrete implementation"
note for Rectangle "Concrete implementation"UML class diagram showing method overriding in Shape hierarchy (abstract base class with concrete subclasses)Example: Method Overriding in Shape Hierarchy
class Shape {
public double area() {
return 0;
}
}
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
private double length, width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double area() {
return length * width;
}
}
Trace: Polymorphic Behavior
Shape shape1 = new Circle(5.0);
Shape shape2 = new Rectangle(4.0, 6.0);
System.out.println(shape1.area()); // Output: 78.54 (πr²)
System.out.println(shape2.area()); // Output: 24.0 (l × w)
Real-World Example: Pathao’s Ride Options
- Superclass:
Ride(abstract methodcalculateFare()). - Subclasses:
BikeRide,CarRide,AutoRide(each overridescalculateFare()). - Benefit: Pathao’s app calls
ride.calculateFare()without knowing the ride type.
4. Encapsulation: Data Hiding and Access Control
Encapsulation bundles data (attributes) and methods (behaviors) into a single unit (class) while restricting direct access to some components.
Key Features:
- Use
privatefor sensitive data. - Provide
publicgetters/setters for controlled access. - Example: A
Studentclass hidesgradebut allows controlled updates.
Example: Student Class with Encapsulation
public class Student {
private String name;
private int grade;
public Student(String name, int grade) {
this.name = name;
setGrade(grade); // Uses setter for validation
}
// Getter
public String getName() {
return name;
}
// Setter with validation
public void setGrade(int grade) {
if (grade >= 0 && grade <= 100) {
this.grade = grade;
} else {
System.out.println("Invalid grade!");
}
}
public int getGrade() {
return grade;
}
}
Trace: Setting an Invalid Grade
| Step | Code Executed | State After Execution |
|---|---|---|
| 1 | Student student = new Student("Rohan", 85); |
student.name = "Rohan", student.grade = 85 |
| 2 | student.setGrade(105); |
Output: "Invalid grade!", grade unchanged |
Real-World Example: Ncell’s User Authentication
- Encapsulation:
Userclass hidespasswordbut provideslogin()method with validation. - Benefit: Prevents unauthorized access to sensitive data.
5. Abstraction: Hiding Complexity
Abstraction exposes only essential features and hides implementation details. Achieved via:
- Abstract Classes: Cannot be instantiated; contain abstract methods (no body).
- Interfaces: Pure abstraction (all methods are abstract before Java 8).
Example: Abstract Animal Class
abstract class Animal {
private String name;
```figure
{"type":"tree","nodes":[{"v":"Animal","children":[{"v":"Dog","children":[{"v":"Puppy","highlight":true}]},{"v":"Cat"}]}],"caption":"Inheritance hierarchy showing abstract Animal class with Dog subclass and Puppy specialization"}
public Animal(String name) {
this.name = name;
}
// Abstract method (no implementation)
public abstract void makeSound();
// Concrete method
public void sleep() {
System.out.println(name + " is sleeping.");
}
}
class Dog extends Animal { public Dog(String name) { super(name); }
@Override
public void makeSound() {
System.out.println("Bark!");
}
}
Trace: Creating a Dog Object
| Step | Code Executed | State After Execution |
|---|---|---|
| 1 | Animal myDog = new Dog("Buddy"); |
myDog.name = "Buddy" |
| 2 | myDog.makeSound(); |
Output: "Bark!" |
| 3 | myDog.sleep(); |
Output: "Buddy is sleeping." |
Real-World Example: NEPSE’s Stock Trading System
- Interface:
Trader(definesbuy(),sell()). - Classes:
RetailTrader,InstitutionalTrader(implementTrader). - Benefit: NEPSE’s system interacts with traders via the
Traderinterface without knowing their internal logic.
6. Interfaces: Contracts for Classes
Interfaces define a contract that classes must follow. A class can implement multiple interfaces (unlike inheritance).
Example: Comparable Interface
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person other) {
return Integer.compare(this.age, other.age);
}
}
Trace: Comparing Two Person Objects
| Step | Code Executed | State After Execution |
|---|---|---|
| 1 | Person p1 = new Person("Alice", 25); |
p1.name = "Alice", p1.age = 25 |
| 2 | Person p2 = new Person("Bob", 30); |
p2.name = "Bob", p2.age = 30 |
| 3 | System.out.println(p1.compareTo(p2)); |
Output: -5 (p1 is younger) |
Real-World Example: Khalti’s Payment Gateway
- Interface:
PaymentProcessor(definesprocessPayment()). - Classes:
CreditCardProcessor,MobileWalletProcessor(implementPaymentProcessor). - Benefit: Khalti’s app calls
processor.processPayment()regardless of the payment method.
7. Composition vs. Aggregation
| Feature | Composition | Aggregation |
|---|---|---|
| Relationship | "Has-a" (strong, part-of) | "Has-a" (weak, independent) |
| Lifetime | Child cannot exist without parent | Child can exist without parent |
| Example | Engine in Car |
Professor in University |
Example: Composition (Engine in Car)
class Engine {
public void start() {
System.out.println("Engine started.");
}
}
class Car {
private Engine engine; // Composition
public Car() {
this.engine = new Engine(); // Engine is created with Car
}
public void start() {
engine.start();
}
}
Example: Aggregation (Professor in University)
class Professor {
private String name;
public Professor(String name) { this.name = name; }
}
class University {
private Professor head; // Aggregation
public University(Professor head) {
this.head = head;
}
}
Real-World Example: Pathao’s Driver System
- Composition:
PathaoAppcontains aGPSobject (GPS cannot exist without the app). - Aggregation:
PathaoAppuses aDriverobject (driver can exist independently).
In the Real World
eSewa’s Payment System
- OOP Concept: Polymorphism via
PaymentMethodinterface. - How: eSewa supports
CreditCardPayment,MobilePayment, andBankTransferclasses, all implementingPaymentMethod.processPayment(). Users select a payment method, and eSewa calls the appropriate implementation without knowing the type.
- OOP Concept: Polymorphism via
NTC’s Network Traffic Management
- OOP Concept: Inheritance for device types.
- How:
NetworkDevice(superclass) defines common methods likeconnect()anddisconnect(). Subclasses likeRouter,Switch, andModemoverride these methods for device-specific behavior. NTC’s software manages all devices uniformly via the superclass interface.
Daraz’s Order Queue System
- OOP Concept: Queue (using LinkedList) for order processing.
- How: Daraz uses a
PriorityQueue<Order>where orders are enqueued based on priority (e.g., express vs. standard). The system dequeues and processes orders in FIFO order, but express orders jump to the front. This ensures faster delivery for premium customers while maintaining fairness for others. - Visualization:
flowchart TD
A["Order Enqueued
(priority: standard)"] --> B["PriorityQueue<Order>"]
C["Order Enqueued
(priority: express)"] --> B
B -->|"dequeue()"| D["Process Order
(FIFO for standard)"]
B -->|"peek()"| E["Express Order
(jumps to front)"]PriorityQueue implementation showing express orders jumping to front (Daraz's order processing)Exam Tip
Understand the "Why" Behind OOP
- Examiners often ask: "Why use inheritance over composition?" or "When to use abstraction?"
- Key Points:
- Use inheritance for "is-a" relationships (e.g.,
Dogis anAnimal). - Use composition for "has-a" relationships (e.g.,
Carhas anEngine). - Use abstraction to hide complexity (e.g.,
PaymentProcessorinterface).
- Use inheritance for "is-a" relationships (e.g.,
Code Traces Are Critical
- Always show the state after each step (like the tables above). For example:
- If asked to trace
myCar.displayDoors(), show the object’s state before and after method calls. - For inheritance, trace how
super()calls the parent constructor.
- If asked to trace
- Always show the state after each step (like the tables above). For example:
Common Pitfalls
- Access Modifiers: Forgetting
privatefor sensitive data (e.g.,passwordinUserclass). - Method Overriding vs. Overloading:
- Overriding changes the method signature (same name, parameters, return type).
- Overloading changes parameters (same name, different parameters).
- Abstract Classes vs. Interfaces:
- Abstract classes can have concrete methods and fields.
- Interfaces (before Java 8) cannot have concrete methods or fields.
- Access Modifiers: Forgetting
Diagrams in Exams
- Draw UML class diagrams for inheritance hierarchies (e.g.,
Animal → Dog → Puppy). - Draw object diagrams to show relationships (e.g.,
Carcontains anEngine). - Example UML for
BankAccount:classDiagram class BankAccount { -String accountNumber -double balance +BankAccount(String, double) +deposit(double) +withdraw(double) +getBalance() }
- Draw UML class diagrams for inheritance hierarchies (e.g.,
Real-World Scenarios
- Expect questions like:
- "How would you design a traffic management system for Kathmandu using OOP?" (Use
Vehiclehierarchy withCar,Bike,Busand polymorphism forcalculateToll().) - "Explain how WhatsApp uses interfaces for different messaging services." (e.g.,
MessageServiceinterface withWhatsAppService,EmailServiceimplementations.)
- "How would you design a traffic management system for Kathmandu using OOP?" (Use
- Expect questions like:
Practical Coding Questions
- Be ready to write:
- A class with encapsulation (e.g.,
Studentwith privategradeand validated setters). - An inheritance hierarchy (e.g.,
Shape → Circle → Sphere). - An interface implementation (e.g.,
Comparablefor sorting objects).
- A class with encapsulation (e.g.,
- Be ready to write:
In the real world
- Pathao’s Ride System: Uses polymorphism to handle different ride types (BikeRide, CarRide) under a common
Rideinterface, callingcalculateFare()without knowing the subclass. - Nepali Banks (e.g., Nabil, Global IME): Apply encapsulation by hiding account details (private fields) while exposing only
deposit(),withdraw(), andgetBalance()methods. - eSewa’s Payment Gateway: Leverages abstraction to define a
PaymentProcessorinterface (e.g.,processPayment()) implemented byCreditCardProcessor,MobileWalletProcessor, etc., without exposing internal logic.
Based on the PU BE Computer (PU) syllabus for Advanced Programming with Java (CMP228), unit 2.
Discussion
Loading…