Packages, Modules & Libraries

Packages

A package is a namespace and a directory, named after your reversed domain:

src/com/example/shop/Cart.java
src/com/example/shop/pricing/Discount.java
package com.example.shop;

import com.example.shop.pricing.Discount;
import java.util.List;
import java.util.List;              // one class
import java.util.*;                 // whole package (rare in projects)
import static java.lang.Math.PI;    // static member

java.lang (with String, Math, IO) is always imported.

Module imports (Java 25)

One import pulls in every exported package of a module:

import module java.base;

void main() {
    var list = new ArrayList<String>();   // java.util
    var path = Path.of("data.txt");       // java.nio.file
}

In compact source files java.base is imported automatically.

The module system

module-info.java declares what a module needs and exposes:

module com.example.shop {
    requires java.net.http;
    requires transitive java.sql;

    exports com.example.shop;
    exports com.example.shop.api;
    // com.example.shop.internal stays private, even for public classes
}

Worth it for libraries and large applications; unnecessary for small programs.

Standard library map

Package Contents
java.lang String, Math, Thread, IO
java.util collections, Optional, Random
java.util.stream streams and collectors
java.time dates and times
java.nio.file files and paths
java.net.http HTTP client
java.util.concurrent threads, executors, locks
java.util.regex regular expressions
java.math BigDecimal, BigInteger

External libraries

By hand it is painful:

javac -cp libs/jackson-databind-2.18.2.jar -d out src/*.java
java  -cp out:libs/* com.example.Main

So every project uses a build tool.

Maven

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>shop</artifactId>
  <version>1.0.0</version>

  <properties>
    <maven.compiler.release>25</maven.compiler.release>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.18.2</version>
    </dependency>
  </dependencies>
</project>
mvn compile   mvn test   mvn package   mvn dependency:tree
src/main/java        production code
src/main/resources   config, text files
src/test/java        tests
target/              build output (do not commit)

Gradle

plugins { application }

java { toolchain { languageVersion = JavaLanguageVersion.of(25) } }

repositories { mavenCentral() }

dependencies {
    implementation("com.fasterxml.jackson.core:jackson-databind:2.18.2")
    testImplementation("org.junit.jupiter:junit-jupiter:5.11.4")
}

application { mainClass = "com.example.shop.Main" }
./gradlew build   ./gradlew run   ./gradlew test
Tip

Maven is rigid but identical everywhere — a good default. Gradle is more flexible and faster on big projects, at the price of learning a small language.

Packaging

jar --create --file shop.jar --main-class com.example.shop.Main -C out .
java -jar shop.jar

A trimmed runtime image with only the modules you need:

jlink --add-modules java.base,java.net.http --output runtime --strip-debug
./runtime/bin/java -cp out com.example.shop.Main

Libraries worth knowing

Purpose Library
JSON Jackson, Gson
tests JUnit 5, AssertJ, Mockito
logging SLF4J + Logback
web Spring Boot, Quarkus, Javalin, Helidon
database JDBC, jOOQ, Hibernate
CLI parsing picocli

★ Exercises

  1. Create a Maven project and get a “hello world” running with mvn package.
  2. Move your chapter 7 solutions into a package and fix the imports.
  3. Add Jackson and round-trip a record to JSON and back.
  4. Write a module-info.java. What happens if you leave out a requires?
  5. Try import module java.base; — which imports can you delete?
  6. Build an executable JAR and run it with java -jar.