Core Java ยท 2. Object-oriented programming, lesson 4 of 16

Access modifiers: private, package-private, protected and public

Beginner3 min read@since 8Code runs on your Java 25
Explain it forThe essentials plus production detail and pitfalls.

Access modifiers decide which code can use a class, field, method or constructor. They're how a class protects its rules: outside code can only touch what you deliberately expose.

  • private: only inside the same class. Use it for fields almost always.
  • package-private (no keyword): any class in the same package.
  • protected: the same package, plus subclasses in other packages (through inheritance).
  • public: any code anywhere.

Rules of thumb:

  • Make fields private and expose behaviour through methods. That's encapsulation.
  • Start with the most restrictive level that works, and widen it only when needed. Narrowing it later breaks other people's code.
  • A top-level class can only be public or package-private; nested classes can use all four.
  • An overriding method can keep or widen the access level, never narrow it.
Diagram

Side by side

Who can access itTypical use
privateThe same classFields, helper methods
(none) package-private+ every class in the same packageImplementation classes inside a feature package
protected+ subclasses in other packagesExtension points of a class designed to be extended
public+ everyone (in exported packages, with modules)The API other code is meant to use

Example

Java
package com.shop.model;

public class Product {                         // public: usable from any package
    private final String sku;                  // private: only Product can touch it
    private long pricePaise;
    String category;                           // package-private: classes in com.shop.model only
    protected int stock;                       // protected: package + subclasses elsewhere

    public Product(String sku, long pricePaise) {
        this.sku = sku;
        setPrice(pricePaise);
    }

    public long price() { return pricePaise; }          // read access through a method

    public void setPrice(long pricePaise) {              // the class enforces its own rule
        if (pricePaise < 0) throw new IllegalArgumentException("Price can't be negative");
        this.pricePaise = pricePaise;
    }

    private String normalise(String s) { return s.trim().toUpperCase(); }   // internal helper
}
The protected surprise
package com.shop.digital;
import com.shop.model.Product;

public class Ebook extends Product {
    public Ebook(String sku) { super(sku, 49900); }

    void restock(Ebook other, Product plain) {
        this.stock = 100;      // OK: inherited, accessed through this subclass
        other.stock = 5;       // OK: through an Ebook reference
        // plain.stock = 5;    // compile error: a different package may only use protected members
        //                        through its own subclass type, not through any Product
    }
}

Common mistake

Making fields public "to save writing getters". Every caller can then put the object into an invalid state, and you can never add validation later without breaking them.

Under the hood

Java 9 modules add a layer above all of this: a public class is only visible outside its module if its package is exported in module-info.java. Interfaces are public by design: their abstract, default and static methods are implicitly public, and since Java 9 they can have private helper methods. Reflection can bypass access checks with setAccessible(true), but strong encapsulation of the JDK (Java 16+) blocks that for JDK internals.

Check yourself

A field has no access modifier. Who can access it?

How this connects

Where this leads

You've reached the end of this thread. Try a learning path for what's next.

Part of OOP in practice.

Was this lesson helpful?

Finished reading? Mark it complete to track your progress.