DEV Community

Asher Toqeer
Asher Toqeer

Posted on

Java Modules Introduction

Classes are the basic unit of a program in Java. Packages are used to manage classes and modules are used to manage packages.

Modules were introduced in Java 9. Before that, an application consists of packages that hold classes. It is a problem in large codebases. A public class means that anyone from anywhere could access that class, which can lead to unwanted dependencies. Modules are uniquely named, reusable groups of related packages as well as resources.

A Module controls which packages should be private for internal use only and which should be public for other modules to use. In modular Java, a public class no longer means that anyone can access that class from anywhere, instead, it is public within a module only, unless module exports its package for other modules to use.

From Java 9, the language itself has broken down into modules. Run java --list-modules on JDK 9 or above. It will print out a list of Java modules.

$ java --list-modules
java.base@11.0.5
java.compiler@11.0.5
java.datatransfer@11.0.5
java.desktop@11.0.5
java.instrument@11.0.5
.
.
.
Enter fullscreen mode Exit fullscreen mode

You can check any module's meta-data with the command: java --describe-module [module name]. For example:

$ java --describe-module java.logging
java.logging@11.0.5
exports java.util.logging
requires java.base mandated
provides jdk.internal.logger.DefaultLoggerFinder with sun.util.logging.internal.LoggingProviderImpl
contains sun.net.www.protocol.http.logging
contains sun.util.logging.internal
contains sun.util.logging.resources
Enter fullscreen mode Exit fullscreen mode

exports java.util.logging the classes in this package are accessible to other modules.

requires java.base mandated this module depends on another module named: mandated.

provides ... this module is providing a service implementation (we won't cover services in this article)

contains this is an internal package, accessible within module only.

A bare minimum Java module Example

Our sample application will contain two modules, one for utilities called utils and one main application module that will use the utils module. Let's code it up:

Create a directory JavaModuleExample and cd into it:

$ mkdir JavaModuleExample
$ cd JavaModuleExample/
Enter fullscreen mode Exit fullscreen mode

Create root and subdirectories to contain code. Utils module will be in module.utils directory and App module will be in module.app directory. Both directories will contain their sub-packages. Let's create them:

$ mkdir -p module.utils/com/module/utils
$ mkdir -p module.app/com/module/app
Enter fullscreen mode Exit fullscreen mode

Final Directory Structure would look like:

$ tree
.
├── module.app
│   └── com
│       └── module
│           └── app
└── module.utils
    └── com
        └── module
            └── utils

8 directories, 0 files
Enter fullscreen mode Exit fullscreen mode

In above structure, module.app has package com.module.app and module.utils has package com.module.utils.

Utils Module

Let's complete module.utils. Create a new Java File Utils.java in package com.module.utils with following cat command. It will start waiting for text input.

$ cat > module.utils/com/module/utils/Utils.java

Enter fullscreen mode Exit fullscreen mode

paste following code in console, press Ctrl+D when finished

package com.module.utils;

public class Utils {
    public static int sum (int arg0, int arg1) {
        return arg0 + arg1;
    }
}

[Press Ctrl+D]
Enter fullscreen mode Exit fullscreen mode

To tell Java we are writing a module, we need to provide module-info.java at the root level of the module. Create the following file again with cat command:

$ cat > module.utils/module-info.java

Enter fullscreen mode Exit fullscreen mode

paste following code in console, press Ctrl+D when finished

module module.utils {
    exports com.module.utils;
}

[Press Ctrl+D]
Enter fullscreen mode Exit fullscreen mode

In the above file, we are specifying our module, the name of the module is module.utils, you can name it whatever you want but is a convention to name module the same way as we name packages.

The Other thing is we are exporting a package com.module.utils. We want this package to be available in other modules. If you don't explicitly export it, then by default, it will be only accessible within the module itself.

That's it, we have created a simple module.

App Module

The Next step is to complete module.app. Again, we need module-info.java at the root of our App module and since our app module has a dependency on the utils module, we need to specify that dependency in module-info.java. Create module-info.java with the following command:

$ cat > module.app/module-info.java
Enter fullscreen mode Exit fullscreen mode

paste following code in console, press Ctrl+D when finished

module module.app {
    requires module.utils;
}

[Press Ctrl+D]
Enter fullscreen mode Exit fullscreen mode

In the above file, we need to explicitly tell which modules do we need as a dependency. requires module.utils is specifying we need utils module in our app module.

Now Create Main class to use Utils.

$ cat > module.app/com/module/app/Main.java

Enter fullscreen mode Exit fullscreen mode

paste following code in console, press Ctrl+D when finished

package com.module.app;

import com.module.utils.Utils;

public class Main {

    public static void main(String[] args) {
        System.out.println(Utils.sum(1,2));
    }
}

[Press Ctrl+D]
Enter fullscreen mode Exit fullscreen mode

we are calling sum(...) function from Utils class which exists in the util module.

Finally, our application would look like:

$ tree
.
├── module.app
│   ├── com
│   │   └── module
│   │       └── app
│   │           └── Main.java
│   └── module-info.java
└── module.utils
    ├── com
    │   └── module
    │       └── utils
    │           └── Utils.java
    └── module-info.java

8 directories, 4 files
Enter fullscreen mode Exit fullscreen mode

Compile

Run following command to compile code:

$ javac -d outDir --module-source-path . $(find . -name "*.java")
Enter fullscreen mode Exit fullscreen mode

javac is java compiler that will compile java code to byte code

-d outDir is where our compiled code will go

--module-source-path . we are specifying module source path, which is current directory .

$(find . -name "*.java") we are specifying which files to compile (all files ending with .java)

Run

Run following command to run code:

$ java --module-path outDir -m module.app/com.module.app.Main
3
Enter fullscreen mode Exit fullscreen mode

java --module-path outDir will specify compiled modules directory

-m module.app/com.module.app.Main specifying which class to run, Main class in module.app

3 is printed as a result, which is the result of Utils.sum(1,2), the code we have written in our Main class in the App module.

That was a basic introduction to modules in Java. As a side note, I would like to mention that you should not confuse Java modules with maven modules or IntelliJ IDEA modules. Maven and IntelliJ IDEA have their concepts of modules too. Also, IntelliJ IDEA supports Java modules too see their blog post here.


Originally Published at https://thebackendguy.com/java-modules-introduction/

Top comments (0)