The labs on this site need JavaScript to run. The same content is written out below.
Learn › Java › Classes and objects
Access modifiers and encapsulation
How far do private, protected, and public open up access?
The access modifier in front of a field or a method determines which code can use that member. The compiler looks at which class and which package the accessing code is in to decide whether to allow the access, and if the access is not allowed, the result is a compile-time error. protected is open to subclasses, but one more condition applies in a subclass in a different package.
Checked against real execution · The 24 combinations were compiled on JDK 21 from sources split into packages, and whether compilation succeeded and the error messages were checked against the results
Concepts in this lab
- Access modifiers
- These are keywords that set the scope from which a class, field, or method can be accessed. public allows access from anywhere (when modules are used, code in another module can access it only if the package is exported), protected allows access from the same package and from subclasses, no modifier (package-private) allows access only from the same package, and private allows access only inside the top-level class where the member is declared (including its nested classes). Keeping access as narrow as possible is the basis of encapsulation.
- Encapsulation
- This means bundling an object's state (fields) together with the methods that work on that state, and hiding the internal implementation so that outside code cannot access it directly. If you make the fields private and let values change only through public methods, the methods can reject invalid values, and you can change the internal structure without affecting outside code.
- Package
- This is a namespace that groups related classes together. The single line package bank; at the top of a source file determines which package the classes in that file belong to, and packages usually match the folder structure. To use a class from another package, you either import it or write the package name as well, as in bank.Account. A member with no access modifier can be used only inside the same package, so a package is a unit that divides names and also a unit that divides access.
- protected
- This is the access modifier that opens a member to the classes in the same package and to subclasses, even those in a different package. It gives wider access than writing no access modifier (only up to the same package) and narrower access than public. A subclass in a different package can use an inherited member only through its own type. That is, access through this or through a variable of its own class type works, but access through a variable of the superclass type is a compile-time error. It is easy to overlook that a class in the same package can also use the member even if it is not a subclass.
- Inheritance
- This means defining a new class (the subclass) that receives the fields and methods of an existing class (the superclass). It reuses common code, and the subclass adds or overrides only the parts it needs. A Java class supports single inheritance only, and a change to the superclass affects every subclass, so it is best used only when there is a clear "is-a" relationship.
Try these
- Default settings — A protected member is read inside a subclass, but reading it through a variable of the superclass type is a compile-time error.
- Reading a protected member as its own — The subclass can read this.balance, its own inherited member.
- protected is also open to the same package — A class in the same package can read the member even if it is not a subclass.
- Reading a private member from the same package — A private member can be used only inside the same class.
- A member with no modifier, from a subclass in another package — Even a subclass cannot read it if the package is different.
- A protected member, from an unrelated class — A class that is in another package and is not a subclass cannot read it.
- With public — Even an unrelated class in another package can read it.
Running with the default settings
- Declaration in bank/Account.java: balance is declared protected. protected means "can be used inside the same package, and also inside a subclass even if the subclass is in a different package".
- read(Account other) in app/SavingAccount.java: Code inside SavingAccount tries to read other.balance. The type of other is the superclass, Account. SavingAccount is a subclass of Account, but it is in a different package, app.
- Check 1: Is the accessing code inside the Account class? No. It is inside the SavingAccount class. If the member were private, the checks would end here, but this member is not private, so the next check follows.
- Check 2: Is the accessing code in the same package as Account? No. The SavingAccount class is in the app package. protected is open to subclasses even in a different package, so the next check follows.
- Check 3: Is the accessing code inside a subclass of Account? Yes. SavingAccount extends Account. However, one more condition applies to a subclass in a different package.
- Check 4: Is the type of the reference written before balance its own class, SavingAccount? No. other is declared with the superclass type, Account. The only thing that protected opens to a subclass in a different package is "access through its own type", so the member cannot be used even though the code is inside a subclass. This is a compile-time error.
Result of this run
- Even inside a subclass in a different package, a protected member cannot be used through a variable of the superclass type. This is because a variable of type Account might hold an object of some other subclass that has nothing to do with SavingAccount.
- this.balance and the balance of a variable of type SavingAccount are allowed. Just changing the type of the variable from Account to SavingAccount makes the code compile.
- Access widens in this order: private (the same class) → no modifier (up to the same package) → protected (up to subclasses in other packages) → public (anywhere).
Code
// File: bank/Account.java
package bank;
public class Account {
protected int balance = 1000;
}
// File: app/SavingAccount.java (different package, subclass of Account)
package app;
import bank.Account;
public class SavingAccount extends Account {
int read(Account other) {
return other.balance; // compile-time error
}
}
Assumptions of this model
The 24 combinations were actually compiled on JDK 21 on this PC from source files split into the packages bank and app, and whether compilation succeeded and the error messages were checked against the results (this lab looks at the result of compilation, not the result of execution). The "Reason for the decision" column and the order of the checks are an explanation that restates the access rules of the Java Language Specification, and they do not mean that the compiler actually checks in this order. When the same access modifiers were put on a method instead of a field and compiled, success and error were exactly the same for the 24 combinations (the wording of the error messages differs in part). Not covered: access modifiers on the class itself, constructors, static members, nested classes, and modules (module-info). The "only through its own type" condition of protected applies to members that are not static. The error messages are the first line of the English javac message.
Previous lab: Interfaces and abstract classes · Next lab: Inner classes and anonymous classes