Java tutorials > Core Java Fundamentals > Object-Oriented Programming (OOP) > What are access modifiers and their scope?

What are access modifiers and their scope?

Access modifiers in Java control the visibility or accessibility of classes, methods, constructors, and variables. They determine which parts of the code can access these elements. Understanding access modifiers is crucial for encapsulation, data hiding, and building robust and maintainable Java applications.

Overview of Access Modifiers

Java provides four access modifiers:
  • public: Accessible from anywhere.
  • protected: Accessible within the same package and by subclasses in different packages.
  • default (package-private): Accessible only within the same package. No explicit keyword is needed.
  • private: Accessible only within the declared class.

Public Access Modifier

The public access modifier grants the widest level of accessibility. Any class, method, or variable declared as public can be accessed from any other class, regardless of the package they belong to.

public class PublicClass {
    public int publicVariable = 10;

    public void publicMethod() {
        System.out.println("This is a public method.");
    }
}

Protected Access Modifier

The protected access modifier provides access within the same package and to subclasses (derived classes) even if they are in a different package. If a subclass tries to access a protected member of its superclass through an instance of the superclass, rather than through inheritance (i.e. using the 'this' keyword, or an instance of itself), it will not be allowed unless they are in the same package.

package package1;

public class ProtectedClass {
    protected int protectedVariable = 20;

    protected void protectedMethod() {
        System.out.println("This is a protected method.");
    }
}

// In a different package
package package2;

import package1.ProtectedClass;

class SubClass extends ProtectedClass {
    public void accessProtected() {
        System.out.println("Protected Variable: " + protectedVariable);
        protectedMethod();
    }
}

Default (Package-Private) Access Modifier

When no access modifier is specified, it defaults to package-private (also known as default). This means the class, method, or variable is accessible only within the same package.

package package1;

class DefaultClass {
    int defaultVariable = 30; // No access modifier specified

    void defaultMethod() { // No access modifier specified
        System.out.println("This is a default method.");
    }
}

//In the same package
package package1;

class AnotherClass {
    public void accessDefault() {
        DefaultClass obj = new DefaultClass();
        System.out.println("Default Variable: " + obj.defaultVariable);
        obj.defaultMethod();
    }
}

Private Access Modifier

The private access modifier provides the strictest level of access control. A class, method, or variable declared as private is only accessible within the class in which it is declared.

public class PrivateClass {
    private int privateVariable = 40;

    private void privateMethod() {
        System.out.println("This is a private method.");
    }

    public void accessPrivate() {
        System.out.println("Private Variable: " + privateVariable);
        privateMethod();
    }
}

// Attempting to access from another class will result in a compilation error
public class AnotherClass {
    public void accessPrivate() {
        PrivateClass obj = new PrivateClass();
        // System.out.println("Private Variable: " + obj.privateVariable); // Compilation error
        // obj.privateMethod(); // Compilation error
    }
}

Scope Summary

Here’s a summary table of the access modifier scope:
Modifier Class Package Subclass World
public Yes Yes Yes Yes
protected Yes Yes Yes No
default (package-private) Yes Yes No No
private Yes No No No

Concepts Behind the Snippet

The key concept is encapsulation, which involves bundling data (attributes/variables) and methods that operate on that data into a single unit (class). Access modifiers support encapsulation by controlling the visibility of class members, helping to protect internal state and prevent unintended modifications.

Real-Life Use Case Section

Consider a BankAccount class. The account balance should be private to prevent direct access and modification from outside the class. public methods like deposit() and withdraw() provide controlled access to modify the balance. A protected method might be used in a subclass to handle overdrafts.

Best Practices

  • Use the most restrictive access modifier that is appropriate for a particular member.
  • Make instance variables private whenever possible to prevent direct access and modification.
  • Avoid using public fields; instead, provide getter and setter methods for controlled access.
  • Consider the package structure and subclass relationships when choosing between protected and default access modifiers.

Interview Tip

Be prepared to explain the differences between the access modifiers and provide examples of when to use each one. Understand the implications of each access modifier on encapsulation and inheritance. A common interview question involves designing a class hierarchy and explaining how access modifiers are used to control access to members.

When to Use Them

  • Use public when you want members to be accessible from anywhere.
  • Use protected when you want members to be accessible within the package and by subclasses.
  • Use default (package-private) when you want members to be accessible only within the same package.
  • Use private when you want members to be accessible only within the class.

Memory Footprint

Access modifiers themselves don't directly affect memory footprint. They are compile-time constructs that control access. The actual memory usage is determined by the data types of the variables and the size of the objects created.

Alternatives

Alternatives to consider are design patterns like the Singleton pattern (which restricts instantiation) or using interfaces to define a contract that classes must adhere to, providing a level of abstraction and control over access. For instance, instead of making a method public, you could have the class implement an interface that exposes a specific method, thereby controlling the access from the outside.

Pros

  • Encapsulation: Protects data and prevents unintended modifications.
  • Abstraction: Hides implementation details and exposes a simplified interface.
  • Maintainability: Makes code easier to understand, modify, and debug.
  • Security: Restricts access to sensitive data and functionality.

Cons

  • Increased Complexity: Overuse of access modifiers can make the code more complex.
  • Limited Flexibility: Too restrictive access can limit the ability to extend and reuse code.

FAQ

  • What happens if I don't specify an access modifier?

    If you don't specify an access modifier, the default (package-private) access modifier is applied. This means the member is only accessible within the same package.
  • Can I change the access modifier of a method in a subclass?

    You can increase the visibility of a method in a subclass (e.g., change from protected to public), but you cannot decrease it (e.g., change from public to private). This is due to the Liskov Substitution Principle.
  • Why should I use private instance variables?

    Using private instance variables promotes encapsulation. It prevents direct access to the internal state of the object from outside the class, allowing you to control how the state is modified through getter and setter methods. This helps maintain data integrity and allows for future modifications without breaking external code.