Access specifiers in Java

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

SpecifierClassPackageSubclassWorld
DefaultYesYesYes (same package)No
PrivateYesNoNoNo
ProtectedYesYesYes (even in other packages)No
PublicYesYesYesYes

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

ModifierSame ClassSubclass (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.

Java developer with 9+ years of IT experience, sharing tutorials and tips to help learners master Java programming.

Leave a Reply

Your email address will not be published. Required fields are marked *