Cosmic Module

J

Qubits of DPK

March 14, 2026

Core Java

Layman Explanation

A person can be a son at home, an employee at work, a customer at a shop — same person, different roles/behaviors. In Java, one object can behave differently based on context. That's polymorphism.

What is Polymorphism?

One name, many forms. The ability of an object to take multiple forms.
javascript
QUBITS OF DPK
1Polymorphism
23├── Compile-time (Static)Method Overloading
4└── Runtime (Dynamic)Method Overriding

Dynamic Method Dispatch (Runtime Polymorphism)

When parent reference holds child object — overridden method is called based on ACTUAL object type at runtime.
java
QUBITS OF DPK
1class Animal {
2    void makeSound() { System.out.println("Generic sound"); }
3}
4
5class Dog extends Animal {
6    @Override
7    void makeSound() { System.out.println("Woof!"); }
8}
9
10class Cat extends Animal {
11    @Override
12    void makeSound() { System.out.println("Meow!"); }
13}
14
15// Parent reference — child objects
16Animal a1 = new Dog();
17Animal a2 = new Cat();
18Animal a3 = new Animal();
19
20a1.makeSound();  // Woof!
21a2.makeSound();  // Meow!
22a3.makeSound();  // Generic sound
JVM decides which method to call at RUNTIME based on actual object type — not the reference type.

Upcasting & Downcasting

Upcasting (automatic)

java
QUBITS OF DPK
1Animal a = new Dog();   // Dog → Animal (automatic) ✅
2// Parent reference can hold child object
3// Access limited to parent's methods

Downcasting (manual)

java
QUBITS OF DPK
1Animal a = new Dog();
2Dog d = (Dog) a;        // Animal → Dog (manual cast)
3d.fetch();              // can now access Dog's methods

ClassCastException Trap

java
QUBITS OF DPK
1Animal a = new Cat();
2Dog d = (Dog) a;   // ❌ ClassCastException at runtime!
3// a holds Cat, can't cast to Dog
4
5// Safe check:
6if (a instanceof Dog) {
7    Dog d = (Dog) a;   // ✅ safe
8}

final Keyword

final variable — constant

java
QUBITS OF DPK
1final double PI = 3.14159;
2PI = 3.0;  // ❌ cannot reassign!

final method — cannot override

java
QUBITS OF DPK
1class Parent {
2    final void show() { System.out.println("Parent"); }
3}
4class Child extends Parent {
5    void show() { }  // ❌ compile error!
6}

final class — cannot extend

java
QUBITS OF DPK
1final class String { }   // no one can extend String!
2class MyString extends String { }  // ❌ compile error

Object Class — Root of All Classes

Every class in Java implicitly extends java.lang.Object.
Key methods from Object class:
java
QUBITS OF DPK
1// equals() — compare objects
2@Override
3public boolean equals(Object obj) {
4    if (this == obj) return true;
5    if (!(obj instanceof Car)) return false;
6    Car other = (Car) obj;
7    return this.brand.equals(other.brand);
8}
9
10// hashCode() — integer representation
11@Override
12public int hashCode() {
13    return Objects.hash(brand, speed);
14}
15
16// toString() — string representation
17@Override
18public String toString() {
19    return "Car{brand='" + brand + "', speed=" + speed + "}";
20}
21
22// getClass(), clone(), finalize() also from Object

equals() + hashCode() Contract

javascript
QUBITS OF DPK
1If a.equals(b) is true → a.hashCode() == b.hashCode() MUST be true
2If hashCodes differ → objects are definitely not equal
3Always override BOTH together!

Wrapper Classes

Each primitive type has a corresponding Wrapper class that wraps it as an Object.
javascript
QUBITS OF DPK
1int      → Integer
2double   → Double
3char     → Character
4boolean  → Boolean
5long     → Long
java
QUBITS OF DPK
1// Autoboxing — primitive → wrapper (automatic)
2Integer i = 5;          // int → Integer
3
4// Unboxing — wrapper → primitive (automatic)
5int x = i;             // Integer → int
6
7// Useful methods
8Integer.parseInt("123");    // String → int
9Integer.MAX_VALUE;           // 2147483647
10Double.parseDouble("3.14"); // String → double

Why Wrapper Classes?

javascript
QUBITS OF DPK
11. Collections (ArrayList, HashMap) need Objects, not primitives
22. Utility methods (parseInt, valueOf, compareTo)
33. Null support (Integer can be null, int cannot)
44. Generics require objects

️ All Traps

java
QUBITS OF DPK
1// Trap 1 — ClassCastException
2Animal a = new Cat();
3Dog d = (Dog) a;   // ❌ runtime crash!
4// Fix: instanceof check first
5
6// Trap 2 — Static methods are NOT polymorphic
7Animal a = new Dog();
8a.staticMethod();  // calls Animal's static, not Dog's!
9// Static is resolved at compile time, not runtime
10
11// Trap 3 — Integer cache (Wrapper trap!)
12Integer a = 127;
13Integer b = 127;
14a == b   // true (cached)
15
16Integer x = 128;
17Integer y = 128;
18x == y   // false! (outside cache range, different objects)
19x.equals(y)  // true ✅
20// Always use .equals() for wrapper comparison!
21
22// Trap 4 — NullPointerException with unboxing
23Integer i = null;
24int x = i;   // ❌ NullPointerException! (unboxing null)

Interview Answer (SDE-2)

"Polymorphism means one interface, multiple implementations. Compile-time polymorphism via method overloading, runtime polymorphism via method overriding with dynamic method dispatch — JVM decides which overridden method to call based on actual object type. Upcasting is automatic (child to parent reference), downcasting is manual and can throw ClassCastException if actual type doesn't match. Final prevents variable reassignment, method overriding, and class extension — String is final. Every class extends Object implicitly, providing equals, hashCode, and toString — always override equals and hashCode together. Wrapper classes box primitives as objects for use in collections and generics."

Interview Questions & MAANG-Level Answers

Q1. What is polymorphism? What are its types?
Polymorphism means one interface, many implementations. Two types: (1) Compile-time (static) via method overloading — same method name, different parameters, resolved at compile time. (2) Runtime (dynamic) via method overriding — parent reference holds child object, JVM decides at runtime which override to call based on actual object type, not reference type. Runtime polymorphism enables writing generic code that works for future subtypes not yet created.
Q2. What is dynamic method dispatch?
When an overridden method is called on a parent reference, JVM determines which implementation to execute based on the ACTUAL object type at runtime:
java
QUBITS OF DPK
1Animal a = new Dog();  // reference = Animal, object = Dog
2a.makeSound();         // calls Dog's makeSound(), not Animal's!
This is resolved at runtime, not compile time. It's the foundation of the Open/Closed principle — code open for extension (new subtypes), closed for modification (existing code doesn't change). Static methods are NOT dispatched dynamically — they're resolved by reference type.
Q3. What is the difference between upcasting and downcasting?
Upcasting: child object assigned to parent reference — automatic, safe, no cast needed. Restricts access to parent's methods only. Animal a = new Dog()a can only call Animal methods. Downcasting: parent reference cast back to child type — manual, risky. Dog d = (Dog) a — works only if a actually holds a Dog at runtime, else ClassCastException. Always use instanceof check before downcasting.
Q4. What is ClassCastException and how to prevent it?
Thrown when you try to cast an object to a type it doesn't actually belong to: Animal a = new Cat(); Dog d = (Dog) a — runtime crash. Prevention: use instanceof check first:
java
QUBITS OF DPK
1if (a instanceof Dog) { Dog d = (Dog) a; d.bark(); }
Java 16+ pattern matching makes this cleaner: if (a instanceof Dog d) { d.bark(); } — combines check and cast. In well-designed OOP, excessive downcasting suggests a design smell — prefer polymorphism instead.
Q5. What are the uses of the final keyword?
Three uses: (1) final variable — constant, cannot be reassigned after initialization. final double PI = 3.14. (2) final method — cannot be overridden in subclasses. Prevents unintended behavior changes. (3) final class — cannot be extended. String, Integer, Math are all final. Used for security (prevent subclass attacks), immutability, and optimization (JVM can inline final methods).
Q6. What is the contract between equals() and hashCode()?
The contract: if a.equals(b) is true, then a.hashCode() == b.hashCode() MUST be true. The reverse is not required (same hash doesn't mean equal). If you break this contract, HashMap and HashSet break — objects that are "equal" end up in different buckets and can't be found. ALWAYS override both together. Use Objects.hash(field1, field2) for hashCode. IDEs and Lombok can auto-generate correct implementations.
Q7. What is autoboxing and unboxing?
Autoboxing: automatic conversion from primitive to wrapper object: Integer i = 5 (int → Integer). Unboxing: automatic conversion from wrapper to primitive: int x = i (Integer → int). These happen implicitly in assignments, method calls, and arithmetic. Performance note: heavy autoboxing in loops creates many short-lived objects — prefer primitives in performance-critical code. NPE risk: unboxing null wrapper throws NullPointerException.
Q8. Why is Integer 127 == 127 true but Integer 128 == 128 false?
Java caches Integer objects from -128 to 127 in the Integer Cache (IntegerCache class). Values in this range always return the same cached object, so == (reference comparison) returns true. Values outside this range create new heap objects each time, so == returns false even with same value. Always use .equals() for Integer comparison — it works for ALL values regardless of cache range. This behavior is in the Java spec and is intentional for performance.