Java Question Bank with Answers
Answers for Core Java Question Bank –
Chapter 3: Inheritance and Interfaces
Moderate Level Questions
8. Explain the concept of "Types of Inheritance" supported by Java. Discuss why multiple inheritance of classes is not allowed in Java.
Answer:
Types of Inheritance
Supported by Java:
Java primarily supports the following types of inheritance among classes:
1. Single Inheritance: A class inherits from only one superclass. This is the simplest and most common form of inheritance.
o Example:
class
B extends A {}
2. Multilevel Inheritance: A class inherits from a class, which in turn inherits from another class. There is a chain of inheritance.
o Example:
class
C extends B {} where B extends A {}
3. Hierarchical Inheritance: Multiple subclasses inherit from a single superclass.
o Example:
class
B extends A {} and class C extends A {}
Java does NOT directly support:
1. Multiple Inheritance of Classes: This means a class cannot extend more than one class directly.
o Example:
class
C extends A, B {}.
2. Hybrid Inheritance: This is a combination of two or more types of inheritance, often involving multiple inheritance. Since multiple inheritance of classes is disallowed, hybrid inheritance using classes is also not supported directly.
Why Multiple Inheritance of Classes is Not
Allowed in Java:
Java disallows multiple inheritance of classes primarily to avoid the "Diamond Problem"
and to maintain simplicity and clarity in the language design .
·
The Diamond Problem: This
problem arises when a class D inherits from two classes B and C, and
both B and C
inherit from a common class A. If there is a method m()
defined in A that
is overridden in both B and C, and D calls
m(), the
compiler faces ambiguity: Which version of m() (from B or C)
should D
inherit and execute?
·A
·/ \
·B C
·\ /
D
This ambiguity makes the inheritance hierarchy complex and difficult to manage and debug.
· Design Simplicity: Java’s designers prioritized simplicity and robustness. Eliminating multiple inheritance avoids complex scenarios related to method resolution, state management (which inherited field to use if names clash), and constructor chaining, which could lead to fragile code.
·
Achieving Similar Functionality with
Interfaces: Java achieves the benefits of multiple
inheritance (primarily inheriting multiple types and behaviors) through interfaces. A class can implement
multiple interfaces, thereby inheriting multiple contracts without inheriting
state or implementation details . This allows for flexible design without the
complexities of the Diamond Problem.
9.
Write a Java program to demonstrate single-level inheritance. Create a Shape class with a method display(), and a Circle class that extends Shape and adds a radius attribute.
Answer:
// Superclass: Shapeclass Shape { String color; // Constructor for Shape public Shape(String color) { this.color = color; } // Method to display basic shape information public void display() { System.out.println("This is a " + color + " shape."); }} // Subclass: Circle, extends Shapeclass Circle extends Shape { double radius; // Constructor for Circle // Calls the superclass constructor using super() public Circle(String color, double radius) { super(color); // Must be the first statement in the constructor this.radius = radius; } // Method specific to Circle public double calculateArea() { return Math.PI * radius * radius; } // Overriding the display method from Shape to add Circle-specific details @Override public void display() { super.display(); // Call the superclass's display method System.out.println("It is a Circle with radius: " + radius); System.out.printf("Area of the Circle: %.2f%n", calculateArea()); }} public class SingleInheritanceDemo { public static void main(String[] args) { // Create an object of the Superclass Shape genericShape = new Shape("Blue"); System.out.println("--- Generic Shape ---"); genericShape.display(); System.out.println(); // Create an object of the Subclass Circle myCircle = new Circle("Red", 5.0); System.out.println("--- My Circle ---"); myCircle.display(); // Calls the overridden display method in Circle System.out.println(); // Accessing inherited members System.out.println("My circle's color (accessed from inherited field): " + myCircle.color); }}
10.
Describe the significance of the super keyword in two
distinct contexts: calling a superclass constructor and accessing a superclass
member. Provide Java code examples for both.
Answer:
The super
keyword in Java is a powerful reference to the immediate parent class object,
crucial for managing inheritance relationships.
1. Invoking a Superclass Constructor (super()):
·
Significance: When
a subclass object is created, the constructor of its superclass is implicitly
invoked to initialize the inherited parts of the object. If the superclass has
a parameterized constructor, or if you want to explicitly call a specific
superclass constructor, you must use super() (or super(arguments)) as
the first statement
within the subclass constructor . This ensures that the superclass’s
initialization logic is executed before the subclass’s own initialization. If super() is
not explicitly called, the compiler automatically inserts a call to the
superclass’s no-argument constructor.
· Code Example:
// Superclass: Vehicleclass Vehicle { String make; int year; // Superclass parameterized constructor public Vehicle(String make, int year) { this.make = make; this.year = year; System.out.println("Vehicle constructor: " + make + ", " + year); }} // Subclass: Carclass Car extends Vehicle { String model; // Subclass constructor public Car(String make, int year, String model) { super(make, year); // Invokes the superclass constructor this.model = model; System.out.println("Car constructor: " + model); } public static void main(String[] args) { Car myCar = new Car("Toyota", 2023, "Camry"); // Output: // Vehicle constructor: Toyota, 2023 // Car constructor: Camry }}
2. Accessing a Superclass Member (super.member or super.method()):
·
Significance: This
form of super is
used to explicitly refer to a member (field or method) of the immediate
superclass when that member has been shadowed
(for fields) or overridden
(for methods) in the subclass . It allows the subclass to still utilize or
extend the superclass’s implementation while having its own specific version.
· Code Example:
// Superclass: Parentclass Parent { String message = "Hello from Parent!"; void displayMessage() { System.out.println("Parent says: " + message); }} // Subclass: Childclass Child extends Parent { String message = "Hello from Child!"; // Shadows the parent's message field @Override void displayMessage() { // Overrides the parent's displayMessage method System.out.println("Child says: " + message); // Refers to Child's message System.out.println("Parent's message via super: " + super.message); // Accesses Parent's message super.displayMessage(); // Calls the overridden method in Parent } public static void main(String[] args) { Child myChild = new Child(); myChild.displayMessage(); // Expected Output: // Child says: Hello from Child! // Parent's message via super: Hello from Parent! // Parent says: Hello from Parent! }}
11. Explain "Method Overriding" and "Runtime Polymorphism" in Java. Provide a code example demonstrating how runtime polymorphism is achieved through method overriding.
Answer:
Method Overriding:
Method overriding occurs when a subclass provides a specific implementation for
a method that is already defined (with the same signature) in its superclass .
The subclass’s version of the method replaces the superclass’s version when
called on an object of the subclass. This allows specialized behavior in
subclasses while maintaining a common method signature.
Runtime Polymorphism:
Runtime polymorphism, also known as dynamic method dispatch, is the ability of
an object to take on many forms. In Java, it’s achieved through method
overriding. When an overridden method is called through a reference variable of
a superclass, the method call is resolved at runtime (not compile-time) based
on the actual type of the object being referred to, not the type of the
reference variable . This means the specific version of the overridden method
in the subclass is executed.
Code Example Demonstrating Runtime Polymorphism:
// Superclassclass Animal { void makeSound() { System.out.println("Animal makes a sound."); }} // Subclass 1class Dog extends Animal { @Override void makeSound() { System.out.println("Dog barks: Woof! Woof!"); }} // Subclass 2class Cat extends Animal { @Override void makeSound() { System.out.println("Cat meows: Meow."); }} public class RuntimePolymorphismDemo { public static void main(String[] args) { // Superclass reference variable Animal myAnimal; // Assign a Dog object to the Animal reference myAnimal = new Dog(); System.out.print("First animal says: "); myAnimal.makeSound(); // Calls Dog's makeSound() at runtime // Assign a Cat object to the Animal reference myAnimal = new Cat(); System.out.print("Second animal says: "); myAnimal.makeSound(); // Calls Cat's makeSound() at runtime // Assign an Animal object to the Animal reference myAnimal = new Animal(); System.out.print("Third animal says: "); myAnimal.makeSound(); // Calls Animal's makeSound() at runtime System.out.println("\n--- Demonstrating with an Array of Animals ---"); Animal[] farmAnimals = { new Dog(), new Cat(), new Animal() }; for (Animal animal : farmAnimals) { System.out.print("An animal in the array says: "); animal.makeSound(); // Polymorphic behavior: calls the appropriate makeSound() } }}
In this example, the myAnimal
reference variable is of type Animal (superclass). However,
when myAnimal.makeSound() is
called, the actual method that gets executed depends on the actual object assigned to myAnimal at
that moment (Dog, Cat, or Animal).
This dynamic decision-making at runtime is the essence of runtime polymorphism.
12.
Discuss the implications of using the final keyword with a class.
When would it be appropriate to declare a class as final?
Answer:
When the final
keyword is applied to a class in Java, it means that the class cannot be extended by
any other class . In other words, a final class cannot have any
subclasses.
Implications of using final with
a class:
1. Prevents Inheritance: The most direct implication is that inheritance is completely disallowed for that class. This means its methods cannot be overridden, and its state cannot be inherited and modified by subclasses.
2. Increased Security: By preventing a class from being subclassed, you can guarantee that its behavior cannot be altered or compromised by malicious or unintended subclassing. This is particularly important for classes that manage sensitive data or perform critical operations.
3. Ensures
Immutability: final classes are often used to
create immutable objects.
If an object of a final class
is also designed with final fields and no setter methods,
its state cannot be changed after creation, making it inherently thread-safe
and predictable. String is a
classic example of a final and immutable class.
4. Simpler Design: It simplifies the design and understanding of the class, as there’s no need to consider the implications of subclassing or method overriding.
When it would be appropriate to declare a
class as final:
1. For
Immutable Classes: When you design a class whose instances
are intended to be immutable (their state cannot change after creation), like String, Integer, or Double.
Immutability provides many benefits, including thread safety and ease of
reasoning.
2. Security-Sensitive Classes: For classes that encapsulate critical logic or security-related operations (e.g., encryption algorithms, authentication handlers) that should not be modified or bypassed by extensions.
3. Utility
Classes with static Methods (less common, but
possible): While most utility classes simply declare
their constructors as private to prevent instantiation,
making them final also
prevents extension.
4. Performance
Optimization: Similar to final
methods, final
classes sometimes allow the JVM to perform minor optimizations, as it knows the
class hierarchy cannot change. However, this is rarely the primary reason.
5. Preventing API Changes: When you want to ensure that a core component of your API or framework has a fixed behavior that cannot be altered by developers extending your code.
13. Design an abstract class Vehicle with
an abstract method start().
Create two concrete subclasses, Car and Motorcycle, that
implement the start() method differently.
Answer:
// Abstract Superclass: Vehicleabstract class Vehicle { String brand; int year; // Constructor public Vehicle(String brand, int year) { this.brand = brand; this.year = year; } // Abstract method: must be implemented by concrete subclasses public abstract void start(); // Concrete method: implemented in the abstract class public void displayInfo() { System.out.println("Brand: " + brand + ", Year: " + year); }} // Concrete Subclass 1: Carclass Car extends Vehicle { int numberOfDoors; public Car(String brand, int year, int numberOfDoors) { super(brand, year); this.numberOfDoors = numberOfDoors; } // Implementation of abstract method @Override public void start() { System.out.println("The " + year + " " + brand + " Car with " + numberOfDoors + " doors starts with a key ignition."); }} // Concrete Subclass 2: Motorcycleclass Motorcycle extends Vehicle { boolean hasSidecar; public Motorcycle(String brand, int year, boolean hasSidecar) { super(brand, year); this.hasSidecar = hasSidecar; } // Implementation of abstract method @Override public void start() { String sidecarStatus = hasSidecar ? "with sidecar" : "without sidecar"; System.out.println("The " + year + " " + brand + " Motorcycle " + sidecarStatus + " starts by kickstarting or electric ignition."); }} // Demo classpublic class AbstractVehicleDemo { public static void main(String[] args) { // Create concrete objects Car myCar = new Car("Honda", 2024, 4); Motorcycle myBike = new Motorcycle("Harley-Davidson", 2023, false); System.out.println("--- Car Details ---"); myCar.displayInfo(); myCar.start(); System.out.println("\n--- Motorcycle Details ---"); myBike.displayInfo(); myBike.start(); // Demonstrating polymorphism with abstract class reference System.out.println("\n--- Polymorphic Behavior ---"); Vehicle v1 = new Car("Mercedes", 2025, 2); Vehicle v2 = new Motorcycle("BMW", 2022, true); v1.start(); // Calls Car's start() v2.start(); // Calls Motorcycle's start() }}
14. Explain the difference between an abstract class and an interface in Java. When would you choose to use an interface over an abstract class?
Answer:
Both abstract classes and interfaces are fundamental tools in Java for
achieving abstraction and polymorphism, but they serve different purposes and
have distinct characteristics.
Differences between Abstract Class and Interface:
|
Feature |
Abstract Class |
Interface |
|
Declaration |
Declared using the |
Declared using the |
|
Type of Methods |
Can have both abstract methods (without body)
and concrete methods (with body) . Can also have |
Until Java 8, all methods were implicitly |
|
Type of Variables |
Can have instance variables, |
All variables are implicitly |
|
Constructors |
Can have constructors. These are used by
subclasses via |
Cannot have constructors. |
|
Inheritance/Implementation |
A class |
A class |
|
Access Modifiers |
Members can have |
All methods are implicitly |
|
Completeness |
Can be partially implemented; it can have defined methods. |
Represents a contract for behavior without
any state or implementation until Java 8 |
|
"Is-A" vs. "Has-A" / "Can Do" |
Best suited for "is-a"
relationships where subclasses are specialized types of the abstract class
(e.g., |
Best suited for "can do"
relationships where classes agree to a certain behavior (e.g., |
When would you choose to use an interface
over an abstract class?
You would typically choose an interface
over an abstract class in the following scenarios:
1. To
Define a Contract for Disparate Classes: When different,
unrelated classes need to share a common behavior, but they don’t share a
common "is-a" hierarchy. For example, Car, Bicycle, and Airplane might
all be Movable, but
they don’t necessarily inherit from a common Vehicle
abstract class. An interface Movable defines the common
behavior.
2. To Support Multiple "Inheritance" of Types: Since Java does not support multiple inheritance of classes, interfaces are the way to achieve polymorphism across multiple behavioral contracts. A class can implement any number of interfaces.
3. To Decouple Implementations from Definitions: When you want to specify what a class should do, without imposing how it should do it, or if you anticipate diverse implementations of the behavior. This promotes loose coupling and flexible architecture.
4. When Designing APIs/Frameworks: Interfaces are excellent for defining an API, allowing framework users to provide their own implementations of the API contracts.
5. When a Class is Already Extending Another Class: Since Java only allows single inheritance for classes, if your class already extends another abstract or concrete class, it can still implement multiple interfaces.
15. Write a Java program to define an
interface Drawable with
a method draw().
Implement this interface in two classes, Circle and Rectangle,
providing their own implementations of the draw()
method.
Answer: // Interface: Drawableinterface Drawable { // Abstract method (implicitly public and abstract) void draw();} // Class 1: Circle implementing Drawableclass Circle implements Drawable { private double radius; private String color; public Circle(double radius, String color) { this.radius = radius; this.color = color; } // Implementation of draw() method @Override public void draw() { System.out.println("Drawing a " + color + " circle with radius " + radius); } public double getRadius() { return radius; }} // Class 2: Rectangle implementing Drawableclass Rectangle implements Drawable { private double length; private double width; private String color; public Rectangle(double length, double width, String color) { this.length = length; this.width = width; this.color = color; } // Implementation of draw() method @Override public void draw() { System.out.println("Drawing a " + color + " rectangle with length " + length + " and width " + width); } public double getArea() { return length * width; }} // Main class to demonstrate interface and polymorphismpublic class InterfaceDemo { public static void main(String[] args) { // Create objects of classes implementing the interface Circle circle1 = new Circle(10.5, "Blue"); Rectangle rect1 = new Rectangle(5.0, 8.0, "Green"); // Call the draw method individually System.out.println("--- Individual Drawing ---"); circle1.draw(); rect1.draw(); // Demonstrating polymorphism using interface reference System.out.println("\n--- Polymorphic Drawing ---"); Drawable[] drawables = { new Circle(7.0, "Red"), new Rectangle(4.0, 6.0, "Yellow") }; for (Drawable item : drawables) { item.draw(); // Calls the appropriate draw() method at runtime } }}
16.
Consider a scenario where a final method is present in a
superclass. Can a subclass override this method? Justify your answer with
respect to Java’s inheritance rules.
Answer:
No, a subclass cannot
override a final method that is present in its
superclass.
Justification with respect to Java’s
inheritance rules:
The final
keyword in Java, when applied to a method, specifically declares that the
method’s implementation is complete and definitive, and it cannot be altered by
any subclass . This is a core rule of Java’s inheritance mechanism.
·
Compiler Enforcement: If a
subclass attempts to provide its own implementation for a final
method inherited from its superclass, the Java compiler will issue a compile-time error. It
explicitly enforces that final methods are not allowed to be
overridden.
·
Purpose: The primary purpose of
marking a method as final is to ensure that its
behavior remains consistent and unchangeable throughout the inheritance
hierarchy . This guarantees that critical algorithms, security checks, or core
business logic cannot be inadvertently or intentionally modified by subclasses.
This consistency is crucial for the predictability and robustness of an
application.
In essence, final
methods are sealed methods, preventing any form of specialization or
modification in derived classes.