What is the purpose of access modifiers?
Categories:
Understanding Access Modifiers in Java: Controlling Visibility and Encapsulation

Explore the purpose and usage of Java access modifiers (public, protected, default, private) to manage class, method, and field visibility, ensuring proper encapsulation and secure code design.
Access modifiers in Java are keywords that set the accessibility (visibility) of classes, constructors, methods, and fields. They are a fundamental aspect of object-oriented programming, primarily used to implement encapsulation, a core OOP principle. By controlling what parts of your code are accessible from where, access modifiers help in building robust, secure, and maintainable applications. This article will delve into each access modifier, explaining its scope and providing practical examples.
The Four Pillars of Access Control
Java provides four types of access modifiers: public
, protected
, default
(no keyword), and private
. Each offers a different level of visibility, ranging from completely open to strictly restricted. Understanding these levels is crucial for designing well-structured and secure applications.
flowchart TD A[Member] --> B{Access Modifier?} B -->|public| C[Accessible Everywhere] B -->|protected| D[Accessible within Package & Subclasses] B -->|default (no keyword)| E[Accessible within Package Only] B -->|private| F[Accessible within Class Only]
Hierarchy of Java Access Modifiers and their visibility scope.
1. Public Access Modifier
The public
access modifier is the most permissive. When a class, method, or field is declared public
, it can be accessed from anywhere within the same project, including other classes, packages, and even different applications that use your code. This is typically used for methods that form the public API of a class or for constants that need to be universally available.
package com.example.app;
public class PublicExample {
public int publicVariable = 10;
public void publicMethod() {
System.out.println("This is a public method.");
}
}
// In another package, e.g., com.example.anotherapp
package com.example.anotherapp;
import com.example.app.PublicExample;
public class AccessPublic {
public static void main(String[] args) {
PublicExample obj = new PublicExample();
System.out.println(obj.publicVariable); // Accessible
obj.publicMethod(); // Accessible
}
}
Demonstrates public
access from different packages.
2. Protected Access Modifier
The protected
access modifier allows members to be accessed within the same package and by subclasses (even if those subclasses are in different packages). This is particularly useful when you want to provide certain functionalities to derived classes while still restricting general public access. It supports inheritance by allowing child classes to interact with parent class members.
package com.example.base;
public class ParentClass {
protected int protectedVariable = 20;
protected void protectedMethod() {
System.out.println("This is a protected method.");
}
}
// In the same package
package com.example.base;
class SamePackageClass {
void accessProtected() {
ParentClass parent = new ParentClass();
System.out.println(parent.protectedVariable); // Accessible
parent.protectedMethod(); // Accessible
}
}
// In a different package, as a subclass
package com.example.derived;
import com.example.base.ParentClass;
public class ChildClass extends ParentClass {
void accessProtectedFromChild() {
System.out.println(protectedVariable); // Accessible
protectedMethod(); // Accessible
}
}
// In a different package, NOT a subclass
package com.example.other;
import com.example.base.ParentClass;
public class OtherClass {
void tryAccess() {
ParentClass parent = new ParentClass();
// System.out.println(parent.protectedVariable); // Compile-time error
// parent.protectedMethod(); // Compile-time error
}
}
Illustrates protected
access within the same package and by subclasses.
3. Default (Package-Private) Access Modifier
When no access modifier is specified for a class, method, or field, it is considered to have default
or package-private access. This means the member is only accessible within its own package. It's a good choice for utility classes or helper methods that are only relevant to other components within the same package and should not be exposed externally.
package com.example.data;
class DefaultExample {
int defaultVariable = 30; // Default access
void defaultMethod() { // Default access
System.out.println("This is a default method.");
}
}
// In the same package
package com.example.data;
public class AccessDefault {
public static void main(String[] args) {
DefaultExample obj = new DefaultExample();
System.out.println(obj.defaultVariable); // Accessible
obj.defaultMethod(); // Accessible
}
}
// In a different package
package com.example.utils;
// import com.example.data.DefaultExample; // Cannot import default class
public class TryAccessDefault {
public static void main(String[] args) {
// DefaultExample obj = new DefaultExample(); // Compile-time error
// obj.defaultMethod(); // Compile-time error
}
}
Shows default
access, restricted to the same package.
4. Private Access Modifier
The private
access modifier is the most restrictive. Members declared private
are only accessible from within the class in which they are declared. They cannot be accessed from outside the class, not even by subclasses or classes within the same package. This is the cornerstone of strong encapsulation, hiding implementation details and exposing functionality only through public methods (getters/setters).
package com.example.model;
public class Account {
private double balance = 1000.0; // Private variable
private void logTransaction(String type, double amount) {
System.out.println("Transaction: " + type + ", Amount: " + amount);
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
logTransaction("Deposit", amount);
}
}
public double getBalance() {
return balance;
}
}
// In another class (even in the same package)
package com.example.model;
public class AccessAccount {
public static void main(String[] args) {
Account myAccount = new Account();
// System.out.println(myAccount.balance); // Compile-time error
// myAccount.logTransaction("Withdraw", 50); // Compile-time error
myAccount.deposit(200); // Accessible via public method
System.out.println("Current Balance: " + myAccount.getBalance());
}
}
Demonstrates private
access, only within the declaring class.
private
and only increase visibility (default
, protected
, public
) if absolutely necessary for functionality. This practice promotes strong encapsulation and reduces coupling.Summary of Access Levels
The following table summarizes the visibility of each access modifier across different scopes. This overview helps in quickly deciding which modifier to use for a given member.

Access Modifier Visibility Summary
Choosing the correct access modifier is a critical design decision that impacts the maintainability, extensibility, and security of your Java applications. By carefully controlling visibility, you can prevent unintended modifications, hide complex implementation details, and create clear, stable APIs for your classes.