Part 2: Advanced Abstraction (Mid-Level)

Nitin SinghNitin Singh
22 min read

📝 About This Part

In Part 1 of this series, we explored the fundamentals of abstraction in Java — what it is, why it matters in OOP, and the most common beginner-level interview questions.

But abstraction questions don’t stop at the basics. For developers with 4–8 years of experience, interviewers dig deeper. They test not just whether you can define abstraction, but whether you understand:

  • How abstraction evolved with Java 8 and 9 features

  • The tricky nuances between abstraction and encapsulation

  • How abstraction interacts with multiple inheritance, lambdas, and enums

  • Edge cases like calling abstract methods in constructors or using private methods in interfaces

This part of the series is designed for those preparing for mid-level Java interviews — where abstraction isn’t just about definitions but about design choices, pitfalls, and real-world scenarios.

By the end of this post, you’ll be comfortable answering advanced questions that separate surface-level knowledge from true understanding.

🔵 Q1: Can you achieve abstraction without using abstract classes or interfaces? How?

💡 Answer:

Yes — while abstract classes and interfaces are the primary tools for abstraction in Java, you can achieve abstraction in other ways as well.

👉 At its core, abstraction is about hiding implementation details and exposing only necessary behavior. This can be achieved through:

✅ 1. Encapsulation with Access Modifiers

By using private fields and exposing only necessary methods, you abstract away the internal complexity.

class Car {
    private String engine; // hidden detail

    public void start() {   // exposed action
        igniteEngine();
        System.out.println("Car started");
    }

    private void igniteEngine() {
        System.out.println("Igniting " + engine);
    }
}

➡️ Here, users only see start(). They don’t care how the engine starts — that’s abstraction without abstract classes or interfaces.

✅ 2. Composition and Delegation

Instead of inheritance, abstraction can be achieved by delegating tasks to helper classes.

class PaymentProcessor {
    private PaymentService service = new PaymentService();

    public void process() {
        service.connect();
        service.pay();
    }
}

➡️ The consumer of PaymentProcessor doesn’t see the internal complexity of PaymentService.


✅ 3. Using Anonymous Classes and Lambdas (Java 8+)

Abstraction can also be implemented inline with anonymous classes or lambdas:

Runnable task = () -> System.out.println("Running task...");
new Thread(task).start();

➡️ The caller doesn’t know how Thread manages execution. They only know the task will “run.”

⚡️ Cross-Questions Interviewers Might Ask:

🔹 Q: If we can achieve abstraction through encapsulation, why do we need abstract classes and interfaces?
➡️ Answer: Abstract classes and interfaces provide formal contracts. Encapsulation hides data, but abstraction formalizes behavior across multiple classes — enabling polymorphism.

🔹 Q: Can we say abstraction = interfaces only?
➡️ Answer: No. Interfaces are just one way to enforce abstraction. Even without interfaces, hiding details and exposing only necessary behavior is abstraction.

🔹 Q: In real-world systems, where do we see abstraction beyond abstract classes/interfaces?
➡️ Answer:

  • JDBC hides database details behind a driver interface.

  • Java Collections API hides sorting algorithms behind methods like Collections.sort().

  • Frameworks like Spring abstract object creation via Dependency Injection.

🎯 Key Takeaway for Interviews:

Abstraction is not limited to abstract classes and interfaces. It’s a design principle. You can achieve it with access modifiers, delegation, frameworks, and functional constructs. Abstract classes and interfaces just provide the strongest guarantees.


🔵 Q2: How does abstraction differ from encapsulation at a deeper level?

💡 Answer:

Although abstraction and encapsulation are related, they serve different purposes in object-oriented programming. Interviewers love this because many candidates confuse the two.

Core Difference

AspectAbstractionEncapsulation
DefinitionHiding implementation details and exposing only essential features.Hiding internal state and controlling access via methods.
FocusWhat an object does.How data is protected and accessed.
Achieved ByAbstract classes, interfaces, method overriding, design contracts.Private fields, getters/setters, access modifiers.
GoalReduce complexity, improve design flexibility.Protect state, ensure data integrity.
LevelDesign level (blueprints, contracts).Implementation level (access control, safety).

⚡️ Illustration with Example

Abstraction (What it does):

interface Payment {
    void process(double amount); // contract only
}

➡️ Here, you only know that payments can be processed. You don’t know how.

Encapsulation (How it’s done safely):

class BankAccount {
    private double balance; // hidden

    public void deposit(double amount) { // controlled access
        if (amount > 0) balance += amount;
    }
}

➡️ Balance is hidden, only modified through controlled methods.

⚠️ Special Attention (Interview Trap):

Many interviewers will ask:

“Is encapsulation part of abstraction, or are they completely separate?”

👉 Correct answer:

  • They are different but complementary.

  • Encapsulation helps implement abstraction.

  • Without encapsulation, it’s hard to truly abstract internal complexity because details may leak.

⚡️ Cross-Questions Interviewers Might Ask

🔹 Q: Can we achieve abstraction without encapsulation?
➡️ Answer: Yes, in theory. For example, an interface defines a contract (abstraction) without hiding any state. But in practice, encapsulation strengthens abstraction by preventing direct access to implementation details.

🔹 Q: Give me a real-world example of both abstraction and encapsulation together.
➡️ Answer:
ATM:

  • Abstraction: User presses “Withdraw” without knowing backend logic.

  • Encapsulation: Account balance is private and validated before withdrawal.

🔹 Q: Which is more important: abstraction or encapsulation?
➡️ Answer (balanced): Both are pillars of OOP. Abstraction improves design clarity, while encapsulation ensures data safety and correctness. In practice, they work hand-in-hand.

🎯 Key Takeaway for Interviews:

  • Abstraction = design contracts (what).

  • Encapsulation = implementation safety (how).

  • They are not the same, but they complement each other to produce clean, safe, and scalable code.


🔵 Q3: How do abstract classes enable partial abstraction?

💡 Answer:

An abstract class in Java allows partial abstraction because it can contain both:

  1. Abstract methods → no body, must be implemented by subclasses.

  2. Concrete methods → already implemented and shared across subclasses.

This mix means an abstract class does not enforce 100% abstraction like an interface (pre–Java 8). Instead, it allows you to hide some details (abstract methods) while also sharing common behavior (concrete methods).

✅ Example:

abstract class Vehicle {
    // Abstract method → forces subclass to provide its own logic
    public abstract void move();

    // Concrete method → shared implementation
    public void startEngine() {
        System.out.println("Engine started...");
    }
}

class Car extends Vehicle {
    @Override
    public void move() {
        System.out.println("Car moves on wheels.");
    }
}

➡️ Here:

  • move() is abstract → forces subclasses (Car, Bike, Truck) to define their own way of moving.

  • startEngine() is concrete → shared across all vehicles.

Thus, abstraction is partial.

⚠️ Special Attention (Interview Trap):

“Why not just use interfaces instead of abstract classes?”

👉 Correct Answer:

  • Use interfaces when you want a pure contract (100% abstraction, multiple inheritance).

  • Use abstract classes when:

    • You want shared state (fields).

    • You need common methods with reusable logic.

    • The classes are closely related.

⚡️ Cross-Questions Interviewers Might Ask

🔹 Q: Can abstract classes exist without any abstract methods?
➡️ Answer: Yes. Even if an abstract class has only concrete methods, it’s still abstract if declared with abstract. This prevents direct instantiation and signals it's meant to be a base class.

🔹 Q: Can abstract classes achieve full abstraction?
➡️ Answer: Yes, if you declare only abstract methods. But in practice, interfaces are the better choice for full abstraction.

🔹 Q: Why is it called partial abstraction?
➡️ Answer: Because it lets you abstract only part of the behavior while still providing default implementations for the rest.

🎯 Key Takeaway for Interviews:

Abstract classes provide partial abstraction, balancing between enforced contracts and shared logic. They’re best when you need a common foundation with flexibility for specialization.


🔵 Q4: Why were default and static methods added to interfaces in Java 8?

💡 Answer:

Prior to Java 8, interfaces only contained abstract methods. This meant if you added a new method to an interface, all implementing classes would break because they had to implement that new method.

To solve this backward compatibility problem, Java 8 introduced:

  1. Default Methods → allow interfaces to provide a default implementation.

  2. Static Methods → allow utility/helper methods inside interfaces.

✅ Example: Default Method

interface Payment {
    void process(double amount);

    // New feature added in Java 8
    default void refund(double amount) {
        System.out.println("Refund of $" + amount + " initiated.");
    }
}

class CreditCardPayment implements Payment {
    public void process(double amount) {
        System.out.println("Processing credit card payment of $" + amount);
    }
}

➡️ Even though refund() was added later, existing classes like CreditCardPayment won’t break because they inherit the default implementation.

✅ Example: Static Method

interface MathUtils {
    static boolean isEven(int num) {
        return num % 2 == 0;
    }
}

➡️ Accessed directly as MathUtils.isEven(10) without needing an object.

⚠️ Special Attention (Interview Trap):

“If interfaces can now have default methods, what’s the difference between abstract classes and interfaces?”

👉 Correct Answer:

  • Abstract classes can hold state (instance variables). Interfaces cannot (only constants).

  • Abstract classes can have constructors. Interfaces cannot.

  • Abstract classes support single inheritance; interfaces support multiple inheritance.

⚡️ Cross-Questions Interviewers Might Ask

🔹 Q: What happens if two interfaces provide the same default method?
➡️ Answer: The implementing class must override the method explicitly.

interface A {
    default void hello() { System.out.println("Hello from A"); }
}
interface B {
    default void hello() { System.out.println("Hello from B"); }
}
class C implements A, B {
    public void hello() {  // must resolve conflict
        A.hello();  
    }
}

🔹 Q: Why not just add new abstract methods and let classes implement them?
➡️ Answer: That would break backward compatibility for thousands of Java libraries. Default methods were introduced specifically to evolve interfaces safely.

🔹 Q: Can static methods in interfaces be inherited?
➡️ Answer: No. They belong to the interface itself and must be called with the interface name (InterfaceName.methodName()).

🎯 Key Takeaway for Interviews:

Default and static methods were added in Java 8 to make interfaces more evolution-friendly, solving the problem of backward compatibility while keeping Java’s OOP principles intact.


🔵 Q5: How does Java handle multiple inheritance via interfaces?

💡 Answer:

In Java, a class can implement multiple interfaces, which allows for multiple inheritance of type.
This solves the problem that Java does not allow multiple inheritance of classes (to avoid ambiguity and the diamond problem).

👉 By restricting multiple inheritance to interfaces only, Java ensures:

  • Flexibility: A class can behave like many types.

  • Safety: There’s no state (instance variables) in interfaces (pre-Java 8), so no ambiguity in fields.

✅ Example

interface Printer {
    void print();
}

interface Scanner {
    void scan();
}

class MultiFunctionDevice implements Printer, Scanner {
    public void print() {
        System.out.println("Printing document...");
    }
    public void scan() {
        System.out.println("Scanning document...");
    }
}

➡️ Here, MultiFunctionDevice inherits behavior from both Printer and Scanner.

⚠️ Special Attention (Java 8 and Beyond):

With Java 8’s default methods, interfaces can now have implementation.
This introduces the diamond problem if two interfaces define the same default method.

Example:

interface A {
    default void hello() { System.out.println("Hello from A"); }
}

interface B {
    default void hello() { System.out.println("Hello from B"); }
}

class C implements A, B {
    @Override
    public void hello() {
        A.super.hello(); // must resolve explicitly
    }
}

➡️ Java forces the class (C) to override and resolve ambiguity, preventing multiple inheritance conflicts.

⚡️ Cross-Questions Interviewers Might Ask

🔹 Q: Why does Java allow multiple inheritance of interfaces but not classes?
➡️ Answer:

  • Multiple class inheritance can lead to ambiguity in state (fields) and constructors.

  • Interfaces are contracts, not state holders (pre-Java 8), making them safe for multiple inheritance.

🔹 Q: What happens if two interfaces define the same constant?
➡️ Answer: It will cause compile-time ambiguity. You must qualify it explicitly using the interface name.

interface A { int VALUE = 10; }
interface B { int VALUE = 20; }
class Test implements A, B {
    public void show() {
        System.out.println(A.VALUE); // must specify
    }
}

🔹 Q: Can interfaces inherit other interfaces?
➡️ Answer: Yes, interfaces can extend multiple other interfaces. This is also multiple inheritance of type.

interface X { void methodX(); }
interface Y { void methodY(); }
interface Z extends X, Y { void methodZ(); }

🎯 Key Takeaway for Interviews:

Java supports multiple inheritance of type via interfaces while avoiding the pitfalls of multiple class inheritance. Since Java 8, if default methods clash, the implementing class must resolve conflicts explicitly.


🔵 Q6: What is the diamond problem, and how does Java resolve it?

💡 Answer:

The diamond problem occurs in multiple inheritance when a class inherits the same method from two different parent types, leading to ambiguity about which method to use.

In Java:

  • Multiple class inheritance is not allowed (so diamond problem doesn’t occur with classes).

  • But since Java 8, interfaces can have default methods, which can create diamond-like situations.

✅ Classic Diamond Problem (in other languages like C++):

     A
    / \
   B   C
    \ /
     D

➡️ If both B and C inherit A.method() and D extends both B and C, which method() should D use?

✅ Diamond Problem in Java Interfaces

interface A {
    default void greet() { System.out.println("Hello from A"); }
}

interface B {
    default void greet() { System.out.println("Hello from B"); }
}

class C implements A, B {
    @Override
    public void greet() {
        A.super.greet();  // explicitly resolve conflict
    }
}

➡️ Here, class C implements both A and B, which define the same default method greet().
➡️ Java forces C to override and decide which version to call.

⚠️ Special Attention (Interview Trap):

“What happens if the implementing class doesn’t override the conflicting method?”

👉 It won’t compile. Java requires explicit resolution to eliminate ambiguity.

⚡️ Cross-Questions Interviewers Might Ask

🔹 Q: Why doesn’t Java allow multiple class inheritance but allows multiple interface inheritance?
➡️ Answer:

  • Multiple class inheritance can lead to state and constructor ambiguity.

  • Interfaces don’t carry state (except constants) and only define contracts, making them safer.

🔹 Q: Can diamond problem occur if one interface has an abstract method and the other has a default method?
➡️ Answer: No. The abstract method doesn’t create conflict. The implementing class simply provides its own implementation.

🔹 Q: What happens if two interfaces extend a third interface and redefine the same default method?
➡️ Answer: Still a conflict. The implementing class must override and choose.

interface A {
    default void hello() {}
}
interface B extends A {}
interface C extends A {}
class D implements B, C {
    public void hello() { System.out.println("Resolved in D"); }
}

🎯 Key Takeaway for Interviews:

The diamond problem in Java arises only with default methods in interfaces (Java 8+).
Java resolves it by forcing the implementing class to override and explicitly choose which method to use — avoiding ambiguity and keeping multiple inheritance safe.


🔵 Q7: Can interface methods be private or protected? What changed in Java 9?

💡 Answer:

Before Java 9:

  • All interface methods were implicitly public and abstract (except default and static methods in Java 8).

  • You could not declare methods as private or protected in an interface.

From Java 9 onwards:

  • Private methods in interfaces were introduced to allow code reusability within the interface itself.

  • Protected methods are still not allowed — because interfaces are about defining public contracts, not inheritance hierarchies with visibility control.

✅ Example (Java 9 Private Methods in Interfaces)

interface Logger {
    default void logInfo(String msg) {
        log(msg, "INFO");
    }

    default void logError(String msg) {
        log(msg, "ERROR");
    }

    // private helper method
    private void log(String msg, String level) {
        System.out.println(level + ": " + msg);
    }
}

➡️ Here, the log() method is private.
➡️ It cannot be called outside the interface, but it helps default methods reuse logic cleanly.

⚠️ Special Attention (Interview Trap):

“Why allow private methods in interfaces but not protected?”

👉 Correct Answer:

  • Interfaces define public APIs — behavior that classes must follow.

  • protected would contradict this because it restricts access to subclasses only, which is against the contract-first design of interfaces.

  • private methods are fine since they are helpers only for the interface itself, invisible to implementing classes.

⚡️ Cross-Questions Interviewers Might Ask

🔹 Q: Can we have private static methods in interfaces?
➡️ Answer: Yes. Since Java 9, both private instance methods and private static methods are allowed.

interface MathHelper {
    static int square(int x) {
        validate(x);
        return x * x;
    }
    private static void validate(int x) {
        if (x < 0) throw new IllegalArgumentException("Negative not allowed");
    }
}

🔹 Q: Can an interface private method be overridden in implementing class?
➡️ Answer: No. Private methods are not inherited, so they can’t be overridden.

🔹 Q: If I want some methods to be visible only to implementing classes, can I use protected in interfaces?
➡️ Answer: No. Interfaces don’t support protected methods because their purpose is to define public behavior contracts. Use an abstract class in such cases.

🎯 Key Takeaway for Interviews:

  • Pre–Java 9 → all interface methods were public.

  • Post–Java 9 → private methods (including private static) allowed for code reuse inside interfaces.

  • protected is still not allowed because interfaces are meant to expose public contracts only.


🔵 Q8: Does abstraction introduce runtime performance or memory overhead?

💡 Answer:

Yes — abstraction can introduce some indirect overhead, but usually negligible compared to its benefits.

👉 Overheads:

  1. Method calls through abstract references (dynamic dispatch)

    • When you call a method on an interface or abstract class reference, the JVM uses dynamic binding (vtable lookups) to resolve which method to call.

    • This adds a tiny runtime overhead, compared to direct calls.

  2. Memory usage

    • Interfaces don’t hold state (so no memory issue).

    • Abstract classes may hold common fields, so memory depends on subclass design, not abstraction itself.

  3. Inlining restrictions

    • JVM may not inline calls across abstraction boundaries as aggressively as direct method calls (though modern JIT optimizers handle this well).

✅ Example:

interface Shape {
    void draw();
}

class Circle implements Shape {
    public void draw() {
        System.out.println("Drawing Circle");
    }
}

public class Test {
    public static void main(String[] args) {
        Shape s = new Circle(); // reference via abstraction
        s.draw();               // dynamic dispatch
    }
}

➡️ JVM resolves s.draw() at runtime → slight overhead, but usually optimized away.

⚠️ Special Attention:

  • In interviews, never say “abstraction makes programs slow.”

  • Correct stance: Abstraction introduces minimal overhead, but modern JVM optimizations make it negligible. The design benefits far outweigh the cost.

⚡️ Cross-Questions Interviewers Might Ask

🔹 Q: So should we avoid abstraction for performance-critical systems?
➡️ Answer: No. Good design is always preferable. Only in ultra-low latency systems (like HFT trading engines) might you trade abstraction for performance.

🔹 Q: How does JVM optimize abstract calls?
➡️ Answer: JIT (Just-In-Time Compiler) performs inlining and monomorphic call site optimization to eliminate overhead.

🎯 Key Takeaway:

Abstraction introduces negligible overhead, mostly in method dispatch. JVM optimizations ensure real-world performance is rarely affected.


🔵 Q9: Can an abstract class implement an interface without overriding all its methods?

💡 Answer:

Yes — an abstract class can implement an interface without providing implementations for all methods.
It then becomes the responsibility of concrete subclasses to implement the remaining methods.

✅ Example:

interface Drawable {
    void draw();
    void resize();
}

abstract class AbstractShape implements Drawable {
    @Override
    public void draw() {
        System.out.println("Drawing shape...");
    }
    // resize() left unimplemented → abstract class
}

class Circle extends AbstractShape {
    @Override
    public void resize() {
        System.out.println("Resizing circle...");
    }
}

➡️ AbstractShape provides partial implementation.
➡️ Circle must implement the remaining resize() method.

⚠️ Special Attention (Interview Trap):

“If abstract class doesn’t implement all interface methods, is it valid?”

👉 Yes. The compiler is fine because the abstract class itself is not concrete. The enforcement passes to subclasses.

⚡️ Cross-Questions Interviewers Might Ask

🔹 Q: Why would you design it this way?
➡️ Answer: To provide shared logic for multiple subclasses while leaving customization to concrete classes.

🔹 Q: Can an abstract class implement multiple interfaces?
➡️ Answer: Yes. An abstract class can implement multiple interfaces and selectively implement methods from each.

🎯 Key Takeaway:

Abstract classes can implement interfaces partially. This is a powerful pattern for defining shared behavior + extensibility.


🔵 Q10: What happens if an abstract class does not provide an implementation for all interface methods?

💡 Answer:

If an abstract class implements an interface but does not override all its methods, the abstract class itself remains abstract.
➡️ It cannot be instantiated, and the responsibility shifts to its concrete subclasses to provide the missing implementations.

✅ Example

interface Drawable {
    void draw();
    void resize();
}

abstract class Shape implements Drawable {
    @Override
    public void draw() {
        System.out.println("Drawing shape...");
    }
    // resize() not implemented → Shape stays abstract
}

class Circle extends Shape {
    @Override
    public void resize() {
        System.out.println("Resizing circle...");
    }
}
  • Shape does not implement resize() → must remain abstract.

  • Circle provides the implementation → can be instantiated.

⚠️ Special Attention (Interview Trap):

“Will the compiler throw an error if an abstract class skips some interface methods?”

👉 No error, as long as the abstract class itself is declared abstract. The compiler only forces concrete classes to implement everything.

⚡️ Cross-Questions

🔹 Q: Can an abstract class implement multiple interfaces but skip methods from both?
➡️ Answer: Yes, as long as the class remains abstract. The subclasses must implement the missing ones.

🔹 Q: Can an abstract class partially implement one interface and fully implement another?
➡️ Answer: Yes. The compiler checks completeness separately for each concrete subclass.

🎯 Key Takeaway:

An abstract class can skip some interface methods, but then it must stay abstract. Concrete subclasses bear the final responsibility.


🔵 Q11: Abstract class vs interface in the context of functional programming (Java 8 Lambdas)

💡 Answer:

In Java 8 functional programming, the role of interfaces changed significantly with the introduction of functional interfaces.
This also highlights the distinction between abstract classes and interfaces in the modern Java world.

✅ Functional Interface Example

@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}

public class Test {
    public static void main(String[] args) {
        Calculator add = (a, b) -> a + b;   // Lambda expression
        System.out.println(add.calculate(5, 3)); // Output: 8
    }
}

➡️ Interfaces with exactly one abstract method (SAM) can be implemented using lambdas, enabling functional-style programming.

⚡️ Why Not Abstract Classes?

  • Lambdas work only with functional interfaces, not abstract classes.

  • Abstract classes can have multiple methods, state, and constructors, making them unsuitable for lambda shorthand.

  • Interfaces represent behavioral contracts, aligning perfectly with functional programming.

⚠️ Special Attention (Interview Trap):

“Can an abstract class ever be used like a functional interface?”

👉 No. Abstract classes cannot be targets for lambda expressions. Only functional interfaces qualify.

⚡️ Cross-Questions

🔹 Q: What is the role of @FunctionalInterface?
➡️ Answer: It’s optional, but ensures the interface has exactly one abstract method. Compiler will error out if you add more.

🔹 Q: Can a functional interface have default or static methods?
➡️ Answer: Yes, as long as it has only one abstract method.

@FunctionalInterface
interface Converter<T, R> {
    R convert(T input);

    default void log(T input) {
        System.out.println("Converting: " + input);
    }
}

🎯 Key Takeaway:

  • Interfaces power functional programming in Java 8+ via lambdas and method references.

  • Abstract classes cannot be used with lambdas.

  • This marks a big shift where interfaces gained more practical weight in modern Java design.


🔵 Q12: Can you create objects of an abstract class using an anonymous inner class?

💡 Answer:

You cannot directly instantiate an abstract class, but you can create an object of an anonymous inner class that extends it and provides implementations for abstract methods.

✅ Example

abstract class Animal {
    abstract void sound();
}

public class Test {
    public static void main(String[] args) {
        Animal dog = new Animal() {  // anonymous inner class
            @Override
            void sound() {
                System.out.println("Woof Woof");
            }
        };
        dog.sound(); // Output: Woof Woof
    }
}

➡️ Here, new Animal() creates an anonymous subclass of Animal.

⚠️ Special Attention (Trap):

“Can we instantiate abstract class objects?”

👉 Strict answer: No.
But we can instantiate a concrete anonymous subclass of an abstract class.

⚡️ Cross-Questions

🔹 Why use anonymous inner classes instead of regular subclasses?
➡️ When you need one-time use implementations (e.g., in GUI event handlers).

🎯 Takeaway:

Abstract classes can’t be instantiated directly, but anonymous inner classes allow quick one-off implementations.


🔵 Q13: Can constructors in abstract classes call abstract methods? What are the pitfalls?

💡 Answer:

Yes — an abstract class constructor can call an abstract method, but it’s generally a bad practice.

Why?

  • When the constructor runs, the subclass part of the object is not yet initialized.

  • If the abstract method relies on subclass fields, it may behave unpredictably (nulls, defaults).

✅ Example (Pitfall)

abstract class Parent {
    Parent() {
        callMe(); // calls abstract method
    }
    abstract void callMe();
}

class Child extends Parent {
    private String message = "Hello";

    @Override
    void callMe() {
        System.out.println(message.toUpperCase()); // NPE risk
    }
}

public class Test {
    public static void main(String[] args) {
        new Child(); // Risk: message not initialized when callMe() runs
    }
}

⚠️ Special Attention:

  • This is an anti-pattern.

  • Constructors should initialize fields, not call abstract/overridable methods.

🎯 Takeaway:

Yes, it’s allowed — but avoid it. It can cause unpredictable behavior due to incomplete subclass initialization.


🔵 Q14: Can abstract classes have main() methods and be run directly?

💡 Answer:

Yes. An abstract class can have a main() method and be executed just like any other class.

  • You still can’t instantiate it, but you can run static methods, including main().

✅ Example

abstract class Test {
    public static void main(String[] args) {
        System.out.println("Running abstract class main method!");
    }
}

➡️ This compiles and runs fine.

⚡️ Cross-Questions

🔹 If abstract classes can run main, what’s the use case?
➡️ For testing, logging, or providing utility/demo entry points.

🎯 Takeaway:

Abstract classes can’t be instantiated, but they can still contain and run main() methods.


🔵 Q15: Is it possible to use abstraction in enums in Java?

💡 Answer:

Yes. Enums in Java can implement interfaces and declare abstract methods, which each enum constant must implement.

This is a powerful way to use abstraction in enums.

✅ Example

enum Operation {
    ADD {
        public int apply(int x, int y) { return x + y; }
    },
    MULTIPLY {
        public int apply(int x, int y) { return x * y; }
    };

    // abstract method to be implemented by each constant
    public abstract int apply(int x, int y);
}

public class Test {
    public static void main(String[] args) {
        System.out.println(Operation.ADD.apply(2, 3));       // 5
        System.out.println(Operation.MULTIPLY.apply(2, 3)); // 6
    }
}

➡️ Each enum constant (ADD, MULTIPLY) implements the abstract method differently.

⚠️ Special Attention:

Enums can’t extend classes (because they implicitly extend java.lang.Enum), but they can use abstraction via interfaces and abstract methods.

🎯 Takeaway:

Enums are more than constants. They can leverage abstraction by implementing interfaces or defining abstract methods for custom behavior per constant.


🧠 Abstraction in Java – Part 2 Summary

Advanced Interview Questions (4–8 Years Experience)

In this part of the series, we moved beyond the basics and explored advanced and tricky abstraction scenarios that interviewers love to test with mid-level to senior Java developers. These questions highlight modern Java features (Java 8+), pitfalls, and real-world design considerations.

🔍 Here’s what we covered:

  • Whether abstraction can exist without abstract classes or interfaces

  • The deeper distinction between abstraction vs. encapsulation

  • How abstract classes enable partial abstraction

  • Why Java 8 introduced default and static methods in interfaces

  • Multiple inheritance of type via interfaces and the diamond problem

  • Private methods in interfaces (Java 9+) and why protected isn’t allowed

  • Performance considerations of abstraction (dynamic dispatch, memory)

  • Abstract classes implementing interfaces partially

  • Abstract classes skipping interface methods and how responsibility passes to subclasses

  • Interfaces vs abstract classes in functional programming (lambdas)

  • Anonymous inner classes instantiating abstract classes

  • Pitfalls of calling abstract methods inside constructors

  • Abstract classes with a main() method

  • Using abstraction inside enums

These are not just theory — they reflect common real interview patterns on multiple platforms.

🙌 Enjoyed this deep dive?
If this helped strengthen your understanding of abstraction for interviews, don’t forget to share it, bookmark it, or leave a ❤️ to support the series.

Nitin
Hashnode | Substack | LinkedIn | GIT | Youtube | Instagram

0
Subscribe to my newsletter

Read articles from Nitin Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nitin Singh
Nitin Singh

I'm a passionate Software Engineer with over 12 years of experience working with leading MNCs and big tech companies. I specialize in Java, microservices, system design, data structures, problem solving, and distributed systems. Through this blog, I share my learnings, real-world engineering challenges, and insights into building scalable, maintainable backend systems. Whether it’s Java internals, cloud-native architecture, or system design patterns, my goal is to help engineers grow through practical, experience-backed content.