Question
Java Access Modifiers Explained: public, protected, package-private, and private
Question
In Java, what are the clear rules for choosing between the access modifiers public, protected, package-private (the default), and private when designing classes and interfaces, especially when inheritance is involved?
Short Answer
By the end of this page, you will understand what each Java access modifier allows, where it can be used, and how it affects visibility from the same class, same package, subclasses, and other classes. You will also learn practical guidelines for choosing the most restrictive modifier that still supports your design.
Concept
In Java, access modifiers control who can see and use a class, method, field, or constructor.
These modifiers are a core part of encapsulation, which means hiding internal details and exposing only what other code truly needs.
Java has four access levels:
public— accessible from anywhereprotected— accessible within the same package, and in subclasses outside the package- package-private — accessible only within the same package; this is the default when no modifier is written
private— accessible only within the same class
Why this matters
Choosing the right access level helps you:
- protect internal implementation details
- prevent accidental misuse
- make APIs easier to understand
- reduce coupling between classes
- change code later without breaking other parts of the program
A common rule in real Java code is:
- start with the most restrictive access possible
- only make something more visible when there is a real need
Where each modifier can be used
For top-level types
Top-level classes and interfaces can only be:
public- package-private
They cannot be private or protected.
For members inside a class
Fields, methods, constructors, and nested classes can use all four:
publicprotected- package-private
private
Visibility summary
| Modifier | Same class | Same package | Subclass in other package | Other package non-subclass |
|---|---|---|---|---|
private | Yes | No | No | No |
| package-private | Yes | Yes | No | No |
protected | Yes | Yes | Yes | No |
public | Yes | Yes | Yes | Yes |
Important note about protected
protected is often misunderstood.
It does two things:
- allows access from any class in the same package
- allows access from subclasses in other packages
So protected is not just “for subclasses.” In the same package, even non-subclasses can access it.
Practical design guidance
A simple way to choose:
- Use
privatefor fields and helper methods unless something outside the class truly needs access. - Use package-private for code that should be shared only inside the same package.
- Use
protectedwhen subclasses are expected to use or override something. - Use
publiconly for the API you intentionally expose to everyone.
This is why many Java classes have:
privatefieldspublicmethods for safe access- package-private helper classes
protectedextension points in frameworks or base classes
Mental Model
Think of a class as a house with rooms and doors:
private= a locked personal drawer. Only the owner can open it.- package-private = rooms shared with people in the same building.
protected= rooms shared with the building, plus family members who live elsewhere.public= the front desk open to everyone.
The key idea is: not every door should be open.
Good Java design means opening only the doors that need to be open. The fewer things other code can depend on, the easier your code is to maintain.
Syntax and Examples
Basic syntax
public class PublicClass {
}
class PackagePrivateClass {
}
public class User {
private String password;
String username; // package-private
protected void reset() {}
public String getUsername() {
return username;
}
}
Example: fields and methods
package app;
public class Account {
private double balance;
String ownerName;
protected void logAccess() {
System.out.println("Access logged");
}
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
}
What this means
balanceisprivate, so onlyAccountcan access it directly.ownerNameis package-private, so only classes in the same package can access it.logAccess()isprotected, so same-package classes and subclasses can use it.getBalance()anddeposit()arepublic, so any code with access toAccountcan call them.
Example: inheritance with protected
package animals;
public class Animal {
protected void makeSound() {
System.out.println("Some sound");
}
}
package pets;
import animals.Animal;
public class Dog extends Animal {
public void bark() {
makeSound();
}
}
Here, Dog can call makeSound() because it is a subclass of Animal, even though it is in a different package.
Step by Step Execution
Consider this example:
package demo;
class Counter {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
counter.increment();
counter.increment();
System.out.println(counter.getCount());
}
}
Step by step
-
Counter counter = new Counter();- A new
Counterobject is created. - Its
privatefieldcountstarts at0.
- A new
-
counter.increment();- The
increment()method runs. - Because
increment()is inside the same class, it can access theprivatefieldcount. countbecomes1.
- The
-
counter.increment();- The method runs again.
countbecomes2.
-
System.out.println(counter.getCount());getCount()returns the value ofcount.- The output is:
2
Key lesson
Outside code cannot do this:
counter.count = 100;
That would fail because count is private. The class controls access through public methods instead.
Real World Use Cases
1. Hiding sensitive data
public class User {
private String passwordHash;
public boolean checkPassword(String input) {
return passwordHash.equals(input); // simplified example
}
}
Sensitive fields should not be directly accessible.
2. Exposing a public API
Libraries often make only a small set of classes and methods public so users interact with a stable API.
public class JsonParser {
public Object parse(String json) {
return null;
}
}
3. Keeping helpers inside a package
A package may contain utility classes meant only for internal use.
class TokenValidator {
static boolean isValid(String token) {
return token != null && !token.isEmpty();
}
}
This avoids exposing internal logic to the whole application.
4. Designing for inheritance
Frameworks often use protected methods as extension points.
public class BaseController {
protected void beforeHandle() {
System.out.println("Common setup");
}
}
Subclasses can customize behavior without exposing the method to every class everywhere.
5. Safe object creation
Constructors may use restricted access to control how objects are created.
public class Config {
private Config() {}
public static Config load() {
return new Config();
}
}
This pattern is common in factories and utility classes.
Real Codebase Usage
In real Java projects, access modifiers are usually chosen as part of API design.
Common patterns
private for fields
Most fields are private.
public class Product {
private String name;
private double price;
public String getName() {
return name;
}
}
This protects object state and allows validation later.
public for stable operations
Methods that form the intended API are often public.
public void addItem(Item item) {
if (item == null) {
throw new IllegalArgumentException("item cannot be null");
}
items.add(item);
}
package-private for internal collaboration
In larger codebases, several classes in one package may work together without exposing themselves to the rest of the system.
Examples:
- parsers and tokenizers in the same parser package
- service helpers used only inside one module
- implementation classes hidden behind public interfaces
protected for extension hooks
Base classes may expose methods to subclasses but keep them out of the wider public API.
public abstract class Job {
public final void run() {
validate();
execute();
}
protected void validate() {
}
protected abstract void execute();
}
This is common in template method patterns and framework code.
Good rule in production code
- prefer
private - use package-private for package internals
- use
protectedonly when subclass access is part of the design - use
publiconly for deliberate external APIs
If you make everything public, other code can depend on too many details, making future refactoring harder.
Common Mistakes
1. Making fields public
Broken design:
public class Person {
public int age;
}
Why this is a problem:
- any code can set invalid values
- you cannot easily add validation later
Better:
public class Person {
private int age;
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
this.age = age;
}
public int getAge() {
return age;
}
}
2. Confusing package-private with protected
Many beginners think protected means “subclasses only.” It does not.
protected void doWork() {}
This is accessible:
- in the same package, even from non-subclasses
- in subclasses outside the package
3. Forgetting that no modifier means package-private
class Helper {
}
This class is not public. It is visible only inside its package.
4. Trying to use private or protected on top-level classes
Broken code:
private class Helper {
}
Top-level classes can only be public or package-private.
5. Exposing methods too early
Beginners often make methods public “just in case.”
A better habit:
- start with
private - widen access only when needed
6. Assuming private means inaccessible to the whole object
Code inside the same class can access private members of another instance of the same class.
public class Box {
private int value;
public boolean sameValue(Box other) {
return this.value == other.value;
}
}
This is valid because both accesses happen inside Box.
Comparisons
Access modifier comparison
| Modifier | Where usable | Visible in same class | Visible in same package | Visible in subclass in other package | Visible everywhere |
|---|---|---|---|---|---|
private | Members, nested classes | Yes | No | No | No |
| package-private | Top-level types, members, nested classes | Yes | Yes | No | No |
protected | Members, nested classes | Yes | Yes | Yes | No |
public | Top-level types, members, nested classes | Yes | Yes | Yes | Yes |
protected vs package-private
| Feature | protected | package-private |
|---|---|---|
| Same package access | Yes | Yes |
| Subclass access from another package | Yes | No |
| Good for inheritance APIs | Yes | No |
| Hidden outside package in non-subclasses | Yes | Yes |
private vs public
| Choice | When to use |
|---|---|
private | Internal state and helper logic |
public | Intended external API |
Practical default choice
If you are unsure:
- start with
private - use package-private if neighboring classes need access
- use
protectedif subclasses need access - use
publicif all callers should access it
Cheat Sheet
Quick rules
private= only inside the same class- package-private = no modifier; same package only
protected= same package + subclasses in other packagespublic= everywhere
Top-level class rules
Allowed:
public class A {}
class B {}
Not allowed:
protected class C {}
private class D {}
Member example
public class Example {
private int a;
int b;
protected int c;
public int d;
}
Best practice
- keep fields
private - expose behavior through methods
- prefer the least visible modifier that works
- use
protectedintentionally, not automatically
Memory aid
Visibility from smallest to widest:
private < package-private < protected < public
Common exam/interview fact
protected includes:
- same package access
- subclass access across packages
It is not limited to subclasses.
FAQ
When should I use private in Java?
Use private for fields and helper methods that should only be used inside the class. This is the safest default.
What is package-private in Java?
Package-private is the default access level when no modifier is written. It allows access only within the same package.
Is protected only for subclasses?
No. protected also allows access from any class in the same package, even if it is not a subclass.
Can a top-level Java class be private?
No. A top-level class can only be public or package-private.
Should Java fields be public?
Usually no. Public fields expose internal state directly and make validation and future changes harder.
When should I use protected instead of package-private?
Use protected when subclasses outside the package need access. If access is only needed inside the same package, package-private is often better.
What is the safest default access modifier?
Usually private for members, and package-private for helper top-level classes that should stay internal to a package.
Mini Project
Description
Build a small Java example that models a bank account and uses all four access levels correctly. This project demonstrates encapsulation, package-level collaboration, and controlled inheritance.
Goal
Create a simple set of classes where sensitive data stays private, internal helpers remain package-private, subclasses use protected behavior, and public methods expose the safe API.
Requirements
- Create a
BankAccountclass with a private balance field. - Add public methods to deposit money and read the balance.
- Add a protected method that subclasses can use for audit behavior.
- Add a package-private helper class used only inside the same package.
Keep learning
Related questions
Add External JAR Files to an IntelliJ IDEA Java Project
Learn how to add external JAR dependencies to an IntelliJ IDEA Java project using module libraries, and when to use Maven or Gradle instead.
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Call a Method After a Delay in Android Java
Learn how to run Java code after a delay in Android using Handler.postDelayed, manage the main thread, and cancel callbacks safely.