If the JAR defines the Automatic-Module-Name header in its manifest (META-INF/MANIFEST.MF),
it defines the module's name, otherwise the JAR file name is used to determine the name.

Since the JAR contains no information which packages are considered public APIs and which are not, the module
system exports all packages and also opens them for deep reflection.

Since a plain JAR expresses no requires clauses, the module system lets automatic modules read all other modules.

Unnamed Module

When a class or JAR is loaded onto the classpath, but not the module path, it is automatically added to the unnamed module.
It is a catch-all module to maintain backward compatibility with previously-written Java code.

All classes within the unnamed module can read all other module (named or unnamed) without any explicit declaration of any kind.

The packages exported by unnamed module can only be read by another unnamed module. It is not possible that a named module
can read (requires) the unnamed module. Because to explicitly use 'requires' clause in a module-info.java or
use a command line option to add the module, we need a module name.

When creating an appication module, it is important that its name is unique. It is expected that module names will follow the "reverse domain
name" convention, just like package names. For example "by.boot.java"

A module name can be the same as package name, however it can be different too, for example java.sql module
contains java.sql and javax.sql packages:

To declare an application module, a module declaration needs to be specified. This is done in a special file called
module-info.java. As you can see from the file extension, this is a .java file that gets compiled
into a .class file by the Java compiler.

module by.boot.java.mod.logger {
}

There could be additional directives between curly braces, but all of them are optional.

in order to fix the problem we must add requires directive to module-info.java, which allows us to
declare dependencies:

module by.boot.java.mod.logger {
requires java.logging;
}

Now by.boot.java.mod.logger module has both a runtime and a compile-time dependency on
java.logging module. And all public types exported from a dependency are accessible
by our module when we use this directive.

You can use requires static to specify that a module dependency is required in the compile time, but optional in the runtime, for example:

module by.boot.java.mod.logger {
requires static java.logging;
}

Static dependencies are useful for frameworks and libraries. Suppose that you are building a library to work with different kinds of databases. The
library module can use static dependencies to require different kinds of JDBC drivers. At compile time, the library’s code can access types defined
in those drivers. At runtime, users of the library can add only the drivers they want to use. If the dependencies are not static, users of the library
have to add all supported drivers to pass the module resolution checks.

exports directive

Assume we create new client module which uses our logger module. We already know that we must declare in module-info.java which modules
are required:

C:\1Z0-817>javac -p ./mod1 mod2/by/boot/java/pkg/client/MyClient.java
mod2\by\boot\java\pkg\client\MyClient.java:3: error: package by.boot.java.pkg.logger is not visible
import by.boot.java.pkg.logger.MyLogger;
^
(package by.boot.java.pkg.logger is declared in module by.boot.java.mod.logger, which is not in the module graph)
1 error

By default, a module doesn’t expose any of its API to other modules. This strong encapsulation was
one of the key motivators for creating the module system in the first place.

As you can see, the by.boot.java.mod.logger module classes are not visible in the by.boot.java.mod.client
module, even after we declared it as required. Our code is significantly more secure, but now we need to explicitly open
our API up to the world if we want it to be usable.

We need to use the exports directive to expose all public members of the by.boot.java.pkg.logger package:

When you export a package, you only export types in this package but not types in its subpackages.

The same Java package can only be exported by a single Java module at runtime. You cannot have two (or more) modules that
export the same package in use at the same time. The JVM will complain at startup if you do. A Java package may not split
members (classes, interfaces, enums) between multiple modules.

When you are using exports directive to export a package in the module declaration, this package is visible to all
modules that use requires directive to require it. Sometimes you may want to limit the visibility of certain packages
to some modules only.

You can restrict which modules have access to your packages by using the exports ... to <module name> syntax:

In the module declaration, you can add the modifier open before module to declare it as an open module. An
open module grants compile time access to explicitly exported packages only, but it grants access to types in
all its packages at runtime:

open module by.boot.java.mod.logger {
requires java.logging;
}

Re-compile the by.boot.java.mod.logger module definition and re-run the client:

NOTE: since we have already compiled MyClient class, we succeeded, because all packages accessible at runtime. However,
if we want to re-compile MyClient.java, then exports declaration will be a must
(e.g. exports by.boot.java.pkg.logger to by.boot.java.mod.client;).

Before Java 9 (and Java 11), it was possible to use reflection to examine every type and member in a package, even
the private ones by using field.setAccessible(true).

The open modifier also grants reflective access to all types in all packages. All types include private
types and their private members. If you use the reflection API and suppress Java language access checks using the method
AccessibleObject.setAccessible(true) — you can access private types and members in open modules.

and run the AccessLog, which attempts to read private field via reflection:

C:\1Z0-817>java -p ./mod1;./mod2 -m by.boot.java.mod.client/by.boot.java.pkg.client.AccessLog
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field private static final java.util.logging.Logger
by.boot.java.pkg.logger.MyLogger.LOG accessible: module by.boot.java.mod.logger does not "opens by.boot.java.pkg.logger"
to module by.boot.java.mod.client
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:340)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:280)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:176)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:170)
at by.boot.java.mod.client/by.boot.java.pkg.client.AccessLog.main(AccessLog.java:10)

In order to fix code, make the by.boot.java.mod.logger module open, and in the same time you can
remove exports directive, since we do not need to compile AccessLog class:

As you can see, open module granted access to private field via reflection API.

opens directive

The opens directive specifies the name of a package to be opened by the current module. This makes
public and protected types in the package, and their public and protected
members, be accessible to code in other modules at run time only.

It also makes all types in the package, and all their members, be accessible via the reflection libraries of the Java SE Platform.

You can use opens directive to open individual packages to other modules. You can access open packages using the
reflection API at runtime. Just like open modules, all types in an open package and all their members (including private) can be
reflected by the reflection API:

As it shows, java.logging module (and specifically java.util.logging.Logger) is not
visible in by.boot.java.mod.client.

It is the same situation we had in the beginning, and it can be resolved by adding requires java.logging; directive
to by.boot.java.mod.client module's module-info.java.

This can be a tedious task when many modules depend on each other. Since this is a common usage scenario, Java 11 provides
built-in support for it. The requires declaration can be extended to add the modifier
transitive to declare the dependency as transitive. The transitive modules that a module
depends on are readable by any module that depends upon this module. This is called implicit readability.

In order to fix, edit module-info.java of by.boot.java.mod.logger module adding transitive modifier: