The modifiers are not a style ladder to climb. A useful default is to make the narrowest thing that works public, and to keep the public surface small enough that you can remember it.
- Fields. Private, always. Expose through accessors when a caller needs them, so you keep the option to add validation later.
- Methods that are called from outside the package. Public, and expect to support them.
- Methods for subclasses you do not control. Protected. If nobody extends the class, use private and final.
- Package-internal helpers. Leave the modifier off. It is the right default for code you share inside one package.
Two habits pay for themselves quickly. Put the narrowest modifier first and the widest last in each class, so a reader can see the intended surface without parsing every line. And add @Override to anything that implements or overrides a parent member, because it converts a mistaken re-declaration of a private method into a compile error instead of a method that is never called.
Widening a member to public is the one change here that cannot be undone without breaking callers. Narrowing, renaming, or changing a parameter type all have the same problem, so decide the widest access early and treat it as a commitment.
Frequently Asked Questions
What are the four access modifiers in Java and what does each allow?
private allows the declaring class only, plus any nested class in the same nest. Package-private, written by omitting the modifier, allows every type in the same package. protected allows the whole package plus subclasses in other packages, subject to a condition on the reference type. public allows anywhere the declaring class is itself visible, which since Java 9 also depends on whether the module exports the package.
Is default a keyword in Java?
No. Package-private access is expressed by writing no modifier at all. Using the word default produces the error "modifier default not allowed here". The name is a convenient label used in discussion, but it is not part of the language, and the reserved word list does not contain it.
Can a subclass access a private method of its parent?
No. A private method is not inherited, so a subclass that declares a method with the same name is defining something new and unrelated rather than overriding anything. Adding @Override to that declaration is what turns the mistake into a compile error. A nested class is a different case: it can reach the enclosing class's private members directly, because the two share a nest.
Why can a subclass not read a protected field through a superclass reference?
Because outside the package, protected access is only permitted through a reference of the subclass's own type. Reading an inherited member directly is fine, but going through a variable typed as the superclass is treated as unrelated code reaching in, and is rejected with "value has protected access in Base". Cast the reference to the subclass type if that access is genuinely what you need.
Can a nested class be private?
Yes. All four modifiers are legal on a nested class. The restriction applies only to top-level types, where private and protected produce "modifier not allowed here", since nothing outside the file could extend or reach such a type in the first place.
Are interface methods always public?
Yes, and you cannot mark them otherwise. An interface method without a body is stored as public abstract, an interface field as public static final, and a class declared inside an interface as public static. Since Java 9 a private method is allowed inside an interface, but only to share implementation between its own default methods.
Does public guarantee that a class is reachable?
Not by itself, since Java 9. A class must be public and its package must be exported by its module before another module can use it, and the error names the withholding module explicitly. Code on the ordinary classpath lives in the unnamed module, which exports everything, so the old behaviour returns and the distinction only bites modular builds.
Is access checked at compile time or at run time?
Both. javac rejects an unreachable member before the program starts, and the JVM checks the access flags in the class file again as it loads and links. The runtime check is what makes reflection throw IllegalAccessException for a private member unless setAccessible(true) was called first.
Can an override reduce visibility?
No. An override may widen access but never narrow it, so a public parent method cannot become protected or private in the child. The reasoning is substitutability: code holding a supertype reference was promised access to that method and must keep it. Widening, on the other hand, is always allowed.
What is the best default access level for a field?
Private, with accessors where callers genuinely need one. Fields are mutable state, and a private field with a getter lets you add validation, caching, or deprecation later without breaking anyone. The exception worth considering is a genuinely immutable constant, which is often written public static final or exposed through a package-private accessor for a small group of collaborating classes.