00:00

Packages in Java – Organising Classes and Controlling Access

Last updated: 7 min read

A package is a namespace that groups related classes and interfaces, much like a folder groups related files. Packages prevent name clashes (two Date classes can live in java.util and java.sql), organise large codebases, and work with access modifiers to control which classes and members other code can see.

This tutorial covers built-in and user-defined packages, naming conventions, the import and import static statements, the required folder structure, compiling and running packaged code, and package-level access.

1. Built-in and User-Defined Packages

Built-in packages in Java such as java.lang, java.util and java.io

The JDK ships with thousands of classes organised into packages:

PackageContents
java.langCore classes: String, Math, Object, wrappers, Thread. Imported automatically.
java.utilCollections, Scanner, Optional, Random
java.io / java.nio.fileInput/output and file handling
java.timeDates and times: LocalDate, Duration
java.net / java.net.httpNetworking and the HTTP client
java.util.concurrentExecutors, locks, concurrent collections
java.sqlJDBC database access

User-defined packages are the ones you create for your own code.

2. Creating a Package

Put a package declaration as the first statement in the source file. Here is a small shop application split into three packages:

Product.java
package com.shop.model;

public class Product {
    private final String name;
    private final double price;
    int stock = 10;                     // package-private: visible only inside com.shop.model

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
    public String getName()  { return name; }
    public double getPrice() { return price; }
}
PriceFormatter.java
package com.shop.util;

import java.text.NumberFormat;
import java.util.Locale;

public class PriceFormatter {
    private static final NumberFormat INR = NumberFormat.getCurrencyInstance(Locale.forLanguageTag("en-IN"));

    public static String format(double amount) {
        return INR.format(amount);
    }
}
ShopApp.java
package com.shop.app;

import com.shop.model.Product;              // single-type import
import static com.shop.util.PriceFormatter.format;   // static import
import java.util.*;                         // on-demand (wildcard) import

public class ShopApp {
    public static void main(String[] args) {
        List<Product> cart = List.of(new Product("Keyboard", 2499), new Product("Mouse", 799));
        for (Product p : cart) System.out.println(p.getName() + " : " + format(p.getPrice()));

        // Fully qualified name instead of an import
        java.time.LocalDate today = java.time.LocalDate.of(2026, 10, 11);
        System.out.println("Order date: " + today);

        // p.stock is not accessible here: package-private in com.shop.model
        System.out.println("Class name: " + Product.class.getName());
        System.out.println("Package:    " + ShopApp.class.getPackageName());
    }
}
Output
Keyboard : ₹2,499.00
Mouse : ₹799.00
Order date: 2026-10-11
Class name: com.shop.model.Product
Package:    com.shop.app

A class without a package statement belongs to the unnamed default package, which is fine for experiments but cannot be imported from named packages. Real projects always use named packages.

3. Folder Structure, Compiling and Running

The directory structure must mirror the package name: com.shop.model.Product lives in com/shop/model/Product.java.

build-and-run.txt
# Project layout: the folder structure mirrors the package names
src/
└── com/shop/
    ├── model/Product.java          (package com.shop.model;)
    ├── util/PriceFormatter.java     (package com.shop.util;)
    └── app/ShopApp.java             (package com.shop.app;)

# Compile everything into the out/ directory (javac creates the package folders)
javac -d out $(find src -name "*.java")

# Run using the fully qualified class name
java -cp out com.shop.app.ShopApp

# Package it as a runnable JAR
jar --create --file shop.jar --main-class com.shop.app.ShopApp -C out .
java -jar shop.jar

Build tools such as Maven and Gradle follow the same convention under src/main/java and handle compilation and classpaths for you.

4. Naming Conventions

  • All lowercase: com.example.billing, not com.Example.Billing.
  • Start with your organisation's reversed internet domain (com.javabytechie, org.apache) to make names globally unique.
  • Then add the project and module: com.javabytechie.shop.payment.
  • Replace invalid characters: hyphens become underscores, and a segment starting with a digit gets an underscore prefix.
  • Never use the java or javax prefixes for your own code.

5. Ways to Use Classes from Other Packages

FormExampleNotes
Single-type importimport com.shop.model.Product;Preferred: explicit and clear
On-demand importimport java.util.*;Imports all public types of one package (not sub-packages)
Fully qualified namejava.time.LocalDate.now()Useful to resolve name clashes
Static importimport static java.lang.Math.max;Imports static members; see Static Import
Module import (Java 25)import module java.base;Imports all packages exported by a module

Imports are purely a compile-time convenience: they do not load classes or affect performance. Wildcards do not include sub-packages: import java.util.*; does not import java.util.concurrent.

6. Packages and Access Control

Packages are an encapsulation boundary. A member with no access modifier is package-private: visible to every class in the same package and to nothing outside. In the example, Product.stock cannot be used from com.shop.app.

ModifierSame classSame packageSubclass (other package)Everywhere
publicYesYesYesYes
protectedYesYesYesNo
(package-private)YesYesNoNo
privateYesNoNoNo

Since Java 9, modules add a stronger layer: a module exports only selected packages, hiding the rest even if their classes are public. See Access Modifiers.

7. Advantages of Packages

  • Name-clash prevention through unique fully qualified names.
  • Organisation: related types live together and are easy to find.
  • Access control: package-private types and members hide implementation details.
  • Reusability and distribution: packages are packaged into JARs and shared as libraries.

Two common organisation styles: package by layer (controller, service, repository) and package by feature (order, payment, customer). Package by feature usually keeps related code together and makes better use of package-private access.

8. Summary

  • A package is a namespace grouping related types; declare it with package as the first statement.
  • Folder structure must mirror the package name; compile with javac -d and run with the fully qualified class name.
  • Use single-type imports, wildcard imports, static imports or fully qualified names to use other packages.
  • java.lang is imported automatically.
  • Package-private access and modules make packages an encapsulation boundary.

Related Tutorials