Understanding Inheritance in Java: Syntax, Types, and the Diamond Problem
-
Last Updated: September 11, 2025
-
By: javahandson
-
Series
Understanding inheritance in Java with syntax, types, constructor chaining, the super keyword, and the diamond problem explained with simple examples.
Inheritance is one of the core concepts of Object-Oriented Programming (OOP). Inheritance allows a new class to acquire the properties and behaviors, i.e., fields and methods, of an existing class. The existing class is called the Parent or Super class, whereas the new class is called a Child or Sub class.
In simple terms, inheritance lets us create a new class based on an existing class, promoting code reuse and establishing a natural IS-A relationship.
Ex. A Student is a parent type that represents anyone who studies. A Student has a name and may have a roll number. From this parent type, we can have more specific types of students, like School Students and College Students.
A School Student still studies and has a name, but might also wear a school uniform, carry a lunch box, and attend daily classes in a fixed classroom. On the other hand, a College Student also studies and has a name, but might attend lecture halls, do internships, or participate in college festivals.
This is called inheritance. The idea is that both School Students and College Students inherit common features of the Student class (like studying and having a name) but also have their own special features that make them unique. This helps us avoid repeating the same details again and again and keeps everything well-organized.
Inheritance is mainly used in Java for these reasons:
1. Code Reuse – We don’t have to rewrite a common piece of code again and again. The child class automatically gets the fields and methods of the parent class, saving effort and reducing duplication.
2. Readability & Simplicity – It keeps code clean and logical. Relationships like Car IS-A Vehicle make programs easier to understand.
3. Maintainability & Extensibility – We can fix or enhance common functionality in the parent class, and all subclasses automatically benefit from it. Adding new features becomes easier, as we can extend existing classes without disturbing tested code.
Inheritance is done using the ‘extends’ keyword. This allows a child class to inherit the fields and methods of an existing parent class.
class Parent {
// fields and methods
}
class Child extends Parent {
// additional fields and methods
}
So the Child class inherits everything that the Parent has. This means Child can use the variables and methods of Parent as if they were its own, but the Child class can also have its own extra features.
package com.javahandson;
class Student {
String name;
void study() {
System.out.println("Student studies");
}
}
class SchoolStudent extends Student {
void getLunchBox() {
System.out.println("School student gets the lunch box");
}
}
class CollegeStudent extends Student {
void getLunchFromCanteen() {
System.out.println("College student gets the lunch from the canteen");
}
}
public class Main {
public static void main(String[] args) {
SchoolStudent schoolStudent = new SchoolStudent();
schoolStudent.name = "Suraj";
System.out.println(schoolStudent.name + " is a School student");
schoolStudent.study();
schoolStudent.getLunchBox();
System.out.println();
CollegeStudent collegeStudent = new CollegeStudent();
collegeStudent.name = "Shweta";
System.out.println(collegeStudent.name + " is a College student");
collegeStudent.study();
collegeStudent.getLunchFromCanteen();
}
}
Output:
Suraj is a School student
Student studies
School student gets the lunch box
Shweta is a College student
Student studies
College student gets the lunch from the canteen
In the above example, SchoolStudent automatically inherits the name and #study() method from School, and it also has its own #getLunchBox() method. Additionally, CollegeStudent automatically inherits the name and #study() method from School, and it also has its own #getLunchFromCanteen() method.
Inheritance can be classified into different types based on how classes are connected in a hierarchy. These types describe how many levels or branches of classes we have.
A subclass inherits from one superclass.
Ex. CollegeStudent inherits from Student. So CollegeStudent gets all the features of Student, plus it can have its own.
package com.javahandson;
class Student {
String name;
void study() {
System.out.println("Student studies");
}
}
class CollegeStudent extends Student {
void getLunchFromCanteen() {
System.out.println("College student gets the lunch from the canteen");
}
}
It’s a chain of inheritance, where a class inherits from another class, which in turn inherits from another class.
Ex. Student → CollegeStudent → EngineeringStudent
package com.javahandson;
class Student {
String name;
void study() {
System.out.println("Student studies");
}
}
class CollegeStudent extends Student {
void getLunchFromCanteen() {
System.out.println("College student gets the lunch from the canteen");
}
}
class EngineeringStudent extends CollegeStudent {
void takePlacementTraining() {
System.out.println("Engineering student has to take the placement training");
}
}
public class Main {
public static void main(String[] args) {
EngineeringStudent engineeringStudent = new EngineeringStudent();
engineeringStudent.name = "Shweta";
System.out.println(engineeringStudent.name + " is a College student");
engineeringStudent.study();
engineeringStudent.getLunchFromCanteen();
engineeringStudent.takePlacementTraining();
}
}
Output:
Shweta is a College student
Student studies
College student gets the lunch from the canteen
Engineering student has to take the placement training
In the above example, EngineeringStudent inherits features from CollegeStudent, which in turn inherits from Student.
Multiple classes inherit from the same parent class.
Ex. SchoolStudent and CollegeStudent both inherit from Student.
package com.javahandson;
class Student {
String name;
void study() {
System.out.println("Student studies");
}
}
class SchoolStudent extends Student {
void getLunchBox() {
System.out.println("School student gets the lunch box");
}
}
class CollegeStudent extends Student {
void getLunchFromCanteen() {
System.out.println("College student gets the lunch from the canteen");
}
}
In the above example, SchoolStudent and CollegeStudent share common Student features but also have their own unique behavior.
Multiple inheritance means a class can inherit from more than one class, but Java does not support this with classes to avoid ambiguity (like the diamond problem).
But Java allows multiple inheritance through interfaces.
package com.javahandson;
class Cricketer {
void playCricket() {
System.out.println("Playing cricket");
}
}
class Artist {
void draw() {
System.out.println("Drawing art");
}
}
// This will cause a compile-time error in Java
class MultiTalentedStudent extends Cricketer, Artist {
void showcaseTalent() {
System.out.println("Showing cricket and art skills");
}
}
The diamond problem is a classic issue in multiple inheritance. It happens when a class inherits from two classes that both inherit from the same superclass. This creates a diamond-shaped inheritance structure, leading to ambiguity about which path to follow to inherit shared members.

package com.javahandson;
class Student {
String name;
void study() {
System.out.println("Student studies");
}
}
class Cricketer extends Student {
void study() {
System.out.println("Playing cricket");
}
}
class Artist extends Student {
void study() {
System.out.println("Drawing art");
}
}
// This will cause a compile-time error in Java
class MultiTalentedStudent extends Cricketer, Artist {
void showcaseTalent() {
study();
}
}
public class Main {
public static void main(String[] args) {
MultiTalentedStudent multiTalentedStudent = new MultiTalentedStudent();
multiTalentedStudent.showcaseTalent();
}
}
The real confusion (diamond problem) generally comes up if both intermediate classes (Cricketer and Artist) override the same method #study() from Student class.
It’s ambiguous: Should it print “Playing cricket” or “Drawing art” or “Student studies”.
If Cricketer and Artist do NOT override #study()?
Then there’s no conflict, because both just inherit study() from Student without changes.
So there’s only one version of study(), from Student.
Still, languages like Java avoid multiple inheritance with classes altogether, to prevent future risks – because if someone later modifies Cricketer or Artist to override study(), the ambiguity appears.
Java allows multiple inheritance through interfaces. Interfaces only define method signatures (no conflicting implementation), so the compiler knows exactly what to do, and there’s no ambiguity.
package com.javahandson;
interface Cricketer {
void playCricket();
}
interface Artist {
void draw();
}
class MultiTalentedStudent implements Cricketer, Artist {
public void playCricket() {
System.out.println("Playing cricket");
}
public void draw() {
System.out.println("Drawing art");
}
void showcaseTalent() {
playCricket();
draw();
}
}
public class Main {
public static void main(String[] args) {
MultiTalentedStudent multiTalentedStudent = new MultiTalentedStudent();
multiTalentedStudent.showcaseTalent();
}
}
Output:
Playing cricket
Drawing art
Now there is no confusion – because MultiTalentedStudent provides its own implementation for each behavior. Java solves the diamond problem by disallowing multiple class inheritance. It uses interfaces to support multiple inheritance of types, forcing the class to define exactly what it wants, avoiding ambiguity.
The super keyword in Java is used to refer to the immediate parent class.
1. If the subclass overrides a method, we can use super.methodName() to call the parent class’s original version of the method.
2. If the subclass has a field with the same name as the parent, we can use super.fieldName to access the parent class’s field.
This is helpful to avoid confusion when both subclass and parent have methods or variables with the same name.
package com.javahandson;
class Student {
String name;
int rollNumber = 101;
void study() {
System.out.println("Super class : Student studies");
}
}
class SchoolStudent extends Student {
int rollNumber = 102;
void studentDetails() {
// Since we don't have a name field in Subclass hence we can use it without super keyword
System.out.println("Student name is : "+ name);
// Since rollNumber field name is same in Subclass hence we have to use super rollNumber to call the Superclass rollNumber field
System.out.println(name + " previous roll number was : "+ super.rollNumber);
System.out.println(name + " current roll number was : "+ rollNumber);
}
void study() {
super.study(); // Super class study method is called
System.out.println("Sub class : Student studies");
}
}
public class Main {
public static void main(String[] args) {
SchoolStudent schoolStudent = new SchoolStudent();
schoolStudent.name = "Suraj";
schoolStudent.studentDetails();
schoolStudent.study();
}
}
Output:
Student name is : Suraj
Suraj previous roll number was : 101
Suraj current roll number was : 102
Super class : Student studies
Sub class : Student studies
We can use super() to call the parent class’s constructor. This is useful when the parent class needs to initialize some data. By default, Java automatically calls super() if we don’t write it but this is only applicable if the parent has a no-argument constructor.
If the parent has parameterized constructors then we must call super(arguments) explicitly.
Ex. No argument constructor
package com.javahandson;
class Student {
Student() {
System.out.println("Superclass : Student constructor");
}
}
class SchoolStudent extends Student {
SchoolStudent() {
System.out.println("Subclass : SchoolStudent constructor");
}
}
public class Main {
public static void main(String[] args) {
SchoolStudent schoolStudent = new SchoolStudent();
}
}
Output:
Superclass : Student constructor
Subclass : SchoolStudent constructor
Ex. Parameterized constructor
package com.javahandson;
class Student {
String name;
Student(String name) {
System.out.println("Superclass : Student constructor : "+ name);
}
}
class SchoolStudent extends Student {
SchoolStudent(String name) {
super(name);
System.out.println("Subclass : SchoolStudent constructor");
}
}
public class Main {
public static void main(String[] args) {
SchoolStudent schoolStudent = new SchoolStudent("Suraj");
}
}
Output:
Superclass : Student constructor : Suraj
Subclass : SchoolStudent constructor
When we use super() to call the parent class constructor, it must always be the first line inside the subclass constructor. If we try to write anything before super(), the compiler will give an error.
As we seen in the above examples when we create an object of a subclass, the no-argument parent class constructor is automatically called first, even if we don’t explicitly write super(). This process is called constructor chaining.
Every constructor in a subclass implicitly calls the no-argument constructor of its immediate parent class using super(). This ensures that the parent’s part of the object is initialized before the child’s part.
Constructors run from the top of the hierarchy down.
That means: Parent constructor → Child constructor → Grandchild constructor…
package com.javahandson;
class Student {
Student() {
System.out.println("Student constructor");
}
}
class CollegeStudent extends Student {
CollegeStudent() {
System.out.println("CollegeStudent constructor");
}
}
class EngineeringStudent extends CollegeStudent {
EngineeringStudent() {
System.out.println("EngineeringStudent constructor");
}
}
public class Main {
public static void main(String[] args) {
EngineeringStudent eng = new EngineeringStudent();
}
}
Output:
Student constructor
CollegeStudent constructor
EngineeringStudent constructor
Java ensures that when we create a subclass object, all parent constructors execute first, building the object from top to bottom. This maintains proper initialization of the entire object chain.
Inheritance is a key concept in Java that enables code reuse and logical class hierarchies. In this article, we explored the basics: what inheritance is, why it’s useful, how to write it using the extends keyword, and the different types supported by Java. We also touched upon constructor chaining and how Java handles the diamond problem using single-class inheritance and interfaces. Understanding these foundations is essential before diving into more advanced inheritance topics, which we’ll cover in the next part.