☕ Unit 6 • Core Java Fundamentals

Object-Oriented Programming: The 4 Pillars & Architecture

The architectural cornerstone of enterprise Java software engineering: Classes and objects, the four pillars of OOP (Encapsulation, Inheritance, Polymorphism, Abstraction), access modifier permeability matrix, constructor chaining with super, method overriding and dynamic method dispatch (vtable), abstract classes vs. interfaces, packages, and the final keyword.

Curriculum: Enterprise Java Software Engineering
Unit: 06 of 15
Prerequisite: Unit 05 (Methods & Constructors)
Target: JDK 21 LTS
01

Classes, Objects & The Four Pillars of OOP

🏛️ Blueprints vs. Instantiated Objects

In Java, Object-Oriented Programming (OOP) models software systems around real-world data and behavior:

  • Class: A user-defined prototype or blueprint that defines the state (instance variables) and behavior (methods) common to all objects of that type. A class occupies memory only in the Method Area.
  • Object: A concrete, runtime instance of a class allocated on the JVM Heap via the new keyword. Each object possesses its own isolated state while sharing method bytecode with all other instances.
Visual Architecture: The Four Pillars of Java OOP
1. Encapsulation
Data Hiding & Integrity
Private fields + Public getters
2. Inheritance
Code Reuse & Extensibility
IS-A Relationship (extends)
3. Polymorphism
Dynamic Method Dispatch
Overloading & Overriding
4. Abstraction
Hiding Implementation
Abstract Classes & Interfaces
02

Encapsulation & Access Modifier Permeability Matrix

🛡️ Encapsulation & Data Hiding

Encapsulation is the bundling of data (fields) and the methods that operate on that data into a single unit (class), while restricting direct outside access to internal representation.
By making fields private and providing public getter and setter methods, classes enforce business validation invariants (e.g., preventing a bank account balance from becoming negative).

🔑 The Four Access Modifiers in Java
  • private: Accessible only within the declaring class. Invisible to subclasses and other classes in the same package.
  • Default (Package-Private): When no modifier is specified. Accessible by any class within the same package, but completely hidden from classes in other packages.
  • protected: Accessible within the same package AND by subclasses in different packages via inheritance.
  • public: Accessible from any class in any package across the entire application.
Access Modifier Same Class Same Package Subclass Same Package Non-Subclass Different Package Subclass Different Package Non-Subclass
public ✓ Yes ✓ Yes ✓ Yes ✓ Yes ✓ Yes
protected ✓ Yes ✓ Yes ✓ Yes ✓ Yes (Inherited) ✗ No
Default (no modifier) ✓ Yes ✓ Yes ✓ Yes ✗ No ✗ No
private ✓ Yes ✗ No ✗ No ✗ No ✗ No
03

Inheritance, Class Hierarchies & The super Keyword

🧬 The IS-A Relationship & Supported Inheritance Models

Inheritance allows a derived subclass to inherit fields and methods from a parent superclass using the extends keyword. Java supports:

  • Single Inheritance: Class B extends Class A.
  • Multilevel Inheritance: Class C extends Class B, which extends Class A.
  • Hierarchical Inheritance: Class B and Class C both extend Class A.
⚠️ The Diamond Problem: Why Multiple Class Inheritance is FORBIDDEN

Java deliberately does NOT support multiple inheritance of classes (i.e., class C extends A, B is illegal). If Class A defines a method display(), and both Subclasses B and C override it, and Class D were allowed to inherit from both B and C, calling d.display() creates an irreconcilable ambiguity: should the JVM invoke B's or C's implementation? This is the classic Diamond Problem. Java cleanly solves this by permitting multiple inheritance only through Interfaces!

⚡ The Three Uses of the super Keyword
  • super(): Calls the superclass constructor. Must be the very first statement in a subclass constructor.
  • super.methodName(): Invokes an overridden superclass method from within the overriding subclass method.
  • super.fieldName: Accesses a superclass field that is shadowed/hidden by a field of the same name in the subclass.
04

Polymorphism: Dynamic Method Dispatch & Overriding

🎭 Compile-Time vs. Runtime Polymorphism
  • Compile-Time Polymorphism (Static Binding / Early Binding): Achieved via Method Overloading. The Java compiler determines which method implementation to call at build time based on parameter signatures.
  • Runtime Polymorphism (Dynamic Binding / Late Binding): Achieved via Method Overriding and Dynamic Method Dispatch. An overridden method call is resolved dynamically at runtime based on the actual object type residing on the Heap, not the reference type!
📜 Strict Rules for Method Overriding
  1. The method name and argument list must be identical to that in the superclass.
  2. The return type must be the same, or a covariant return type (a subclass of the superclass return type).
  3. The access modifier cannot be more restrictive than the superclass method (e.g., if parent is protected, child can be protected or public, but not private).
  4. private, static, and final methods cannot be overridden.
  5. Always annotate overridden methods with @Override so the compiler catches accidental signature typos.
Visual Architecture: Dynamic Method Dispatch (Parent Ref ➔ Child Heap Object)
Stack Reference Variable
Employee emp = @0xHeapRef
(Type: Base Class)
─────── Heap Pointer ───────➔
Heap Object
@0xHeapRef [ Manager Object ]
(Actual Runtime Type)
▼ emp.calculateBonus() invoked
JVM Virtual Method Table (vtable) Resolution
JVM discovers runtime object is Manager ➔ Executes Manager.calculateBonus()!
05

Abstraction: Abstract Classes vs. Interfaces

🏛️ Abstract Classes vs. Interfaces Architectural Matrix

Abstraction is the principle of hiding internal implementation details and exposing only essential functional interfaces to the consumer:

  • Abstract Class (abstract): A partially implemented template. Can contain abstract methods (no body) as well as concrete methods, instance state variables, and constructors. Cannot be directly instantiated via new. Represents an IS-A identity relationship.
  • Interface (interface): A pure behavioral contract. A class can implement multiple interfaces (implements A, B, C), enabling multiple behavioral inheritance. Represents a CAN-DO capability contract.
Dimension Abstract Class Interface
Inheritance Modality Single inheritance (extends SingleClass) Multiple implementation (implements InterfaceA, B)
State & Fields Can have instance variables of any access type Fields are implicitly public static final (Constants only)
Constructors Has constructors (called via super()) Cannot have constructors
Method Implementations Can have both abstract and fully concrete methods Abstract methods by default; allows default and static methods (Java 8+)
Design Purpose Code reuse among closely related domain classes Defining standardized peripheral capabilities across unrelated classes
06

Packages & The final Keyword in Java

📦 Packages: Namespace Architecture

A package is a namespace mechanism that groups related classes and interfaces into a directory hierarchy. Packages prevent naming collisions (e.g., java.util.Date vs. java.sql.Date) and provide access protection boundaries (package-private). Standard enterprise convention uses inverted internet domain names: package me.devascentacademy.finance;.

🔒 The Three Applications of the final Keyword
  • Final Variable: Value cannot be mutated once initialized (acts as a constant).
  • Final Method: Method cannot be overridden by any subclass. Used to lock down critical security routines.
  • Final Class: Class cannot be subclassed / extended (e.g., public final class String). Prevents malicious subclasses from breaking immutability guarantees.
07

Unit 6 Practical Capstone: Enterprise Payment Gateway Architecture

🚀 Practical Engineering Project

This comprehensive application unites all four pillars of OOP: abstract class templates with super() constructor chaining, interface contracts (Auditable), dynamic method dispatch polymorphism, encapsulated fields with getter validation, and final security tokens.

PaymentArchitectureDemo.java — Complete Capstone Implementation
/**
 * DevAscent Academy — Unit 6 Capstone Project
 * Demonstrates Encapsulation, Inheritance, Polymorphism, Abstraction, and Interfaces.
 */

// 1. Interface: Behavioral Contract
interface Auditable {
    void logAuditTrail(String auditId);
}

// 2. Abstract Superclass: Domain Foundation
abstract class PaymentTransaction implements Auditable {
    private final String transactionId; // Encapsulated immutable state
    private double amount;

    // Superclass Constructor
    public PaymentTransaction(String transactionId, double amount) {
        this.transactionId = transactionId;
        this.setAmount(amount);
    }

    // Encapsulated Getter & Setter with business validation
    public double getAmount() { return this.amount; }
    public void setAmount(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("Amount must be positive.");
        this.amount = amount;
    }

    public String getTransactionId() { return this.transactionId; }

    // Abstract Method: Enforces dynamic polymorphic implementation
    public abstract void processPayment();

    // Concrete Implementation of Interface Method
    @Override
    public void logAuditTrail(String auditId) {
        System.out.printf("  [AUDIT-%s] Txn %s verified on compliance ledger.%n", 
                auditId, this.transactionId);
    }
}

// 3. Concrete Subclass A: Credit Card Payment
class CreditCardPayment extends PaymentTransaction {
    private final String cardLast4;

    public CreditCardPayment(String txnId, double amount, String cardLast4) {
        super(txnId, amount); // Calling Superclass Constructor
        this.cardLast4 = cardLast4;
    }

    @Override
    public void processPayment() {
        System.out.printf("Charging Credit Card (**** **** **** %s) | Amount: $%.2f [SUCCESS]%n",
                this.cardLast4, this.getAmount());
    }
}

// 4. Concrete Subclass B: Crypto Wallet Payment
class CryptoPayment extends PaymentTransaction {
    private final String walletAddress;

    public CryptoPayment(String txnId, double amount, String walletAddress) {
        super(txnId, amount);
        this.walletAddress = walletAddress;
    }

    @Override
    public void processPayment() {
        System.out.printf("Broadcasting to Blockchain Wallet (%s) | Amount: $%.2f [CONFIRMED]%n",
                this.walletAddress, this.getAmount());
    }
}

// 5. Execution Controller
public class PaymentArchitectureDemo {
    public static void main(String[] args) {
        System.out.println("==================================================");
        System.out.println("   DEVASCENT ENTERPRISE PAYMENT DISPATCH ENGINE   ");
        System.out.println("==================================================");

        // Polymorphic Array: Superclass reference array holding derived objects
        PaymentTransaction[] paymentQueue = {
            new CreditCardPayment("TXN-801", 249.99, "4482"),
            new CryptoPayment("TXN-802", 1200.00, "0x7A1F...99B"),
            new CreditCardPayment("TXN-803", 34.50, "1109")
        };

        // Dynamic Method Dispatch: JVM resolves correct subclass implementation at runtime!
        for (PaymentTransaction txn : paymentQueue) {
            System.out.println("Initiating Txn ID: " + txn.getTransactionId());
            txn.processPayment();  // Polymorphic dispatch
            txn.logAuditTrail("SEC99");
            System.out.println("--------------------------------------------------");
        }
    }
}
Execution Result (javac & java)
$ javac PaymentArchitectureDemo.java $ java PaymentArchitectureDemo ================================================== DEVASCENT ENTERPRISE PAYMENT DISPATCH ENGINE ================================================== Initiating Txn ID: TXN-801 Charging Credit Card (**** **** **** 4482) | Amount: $249.99 [SUCCESS] [AUDIT-SEC99] Txn TXN-801 verified on compliance ledger. -------------------------------------------------- Initiating Txn ID: TXN-802 Broadcasting to Blockchain Wallet (0x7A1F...99B) | Amount: $1200.00 [CONFIRMED] [AUDIT-SEC99] Txn TXN-802 verified on compliance ledger. -------------------------------------------------- Initiating Txn ID: TXN-803 Charging Credit Card (**** **** **** 1109) | Amount: $34.50 [SUCCESS] [AUDIT-SEC99] Txn TXN-803 verified on compliance ledger. --------------------------------------------------
08

University Exam & Technical Interview Viva Preparation Guide

Q1. Why does Java disallow multiple inheritance of classes, but permits multiple implementation of interfaces?
Multiple inheritance of classes leads to the Diamond Problem: if class D extends classes B and C, which both inherit and override a method from class A, class D has no way to determine which parent's implementation to inherit. With interfaces, classical interface methods do not contain implementation state. Even with Java 8 default methods, the compiler forces the implementing class to explicitly override and disambiguate any colliding default implementations (e.g., InterfaceA.super.method()).
Q2. What is Dynamic Method Dispatch and how does the JVM implement it?
Dynamic Method Dispatch is the mechanism by which a call to an overridden method is resolved at runtime rather than compile time. When a superclass reference points to a subclass object (Parent p = new Child(); p.method();), the JVM inspects the object's internal Virtual Method Table (vtable) located in heap metadata to find the memory address of the overriding subclass method, executing it dynamically.
Q3. Can an abstract class have constructors? If it cannot be instantiated, what is their purpose?
Yes, abstract classes can have constructors. Although an abstract class cannot be instantiated directly via new AbstractClass(), its constructors are executed during subclass instantiation when the subclass constructor calls super(). This ensures that the abstract superclass's state fields and initialization invariants are properly configured.
Q4. What is the difference between Method Overloading and Method Overriding?
Method Overloading occurs within the same class; methods share the same name but have different parameter lists; it represents compile-time polymorphism. Method Overriding occurs across parent-child classes; the subclass defines a method with the exact same name, return type, and parameters as the superclass; it represents runtime polymorphism.
Q5. Why is the String class declared final in Java?
If String were not final, an unauthorized developer could create a subclass (e.g., MutableString) that overrides methods like hashCode() or allows mutation of internal character arrays. Because Strings are fundamental to JVM security (holding database credentials, socket configurations, and classloading paths), declaring String final guarantees that its immutability contract can never be broken or spoofed.
Q6. What are the rules regarding access modifiers during Method Overriding?
When overriding a method, the subclass method's access modifier cannot be more restrictive than the superclass method's modifier. For example, if the superclass method is protected, the subclass method can be protected or public, but cannot be default (package-private) or private. This adheres to the Liskov Substitution Principle: a subclass must fulfill at least the visibility contract of its parent.
Q7. What is a Covariant Return Type?
Introduced in Java 5, a covariant return type allows an overriding method in a subclass to return a sub-type of the return type declared in the superclass method. For instance, if SuperClass.produce() returns Animal, the overriding SubClass.produce() is legally allowed to return Dog without requiring type casting by the caller.
Q8. What are Default Methods in Java Interfaces and why were they introduced?
Introduced in Java 8, Default Methods (declared with the default keyword) allow interfaces to provide concrete method implementations. They were introduced to ensure backward compatibility: allowing architects to add new methods to existing foundational interfaces (such as adding stream() and forEach() to java.util.Collection) without breaking millions of legacy third-party classes that implemented those interfaces.