April 17, 2024

lealceldeiro

The access control mechanisms in Java allow us to have a more secure way of building our applications, libraries, and code in general. In this article, you’ll learn what are the access controls implemented by the Java platform, how to use them and have a better picture of how they work in general.

What’s "access control" in Java?

Access control refers to the mechanisms defined by Java to guarantee that packages, classes, etc. are accessible only to those entities which have the correct permissions and not to others which are not entitled to access them.

This sometimes can be seen as a restrictive behavior, but it’s more than that: it shields users of a class or package from depending on specific implementation details that are irrelevant to them and that could potentially break their code if such details are changed.

In short, if access to an entity is permitted, that entity is said to be accessible.

How’s accessibility to an entity determined?

Top level classes

Top level class can be declared only either public or package private, any other attempt to specify an access modifier would result in a compilation error.

Public classes

Let’s start with the most permissive one. If a top level class or interface is declared public, then it may be accessed by any code declared within the same module.

On top of that, if such a class or interface is a member of a package that is exported by its module, then it’ll also be accessible by code located in another module to which the package is exported, as long as the compilation unit in which the class or interfaces is declared is visible to that other module (§6.6.1).

In contrast, if the class or interface is part of a module that’s not exported, it’ll be accessible only by code located in the same module.

package access

When a top level class or interface is declared as "package private" it’ll be accessible only from code located within the same package.

package access is the implicit (default) access given when no modifier (public or private) is specified.

Members and constructors

A member (class, interface, field, or method) of a class, interface, type parameter, or reference type, or a constructor of a class, is accessible only if (§6.6.1):

  • the class, interface, type parameter, or reference type is accessible, and

  • the member or constructor is declared to permit access:

    1. If the member of constructor is declared public, then access is permitted

    2. Otherwise, if the member or constructor is declared protected, then access is permitted only when:

      • the access occurs from within the package containing the class in which the protected member or constructor is declared

      • or, the code that tries to access the protected member or constructor of an object is responsible for the implementation of that object (§6.6.2).

    3. Otherwise, if the member or constructor is declared with package access, then access is permitted only when the access occurs from within the package in which the class, interface, type parameter, or reference type is declared.

    4. Otherwise, when the member or constructor is declared private, then access is permitted only when:

      • the access occurs from within the body of the top level class or interface that encloses the declaration of the member or constructor

      • or, the access occurs in the permits clause of the top level class or interface that enclose the declaration of the member

      • or, the access occurs in the record component list of the top level record class that encloses the declaration of the member

All members of interfaces lacking access modifiers are implicitly public.

Array types

An array type is accessible if, and only if, its element type is accessible.

Wrap up

Compilation units located in different modules

When two compilation units (source files), let’s say class A and B are located in different modules, let’s say moduleA and moduleB respectively, if class A is exported by moduleA to moduleB, then class A and its members and constructor(s) might be accessible to class B, following the previously explained rules and summarized in the next section.

Otherwise, if class A is not exported from moduleA to moduleB, it won’t be accessible by class B, regardless of the access modifier specified (and the next section doesn’t apply), period.

Compilation units within the same module or exported to another module(s)

Given we have some compilation units (source files) accessible to one another, from a module visibility point of view, then we can summarize their accessibility as follows.

In the next two tables, you can see, respectively, the different access levels for top level classes, and for members and constructor. If you’re just starting to learn or practice this content, it may be useful to keep them at hand—kind of cheat sheet. However, make sure you understand all the details and nuances previously explained and, ideally, the content from the linked resources.

Table 1. Access levels for top level classes, interfaces, etc. means accessible, blank means not accessible
Type (class, interface, etc.) Unrelated type within the package containing the accessed type Subtype in the same package Subtype in a different package Any other code that tries to access it

public

package private

Remember, we can’t declare a top level class, interface, etc. as private or protected, otherwise the code won’t compile.
Table 2. Access levels for members and constructors. means accessible, blank means not accessible
Enclosing type (class/interface, etc.) Unrelated type within the package containing the enclosing type Subtype in the same package Subtype in a different package Any other code that tries to access it

public

protected

package private

private