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

ModifierSame classSame packageSubclass (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 β€” every public symbol 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 protected as a looser public β€” if subclasses aren't a real use case, it's just package-private with extra noise.
  • Not thinking about the public API β€” every public method 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.