access control
private, package-private, protected, public. who can see what.
- chapter
- chapter 03.03
- reading time
- 6 min read
four levels, from tight to loose: private (this class only), package-private (no keyword: this package), protected (this package plus subclasses anywhere), public (everyone). javac checks them at compile time.
default to the tightest one that works. every public is a promise you'll have to keep.
the picture
think of a house. private is your diary (only you). package-private is the fridge (anyone living in the house). protected is the family recipe (the house, plus your kids who moved out, but they only get their own copy). public is the front porch.
the important bit: access is about which code is asking, not which object. it's checked by javac, file by file, using two facts: what class the code is in, and what package that class is in.
(nothing): package-private: anything in package shop. subclasses elsewhere are out.
Account.balance. green classes can read and write it, the rest get a compile error. notice that protected lights up everything (nothing) does, plus one more.every cell, compiled
a table is a claim. here are the sixteen proofs. each cell is two real files and what javac (25) said about them.
| modifier | classsame class | packagesame package | subclasssubclass, other package | worldanywhere else |
|---|---|---|---|---|
| private | ||||
| (nothing) | ||||
| protected | ||||
| public |
* only through a reference to your own class. click it.
1package shop;23public class Account {4 protected int balance;5}
1package bank;23import shop.Account;45class Savings extends Account {6 void poke() {7 this.balance = 1;8 }9}
Account.java is the declaration, the green or red line is the access. then open the protected × subclass cell and switch via.protected's weird rule
you know how everyone says "protected means subclasses can access it"? that's only half true, and the other half is the one you hit in prod.
a subclass in another package can touch a protected member only through a reference of its own type (or a subtype). this.balance is fine. otherSavings.balance is fine. someAccount.balance is not, even though Savings extends Account.
package bank;
import shop.Account;
class Savings extends Account {
void poke(Savings mine, Account any) {
this.balance = 1; // ok: it's me
mine.balance = 1; // ok: a Savings, same family line as me
any.balance = 1;
}
}error: balance has protected access in Account
why? because any might be a Checking, a totally different subclass written by someone else. protected means "my subclasses can use what they inherited", not "any subclass can poke into any account". the rule keeps Savings out of Checking's business.
private is per class, not per object
class Wallet {
private int cash;
Wallet(int cash) { this.cash = cash; }
void steal(Wallet other) {
this.cash += other.cash;
other.cash = 0;
}
int cash() { return cash; }
}
// in main:
Wallet mine = new Wallet(10);
Wallet yours = new Wallet(50);
mine.steal(yours);
IO.println(mine.cash() + " " + yours.cash());the same idea covers nesting: a class and the classes nested inside it can all see each other's privates (they're "nestmates" since java 11, so the JVM checks it directly, no synthetic accessor methods).
public class Main {
private int secret = 42;
class Peeker {
int peek() {
return secret; // inner reads outer's private
}
}
static class Box {
private int hidden = 7;
}
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.new Peeker().peek() + " " + new Box().hidden); // outer reads nested private
}
}42 7
the smaller rules
- a top-level class can only be
publicor package-private. onepublictop-level class per file, and the file must be named after it. - overriding can't shrink access. a
publicmethod stayspublicin every subclass, or javac saysattempting to assign weaker access privileges; was public. (it can widen:protected→publicis fine.) - interface members are implicitly
public(methods can beprivatesince java 9, for helpers). - local variables have no modifiers at all. their scope is the block.
- modules sit on top. since java 9, a
publicclass in a package your module doesn't export is invisible outside the module. see modules.
public class Account {
public int balance; // anyone, anywhere, any value
}public class Account {
private int balance;
public void deposit(int amount) {
if (amount <= 0) throw new IllegalArgumentException();
balance += amount;
}
public int balance() { return balance; }
}the field is an implementation detail. the method is the API. once a field is public, you can never add validation, change its type, or make it computed, because someone out there reads it directly.
- private → class, (nothing) → package, protected → package + subclasses, public → everyone.
- access is checked on the code doing the access, not the object being accessed.
- protected from another package only works through your own type's references.
- protected is wider than package-private. it's not "subclasses only".
- start private. widen only when something real needs it.