Java Packages: How to Create, Use, and Compile Them

Java packages group related classes and interfaces under a shared name, helping you organize code and avoid naming conflicts. To create and use one, declare its package at the top of each source file, place the file in a matching directory, then compile and run it with the correct classpath.

What Java packages are and why they matter

A package is a namespace for Java types: classes, interfaces, enumerations, and other declarations. A type’s fully qualified name combines its package with its own name. For example, a class named Parser in com.example.text is referred to as com.example.text.Parser.

Packages have two practical purposes. First, they let separate parts of a program use the same simple class name without colliding, provided the full names differ. Second, they provide an organizational structure that makes code easier to navigate and maintain. Package membership also affects access: a class or member with no access modifier is available only within its package, subject to Java’s access rules.

Java includes standard packages for common capabilities, and applications can define their own. A package is not the same thing as a folder in principle, but Java tools conventionally map a package name to a directory path. That convention is central to compiling and locating classes.

Package declarations and naming rules

A source file declares its package with a package statement. It must appear before import statements and type declarations, though comments and whitespace may appear before it. A source file can have at most one package declaration. If there is no declaration, the type belongs to the unnamed, often called default, package.

package com.example.greetings;

public class Greeter {
    public static void main(String[] args) {
        System.out.println("Hello");
    }
}

Package names are normally written in lowercase to distinguish them visually from class names, which conventionally begin with a capital letter. A package name consists of identifiers separated by dots; each part must follow Java identifier rules. Teams often use a reversed domain-style prefix to make names distinctive, followed by a description of the code’s purpose. This is a convention, not a requirement that the domain be registered or reachable.

For example, com.example.greetings has three name components. The corresponding source path is com/example/greetings/Greeter.java when the source root is the directory above com. The source root is not itself part of the package name.

How to create a package

Creating a package means choosing its name, adding the declaration, and arranging the source file beneath a suitable source root. These steps work for a small command-line project as well as within a larger build:

  1. Choose a distinctive package name, using lowercase components and a meaningful prefix.
  2. Create a directory path that mirrors the package name beneath the project’s source directory.
  3. Save each source file in the directory corresponding to its declared package.
  4. Put the matching package declaration at the top of each file in that package.
  5. Compile from the source root or configure the build tool to recognize the source root.

For example, if the source root is src, the file for com.example.greetings.Greeter would be at src/com/example/greetings/Greeter.java. The directory name and declaration should agree. Although some compiler setups can accept source files in unconventional locations, relying on mismatched paths makes projects harder to build and troubleshoot.

A package can contain several source files. Each file that declares a type in that package uses the same package declaration and sits in the same matching directory. Java source files can also contain more than one top-level type, but keeping public types in appropriately named files is the usual, clearer organization.

Using types from another package

Code can refer to a type in another package by writing its fully qualified name, or by importing it. An import makes a type name available for use without repeating its package prefix; it does not copy the class or make an inaccessible type public.

package com.example.app;

import com.example.greetings.Greeter;

public class Main {
    public static void main(String[] args) {
        Greeter.main(args);
    }
}

In ordinary application code, it is usually better for one class to expose a purpose-built public method than to call another class’s main method. The short example focuses on showing the import. A fully qualified reference, such as com.example.greetings.Greeter, is useful when two imported types have the same simple name or when an import would make a reference unclear.

An import statement does not import a package’s subpackages. Importing com.example does not automatically import types in com.example.greetings; each package is a separate namespace. The wildcard form, such as import com.example.greetings.*;, makes accessible types in that package available by simple name, but it does not include subpackages. Explicit imports can make dependencies easier to see while reading a file.

Compile and run a packaged Java program

For a simple project, the compiler’s -d option places generated class files beneath a chosen output directory and creates package directories as needed. Run these commands from the project directory, where src contains the source root:

javac -d out src/com/example/greetings/Greeter.java
java -cp out com.example.greetings.Greeter

The first command compiles the source and writes the class file under out/com/example/greetings. The second tells the Java launcher to search out for classes, then names the class using its fully qualified name. Do not include the .class suffix or translate the dots in that class name into slashes.

If a program has several source files, compile all required files together or arrange for the compiler to find source files through its configured source path. For example, compiling both an application class and a helper class in one command is straightforward for a small project. In a larger project, a build tool normally manages source roots, dependencies, and output locations, reducing the need to maintain long command lines by hand.

The classpath is a list of locations where Java tools look for compiled classes and, in relevant situations, other resources. A classpath entry is generally a directory, an archive, or another supported location. The classpath root must be the directory above the first package component. If it instead points inside com/example, Java cannot find a class by the name com.example.greetings.Greeter using that entry.

Organizing packages in a project

Choose packages around coherent responsibilities rather than making a separate package for every class. A small program may need only a few packages; a larger application might separate its user interface, business rules, data access, and shared utilities. The best boundaries depend on how the project changes and which parts need to depend on one another.

  • Keep closely related types together when they work on the same part of the problem.
  • Use package names that describe a role or domain, not a temporary implementation detail.
  • Avoid a catch-all package for unrelated classes, because it becomes difficult to navigate.
  • Keep dependencies understandable; a lower-level utility should not need to depend on a user-interface class without a clear reason.
  • Follow the project’s existing source-root and naming conventions so other contributors can build it consistently.

Source layout and package layout serve different levels of organization. A source root such as src tells a compiler or build system where source files begin; the package path starts below it. Output directories such as out or a build tool’s configured destination hold compiled files. Keeping source and generated output separate prevents class files from becoming mixed with editable source.

Packages, modules, and access control

A package is not a Java module. Packages organize types and influence package-level access; modules are a higher-level way to group packages and describe dependencies and which packages are exposed to other modules. A project can use packages without declaring a module, and a module can contain multiple packages.

Access modifiers also matter when organizing code. A public type can be used from other packages when it is otherwise available on the classpath or module path. A type with no access modifier is package-private and is ordinarily accessible only to code in the same package. Public methods and fields have their own access rules, so putting two classes in one package does not make every private member visible to the other class.

Use packages to create a useful boundary, not merely to hide code. If unrelated classes are placed together just to gain package-level access, the structure can become tightly coupled. Prefer the narrowest access that suits the design, and expose public types and methods deliberately.

Common problems and how to fix them

Many beginner errors come from treating the package declaration, directory path, and classpath as interchangeable. They are related, but each has a distinct job. Check the following when compilation or launch fails:

  • Package declaration and path disagree: Compare the statement at the top of the file with the directories below the source root.
  • Wrong classpath root: Point the classpath at the output directory above the package folders, not at the package folder itself.
  • Launching with a file path: Use the fully qualified class name, with dots between package components, rather than a path or a .class suffix.
  • Missing import: Import the needed type or use its fully qualified name; a package’s types are not automatically visible everywhere.
  • Assuming an import includes subpackages: Import or reference types from each package that the code uses.
  • Using the default package in a larger project: Named packages are easier to organize and integrate; code in named packages cannot import types from the unnamed package.

Also distinguish a compiler error from a launcher error. A compiler may report that a type cannot be found because a dependency or source file is missing. A launcher may say that the main class cannot be found because the classpath root or fully qualified name is wrong. Reading the complete error message helps identify which stage failed.

Frequently Asked Questions

Can a Java file belong to more than one package?
No. A source file has at most one package declaration, and its top-level types belong to that package. To place code in another package, put it in a separate source file with that package’s declaration.

Does every Java project need a package?
No. A small learning example can use the unnamed package. For maintainable application code, named packages are generally preferable because they create a clear namespace and work better with project structure. Code in a named package cannot import a type from the unnamed package.

Does importing a package import all of its subpackages?
No. Each package is separate. A wildcard import covers accessible types in the package named in the import, not types in its subpackages.

Can two packages contain classes with the same name?
Yes. Their fully qualified names differ, so Java can distinguish them. If code needs both, use fully qualified references where necessary to avoid ambiguity.

Why does Java say it cannot find or load the main class?
Check that compilation succeeded, the class file is under the expected package directories, the classpath points to their parent output directory, and the launch command uses the exact fully qualified class name.

Practical takeaway

Think of a package as a type namespace that also provides a useful boundary for access and organization. Declare it consistently, mirror its name in directories below a source root, compile into a separate output directory, and launch classes by fully qualified name. For projects that grow beyond a few files, use a consistent build setup and package structure so the same layout is easy to compile, test, and maintain.