Page Content

Tutorials

What is Encapsulation in Java? & What is Access Control?

Encapsulation in Java

Encapsulation, a key idea of object-oriented programming (OOP), unites data (variables) and methods (code) into a class. It protects an object’s internal state from external code. A well-defined interface controls code and data access in this “wrapper”.

A common term for this idea is “information hiding.” A class’s implementation details are concealed, allowing users to interact with an object solely through the public services it offers without having to understand how those services are put together. Numerous advantages result from this:

  • Reduced Coupling: If a class’s public interface remains consistent, internal implementation changes don’t break code that uses it.
  • Increased Robustness and Security: It prevents external programs from mistakenly or maliciously changing an object’s state.
  • Improved Maintainability and Reusability: Classes can be reused without modification and are easier to maintain.

Access Control in Java

Java employs access control as a means of encapsulation enforcement. It controls how other program sections can access class variables and methods. These access levels can be set in Java using public, private, protected, or default (no keyword).

Access Control in Java
Access Control in Java

Let’s illustrate each modification. A scenario with two packages, p1 and p2, each containing multiple classes, will be used to demonstrate.

Package Structure:

.
└── your_project/
    ├── p1/
    │   ├── Protection.java
    │   ├── Derived.java
    │   └── SamePackage.java
    └── p2/
        ├── Protection2.java
        └── OtherPackage.java

Private Access Modifier ()

The most restricted access level is the private keyword. A member who has been designated as private is only accessible by other members in the same class. This is the main method used in Java to hide data. Subclasses and outside code cannot access private members, which are excluded from inheritance.

Accessor (getters) and mutator (setters) methods regulate private variable access. Before reading or modifying private data, these public methods allow validation or other logic.

Code Example (Protection.java in p1):

// p1/Protection.java
package p1;
public class Protection {
    int n = 1;               // Default access
    private int n_pri = 2;   // Private access
    protected int n_pro = 3; // Protected access
    public int n_pub = 4;    // Public access
    public Protection() {
        System.out.println("base constructor");
        System.out.println("n = " + n);
        System.out.println("n_pri = " + n_pri); // Accessible within its own class
        System.out.println("n_pro = " + n_pro);
        System.out.println("n_pub = " + n_pub);
    }
}

It is possible to access n_pri from within its own constructor, as demonstrated in Protection.java. A compilation error will appear if you attempt to access n_pri directly from any other class, including subclasses or classes within the same package. As demonstrated in the code for Derived.java below, for instance, Derived (a subclass in the same package) is unable to directly access n_pri.

Default Access Modifier (No keyword)

A class member is said to have default access if no access modifier is specifically assigned. Accordingly, the member can only be seen within its own package. “Package-private” or “friendly” access are other names for it. This level of access is helpful for maintaining the complete accessibility of other related classes within the same package while concealing internal helper classes or methods from code outside the package.

Code Example (Derived.java and SamePackage.java in p1):

// p1/Derived.java
package p1;
class Derived extends Protection { // Default access for class Derived
    Derived() {
        System.out.println("derived constructor");
        System.out.println("n = " + n); // Accessible: same package, subclass
        // System.out.println("n_pri = " + n_pri); // ERROR: private access
        System.out.println("n_pro = " + n_pro); // Accessible: same package, subclass
        System.out.println("n_pub = " + n_pub); // Accessible: public
    }
}
// p1/SamePackage.java
package p1;
class SamePackage { // Default access for class SamePackage
    SamePackage() {
        Protection p = new Protection();
        System.out.println("same package constructor");
        System.out.println("n = " + p.n); // Accessible: same package, non-subclass
        // System.out.println("n_pri = " + p.n_pri); // ERROR: private access
        System.out.println("n_pro = " + p.n_pro); // Accessible: same package, non-subclass (via instance)
        System.out.println("n_pub = " + p.n_pub); // Accessible: public
    }
}

Because Derived, a subclass in p1, and SamePackage, a non-subclass in p1, are all part of the same package p1, they can both access the n variable (default access) in Protection in the example.

Protected Access Modifier ()

Access to all subclasses, whether they are in the same package or a separate one, is granted by the protected keyword within its own package. Subclasses can utilise and even override members that are meant to be a part of an inheritance hierarchy thanks to this common practice.

Code Example (Protection2.java in p2):

// p2/Protection2.java
package p2;
import p1.Protection; // Import Protection class from p1
class Protection2 extends Protection { // Default access for class Protection2
    Protection2() {
        System.out.println("derived other package constructor");
        // System.out.println("n = " + n); // ERROR: Default access, not visible outside p1
        // System.out.println("n_pri = " + n_pri); // ERROR: private access
        System.out.println("n_pro = " + n_pro); // Accessible: subclass in different package
        System.out.println("n_pub = " + n_pub); // Accessible: public
    }
}

The n_pro variable (protected access) in p1.Protection is accessible from p2.Protection2 because Protection2 is a subclass of Protection, even though they are in different packages. Note that the n variable (default access) is not accessible here, as it’s outside its package and Protection2 is not in the same package as Protection.

Public Access Modifier ()

The keyword with the least restriction is public. Any other class in any Java package can access a member that has been marked public. It creates a class’s public interface, facilitating the most interactivity and visibility possible.

Code Example (OtherPackage.java in p2):

// p2/OtherPackage.java
package p2;
import p1.Protection; // Import Protection class from p1
class OtherPackage { // Default access for class OtherPackage
    OtherPackage() {
        Protection p = new Protection();
        System.out.println("other package constructor");
        // System.out.println("n = " + p.n); // ERROR: Default access, not visible outside p1
        // System.out.println("n_pri = " + p.n_pri); // ERROR: private access
        // System.out.println("n_pro = " + p.n_pro); // ERROR: Protected access, not a subclass in p2
        System.out.println("n_pub = " + p.n_pub); // Accessible: public
    }
}

The n_pub variable (public access) in p1.Protection is accessible from p2.OtherPackage because public members are globally visible.

Code Compilation and Output

To compile these files, you would navigate to the your_project directory (the parent of p1 and p2) in your terminal and use javac: javac p1/*.java p2/*.java

Everything in both packages that ends in .java will be compiled. The compiler would indicate faults if any lines that were commented out attempted to get unauthorised access.

In order to execute, you would normally have a main method in one of these classes. For example, if OtherPackage.java were public, you could add a main method to it:

// Example main method in OtherPackage.java (hypothetical, as OtherPackage is default class here)
// public class OtherPackage { // This class would need to be public to have a public static void main
//    public static void main(String[] args) {
//        new OtherPackage();
//    }
// }

Should the operation be carried out, the output would show the successful and unsuccessful (commented out) access attempts:

Output from new p1.Protection(); (if run directly or from a main in p1):

base constructor
n = 1
n_pri = 2
n_pro = 3
n_pub = 4

Output from new p1.Derived(); (if run from a main in p1):

base constructor
n = 1
n_pri = 2
n_pro = 3
n_pub = 4
derived constructor
n = 1
n_pro = 3
n_pub = 4

Output from new p1.SamePackage(); (if run from a main in p1):

base constructor
n = 1
n_pri = 2
n_pro = 3
n_pub = 4
same package constructor
n = 1
n_pro = 3
n_pub = 4

Output from new p2.Protection2(); (if run from a main in p2):

base constructor
n = 1
n_pri = 2
n_pro = 3
n_pub = 4
derived other package constructor
n_pro = 3
n_pub = 4

Output from new p2.OtherPackage(); (if run from a main in p2):

base constructor
n = 1
n_pri = 2
n_pro = 3
n_pub = 4
other package constructor
n_pub = 4
Index