Java Access Modifiers β public, private, protected, default
Access modifiers control which other code can see a class, field, method or constructor. Java has exactly four levels: public, protected, package-private (no keyword) and private. Choosing the narrowest one is a safety and maintainability win β the less code can touch something, the fewer places can break it.
The visibility table
| Modifier | Same class | Same package | Subclass (other pkg) | Everywhere |
|---|---|---|---|---|
public | β | β | β | β |
protected | β | β | β | β |
| no keyword (package-private) | β | β | β | β |
private | β | β | β | β |
Which to use
private: default for fields and helper methods. Encapsulation by default.- package-private: default for collaborators that live in the same package. Keeps the public API minimal.
protected: only for things subclasses legitimately need. Rarely the right choice outside frameworks.public: deliberate β everypublicsymbol is a promise to callers. Keep the public surface as small as possible.
Example
package com.example.billing;
public class Invoice {
private final BigDecimal amount; // only Invoice sees it
final Customer customer; // package-private: used by InvoicePrinter
protected String internalRef; // subclasses override/extend
public String number() { return "INV-" + id; } // the public API
}
Class-level vs member-level
Top-level classes can only be public or package-private. Only nested classes can be private or protected:
// Foo.java
public class Foo { ... } // β
public β visible everywhere
class Bar { ... } // β
package-private
// private class Baz { ... } // β not allowed at top level
public class Outer {
private static class Inner {} // β
allowed β member of Outer
}
The Java module system (Java 9+)
Modules (module-info.java) add a second layer: a class can be public but the package it lives in may not be exported by its module, making it invisible to other modules. Useful for libraries that want a clean API surface beyond what public offers.
All sub-topics
Common mistakes
- Making fields
publicβ bypasses encapsulation. Use private + getter, or switch to a record. - Using
protectedas a looserpublicβ if subclasses aren't a real use case, it's justpackage-privatewith extra noise. - Not thinking about the public API β every
publicmethod is a commitment. Mark it only when you mean it.
Try it & related tools
Experiment with visibility in the Java Online Compiler. To read real-world examples, inspect a Spring or JDK class in the Beautifier.