JavaData & JavaUnderstanding the Intricacies of Java Access Modifiers

Understanding the Intricacies of Java Access Modifiers

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Java is a pure object-oriented language where classes and interfaces are its basic units of abstraction. An important consideration for well-formed, object-oriented design is the degree of hiding of its internal data and implementation details in a way that other modules are unable to communicate except through its APIs. This tenet (called information hiding or encapsulation) not only encourages decoupling, but also, in a way, creates a sense of separation of concern from the other modules that comprise a system. Access modifiers are the means to achieve this end.

Levels of Accessibility

Accessibility is a static property. Java provides four categories of modifier: private, protected, public, and default access. All fields, constructors, methods, classes, and interface declarations are preceded by an access modifier. Generally, instance variables are declared private and methods are declared public. On occasion, it may be appropriate to declare certain methods private and some instance variables protected as well.

  • Private: With a private access modifier, the members are accessible only to the methods of the top-level class in which they were declared.
  • Protected: Protected members are accessible from its derived classes or any classes in the package where it is declared.
  • Public: Public members are completely open and are accessible from anywhere.
  • Package-private: Termed as default access, these members are accessible from any class within the package and are declared without specifying any modifier.

Public and private access modifiers are pretty straightforward, but protected and default access modifiers are not that obvious. As we know, protected access modifiers allow access to all its subclasses, in whatever package they may reside. On the other hand, default specifiers allow access within the package but not to the subclasses residing in another package. To sum up…

Access Modifiers Top-level Class Package Sub-classes World
Private Yes No No No
Protected Yes Yes Yes No
Public Yes Yes Yes Yes
Default
(Package-private)
Yes Yes No No

Let’s try a few code examples to illustrate the concept further.

package org.accessmodifier.example;
public class AnyClass {
   String defaultStr;
   private String privateStr;
   protected String protectedStr;
   public String publicStr;

   public AnyClass(){
      defaultStr="This is a default string";
      privateStr="This is a private string";
      protectedStr="This is a protected string";
      publicStr="This is a public string";
   }
}

package org.anotherpackage.example;                 //different package
import org.accessmodifier.example.AnyClass;
public class TestAccess {
   public static void main(String[] args){
      AnyClass aClass=new AnyClass();
      //System.out.println(aClass.defaultStr);     //defaultStr is not visible
      //System.out.println(aClass.privateStr);     //privateStr is not visible
      //System.out.println(aClass.protectedStr);   //protectedStr is not visible
      System.out.println(aClass.publicStr);        //OK, publicStr is visible
   }
}

Now, if we put the TestAccess class within the same package such as you see in this next code segment, the scenario changes.

package org.accessmodifier.example;              //within the same package
public class TestAccess {
   public static void main(String[] args){
      AnyClass aClass=new AnyClass();
      System.out.println(aClass.defaultStr);     //OK, defaultStr is visible
      //System.out.println(aClass.privateStr);   //privateStr is not visible
      System.out.println(aClass.protectedStr);   //OK, protectedStr is visible
      System.out.println(aClass.publicStr);      //OK, publicStr is visible
   }
}

Intricacies of Access Modifiers for Classes and Interfaces

Classes and interface can never be private or protected. They can either be public or default (package-private). But, when we a write class or interface within the class, it has a different say. For example:

private class AnyClass {...}

or

private interface AnyInterface {...}

is not allowed. But, when we write a inner class or interface the following is a valid statement.

public class AnyClass {
   private interface innerInterface1{...}
   private class innerClass1{ ...}
   protected interface innerInterface2{...}
   protected class innerClass2{...}
   public interface innerInterface3{...}
   public class innerClass3{...}
   interface innerInterface4{ ...}
   class innerClass4{...}
}

However, inner classes and inner interfaces within a public interface must be declared public or default, and nothing else.

public interface myinterface {
   public interface innerInterface1{...}
   public class innerClass1{...}
   interface innerInterface2{...}
   class innerClass2{...}
}

Intricacies of Access Modifiers for Constructors

A constructor can have private, protected, public, or default accessibility. A constructor declared protected is accessible in the subclass or code within the package and from the subclasses outside the package. That means that an object outside the package can be instantiated only if it is a subclass of the top-level class.

Accessible within the same package:

package org.accessmodifier.example;
public class AnyClass {
   protected AnyClass(){...}
}

package org.accessmodifier.example;
public class TestAccess{
   public TestAccess() {
      AnyClass a=new AnyClass();
   }
   public static void main(String[] args){
      AnyClass ta=new AnyClass();
   }
}

Accessible outside the package only if it is a subclasses:

package org.anotherpackage.example;
import org.accessmodifier.example.AnyClass;
public class test extends AnyClass{
   public test(){
      super();
   }
}

However, if the constructor is declared with default access, objects can be instantiated only in the code within the package or subclass within the package. Outside the package, it is completely invisible. In the following code, the AnyClass constructor has default (package-private) accessibility.

package org.accessmodifier.example;
public class AnyClass {
   AnyClass(){...}
}

Therefore, the following code will not work.

package org.anotherpackage.example;
import org.accessmodifier.example.AnyClass;
public class test extends AnyClass{
   public test(){
      super();
   }
}

These are some of the intricacies of access modifiers in Java. But before we conclude, let’s try to understand the reason and utility of such restricted accessibility.

Reasons for Restriction

Accessibility promotes software reuse, and proper use of it can ease the burden of maintenance in the long run. Accessibility restriction is the way how encapsulation or information hiding is implemented. This tenet of information hiding isolates the concern of one class from another. Debugging or modifying each module becomes quick and efficient with minimal unwanted ripple effect. Modules created from one or more such classes in a system share the same phenomenon. Modules are like branches of the system tree where leaves are its individual classes.

Performance purists may argue that this restriction of accessibility degrades performance; this is true, but it can be tuned to a great extent if designed correctly. After the system is built, profiling can identify the modules that are causing performance problems. Because modules are not tightly coupled, they can be separately tuned without degrading the performance of other modules. Further, information hiding decreases the risk of a complete revamp even if the system as a whole is not working because glitches may be in one or two modules while other modules may be quite upright at their stakea. Therefore, the idea of accessibility restriction is to incorporate some of the important software engineering principles such as loose coupling, cohesiveness, and re-usability. Because a pictures speaks a thousand words, the idea of loose and tight coupling can be illustrated as follows.

Intracacies1 Intracacies2
Figure 1: Tight coupling Figure 2: Loose coupling

Conclusion

Access modifiers are the ends to justify the means of a wider connotation of encapsulation, a basic feature of object-oriented technology. If you move deeper with these modifiers with public, private, protected, or default several interesting combinations may crop up. Most critical combinations would have rare practical uses, but they are there. We can always tinker for the cause of tinkering :). However, I believe the best way to understand the basis of access modifiers and their intricacies is by applying a common sense approach.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories