Java Inheritance and the super Keyword
A student account is a member account with additional information—not a second account pasted inside it. Learn how extends, constructor chaining, inherited methods, and super preserve that relationship, then compare it with composition.
Before you start: review constructor delegation, private state and access, and instances and aliases. Every runnable example is a complete Java 21 Main.java program. No standard input or external service is needed. Method overriding and full dynamic-dispatch explanations come in the next lesson.
What you will be able to do
- Read a superclass/subclass relationship and justify its is-a meaning.
- Initialize superclass state with super(...) under Java 21 rules.
- Use inherited behavior without exposing or duplicating private fields.
- Distinguish super(...), super.method(), and field hiding.
- Use a superclass reference without copying or changing the object.
- Choose composition for a has-a relationship and verify shared behavior.
Start with a genuine is-a relationship
A StudentAccount is a kind of MemberAccount in this small library model. It has a member name, a borrowing count, and the same borrow and return rules, with extra campus information. That is the reason for inheritance here—not merely that two classes happen to need a String field.
The shared contract allows zero through three borrowed items. Borrowing at the limit returns false without changing state; returning when empty does the same. A student account must keep those promises wherever a member account is expected. Adding campus metadata does not justify silently weakening the base class's rules.
This is a fictional, single-threaded counter simulation. It does not track book identities, authenticate members, reserve inventory, or persist loans. Its purpose is to make inheritance and construction visible before the next lesson introduces overriding and dynamic dispatch in detail.
extends names the direct superclass
class StudentAccount extends MemberAccount declares MemberAccount as StudentAccount's direct superclass. StudentAccount adds campus while reusing the accessible inherited member methods. Java classes have one direct superclass; you cannot extend two classes in one class declaration.
The student is one object with the superclass's state and the subclass's additional state, not two independently allocated accounts joined by the super keyword. Constructing it initializes the superclass portion before the subclass constructor body continues. The ordinary MemberAccount class can still be instantiated on its own.
A class without an explicit extends clause normally has Object as its superclass. You do not need to write that for every class. final classes cannot be extended; access and constructor availability also constrain whether a proposed subclass can be built.
Main.java · A student account is a member account
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Mina
North
Mina | borrowed: 0 | campus: North
Read the model in plain language
- MemberAccount defines the shared borrowing rules and each instance's name and counter.
- StudentAccount keeps those same promises and adds campus.
- A student is one instance, with its member state initialized by the superclass constructor.
- Another new student gets separate instance state, not a shared counter.
Inherited methods can use private state that the subclass cannot access directly
borrowedCount and memberName are private to MemberAccount's permitted class context. The separate top-level StudentAccount cannot directly read or assign them, and private members are not inherited as accessible subclass members. The student uses the inherited public methods instead.
When student.borrow() executes the MemberAccount implementation, it updates the count on that same student object. The method's location in the superclass does not make its state static. No duplicate borrowedCount field is needed in StudentAccount.
Do not change every private field to protected simply to make subclass code easier to write. Protected also permits same-package access and has special cross-package subclass rules; it is not subclasses-only privacy. Keep the invariant behind methods unless a carefully chosen extension contract really requires more exposure.
Main.java · Reuse inherited behavior without duplicating state
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Borrow: true
Return: true
Return empty: false
Mina | borrowed: 0 | campus: Main
super(...) selects a superclass constructor
StudentAccount(String memberName, String campus) begins with super(memberName). That invokes the matching MemberAccount constructor to validate and store the member name. The subclass then checks campus and assigns its own field. Constructors are not inherited; StudentAccount declares the creation forms it supports.
For the Java 21 baseline used by this course, an explicit super(...) or this(...) invocation must be the first statement of its constructor. A constructor can delegate to another constructor in the same class with this(...), which in turn eventually initializes the superclass. It cannot invoke both explicitly in one body.
If neither explicit invocation is written, Java normally inserts an implicit super(). That only works when an accessible no-argument superclass constructor exists. Our MemberAccount requires a name, so omitting super(memberName) causes a compilation error rather than automatically forwarding the subclass's parameter.
Main.java · Superclass construction completes before the subclass body continues
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Member constructor: Mina
Student constructor: North
Main: ready
Trace the real student's initialization
- The two-argument StudentAccount constructor is selected.
super(memberName)invokes MemberAccount's constructor.- The base validates and stores the name and initializes the count to zero.
- The child resumes, validates campus, and stores it.
- Only after normal completion does the new expression supply its reference to the caller.
Printing in the small trace example is diagnostic only. The actual account constructors do not print or call overridable methods.
Follow validation order and avoid exposing an unfinished object
Superclass construction happens first, so an invalid member name is rejected before campus validation. If the base constructor throws, the subclass's remaining body does not execute. If campus is invalid after a valid name, construction still fails and the new expression does not complete normally for assignment.
The simple trace example prints only to show order; normal model constructors should avoid unnecessary output and external side effects. Do not call overridable instance methods from a constructor to ask the child for information: that can run subclass behavior before its fields have been initialized.
The one-argument StudentAccount constructor delegates to the two-argument form with campus Main. Both routes therefore share the same base initialization and campus validation. No constructor publishes this or creates a second account as a substitute for superclass initialization.
super.method() reuses superclass behavior on the current object
studentSummary calls super.summary(), then appends campus information. super.summary() selects the superclass implementation in this context; it does not ask a separate parent object for a report. That implementation reads the current student's member name and borrowing count.
super(memberName) and super.summary() are different operations. The first is a constructor invocation used during initialization. The second is an instance-method call used later. super cannot be used as a standalone object variable or as a current instance inside static main.
We intentionally give the specialized report a different name in this introductory lesson. studentSummary adds a capability without overriding summary. The next lesson will explain @Override, replacement behavior, and runtime method selection; do not assume a similarly named field or overloaded method is overriding.
Main.java · Build a specialized report from superclass behavior
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Mina | borrowed: 1
Mina | borrowed: 1 | campus: North
A superclass reference does not copy or downgrade the object
MemberAccount member = student is a widening reference assignment. It is allowed because the student is a member account under this model. The assignment copies the reference value, not the object, so calling member.borrow() changes the count later observed through student.
The declared type of member determines which members the compiler lets you name through that reference. getCampus is not declared on MemberAccount, so member.getCampus() does not compile even though this particular object is a StudentAccount. The student reference still exposes that student-specific query.
You do not need a downcast for this lesson. Use the shared member API when that is the capability the caller needs, and retain a more specific reference only where the extra student behavior is part of the contract. Detailed runtime dispatch is the next topic, not a reason to add casts everywhere.
Main.java · A superclass reference still reaches the same object
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Same object: true
Mina | borrowed: 1
North
Try an intentional type error
Add member.getCampus() to the example. It fails compilation because MemberAccount does not declare getCampus. Remove it and use the student reference when campus is the required capability. The assignment to member did not erase campus or change the actual object.
Do not confuse field hiding with overriding behavior
ParentLabel and ChildLabel deliberately declare two fields named label. Inside the child, this.label selects its child field while super.label selects the accessible superclass field. The output shows two stored values, not one field whose value was replaced by a better implementation.
This is a diagnostic example, not the pattern used by our accounts. A child borrowedCount field would leave inherited methods updating the original superclass state while child code might read another counter. Avoid duplicate state with identical names rather than using super qualifiers to maintain two competing truths.
super does not bypass private access. If ParentLabel.label were private, the child's super.label access would fail compilation. Use an appropriate superclass query or behavior method instead of weakening privacy merely to preserve a field-hiding design.
Main.java · Field hiding creates two fields, not one improved field
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
this.label: child
super.label: parent
Use composition when the relationship is has-a
A MemberPortal displays an account, but it is not itself a borrowing account. Making it extend MemberAccount just to reuse summary would incorrectly give the portal account behavior and force it into the wrong construction contract. Composition expresses its real relationship: it has a MemberAccount reference.
The portal constructor receives an account, rejects null, and stores the reference. heading delegates to that account's public summary method. Because this is a shared reference rather than a snapshot, a later borrow on the account is visible in the next portal heading.
Composition is not automatically deep copying or exclusive ownership. Decide who may mutate a referenced object and document that choice. Prefer inheritance when substitutability is meaningful and stable; prefer composition when the goal is using a collaborator without becoming that collaborator's type.
Main.java · A portal has an account; it is not an account
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Portal: Mina | borrowed: 1
Verify the same promises across base and specialized accounts
Create a regular member and two students, then exercise the same borrow/return rules on each. Fill to the three-item limit, reject one more, return exactly to zero, and reject another return. Rejected operations must preserve count, name, and campus metadata.
Also test construction errors, explicit constructor order, a default-campus student, independent instances, and aliases through a base reference. Repeated reports must not mutate state. If you test only the child-specific campus getter, you have not tested the inherited contract.
The final lab combines the shared counter rules, a superclass-typed reference, a super-based specialized report, and a composed portal. Keep all source in one Main.java with separate package-private model classes so it runs in the existing website sandbox. No inheritance hierarchy deeper than these two account levels is needed.
Main.java · Inheriting behavior does not share instance state
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Mina | borrowed: 1 | campus: North
Omar | borrowed: 0 | campus: South
Repair exercise: the named superclass was never initialized
The subclass constructor forgot to initialize the named superclass. This starter intentionally fails compilation because its implicit super() cannot find a no-argument MemberAccount constructor. Add the correct explicit superclass invocation, following the Java 21 first-statement rule.
Main.java · Intentionally missing super argument
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Expected result: a constructor compilation error because MemberAccount cannot be constructed with no arguments. No program output is produced.
Reveal the explained repair
Java does not automatically forward memberName and does not inherit the MemberAccount constructor into StudentAccount. Writing super(memberName) first calls the available constructor for the superclass portion of this same instance. Adding an unrelated default constructor or creating new MemberAccount(memberName) inside the body would not express the required initialization contract.
Main.java · Initialize the superclass explicitly
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Mina | borrowed: 0 | campus: North
Independent lab: specialize an account, compose a portal
Build a member account, a substitutable student account, and a portal that uses composition. Keep inherited state private and follow the same borrowing rules through student and member references. This is a local teaching simulation with no persistence or membership authorization.
- Declare public Main and separate package-private MemberAccount, StudentAccount, and MemberPortal in Main.java. MemberAccount has public static final int MAX_BORROWED = 3, private final String memberName, and private int borrowedCount initially zero.
- Implement MemberAccount(String memberName), rejecting null/blank with IllegalArgumentException and message Member name must not be blank.; strip accepted names. Expose getMemberName, getBorrowedCount, and summary returning <name> | borrowed: <count>.
- Implement borrow and returnOne on MemberAccount. Borrow accepts below the limit; returnOne accepts only above zero. Return false without mutation when rejected and true after an accepted one-step change. Do not print inside model methods.
- Declare StudentAccount extends MemberAccount with private final String campus. Its two-argument constructor calls super(memberName) first, then rejects null/blank campus with message Campus must not be blank. and stores campus.strip(). Its one-argument constructor delegates to campus Main.
- Expose getCampus and studentSummary on StudentAccount. Build the specialized report with super.summary() + " | campus: " + campus. Do not override summary, duplicate the base counter, or expose private fields. MemberPortal stores a non-null MemberAccount and returns "Portal: " + account.summary(); null rejection message is Account must not be null.
- Follow the sample sequence: student Mina/North borrows four times, returns once through a MemberAccount view, and reports alongside untouched regular member Omar. Print reference identity, the portal heading, and a repeated-query state check. Then test the shared contract on both account types.
Use student Mina at North and regular member Omar. Request four borrows from Mina, then return once through the member reference. Expected output:
Borrow 1: true
Borrow 2: true
Borrow 3: true
Borrow 4: false
Return through member view: true
Mina | borrowed: 2 | campus: North
Regular: Omar | borrowed: 0
Same object: true
Portal: Mina | borrowed: 2
Queries unchanged: true
Main.java · Build the shared and specialized contracts
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Stage 1 hints: finish the base contract
Validate and strip the member name in MemberAccount. Keep count private, initialize zero, and guard borrow at three and return at zero. Test those methods on an ordinary member before adding the student class.
Stage 2 hints: initialize the subclass without duplicating state
Call super(memberName) first in the two-argument student constructor. Validate and strip campus afterward. Delegate the one-argument form with this(memberName, "Main"). Use super.summary() for the specialized report; do not add a second name or borrowing counter.
Stage 3 hints: composition and observations
MemberPortal holds a non-null account reference and delegates a query. Main keeps both student and member-typed references to the same student, compares their identity, and verifies the unchanged regular member. No cast or overridden method is needed.
Reveal the complete explained solution
Main.java · Complete account inheritance and portal composition
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
The student follows the member's three-item limit: the fourth borrow is rejected. memberView reaches the same student object, so its successful return leaves count two. The regular member remains at zero. studentSummary adds campus to the base report, while the composed portal uses the shared member API. Repeated queries leave the count unchanged.
Test changes and boundaries
Predict constructor selection, action results, remaining count, and reference relationships before running. Reset restores the original example.
- Test regular and student accounts with zero, one, and three loans. Reject a fourth borrow and a return at zero, preserving all state. Repeat a mixed sequence through a base reference.
- Reject null, empty, and blank member names and campuses. Verify member-name validation happens first when both inputs are invalid. The one-argument student uses campus Main and still validates its name.
- Create two independent students, plus a MemberAccount alias of one. Verify receiver-specific count changes and unchanged metadata. Base-reference assignment must not create or copy an account.
- Trace superclass-before-subclass constructor output. Try omitting super(memberName), reading the private counter directly from the child, or calling getCampus through a MemberAccount reference: each must fail compilation for its own reason.
- Test the composed portal with a regular account and a student, reject null, and observe an account change through a later heading. Confirm every report is read-only; use the portal for a has-a relationship rather than extending the account just to reuse text.
Common mistakes and how to diagnose them
Assuming constructors are inherited
The subclass must declare its own supported constructors and initialize an accessible superclass constructor. Without an explicit invocation, implicit super() only works when an accessible no-argument constructor exists.
Creating a new parent instead of initializing the superclass
new MemberAccount(name) allocates a separate account. super(name) initializes the superclass portion of the student being constructed. Those expressions do not solve the same problem.
Duplicating the private counter in the child
A second borrowedCount would be a different field. Inherited methods would still use the superclass's original state. Reuse the public contract instead of creating conflicting counters.
Treating super as an access-control bypass
super cannot make a private field accessible to a separate subclass. Use a deliberate superclass query or operation; do not make every field protected just to avoid compiler errors.
Expecting child-only methods on a base-typed reference
MemberAccount member = student retains the same object, but the compiler exposes members from the declared reference type. getCampus is not part of MemberAccount's API.
Using inheritance only to borrow a helper method
A portal is not a borrowing account. Composition lets it use a member account without pretending to satisfy the account's type and construction contract. Choose the relationship before the syntax.
Frequently asked questions
Does extends copy a parent object?
No. A newly created student is one instance with superclass state and student-specific state. The superclass constructor participates in initializing that same instance; it does not create a separate member account for the child to own.
Can a subclass directly access private superclass fields?
Not in these separate top-level classes. Inherited public methods can operate on the superclass's private state, but the child cannot directly name or assign that state merely because it extends the class.
Is super(...) the same as super.summary()?
No. super(...) invokes a superclass constructor during initialization. super.summary() invokes superclass behavior on the current instance later. Neither expression is a second parent object reference.
Can a class extend more than one class?
Java permits one direct superclass for a class. Interfaces later provide another way to express shared behavior contracts; do not try to list two superclass names after extends.
When is composition clearer?
When one object uses or contains a collaborator without being that collaborator's kind. The portal has an account reference and delegates a query, so it does not need to extend the account or inherit borrowing behavior.
A relationship should preserve promises, not merely reuse code
extends creates a subtype relationship. super(...) initializes superclass state on the same instance, inherited methods keep private invariants intact, and super.method() reuses superclass behavior. A base reference retains the object while exposing the shared declared API.
Before moving on, explain why constructors are not inherited, why hiding a counter breaks the model, and why the portal uses composition. Next: polymorphism and method overriding, where you will learn how one shared method call can select different implementations at runtime.
Can you justify the relationship and trace its construction?
Answer all six questions and read the explanations. Answers are temporary on this page and do not save course progress.
References and related learning
The account policy, scenarios, examples, and exercises are original SovranCode material. This is an in-memory teaching model. References support the Java 21 inheritance and constructor rules used here.
- Java 21 specification: superclasses and subclasses
- Java 21 specification: explicit constructor invocations
- Java 21 specification: superclass member access
Review constructor delegation · Review access boundaries · Review a complete class model · Java course roadmap.
Last updated: