Skip to content
Ameen Abozaid
PublishedBEG-021-L02

Access modifiers

  • Lesson 2 of 6
  • 11 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

The last lesson used one visibility keyword, private, to build the vault wall. Java actually has four visibility levels, and choosing among them is a daily decision for a backend developer. This lesson names the four levels, shows exactly what each allows, and gives a simple rule for choosing: start as closed as possible and open up only when you must.

We stay with the bank picture. Inside the building there are rooms: the vault (only the bank's own staff), the back office (anyone in the same building), the partner desk (the building plus approved relatives), and the public window (everyone). Each Java level matches one of these.

Why this matters: every method and field you declare is a promise to other code. Level by level, this lesson teaches you to make small promises.

This lesson builds on "Why hide data". It previews two ideas taught later: packages (modules in this course teach them in detail) and subclasses (taught in the inheritance module). You only need the short explanations given here.

Learning Objectives

By the end of this lesson you will be able to:

  • Name the four access levels in Java and state who can use each one.
  • Say which level applies when you write no modifier.
  • Read the compiler's error when code uses a member it is not allowed to use.
  • Choose a level for a field, a helper method, a service method, and a method meant for subclasses.
  • Apply the rule "start with private, widen only with a reason".
  • Explain what package access means in terms of folders.

Written Explanation

Two small previews

Package. A package is a named group of related classes. In a project it corresponds to a folder, and the first line of a source file declares it, for example package bank;. Classes in the same package live side by side. You will learn packages properly later; for now, "same package" means "same group".

Subclass. A class can be built on top of another class and reuse it (this is called inheritance, taught later). The new class is the subclass. For this lesson, treat a subclass as "a relative of the class".

The four levels

The Java tutorial lists the levels from most to least open. Here they are from the most closed, which is the order you should think in.

LevelKeywordSame classSame packageSubclass in another packageAnywhere
Privateprivateyesnonono
Package-private(no keyword)yesyesnono
Protectedprotectedyesyesyesno
Publicpublicyesyesyesyes

Each row includes everything the row above it allows: every level is at least as open as the one before.

  • private: usable only inside the class that declares it. The vault.
  • Package-private: what you get when you write no modifier at all. Usable by any class in the same package. The back office. The Java Language Specification calls this package access and says it is the default for most declarations.
  • protected: same package, plus subclasses even when they are in other packages. The partner desk.
  • public: usable from anywhere the class itself can be seen. The window.

A top-level class (one not written inside another class) can be only public or package-private. private and protected apply to members: fields, methods, and constructors.

The same class always has full access

Inside the class, everything is visible, whatever its level. A private field is as easy to use in the class's own methods as a public one. Levels only restrict other code.

A worked example

Here is an Account in package bank with one member at each level:

java
package bank;

public class Account {
    private double balance;           // only Account
    String branch = "Central";        // package-private: package bank
    protected String note = "none";   // package bank + subclasses
    public String owner;              // everywhere
    // constructor and methods follow
}

(In real code you would not make owner public; this is only a demonstration. Lesson 4 returns to it.)

A class in a different package, with no relation to Account, tries to use every member:

java
System.out.println(a.branch);
System.out.println(a.note);
System.out.println(a.balance);
System.out.println(a.fee(10));   // fee is a private method
text
error: branch is not public in Account; cannot be accessed from outside package
error: note has protected access in Account
error: balance has private access in Account
error: fee(double) has private access in Account

Only owner was usable. A class in package bank could use branch and note too. A subclass of Account in another package can use note but not branch.

Choosing a level: the rule

The Java tutorial advises using the most restrictive level that makes sense, and private unless there is a specific reason. Turn that into a short checklist:

  1. Fields: private. Almost always. The previous lesson showed why.
  2. Helper methods that exist only to help one method of the class: private. Nobody else should depend on them, so you stay free to rename or delete them.
  3. Methods other classes are meant to call, your teller-window operations such as deposit and withdraw: public.
  4. Methods meant to be used or overridden by subclasses, but not by the general public: protected.
  5. Things shared only by closely related classes in one package: package-private. Do this on purpose, not by forgetting a keyword (a later lesson in this course covers this "forgotten modifier").

If you cannot decide, pick the more restrictive level. Opening a member later is easy: no existing code can break. Closing a member later is hard: code somewhere may already use it.

A note on constructors and classes

The same four levels apply to constructors. A private constructor means "nobody outside may create this object". Classes declared with no modifier are visible only inside their package. For now, make the classes you want others to use public.

Examples

Example 1: a helper stays private (simple)

java
public class Account {
    private double balance;

    public Account(double balance) {
        this.balance = balance;
    }

    private double fee(double amount) {   // helper: only Account needs it
        return amount * 0.01;
    }

    public void withdraw(double amount) {
        double total = amount + fee(amount);
        if (amount > 0 && total <= balance) {
            balance -= total;
        }
    }
}

fee is private, so tomorrow you can change the fee formula, rename the method, or remove it, and no outside code can have depended on it. withdraw is the only door.

Example 2: one call per level (practical)

Three files: bank/Account.java (the class from the worked example, with deposit, withdraw, and a public balance() method that returns the number), bank/Audit.java in the same package, and app/Main.java in a different package.

java
package bank;

public class Audit {
    public static void print(Account a) {
        System.out.println(a.owner + " @ " + a.branch + " / " + a.note);
    }
}
java
package app;

import bank.Account;
import bank.Audit;

public class Main {
    public static void main(String[] args) {
        Account a = new Account("Sara", 100);
        a.deposit(50);
        a.withdraw(100);
        System.out.println(a.balance());
        Audit.print(a);
    }
}

Compiled with javac -d out bank/*.java app/Main.java and run with java -cp out app.Main, the output is:

text
49.0
Sara @ Central / none

Audit is in package bank, so it may read branch (package-private) and note (protected). Main may only use the public members. The balance is 150 minus 100 minus a 1.0 fee, which is 49.0.

Example 3: protected for a subclass (realistic)

Another class, in the app package, extends Account:

java
package app;

import bank.Account;

public class Premium extends Account {
    public Premium(String owner, double balance) {
        super(owner, balance);
    }

    public void tag() {
        note = "premium";
        System.out.println(note);
    }

    public static void main(String[] args) {
        new Premium("Omar", 10).tag();
    }
}

Output:

text
premium

Premium is in a different package, yet it can use note because note is protected and Premium is a subclass. If note had no modifier, this would not compile. (The words extends and super are taught in the inheritance module; here, just notice that the subclass gets access.) Use protected sparingly: it opens a member to all current and future subclasses, which you may not control.

Backend Insight

In a typical backend, a service class exposes a handful of public methods, "approve this order", "cancel this booking", and keeps almost everything else private. Reviewers look at the public surface first, because that is what the rest of the team will depend on. When something later needs to change, a small public surface means a small blast radius.

Frameworks go one step further: libraries often mark classes as internal by hiding them in a package that application code is not supposed to touch, or by keeping them package-private. If you see a class you cannot reach, it is usually hidden on purpose.

Common Mistakes

Mistake 1: Making everything public to "make the error go away"

  • Incorrect: the compiler says fee(double) has private access in Account, so you change private to public.
  • Correct: ask whether the caller should be using that member at all. Often you need a public method that does the whole job instead.

Mistake 2: Forgetting that no modifier is not private

A field with no modifier looks harmless but is open to the whole package. Write private explicitly when you mean it.

Mistake 3: Thinking protected means "subclasses only"

It also allows the whole package. If you want "subclasses only", protected is the closest Java offers, but it is wider than the name suggests.

Mistake 4: Choosing a level by guessing

Use the checklist: field means private, helper means private, operation for callers means public, hook for subclasses means protected, shared inside the package means package-private.

Exercises

Try each exercise yourself before opening the solutions.

Level A: Foundation

A1. Which access level applies when you write no modifier?

Show solution A1

A1. Package-private (package access).

A2. Which of the four levels is the most closed? The most open?

Show solution A2

A2. private is the most closed, public the most open. Package-private and protected lie between them, in that order.

A3. True or false: a private field cannot be used by the class's own methods.

Show solution A3

A3. False. A class always has full access to its own members.

Level B: Application

B1. Choose a level for each: (a) the ssn field of a Customer, (b) a method round(double) used only by Invoice.total(), (c) Invoice.total() called by many classes in other packages, (d) a method validate() meant to be customized by subclasses.

Show solution B1

B1. (a) private. (b) private. (c) public. (d) protected.

B2. Class Order is in package shop, and has int quantity; with no modifier. Class Report is in package shop, and class Checkout is in package web. Which of them can read quantity?

Show solution B2

B2. Report can, since it is in the same package. Checkout cannot, since it is in a different package and package-private access stops at the package boundary.

B3. Class Order has protected void audit(). A class Coupon is in another package and is not a subclass of Order. May Coupon call audit() on an Order?

Show solution B3

B3. No. protected allows the same package and subclasses; Coupon is neither, so the compiler rejects the call.

Level C: Challenge

C1. You have a class Cart with a public field items (a count), and the whole team uses it. You want to make it private. What happens to the existing code, and how do you keep a safe way to read the value?

Show solution C1

C1. Every line that used cart.items directly stops compiling, and you must fix each one. The remedy is a public method that returns the count (a getter, covered in lesson 4) or, better, a behavior method such as isEmpty() if that is all callers wanted. The pain of this step is itself the argument for starting with private.

C2. Explain, using the idea of "promises", why choosing private first is safer than choosing public first.

Show solution C2

C2. Every public member is a promise that other code can depend on it, so you cannot remove or change it without risk. Starting private makes no promise; you add promises one at a time, only when needed. Widening a member later breaks nothing, whereas narrowing one may break existing callers.

Mini Challenge

Design the visibility of a Library class that has: a list of books kept as an array field, a method borrow(String title) used by other packages, a helper findIndex(String title) used only inside borrow, and a method onBorrowed() that subclasses may override to send a notification. Write the declarations (just signatures and fields, bodies may be empty) with the right level on each, and add one comment per line explaining your choice.

Check your work: the array and findIndex are private, borrow is public, onBorrowed is protected, and no member is more open than it needs to be.

Summary & Next Lesson

Key takeaways

  • Java has four levels: private, package-private (no keyword), protected, and public.
  • Package-private is the default when no modifier is written; protected also includes the whole package.
  • A class can always use its own members; the levels restrict other code.
  • Choose the most restrictive level that works: fields and helpers private, caller-facing operations public.
  • Opening a member later is easy; closing it later is hard.

Next lesson

Hiding data is only half of the work. The next lesson, "Invariants: rules the object guarantees", shows how a class uses its private fields to make sure its rules are never broken.

Self-check

  1. Who can use a private member?
  2. What is the default access level?
  3. Which levels let a class in the same package use a member?
  4. Which level should a field normally have?
  5. Why is it risky to start with public?
Show answers
  1. Only code inside the class that declares it.
  2. Package-private.
  3. Package-private, protected, and public.
  4. private.
  5. Public members are promises that other code may depend on, so they are hard to change or remove later.

References

  • Oracle, Controlling Access to Members of a Class. It supports the four levels and who can access each, that top-level classes are only public or package-private, and the advice to be as restrictive as practical.
  • Oracle, Java Language Specification SE 21, chapter 6. Its introduction supports that without a modifier most declarations have package access, which is the default.
  • The compiler messages and outputs in this lesson come from compiling and running the examples with JDK 21. The folder-to-package correspondence is stated as a general convention and was not checked against a fetched page here.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.