From lesson 5-3, Player's score field was marked private so that every change goes through addPoints, which enforces the rules.
That is encapsulation: the object controls its own state. Outside code cannot set score directly and has to call addPoints, which rejects negative values.
Reading is still possible through the public getScore() getter, so hiding the field restricted writes without hiding the information.
Inheritance
Classes often form families. A Dog is an Animal, and a SavingsAccount is a BankAccount. Without a language feature for that, the shared parts get copied into every family member, and a bug fix has to be repeated in each copy, which is the exact disease methods cured within one class now recurring at the scale of whole classes.
Inheritance lets a class reuse another class's fields and methods with extends. It is also how you plug into other people's code, since professional Java means extending framework classes, such as an Android screen extending Activity, far more often than writing hierarchies from scratch.
class Animal { String name; Animal(String name) { this.name = name; } void eat() { System.out.println(name + " is eating"); } } class Dog extends Animal { Dog(String name) { super(name); // run Animal's constructor first } void fetch() { System.out.println(name + " fetches the ball"); } }
Dog is the subclass and Animal the superclass. A Dog object has everything Animal declared, meaning name and eat, plus its own additions such as fetch.
super(...) calls the superclass constructor and must be the first line whenever the parent needs arguments. Python's class Dog(Animal) with super().__init__(name) is the same idea in different syntax.
A method the subclass never wrote
Dog defines only fetch, yet a Dog object can eat.
public class Main { public static void main(String[] args) { Dog rex = new Dog("Rex"); rex.eat(); rex.fetch(); } } class Animal { String name; Animal(String name) { this.name = name; } void eat() { System.out.println(name + " is eating"); } } class Dog extends Animal { Dog(String name) { super(name); } void fetch() { System.out.println(name + " fetches the ball"); } }
Output
Rex is eating Rex fetches the ball
rex.eat() runs Animal's method on a Dog object, and it prints Rex because super(name) handed the constructor argument up to Animal, which stored it in the inherited field.
fetch reads name too, even though Dog never declared it. Inherited fields are part of the object, so subclass methods use them like their own.
What gets inherited
A subclass inherits every field and method except constructors and anything marked private. Private members do exist inside the object, but they are only reachable through the parent's own methods.
Each class can extend exactly one superclass. Chains are allowed, so Puppy extends Dog extends Animal works and a Puppy has all three levels of behavior.
As for when to use it, only a true is-a relationship qualifies:
| Relationship | Model it as |
|---|---|
| a Dog is an Animal | class Dog extends Animal |
| a Car has an Engine | an Engine field inside Car |
"Is-a gets extends, has-a gets a field" prevents most inheritance misuse you will ever be tempted by.
The temptation usually arrives as convenience. A class happens to contain code you want, so extending it seems cheaper than writing a field. That shortcut ties the two classes together permanently, and every later change to the parent lands in your class whether it makes sense there or not.
A subclass that fixes a parent value
Motorcycle takes no arguments and supplies the wheel count itself.
public class Main { public static void main(String[] args) { Motorcycle m = new Motorcycle(); m.describe(); m.wheelie(); } } class Vehicle { int wheels; Vehicle(int wheels) { this.wheels = wheels; } void describe() { System.out.println("has " + wheels + " wheels"); } } class Motorcycle extends Vehicle { Motorcycle() { super(2); } void wheelie() { System.out.println("up on one wheel!"); } }
Output
has 2 wheels
up on one wheel!Vehicle's constructor stores the wheel count, and Motorcycle's constructor immediately calls super(2) to fill it with the value every motorcycle shares. Callers never pass a wheel count at all.
describe is inherited unchanged, so Motorcycle does not redefine it. The subclass adds one capability and fixes one parent value, which is inheritance doing exactly the work it is good at.