Skip to content

"Classes and Objects" tutorial (https://dev.java/learn/classes-objects/ ) discussion of access modifiers should probably be updated for changes with Java Platform Module System released in Java 9 (in 2017) #211

Closed

Description

@willy-b

Hello dev.java team!

Thanks again for all these wonderful articles.

It seems though there are excellent separate articles within dev.java on the impacts of the Java Platform Module System (JPMS; since Java 9 in 2017, https://openjdk.org/projects/jigsaw/spec/ ) when used for a project on access,
the "Classes and Objects" tutorial https://dev.java/learn/classes-objects/ has not yet been updated for the impact of modules on access. Also, that reflection can bypass access controls under some circumstances might be mentioned in passing (could link to e.g. https://dev.java/learn/reflection/fields/#accessible (archived as is at: https://web.archive.org/web/20260317205612/https://dev.java/learn/reflection/fields/#accessible )).


Regarding https://dev.java/learn/classes-objects/creating-classes/ (archived as is at https://web.archive.org/web/20260317200949/https://dev.java/learn/classes-objects/creating-classes/ ):

  • Re: "public modifier—the field is accessible from all classes." it seems this should be updated to account for the Java Platform Module System which, when used, only reveals to code outside the module the public modified items in packages exported by the module (or opened, if considering reflective access) (exports or opens can also be qualified to restrict such access only to other particular target modules).

[dev.java already has other articles on the impact of JPMS on access at https://dev.java/learn/modules/intro/ (archived as is at: https://web.archive.org/web/20260317203840/https://dev.java/learn/modules/intro/ ),
and https://dev.java/learn/modules/qualified-exports-opens/ (archived as is at: https://web.archive.org/web/20260317204235/https://dev.java/learn/modules/qualified-exports-opens/ ), and https://dev.java/learn/modules/opening-for-reflection/ (archived as is at: https://web.archive.org/web/20260317204550/https://dev.java/learn/modules/opening-for-reflection/ ).

See also:
https://docs.oracle.com/javase/specs/jls/se25/jls25.pdf

Example 6.6-2. Access to public Fields, Methods, and Constructors
A public class member or constructor is accessible throughout the package where it is
declared and from any other package, provided the package in which it is declared is
observable (§7.4.3).

Where §7.4.3 is 7.4.3 Package Observability and Visibility,
which states:

A package is visible to a module M if and only if an ordinary compilation unit
containing a declaration of the package is visible to M
[...]
A package is uniquely visible to a module M if and only if one of the following holds:
• An ordinary compilation unit associated with M contains a declaration of the
package, and M does not read any other module that exports the package to M.
• No ordinary compilation unit associated with M contains a declaration of the
package, and M reads exactly one other module that exports the package to M.

and re: classes the JLS 25 in 6.6.1 says

If a top level class or interface (§7.6) is declared public and is a member of a
package that is exported by a module, then the class or interface may be accessed
by any code in the same module, and by any code in another module to which
the package is exported, provided that the compilation unit in which the class or
interface is declared is visible to that other module (§7.3).
• If a top level class or interface is declared public and is a member of a package
that is not exported by a module, then the class or interface may be accessed by
any code in the same module [me: including the unnamed module].

]


In https://dev.java/learn/classes-objects/more-on-classes/#controlling-access (archived as is at: https://web.archive.org/web/20260317203241/https://dev.java/learn/classes-objects/more-on-classes/#controlling-access )

Re: the table shown for "Controlling Access to Members of a Class", quoted below:

The following table shows the access to members permitted by each modifier.
Modifier Class Package Subclass World
public Y Y Y Y [should be updated for JPMS post Java 9, for projects using JPMS]
protected Y Y Y N
no modifier Y Y N N
private Y N N N

This "Controlling Access to Members of a Class" table should also probably be updated for the release of the Java Platform Module System (JPMS) in Java 9, which, when a project uses JPMS, restricts access to even public types and members by code outside the module based on which packages are exported by the module (or opened, concerning reflective access) (and exports or opens can be qualified to restrict such access only to other particular target modules).

[again, for ease of reference, dev.java already has other articles on the impact of JPMS on access at https://dev.java/learn/modules/intro/ (archived as is at: https://web.archive.org/web/20260317203840/https://dev.java/learn/modules/intro/ ), and https://dev.java/learn/modules/qualified-exports-opens/ (archived as is at: https://web.archive.org/web/20260317204235/https://dev.java/learn/modules/qualified-exports-opens/ ), and https://dev.java/learn/modules/opening-for-reflection/ (archived as is at: https://web.archive.org/web/20260317204550/https://dev.java/learn/modules/opening-for-reflection/ ).

See also:
https://docs.oracle.com/javase/specs/jls/se25/jls25.pdf

Example 6.6-2. Access to public Fields, Methods, and Constructors
A public class member or constructor is accessible throughout the package where it is
declared and from any other package, provided the package in which it is declared is
observable (§7.4.3).

]

It may also be useful to mention on this page as well that the package, protected, and private access checks can sometimes be bypassed via reflection under certain conditions (see https://dev.java/learn/reflection/fields/#accessible (archived as is at: https://web.archive.org/web/20260317205612/https://dev.java/learn/reflection/fields/#accessible ) (relevant: section was added in discussion at #191 (comment) )).


Thanks so much! Feel free to leave open even if you address the above as I would also be happy to confirm any changes before closing if desired.

Activity

  1. danthe1st commented on Mar 17, 2026

    @danthe1st
    Contributor

    To be honest, I am not sure whether adding that to this article would be much of an improvement.

    I'd say the target group of that article doesn't need to know about modules "yet" and writing about that would just cause confusion.

  2. JosePaumard commented on Mar 18, 2026

    @JosePaumard
    Contributor

    Thank you again for your contribution! I just pushed a fix.
    I don't want to go into too much detail on this page, since it is a section meant to introduce the dirferent levels of visibility for beginner people. So I just added a quick note that refers to the JPMS and the Reflection API. Sending all the info at once would probably scare beginners.

  3. willy-b commented on Mar 21, 2026

    @willy-b
    Author

    Excellent!

    As of the latest "More on Classes" article, https://dev.java/learn/classes-objects/more-on-classes/#controlling-access (archived as is at https://web.archive.org/web/20260318150608/https://dev.java/learn/classes-objects/more-on-classes/#controlling-access )
    the following has been added (vs old version at https://web.archive.org/web/20260317203241/https://dev.java/learn/classes-objects/more-on-classes/#controlling-access ):

    Using the Java Module System or the Reflection API
    Note that the Java Module System can add a layer of control of the visibility of public classes from outside their module. You can refer to the section on the Java Module System for more information.
    Also, note that the Reflection API can give you access to non-visible classes or class members under certain circumstances. You can refer the Reflection API section for more information.

    Which addresses the concerns I raised for that article, thanks so much!


    However, in the earlier article in the tutorial, https://dev.java/learn/classes-objects/creating-classes/#controlling-access (as of https://web.archive.org/web/20260318150146/https://dev.java/learn/classes-objects/creating-classes/#controlling-access ), there has not yet been any update to cover the effect of the Java Platform Module System or the Reflection API on access.

    Note also that this "Creating Classes" article has two sections (1) "Declaring Classes" and (2) "Declaring Member Variables" before discussing access control in "Controlling who has Access to a Member" which only mentions controlling access to member fields, not to the class itself (though the effect of the module system is similar on either), covering (2) and not (1) from an access perspective.

    Thus for https://dev.java/learn/classes-objects/creating-classes/#controlling-access it might be worth mentioning both class and field access (if not other types of members like methods) explicitly.

    Then it may be useful to link to https://dev.java/learn/classes-objects/more-on-classes/#controlling-access from this section so that nobody risks stopping with the understanding that

    public modifier—the field is accessible from all classes.

    Not necessarily true since the release of Modules, see JLS 25 example 6.6-2 and 7.4.3 in https://docs.oracle.com/javase/specs/jls/se25/jls25.pdf , quoted previously and again below.

    private modifier—the field is accessible only within its own class

    Not necessarily true if reflection is used, see https://dev.java/learn/reflection/fields/#accessible (archived as is at: https://web.archive.org/web/20260317205612/https://dev.java/learn/reflection/fields/#accessible ) (relevant: section was added in discussion at #191 (comment) )

    Of course you are welcome to acknowledge the above and intentionally omit it, I just want to make sure it is intentional!


    Here is supporting detail for my comment on the public modifier description above, repeated for convenience and to ensure it is included in the generated email thread.
    The limiting effect of the Java Platform Module System (released in Java 9 in 2017) on the public access modifier according to the JLS 25 ( https://docs.oracle.com/javase/specs/jls/se25/jls25.pdf ) is e.g.:

    • For public members (fields, methods, and constructors):
    Example 6.6-2. Access to public Fields, Methods, and Constructors
    A public class member or constructor is accessible throughout the package where it is
    declared and from any other package, provided the package in which it is declared is
    observable (§7.4.3).
    

    Where §7.4.3 is 7.4.3 Package Observability and Visibility,
    which states:

    A package is visible to a module M if and only if an ordinary compilation unit
    containing a declaration of the package is visible to M
    [...]
    A package is uniquely visible to a module M if and only if one of the following holds:
    • An ordinary compilation unit associated with M contains a declaration of the
    package, and M does not read any other module that exports the package to M.
    • No ordinary compilation unit associated with M contains a declaration of the
    package, and M reads exactly one other module that exports the package to M.
    
    • on class access (JLS 25 section 6.6.1):
    If a top level class or interface (§7.6) is declared public and is a member of a
    package that is exported by a module, then the class or interface may be accessed
    by any code in the same module, and by any code in another module to which
    the package is exported, provided that the compilation unit in which the class or
    interface is declared is visible to that other module (§7.3).
    • If a top level class or interface is declared public and is a member of a package
    that is not exported by a module, then the class or interface may be accessed by
    any code in the same module [me: including the unnamed module].
    

    Thanks so much!

  4. willy-b commented on Mar 23, 2026

    @willy-b
    Author

    FYI, so as to avoid confusion from possibly passing compile-time checks when considering whether the claim

    public modifier—the field is accessible from all classes

    is accurate (from https://dev.java/learn/classes-objects/creating-classes/#controlling-access , as of https://web.archive.org/web/20260318150146/https://dev.java/learn/classes-objects/creating-classes/#controlling-access ).

    Access to public fields and methods in classes in a package which is not exported by its module from outside that module, e.g. from the unnamed module, can also be denied at runtime with IllegalAccessError (using access from the unnamed module for an extreme example, as it is highly concerning if a file updated on the classpath depended on by say an automatic module one is requiring could in its initializer access something public in your module still not intended to be used by it; I am not claiming it never can, but IllegalAccessError blocks simple attempts for non-final fields, in agreement with the JLS).

    E.g. in my own testing on Java SE 21, I see when accessing public static fields and methods (not public static final compile-time constant fields, though) from outside the module, e.g. from unnamed module (trying to print Example.<public static variable name>):

    Exception in thread "main" java.lang.IllegalAccessError: 
    class p2.NonModularPackagedClass2 (in unnamed module @0x255316f2) 
    cannot access class notexportedpackage.Example (in module m.A) 
    because module m.A does not export notexportedpackage to unnamed module @0x255316f2
    

    (line breaks added to fit normal viewing width, no other edits)

    The example I tested locally was to have a module which does not export or open anything require a non-modular jar on the module path (automatic module, which is allowed then to read classes on the classpath), where that non-modular jar then reads another non-modular non-jar class file on the classpath and causes an initializer there to fire which tries to access public static fields and methods of a class in the original module (in a package not exported or opened).

    I was able to get past the compile-time checks in this arrangement but I do encounter the IllegalAccessError above except for accessing public static final compile-time constant fields in the package not exported by a module.

    That is:

    // after passing compile-time checks when Java is running modular and someString is a public static field of a public class which is not exported by its module, and the access is attempted from outside the module
    public static String someString = "a value";  // can be blocked by IllegalAccessError at runtime 
    

    Whereas:

    // after passing compile-time checks when Java is running modular and SOME_FINAL_STRING is a public static final field of a public class which is not exported by its module, and the access is attempted from outside the module
    public static final String SOME_FINAL_STRING = "a value";  // can be accessed in some cases, even from unnamed module
    

    Note that the above situation is running the Java application as a modular application (recommended way since Java 9 in 2017) with main inside the module (running the modular code in a non-modular way via classpath wouldn't count).

    Thanks

  5. JosePaumard commented on Mar 23, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! I just pushed a fix.
    I don't want to deep dive too much on the complete visibility rules too early in this section. As much as I agree with your comment, this is a beginner level section, and I think that talking about JPMS or Reflection at this point would only create confusion. I just added a note to say that there are exceptions, that are covered later.

  6. willy-b commented on Mar 23, 2026

    @willy-b
    Author

    Got it.

    As of https://web.archive.org/web/20260323154257/https://dev.java/learn/classes-objects/creating-classes/#controlling-access , the added caveat:

    Note that there are some exceptions to these rules, that are covered later in this section.

    Presumably referring to the note added to https://web.archive.org/web/20260318150608/https://dev.java/learn/classes-objects/more-on-classes/#controlling-access :

    Using the Java Module System or the Reflection API
    Note that the Java Module System can add a layer of control of the visibility of public classes from outside their module. You can refer to the section on the Java Module System for more information.
    Also, note that the Reflection API can give you access to non-visible classes or class members under certain circumstances. You can refer the Reflection API section for more information.

    Covers the concern here, while as you noted allowing for a gradual introduction to the topic, thanks so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions