Access Modifiers in Java
Four levels of visibility control who can see a member, and choosing the narrowest one that works is the single easiest design win.
-
Java Basics
- Introduction to Java
- Setting Up Java and Writing Your First Program
- Variables, Data Types and Literals in Java
- Type Casting and Type Conversion in Java
- Operators and Expressions in Java
- Input and Output in Java
- Comments, Keywords and Naming Conventions in Java
- Control Flow in Java: if, else and switch
- Loops in Java: for, while and do-while
- Methods
- Arrays and Strings
-
OOP
- Classes and Objects in Java
- Constructors in Java
- The this Keyword in Java
- The static Keyword in Java
- Encapsulation in Java
- Access Modifiers in Java
- Inheritance in Java
- Method Overriding and super in Java
- Polymorphism in Java
- Abstraction, Abstract Classes and Interfaces in Java
- Composition, Aggregation and Association in Java
- The Object Lifecycle in Java
- Core Java
- Exception Handling
-
Collections
- The Java Collections Framework
- List in Java: ArrayList, LinkedList, Vector and Stack
- Set in Java: HashSet, LinkedHashSet and TreeSet
- Map in Java: HashMap, LinkedHashMap and TreeMap
- How HashMap Works Internally in Java
- Queue and Deque in Java: ArrayDeque and PriorityQueue
- Iterators in Java
- Comparable and Comparator in Java
- Collections Utilities and Choosing the Right Collection
- Generics
- Java 8+
- Stream API
- Date and Time
- File and I/O
-
Multithreading
- Threads in Java: Processes, Runnable and Thread
- Thread Lifecycle in Java
- Synchronization in Java: synchronized and volatile
- Locks and Atomic Classes in Java
- Race Conditions and Deadlocks in Java
- The Executor Framework and Thread Pools in Java
- Future and CompletableFuture in Java
- Concurrent Collections in Java
- The Java Memory Model
- JVM and Memory
- Advanced Java
- Networking
- JDBC
- Testing
The four levels
| Modifier | Same class | Same package | Subclass, other package | Anywhere |
|---|---|---|---|---|
private | Yes | No | No | No |
| package private (no keyword) | Yes | Yes | No | No |
protected | Yes | Yes | Yes | No |
public | Yes | Yes | Yes | Yes |
Writing no modifier at all is a real choice, called package private or default access. It is not the same as public.
Example
package com.example.billing;
public class Invoice {
private double total; // this class only
double taxRate; // package private: com.example.billing
protected String reference; // package, plus subclasses anywhere
public String customerName; // everywhere
private double tax() { // an internal helper
return total * taxRate;
}
public double payable() { // the public operation
return total + tax();
}
}What protected really means
package com.example.reports;
import com.example.billing.Invoice;
public class TaxReport extends Invoice {
void show() {
System.out.println(this.reference); // allowed: through inheritance
}
void showOther(Invoice other) {
// System.out.println(other.reference); // not allowed
}
}From another package, protected gives a subclass access to the member through its own inheritance, not on arbitrary instances of the superclass. This restriction surprises people and is worth remembering.Top level types
public class Order { } // visible everywhere, file must be Order.java
class OrderValidator { } // package private, a helper for this package onlyA top level class can only be public or package private. private and protected apply to nested types, not to top level ones.
Access and overriding
class Base {
protected void run() { }
}
class Derived extends Base {
@Override
public void run() { } // widening is allowed
// private void run() { } // narrowing is a compile error
}An override may widen visibility but never narrow it, because a subclass must remain usable everywhere the superclass is.
A practical layout
public class NoteService {
private final NoteRepository repository; // internal collaborator
public NoteService(NoteRepository repository) {
this.repository = repository;
}
public Note publish(long id) { // the API
Note note = repository.find(id);
validate(note);
return repository.save(note.withStatus("published"));
}
private void validate(Note note) { // an implementation detail
if (note.title().isBlank()) {
throw new IllegalArgumentException("Title is required");
}
}
}Two public members and one private helper. Anyone reading the class knows immediately what it offers and what is internal.
Modules
Since Java 9 a module adds a further layer: a package is only visible outside the module if the module declaration exports it. A public class in a package that is not exported is unreachable from other modules. Access control is therefore a combination of the modifier and the module declaration.
Common mistakes
- Making everything
publicso the code compiles quickly, and then being unable to change anything later. - Using
protectedfor fields, which permanently ties every subclass to the current representation. - Assuming no modifier means public.
- Trying to narrow visibility in an override.
- Assuming
privatehides a member from reflection. It does not by itself.
Best practices
- Start with
privateand widen only when something outside genuinely needs access. - Keep fields private and expose behaviour instead.
- Use package private for helper classes that support one package.
- Treat
protectedas part of the public API, because subclasses will depend on it forever. - Keep the public surface of a class small enough to describe in one sentence.
Practice
- For a
Paymentclass, decide the modifier for each member and justify each choice. - Why can a subclass in another package not read
other.referenceon a different instance? - What happens when an override tries to change
publictoprotected? - Explain when package private is a better choice than
public, with an example. - Describe how a module declaration can make a
publicclass unreachable.
Conclusion
Visibility is a design decision, not paperwork. Choose the narrowest access that works, remember that anything public or protected becomes a promise, and keep the rest free to change.