Introduction
In Java, access specifiers (also called access modifiers) define the scope and visibility of classes, methods, variables, and constructors. They are an essential part of object-oriented programming because they enforce encapsulation, prevent misuse of code, and allow developers to design secure and maintainable applications.
By controlling how different parts of a program interact, access specifiers help you decide who can see what in your code.
✅What Are Access Specifiers?
Access specifiers in Java are keywords that control the visibility of classes and their members. Their primary purpose is to:
- Enforce encapsulation → Hide implementation details.
- Provide controlled access → Expose only what is necessary.
- Improve security → Prevent unauthorized use or modification.
- Enhance maintainability → Reduce unintended dependencies.
✅Types of Access Specifiers in Java
Java provides four levels of access control:
Default (Package-Private)
- No keyword is used.
- Accessible only within the same package.
Private
- Accessible only within the declared class.
- Cannot be applied to top-level classes.
Protected
- Accessible within the same package and by subclasses, even if they are in different packages.
- Useful in inheritance scenarios.
Public
- Typically used for APIs or classes designed for global use.
- Accessible from anywhere, across all packages and classes.
1. Default (Package-Private) Access
If no access modifier is specified, Java applies default (package-private) access.
class MyClass {
void display() {
System.out.println("Default access method");
}
}
- When no access modifier is specified, it is considered the default access modifier, which allows access to class members only within the same package.
- Useful for creating package-level APIs or services.
2. Private Access
Private members are strictly confined to their class.
public class Example {
private int age;
private void printAge() {
System.out.println("Age: " + age);
}
}
- The private access modifier restricts access to a class, method, or variable within the same class only; it cannot be accessed from any other class.
- Ideal for sensitive data (e.g., passwords, configurations).
- Note: Top-level classes cannot be private.
3. Protected Access
Protected members are more permissive than private but still controlled.
package animals;
public class Animal {
protected void sound() {
System.out.println("Animal makes a sound");
}
}
package pets;
import animals.Animal;
public class Dog extends Animal {
public void bark() {
sound(); // accessible because Dog is a subclass
}
}
- The protected access modifier allows access to class members within the same package and also to subclasses in different packages.
- Commonly used in inheritance for customization.
4. Public Access
Public members are universally accessible.
public class User {
public void login() {
System.out.println("User logged in");
}
}
- The public access modifier makes a class, method, or variable accessible from any other class, with no restrictions on its visibility.
- Often used for entry points (e.g.,
main
method), API methods, and utility classes.
Scope and Usage Table
Specifier | Class | Package | Subclass | World |
---|---|---|---|---|
Default | Yes | Yes | Yes (same package) | No |
Private | Yes | No | No | No |
Protected | Yes | Yes | Yes (even in other packages) | No |
Public | Yes | Yes | Yes | Yes |
✅ Access Modifiers with Classes
- public Class
- A
public
class can be accessed from any other class, in any package.
✔️Example:
// File: Vehicle.java
public class Vehicle {
public void display() {
System.out.println("This is a public class.");
}
}
// File: TestVehicle.java (in the same or different package)
public class TestVehicle {
public static void main(String[] args) {
Vehicle v = new Vehicle();
v.display(); // Accessible everywhere
}
}
- Default Class (Package-Private)
- If no modifier is specified, the class is package-private and accessible only within the same package.
✔️Example :
// File: Engine.java
class Engine {
void showType() {
System.out.println("This is a default class.");
}
}
// File: TestEngine.java (Same Package)
public class TestEngine {
public static void main(String[] args) {
Engine e = new Engine();
e.showType(); // Accessible because it's in the same package
}
}
👉 Classes cannot be declared private
or protected
.
✅Access Modifiers with Interface
By default, interface members (methods and variables) are public
.
- Methods are implicitly
public abstract
. - Variables are implicitly
public static final
.
public interface Animal {
// Implicitly public abstract method
void sound();
// Implicitly public static final variable
int LEGS = 4;
}
public class Dog implements Animal {
@Override
public void sound() {
System.out.println("Dog barks.");
}
public void showLegs() {
System.out.println("Legs: " + LEGS);
}
}
public class TestAnimal {
public static void main(String[] args) {
Dog dog = new Dog();
dog.sound(); // Output: Dog barks.
dog.showLegs(); // Output: Legs: 4
}
}
👉 Access modifiers cannot restrict interface methods or variables further.
✅ Access Modifiers in Inheritance
Inheritance allows a subclass to inherit fields and methods from a superclass. Access modifiers determine what can be inherited.
✔️Example of Access Modifier Effect in Inheritance
public class Parent {
public int publicVar = 1;
protected int protectedVar = 2;
int defaultVar = 3; // Package-private
private int privateVar = 4;
public void showAll() {
System.out.println("Public: " + publicVar);
System.out.println("Protected: " + protectedVar);
System.out.println("Default: " + defaultVar);
System.out.println("Private: " + privateVar);
}
}
public class Child extends Parent {
public void display() {
System.out.println("Access publicVar: " + publicVar); // Accessible
System.out.println("Access protectedVar: " + protectedVar); // Accessible
System.out.println("Access defaultVar: " + defaultVar); // Accessible if same package
// System.out.println("Access privateVar: " + privateVar); // ❌ Not accessible
}
}
public class TestInheritance {
public static void main(String[] args) {
Child c = new Child();
c.display();
}
}
✅ Summary Table – Access Modifiers in Inheritance
Modifier | Same Class | Subclass (Same Package) | Subclass (Different Package) | Other Classes |
---|---|---|---|---|
public | ✔ | ✔ | ✔ | ✔ |
protected | ✔ | ✔ | ✔ | ❌ |
default | ✔ | ✔ | ❌ | ❌ |
private | ✔ | ❌ | ❌ | ❌ |
✅Best Practices for Access Specifiers
- Use
private
by default → Start with the most restrictive access and open up only if required. - Encapsulate sensitive data → Keep variables private and expose them via getters/setters if necessary.
- Use
protected
carefully → Only for methods intended to be customized by subclasses. - Use
public
sparingly → Overexposing methods leads to tight coupling and harder maintenance. - Prefer default access → For internal classes and methods that should stay package-bound.
Common Mistakes to Avoid
- ❌ Declaring top-level classes as private/protected → Not allowed, only public or default is permitted.
- ❌ Overusing public → Leads to poor encapsulation and fragile code.
- ❌ Forgetting constructor visibility → A class with a private constructor cannot be instantiated from outside (common in Singleton design pattern).
- ❌ Mixing access with unrelated logic → Choose access levels based on design needs, not convenience.
Conclusion
Access specifiers are a fundamental concept in Java that every developer must master. By carefully choosing the right modifier—private
, default
, protected
, or public
—you can design classes that are secure, extensible, and maintainable.