SECTION 1
Historical Context & Motivation
In the early days of programming, all variables and functions in a program could be seen and changed by any other part of the code. This might sound convenient, but it led to disastrous bugs—imagine a teammate accidentally overwriting your carefully calculated score variable from a completely unrelated section of the game. As programs grew from a few hundred lines to tens of thousands, developers realized they needed a way to hide internal details and only expose what was truly necessary.
The concept of encapsulation—bundling data with the methods that operate on it and restricting outside access—became one of the pillars of object-oriented programming (OOP). Java, released in 1995 by Sun Microsystems, baked this idea directly into the language through access modifiers—keywords that tell the compiler exactly who is allowed to use a class, method, or variable.
1967 • Simula Introduces OOP
1972 • Smalltalk Refines Encapsulation
1983 • C++ Adds Access Keywords
1995 • Java Launches with Four Levels
The central question access modifiers answer is deceptively simple: "Which parts of my code should other classes be allowed to touch?" Getting this right is the foundation of writing clean, maintainable Java programs.
SECTION 2
Core Principles & Definitions
An access modifier is a keyword you place before a class, method, or variable declaration to control its visibility—that is, which other classes and packages can see and use it. Java provides four levels of access, ranging from the most open to the most restrictive.
public
protected
package-private (default)
private
KEY TAKEAWAY
SECTION 3
Visual Explanation
The diagram below shows the four access levels as concentric circles. The innermost circle represents the most restrictive level (private), and each outer ring adds more visibility. Notice how each level includes all the access of the levels inside it.
In the diagram, MyClass sits at the center and can access everything marked private, package-private, protected, or public within itself. A Helper class in the same package can see everything except private members. A Child subclass from another package can also reach protected members through inheritance. Finally, an unrelated class (Other) in a different package can only see public members.
SECTION 4
How Access Modifiers Work in Practice
When you compile a Java program, the compiler checks every reference to a class, method, or field. If the access modifier does not permit the reference, you get a compile-time error—the program will not even run. This is different from a runtime error; Java catches access violations before your code ever executes.
Syntax Patterns
Each modifier is placed immediately before the data type in a declaration. For the default level (package-private), you simply leave the keyword out entirely.
| Modifier | Syntax Example | Effect |
|---|---|---|
public | public int score; | Any class in any package can read or modify score. |
protected | protected void heal() { } | Accessible within the same package and by subclasses elsewhere. |
| (none — default) | int health; | Only classes in the same package can see health. |
private | private String password; | Only the class itself can read or change password. |
Java access modifier syntax and effects
The Package System
A package is Java's way of organizing related classes into folders. A package declaration at the top of a file (for example, package com.school.game;) tells the compiler which group this class belongs to. Two classes share a package only if their package statements are identical. Both package-private and protected rely on packages for their rules, so understanding packages is essential before choosing the right modifier.
Common Mistake
SECTION 5
Detailed Breakdown — Who Can Access What?
The table below is the single most important reference for access modifiers. It answers one question from four perspectives: given a member declared with a specific modifier, can it be accessed from (1) the same class, (2) another class in the same package, (3) a subclass in a different package, or (4) an unrelated class in a different package?
| Modifier | Same Class | Same Package | Subclass (other pkg) | Any Class (other pkg) |
|---|---|---|---|---|
public | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
protected | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No |
| package-private (default) | ✅ Yes | ✅ Yes | ❌ No | ❌ No |
private | ✅ Yes | ❌ No | ❌ No | ❌ No |
Complete access matrix for Java's four access levels
Study the flowchart above to internalize the logic. When you are writing code and choosing a modifier, mentally walk through these questions. The general rule of thumb is to start with private and only widen access when you have a clear reason.
SECTION 6
Worked Example — Designing a Student Class
Imagine you are building a school management system. You need a Student class that stores a name, GPA, and student ID. Let's walk through choosing the right access modifier for each member.
Choosing Access Modifiers for a Student Class
- 1
Step 1 — Identify the Fields
OurStudentclass has three fields:name(String),gpa(double), andstudentId(String). We also need a methodcalculateHonorRoll()and a helper methodvalidateGPA().Five members to assign modifiers to. - 2
Step 2 — Apply the Principle of Least Privilege
Start by making everythingprivate. This is the safest default. All three fields (name,gpa,studentId) should be private because outside code should never directly change a student's GPA or ID without going through validation.private String name; private double gpa; private String studentId; - 3
Step 3 — Widen Access for Public Methods
Other parts of the system (like a report card generator) need to callcalculateHonorRoll(). Since it could be called from any package, we mark it public. We also add public getter methods (getName(),getGPA()) so outside code can read, but not directly change, the data.public boolean calculateHonorRoll() { return gpa >= 3.5; } - 4
Step 4 — Keep Helpers Private
The methodvalidateGPA()checks that a GPA is between 0.0 and 4.0. No outside class ever needs to call this directly—it's an internal detail. So it stays private.private boolean validateGPA(double g) { return g >= 0.0 && g <= 4.0; } - 5
Step 5 — Consider Protected for Inheritance
If we plan to create aGraduateStudentsubclass that overrides honor roll rules, we might changecalculateHonorRoll()to protected so subclasses in other packages can override it, while keeping it hidden from unrelated classes.protected boolean calculateHonorRoll() { return gpa >= 3.5; }
SECTION 7
Strengths, Limitations & Comparisons
Each access modifier has situations where it shines and situations where choosing it would be a mistake. Understanding these trade-offs helps you write code that is both flexible and secure.
| Modifier | When to Use | When to Avoid |
|---|---|---|
public | Main methods, class constructors used by other packages, library APIs that external code must call. | Fields (instance variables). Making fields public breaks encapsulation and lets any class change your data. |
protected | Methods or fields that subclasses in other packages need to inherit or override. | When you don't intend for the class to be extended. Overusing protected can leak internal details. |
| package-private | Utility classes, helper methods shared only within a single package. | When you need cross-package access or when the absence of a keyword might confuse readers. |
private | All fields by default, internal helper methods, anything that is an implementation detail. | Methods that the rest of the program genuinely needs to call. Over-restricting causes frustration. |
When to use and avoid each access level
THE GOLDEN RULE
SECTION 8
Connection to Advanced Topics
Access modifiers are not just a beginner concept—they are the foundation for many advanced programming patterns. As you continue learning Java, you will encounter more sophisticated uses of visibility control.
| What You Learn Now | Where It Leads |
|---|---|
private fields with public getters/setters | Encapsulation — core OOP principle, enforced in frameworks like JavaBeans and Spring. |
protected methods for subclass overriding | Polymorphism and Inheritance — advanced OOP that enables flexible, reusable designs. |
| Package-private helper classes | Modular design — Java 9 introduced the Module System (Jigsaw) for even stronger encapsulation boundaries. |
| Choosing the right modifier | API design — professional libraries carefully choose which methods to expose, as changing a public method later can break thousands of other programs. |
How access modifiers connect to advanced Java concepts
When you eventually study design patterns like Singleton or Factory, you will see that access modifiers are the key mechanism that makes them work. A Singleton class, for example, hides its constructor with private and provides a single public method to retrieve the one instance. Understanding access modifiers now gives you the vocabulary for these powerful techniques later.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
int count; without any keyword before the type. What access level does this field have, and from which locations can it be accessed?Show answer
count can be accessed by any class within the same package, but it cannot be accessed by classes in a different package—even subclasses. Many beginners mistakenly believe no keyword means public, but it is actually more restrictive. The field is accessible from the same class and from other classes that share an identical package declaration.Question 2: BASIC CALCULATION
com.school:
public class Dog { private String name; public int age; protected void bark() {} }
A class Kennel is in the same package (com.school) but is NOT a subclass of Dog. Which members of Dog can Kennel access?Show answer
Kennel can access age (public — accessible everywhere) and bark() (protected — accessible within the same package). It cannot access name because it is private, meaning only the Dog class itself can see it. Remember that protected grants same-package access as well as subclass access, so even though Kennel is not a subclass, it still gets access because it is in the same package.Question 3: INTERMEDIATE
Vehicle is in package com.transport and has a protected int speed; field. Class Car extends Vehicle but lives in package com.cars. Can a method inside Car read speed? Now suppose class Garage is also in com.cars but does NOT extend Vehicle. Can Garage read speed from a Car object?Show answer
Car can read speed because protected allows subclasses in other packages to access the member through inheritance. However, Garage cannot directly access speed on a Car object. Even though Garage is in the same package as Car, the protected field speed was declared in Vehicle (a different package). Protected access from outside the declaring package is only available through inheritance, not through other classes in the subclass's package. This is a subtle but important distinction.Question 4: APPLIED
BankAccount class. It has a balance field, a deposit() method, a withdraw() method, and an internal logTransaction() helper method. Choose the best access modifier for each and justify your choices.Show answer
balance field should be private because you never want external code to directly set a balance to an arbitrary value—all changes should go through deposit() or withdraw(), which can validate the amount. Both deposit() and withdraw() should be public because these are the main operations other parts of the program (like an ATM class) need to call. The logTransaction() method should be private because it is an internal implementation detail that outside code should never invoke directly. This design follows the Principle of Least Privilege.Question 5: CRITICAL THINKING
Show answer
balance is public, any teammate can write account.balance = -5000; and put the object into an invalid state, causing bugs that are extremely hard to trace. Second, it creates tight coupling: if every internal method is public, other classes will start depending on those methods. When you later refactor or rename an internal method, you break every class that used it. A better strategy is to start with everything private, then promote only the members that genuinely need wider access. Public fields should almost always be replaced with private fields plus public getter and setter methods that include validation logic.SUMMARY
Summary
Summary
Java provides four access modifiers that control the visibility of classes, methods, and fields. public makes a member accessible from any class anywhere. protected allows access within the same package and from subclasses in other packages. Package-private (the default, with no keyword) restricts access to classes in the same package. private is the most restrictive, allowing access only within the declaring class itself.
The guiding principle is least privilege: start with private and widen access only when necessary. Fields should almost always be private with public getter and setter methods to preserve encapsulation. Mastering access modifiers is essential for writing clean, maintainable, and secure Java programs.