final classes and methods
On This Lesson 11 topics
Lesson Overview
The last two lessons showed how easy it is to extend a class and replace its methods. That freedom has a cost: the author of a class cannot know what children will do with it. A child can override a method that a rule depends on, or extend a class that was never designed to be extended.
This lesson introduces final on classes and methods: a way for the author to say "this stops here". In the family tree picture, a final method is a family rule that no child may change, and a final class is a line with no descendants at all.
Why this matters: backend code holds money, identity, and security rules. Some behavior must be the same for every object, whatever the subclass. final is how you guarantee it in Java.
This lesson builds on overriding from "Overriding methods", and on final fields and immutable objects from the previous module.
Learning Objectives
By the end of this lesson you will be able to:
- Declare a method
finalto forbid overriding, and read the compiler error when someone tries. - Declare a class
finalto forbid extending it, and read that error. - Name a library class that is
finaland say why. - Explain why immutable classes should be
final. - Explain why a constructor should not call an overridable method.
- Decide whether a class should be open for extension or closed.
Written Explanation
Three meanings of final
You already know final on a variable or field: it can be assigned once. The same keyword has two more meanings in the world of inheritance:
| Where | What it means |
|---|---|
final field or variable | Assigned once, then never changed |
final method | Cannot be overridden in a subclass |
final class | Cannot be extended by any subclass |
The three share one idea: "this part is settled".
final methods
Put final in the method declaration and children can use the method but never replace it. The Oracle tutorial says that you might make a method final if its implementation should not change and is critical to the consistent state of the object.
class Account {
private double balance;
final void audit() {
System.out.println("audit: balance = " + balance);
}
}A subclass that tries to override it fails to compile:
class CheckingAccount extends Account {
@Override
void audit() {
System.out.println("skipping audit");
}
}Main.java:13: error: audit() in CheckingAccount cannot override audit() in Account
void audit() {
^
overridden method is finalAll children share the exact audit() code. A rule that must hold for every account type, such as an audit step, is a good candidate.
A final method is still inherited and callable: checking.audit() works. Only replacing it is forbidden.
final classes
Put final before class and nobody can extend it:
final class Money {
private final long cents;
Money(long cents) { this.cents = cents; }
Money plus(Money other) { return new Money(cents + other.cents); }
long getCents() { return cents; }
}
class Euro extends Money { ... }Main.java:11: error: cannot inherit from final Money
class Euro extends Money {
^The Java Language Specification (section 8.1.4) says it is a compile-time error if the class named in extends is final, because final classes are not allowed to have subclasses. The tutorial's example of a final class from the standard library is String. I also confirmed it by checking the class modifiers of String in a short program; they are public final.
Why immutable classes should be final
In the previous module you built an immutable Money: final fields, no setters. But immutability is a promise about this class. If it can be extended, a subclass can break the promise. Here Money is not final, and a sneaky subclass keeps its own changeable value:
class Money {
private final long cents;
Money(long cents) { this.cents = cents; }
long getCents() { return cents; }
}
class SneakyMoney extends Money {
private long override;
SneakyMoney(long cents) { super(cents); override = cents; }
void change(long c) { override = c; }
@Override
long getCents() { return override; }
}In main, store a SneakyMoney in a variable of type Money (a subclass object may be used where the parent type is expected; the next module explains it fully), print getCents(), call change(1) on the sneaky object, and print again:
500
1Code that received a "Money" and believed it could never change was wrong. Making Money final, or getCents() final, closes the hole. That is why immutable classes should be final, and why String is.
A constructor must not call an overridable method
Remember the construction order from Lesson 2: the parent part is built first. If a parent constructor calls a method that a child overrides, the override runs before the child's own fields are set:
class Report {
Report() {
System.out.println(title());
}
String title() { return "Report"; }
}
class SalesReport extends Report {
private String quarter = "Q3";
@Override
String title() { return "Sales " + quarter; }
}new SalesReport() prints:
Sales nullquarter was not assigned yet when the parent constructor ran, so the override saw null. The tutorial gives the same advice: methods called from constructors should generally be declared final, because a subclass could redefine them with surprising results. Two fixes: declare title() final in Report (then it prints Report), or do not call overridable methods from constructors.
Open or closed: a design decision
Inheritance is a promise to your future children, and you may not want to keep it. A common rule among Java developers is "design and document for inheritance, or else prohibit it". Reasons to leave a class open:
- It is meant as a base class and its methods are documented as extension points.
Reasons to close it with final:
- It is a value or security-critical class (
Money, identifiers, passwords). - It is immutable.
- You have not thought about how it would be extended. Closing is safe: you can remove
finallater without breaking any user, but you cannot add it after others have already extended the class without breaking them.
Java has further tools for fine control over who may extend a class (such as sealed classes, which appear in the language specification), but this course does not need them yet.
Examples
Example 1: a final audit rule (simple)
class Account {
private double balance;
final void audit() {
System.out.println("audit: balance = " + balance);
}
boolean withdraw(double amount) { return false; }
}withdraw is open for children to override (checking accounts will), but audit is not. The compiler enforces the split: overriding audit() fails with overridden method is final, while overriding withdraw is fine.
Example 2: a final value class (practical)
The Money class above is final. Code that receives a Money can rely on it: it holds exactly the cents it was created with, and plus always returns a new Money. Trying class Euro extends Money gives cannot inherit from final Money. If you need euro behavior, hold a Money inside another class (composition, Lesson 5).
Example 3: the constructor trap, fixed (realistic)
The Report and SalesReport classes above printed Sales null. Making the call safe:
class Report {
Report() {
System.out.println(title());
}
final String title() { return "Report"; }
}
class SalesReport extends Report { }new SalesReport() now prints:
ReportThe method cannot be overridden, so the parent constructor always runs the parent's own code. Real classes meet this trap in framework base classes that call a "hook" method from the constructor: the hook can run before the subclass state is ready.
Backend Insight
Backend code protects invariants that must hold everywhere: totals add up, identifiers are never changed, the audit trail is written. Marking those methods final means a child class written months later by someone else cannot silently skip them. Finals also help teams: a final class tells reviewers "this is not an extension point", so they do not build on it by accident.
The trade-off is flexibility. Frameworks sometimes need to extend classes dynamically, and some libraries avoid final for that reason. For your own domain code, the safe default is to close what you did not design for extension, and open it deliberately later.
Common Mistakes
Mistake 1: Leaving everything open by default
Without final, any child may override any method and break your rules. Decide, for each class, whether it is a base class.
Mistake 2: Making a class final and then trying to extend it
cannot inherit from final X means the author forbade it. Use composition, or ask the author to change the design.
Mistake 3: Thinking final on a method stops callers from using it
final limits only overriding. The method is inherited and callable as usual.
Mistake 4: Calling overridable methods from a constructor
The override can run before the child's fields are initialized. Keep constructors simple, or call only final or private methods.
Mistake 5: Making an immutable class non-final
A child can add mutable state and break the promise.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. What does final do on (a) a class, (b) a method, (c) a field?
Show solution A1
A1. (a) The class cannot be extended. (b) The method cannot be overridden. (c) The field can be assigned only once.
A2. Name one class from the Java standard library that cannot be extended.
Show solution A2
A2. String (the tutorial gives it as its example).
A3. True or false: a final method cannot be called from a subclass.
Show solution A3
A3. False. A subclass can call an inherited final method; it just cannot override it.
Level B: Application
B1. Write an Invoice class with a final method total() returning a fixed 100.0, and a subclass ExpressInvoice that tries to override it. What error message ends the compiler output?
Show solution B1
B1.
class Invoice {
final double total() { return 100.0; }
}
class ExpressInvoice extends Invoice {
@Override
double total() { return 120.0; }
}The compiler reports that total() in ExpressInvoice cannot override total() in Invoice, and ends with overridden method is final.
B2. For each, decide final or not and say why: (a) class Password (b) class BaseController that is documented as the parent for every web controller (c) class EmailAddress, an immutable value.
Show solution B2
B2. (a) final: a security-critical value, nobody should change its behavior. (b) Not final: it is designed to be extended, so keep it open and document the extension points. (c) final: an immutable value class, so children cannot add mutable state.
B3. Predict the output:
class Base {
Base() { show(); }
void show() { System.out.println("base"); }
}
class Child extends Base {
private int value = 7;
@Override
void show() { System.out.println("child " + value); }
}What does new Child() print, and how would you make it print base?
Show solution B3
B3. It prints child 0, because value is still at its default 0 when the parent constructor runs (the field initializer = 7 has not run yet). To get base, declare show() final in Base and remove the override, or stop calling show() from the constructor.
Level C: Challenge
C1. A teammate makes every method in Account final "for safety". What is the effect on CheckingAccount, and what would be a better approach?
Show solution C1
C1. CheckingAccount could not override withdraw, so the overdraft feature would be impossible, and the subclass would add nothing. Better: make final only the methods whose behavior must be identical in every account type (such as an audit step), and leave the extension points like withdraw open and documented.
C2. Explain why a non-final immutable class cannot guarantee that no object of that type can change.
Show solution C2
C2. A subclass may add mutable fields and override the getters to return them, as SneakyMoney did. Code holding the parent type then sees values change. Making the class final removes the possibility.
Mini Challenge
Take the Account from Example 1. Add a final method audit() that prints the balance. Create CheckingAccount extends Account that tries to override it and read the error. Then remove the override and call new CheckingAccount(...).audit() to confirm that the child still has the method. Finally, write final class Money and try to extend it, noting the second error.
Check your work: you triggered both overridden method is final and cannot inherit from final, and you showed that a final method is still callable.
Summary & Next Lesson
Key takeaways
finalon a method forbids overriding; on a class it forbids extending; on a field it allows one assignment.Stringis a final class in the standard library.- Immutable and security-critical classes should be final, otherwise a subclass can break their promises.
- Do not call overridable methods from a constructor: the child may not be initialized yet.
- Closing a class is the safe default; open it deliberately where you design for extension.
Next lesson
The next lesson, "Composition over inheritance", gives you the alternative to extends when "is-a" does not fit, and when closed classes cannot be extended.
Self-check
- What error do you get when overriding a final method?
- Can you call a final method on a subclass object?
- Why is
Stringfinal? - What is the risk of calling an overridable method from a constructor?
- Which is safer by default: a final or a non-final class?
Show answers
overridden method is final.- Yes. Only overriding is forbidden.
- Because it is an immutable class, and a subclass could otherwise change what a
Stringbehaves like. - The override may run before the child's fields are set.
- Final, because you can open it later without breaking anyone.
References
- Oracle, Java Tutorials, Writing Final Classes and Methods. Supports: final methods cannot be overridden, final classes cannot be subclassed,
Stringas an example, and the advice to make methods called from constructors final. - Oracle, Java Language Specification SE 21, Section 8.1.4. Supports: extending a final class is a compile-time error; mentions sealed classes.
- The "design for inheritance or prohibit it" rule is a widely taught guideline and is not quoted from a source I fetched. The error messages and outputs were produced with JDK 21; the claim that
Stringispublic finalwas confirmed by a small reflection program.
Practice
Apply this lesson with the module's lab, exercises and quiz.
Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.