Inheritance
1. What is inheritance in Java?
Inheritance is one of the fundamental principles of Object-Oriented Programming (OOP) in Java. It's a mechanism where a new class, known as a subclass (or child class), acquires the properties (variables) and behaviors (methods) of an existing class, known as a superclass (or parent class).
The extends
keyword is used in Java to indicate that a class is inheriting from another. This process allows for the creation of hierarchical classifications, where a more specific class inherits traits from a more general class.
A subclass is essentially a specialized version of its superclass and represents an "is-a" relationship (e.g., a Dog
"is an" Animal
).
a Car
is-a Vehicle
. The Car
class can inherit common attributes like speed
and color
from the Vehicle
class, while also adding its own unique features, like numberOfDoors
.
2. What are the advantages of using inheritance?
Inheritance provides several key advantages in software development:
Logical Structure and Readability: It allows to create a clear and logical hierarchy of classes that mirrors real-world relationships, making code more organized and easier to understand.
Code Reusability: This is the primary benefit. You can define common attributes and methods in a single superclass and reuse them across multiple subclasses, avoiding code duplication and making maintenance easier.
Method Overriding (Runtime Polymorphism): Inheritance allows a subclass to provide a specific implementation for a method that is already defined in its superclass. This enables dynamic and flexible behavior where the same method call can execute different code depending on the object's actual type.
3. Provide an example of inheritance in Java.
This example shows a Dog
class inheriting from an Animal
class. The Dog
object can use both its own bark()
method and the eat()
method inherited from Animal
.
// Superclass
class Animal {
public void eat() {
System.out.println("This animal eats food.");
}
}
// Subclass that extends Animal
class Dog extends Animal {
public void bark() {
System.out.println("The dog barks: Woof!");
}
}
// Demo class to run the example
public class InheritanceDemo {
public static void main(String[] args) {
// Create an object of the subclass
Dog myDog = new Dog();
// Call the method from the subclass
myDog.bark();
// Call the method inherited from the superclass
myDog.eat();
}
}
Output:
The dog barks: Woof!
This animal eats food.
4. Illustrate the effect of access modifiers on inheritance.
Inheritance interacts with Java's access control mechanisms. Access modifiers like public
, private
, and protected
, along with default access (when no modifier is used), determine the visibility of class members.
Default Access:
- When a class member (variable or method) has no explicit access modifier, it is considered to have default access.
- A member with default access is visible within its package but not outside its package.
- In the context of inheritance, if a subclass and its superclass are in the same package, the subclass can directly access the default members of its superclass.
Private Access:
- A member declared as private is the most restrictive; it is accessible only by other members of its own class.
- Inheriting a class does not grant access to its private members. The private access restriction is not overruled by inheritance.
- To allow code outside the superclass (including subclasses) to interact with private members, the superclass must provide public (or other suitably accessible) accessor methods.
class Parent {
public String publicMessage = "This is public.";
String defaultMessage = "This is default (package-private).";
private String privateMessage = "This is private.";
// A public method to access the private member
public void displayPrivateMessage() {
System.out.println("Parent accessing its private member: "
+ privateMessage);
}
}
// Subclass in the same package
class Child extends Parent {
public void accessMembers() {
System.out.println("--- Accessing members from Child class ---");
// OK: public members are always inherited.
System.out.println("Accessing publicMessage: "
+ publicMessage);
// OK: default members are inherited by subclasses in the same package.
System.out.println("Accessing defaultMessage: "
+ defaultMessage);
// ERROR: private members are NOT inherited and cannot be accessed directly.
System.out.println(privateMessage);
System.out.println("Cannot access privateMessage directly.");
}
}
public class AccessModifierDemo {
public static void main(String[] args) {
Child myChild = new Child();
// Demonstrate what the child can and cannot access
myChild.accessMembers();
// public method inherited from the parent,
// which in turn can access the private member.
myChild.displayPrivateMessage();
}
}
Output:
--- Accessing members from Child class ---
Accessing publicMessage: This is public.
Accessing defaultMessage: This is default (package-private).
Cannot access privateMessage directly.
Parent accessing its private member: This is private.
Super Keyword
1. Describe the use of the super
keyword in the context of inheritance. Provide an example.
The super
keyword in Java is a reference variable used to refer to the immediate parent class object. It's used in two main ways:
super
keyword is used to access members of the superclass (fields or methods) that have been hidden or overridden by the subclass.super()
method is used to invoke the constructor of superclass from the subclass's constructor.
Dog
class using super
to call the display()
method of its Animal
superclass.
// Superclass
class Animal {
public void display() {
System.out.println("I am an animal.");
}
}
// Subclass
class Dog extends Animal {
@Override
public void display() {
System.out.println("I am a dog.");
}
public void printMessage() {
// display() method of Dog class
display();
// display() method of the Animal class
super.display();
}
}
public class SuperKeywordDemo {
public static void main(String[] args) {
Dog myDog = new Dog();
myDog.printMessage();
}
}
Output:
I am a dog.
I am an animal.
Invoking a Superclass Constructor with super()
Calling super()
from a subclass constructor invokes a specific constructor of its immediate superclass. This is used to initialize the superclass part of the object with specific values.
Note: The super()
call, if used, must be the very first statement in the subclass constructor.
Manager
subclass using super()
to pass an id
to the Employee
superclass constructor.
// Superclass
class Employee {
int id;
Employee(int id) {
this.id = id;
System.out.println("Employee constructor called with ID: " + id);
}
}
// Subclass
class Manager extends Employee {
String department;
Manager(int id, String department) {
// Call the superclass constructor and pass the id
super(id);
this.department = department;
System.out.println("Manager constructor called.");
}
}
public class SuperConstructorDemo {
public static void main(String[] args) {
Manager mgr = new Manager(101, "Sales");
System.out.println("Manager ID: " + mgr.id);
System.out.println("Manager Dept: " + mgr.department);
}
}
Output:
Employee constructor called with ID: 101
Manager constructor called.
Manager ID: 101
Manager Dept: Sales
Order of Constructor Execution
In a multilevel hierarchy, constructors are always executed from the top down. The constructor of the top-most superclass is executed first, followed by the constructor of its immediate subclass, and so on, down to the constructor of the final subclass.
This happens because every subclass constructor implicitly or explicitly calls super()
as its first statement, creating a chain of calls up to the Object
class.
This example uses a three-level hierarchy (A
-> B
-> C
) to demonstrate the execution order.
// Top-level superclass
class A {
A() {
System.out.println("Inside A's constructor.");
}
}
// Subclass of A
class B extends A {
B() {
// Java implicitly adds super() here if you don't.
System.out.println("Inside B's constructor.");
}
}
// Subclass of B
class C extends B {
C() {
System.out.println("Inside C's constructor.");
}
}
public class ConstructorOrderDemo {
public static void main(String[] args) {
System.out.println("Creating an object of class C...");
C c = new C();
}
}
When an object of class C is created, the constructor for A runs first, then B, and finally C.
Creating an object of class C...
Inside A's constructor.
Inside B's constructor.
Inside C's constructor.