1. Introduction to Packages in Java

A Java application may contain dozens or thousands of classes. A retail system, for example, may have types for products, carts, orders, customers, payments, shipping, inventory, discounts, and reviews. Keeping all those types in one place quickly becomes difficult.

A package is a named namespace that organizes related Java types, including classes, interfaces, enums, and annotations. Packages help developers organize code, prevent naming conflicts, and define boundaries for package-private access.

Imagine a shopping module and a warehouse module that both need a class named Order. These classes can share the same short name if they belong to different packages. Their fully qualified names distinguish them.

Packages provide several benefits:

  • Naming: Types with the same simple name can exist in separate packages.
  • Organization: Related types can be grouped by feature or responsibility.
  • Access control: Package-private members can be shared inside a package without becoming public API.
  • Maintainability: Predictable package structures make code easier to browse and change.
  • Reuse: Utility and API types can be organized for use by other parts of a program.

Packages are more than folders: they are part of Java's type naming and access-control system.

2. Package Names and Fully Qualified Class Names

A fully qualified name (FQN) combines a package name and a type name. For example, com.example.shop.Order and com.example.warehouse.Order are different types, even though both have the simple name Order.

A package name consists of segments separated by dots. In com.example.shop.cart, the segments are com, example, shop, and cart. Each name denotes a package.

A package hierarchy can represent the responsibilities in an application:

Whiteboard
Loading diagram...

Here, shop contains shopping-related packages and util represents shared helpers. The exact structure is a design choice; Java does not require a specific business hierarchy.

Important: a subpackage is not automatically part of its parent package for access-control purposes. com.example.shop and com.example.shop.cart are separate packages.

3. Declaring a Package

A source file joins a named package through a package declaration. If present, it must be the first non-comment statement in the file.

The fully qualified name of this class is com.example.shop.Product.

Declaration rules

  • The package statement appears before imports and type declarations.
  • A source file can declare at most one package.
  • Each file in the package repeats its own package declaration.
  • Comments may appear before the declaration.
  • If the declaration is omitted, the type belongs to the default (unnamed) package.
  • The source directory structure should match the package declaration.

A file does not join a package merely because it is saved beside another file. The declaration determines package membership, while the directory layout helps compilers and build tools locate source and compiled files reliably.

Types in the same package can generally refer to each other by simple name, subject to access rules. For example, CartItem can refer to Product without an import if both belong to com.example.shop.

4. Package Naming Conventions

Java permits many valid package names, but common conventions make projects easier to read.

Use lowercase

Preferred: com.example.shop

Discouraged: com.Example.Shop

Mixed-case package names may compile, but lowercase names match established Java style and reduce confusion across environments.

Use a reverse-domain prefix

Organizations commonly write a domain name in reverse order as the package root:

  • example.com → com.example
  • algomaster.io → io.algomaster

This helps reduce accidental collisions between organizations. It is a naming convention, not a requirement that the application connect to the domain.

Use valid Java identifiers

Each segment must follow Java identifier rules. It cannot contain a hyphen, begin with a digit, or be a Java keyword. Letters, digits, underscores, and dollar signs are permitted by identifier syntax, although underscores and dollar signs are uncommon in ordinary package names.

Package nameStatusReason
com.example.shopPreferredLowercase and valid
com.example.shop.cartPreferredClear subpackage
com.Example.ShopValid but discouragedMixed case
com.example.shop-cartInvalidHyphen is not valid in an identifier
com.example.newInvalidnew is a Java keyword
com.example.3dInvalidSegment begins with a digit

Choose meaningful names that reflect responsibility. Feature-based names such as shop.cart and shop.orders are often more informative than vague names such as misc. Avoid making the hierarchy unnecessarily deep.

5. How Package Names Map to Folders

In a conventional Java source tree, each dot in the package name corresponds to a directory boundary.

Package declarationDirectory below the source root
package com.example.shop;com/example/shop/
package com.example.shop.cart;com/example/shop/cart/
package com.example.shop.app;com/example/shop/app/

A typical project can look like this:

Product.java and CartItem.java belong to com.example.shop; Main.java belongs to com.example.shop.app. The src directory is the source root, so the package path begins below it.

The source file name should match the public top-level class name. A public class named Product belongs in Product.java.

Folder mapping flowchart

Whiteboard
Loading diagram...

A mismatch between the declaration and source location can cause compiler or build-tool problems. Keep the package declaration, source tree, and build configuration consistent.

6. Importing Classes from Other Packages

An import statement lets source code use a type's simple name instead of repeating its fully qualified name.

The imports allow the source file to refer to Product and List by their short names.

Import facts to remember

  • Imports come after the package declaration and before type declarations.
  • An import does not move a type into the current package.
  • An import does not copy a class into the source file; it helps the compiler resolve a simple name.
  • You can use a fully qualified name instead of importing a type.
  • Types in the same package generally do not need imports.
  • Types in java.lang, such as String, Object, System, and Math, are available without explicit imports.
  • Other packages usually need an import or a fully qualified name.

A single-type import names one type, such as import java.util.ArrayList;. An import-on-demand statement such as import java.util.*; makes types in that package available for simple-name resolution; it does not recursively import subpackages. Thus, java.util.concurrent is not imported by java.util.*.

A static import lets code refer to accessible static members without qualifying them with their declaring type. Use static imports selectively, because excessive use can make it harder to tell where a method or constant comes from.

7. Compiling and Running Packaged Classes

Packages affect both the directory layout of compiled files and the class name used to launch a program.

Suppose the source files are under src/ and the entry point is com.example.shop.app.Main. From the project directory, compile the files into a separate output directory:

The -d out option tells javac to write .class files beneath out/, preserving the package directory structure. For example, the output may include:

The precise list of source files depends on the project. Include all required sources or use a build tool to manage compilation.

Run the entry point using its fully qualified name:

Here, -cp out (also written --class-path out) tells the JVM that out is the root of the compiled package tree. The class name is written with dots, not slashes.

Frequent command mistakes

Incorrect command or assumptionWhy it failsCorrect approach
java -cp out MainLooks for Main in the default packageUse the fully qualified class name
java out/com/example/shop/app/Main.classjava expects a class name, not a file pathPass com.example.shop.app.Main
Running without -cp out when classes are in out/The default classpath may not include the output directoryAdd -cp out
Using slashes in the class nameThe launcher expects a fully qualified class nameSeparate package segments with dots

Maven and Gradle usually automate source discovery, dependency management, compilation, and output directories. Understanding the basic commands remains useful when diagnosing classpath and build problems.

8. The Default (Unnamed) Package

If a source file has no package declaration, its types belong to the default package, also called the unnamed package. It can be convenient for a tiny experiment or a one-file beginner example, but it is not suitable for most real applications.

Its main limitations are:

  1. Named-package code cannot import its types. A class in com.example.shop cannot use a default-package type through a normal import.
  2. It offers weak namespace organization. There is no meaningful package prefix to distinguish unrelated classes with the same simple name.
  3. It does not fit named Java modules. Types in the unnamed package cannot be part of a named module.

For projects that contain multiple files, reusable code, or a build system, choose a named package early. Introducing packages later often requires moving files and updating references.

9. Package-Private Access and Package Boundaries

A member declared without an access modifier has package-private access, sometimes called default access. Such a member can be accessed by classes in the same package, subject to the other language rules, but not by unrelated code in another package.

For example, com.example.shop and com.example.shop.app are different packages. The second package does not gain access to package-private members in the first merely because its name starts with the same text.

Access levels at a glance

ModifierSame classSame packageSubclass in another packageUnrelated code elsewhere
publicYesYesYesYes, subject to module/API accessibility
protectedYesYesYes, under Java's cross-package subclass rulesNo
No modifier (package-private)YesYesNo, solely by being a subclass elsewhereNo
privateYesNoNoNo

This is a simplified guide. Cross-package protected access has specific rules; a subclass in another package does not gain unrestricted access through every superclass instance.

When package-private access is useful

Package-private visibility is appropriate for implementation details that several classes within a package need to share but that should not become public API. Examples include helper methods, internal supporting types, and methods used only by a tightly related group of classes.

Use private when access should be limited to the declaring class. Use public when a type or member is intentionally part of the API. Packages help establish boundaries, but they do not replace careful API design.

10. Common Java Standard-Library Packages

Java's standard library is organized into packages. Recognizing common package names helps you find the types you need.

PackageCommon types and purpose
java.langCore types such as String, Object, System, Math, Integer, and Thread
java.utilCollections and utilities such as List, Map, ArrayList, HashMap, Scanner, and Random
java.ioTraditional input/output streams, readers, writers, and related types
java.nio.fileFile-system APIs such as Path and Files
java.netNetworking APIs, including URI- and URL-related types
java.timeDate/time types such as LocalDate, Instant, and Duration
java.mathArbitrary-precision types such as BigInteger and BigDecimal
javax.cryptoCryptographic APIs
javax.sqlExtended JDBC-related APIs

Why java.lang is special

Types in java.lang are automatically available by simple name. That is why code can use String and System without importing them. Other packages usually need an explicit import or fully qualified name.

The javax namespace began as an extension namespace, and many API names remained for compatibility as Java evolved. Do not assume every javax.* API is available in every runtime configuration; check the Java version and dependencies for the API you need.

11. Naming Collisions and Subpackages

Packages allow different parts of a codebase to define types with the same simple name. If two imported types share the same simple name, the compiler cannot resolve that name unambiguously. Use a fully qualified name for one of the types or redesign the imports to avoid the conflict.

Remember that a package hierarchy is a naming and organizational hierarchy, not an access-control inheritance tree. com.example.shop.cart does not automatically receive package-private access to com.example.shop.

Good package design aims for clear responsibilities, low accidental coupling, and names that make dependencies understandable. Do not create a new package for every class without a reason; group types according to the way they work together and the boundaries you want to maintain.

12. Common Mistakes and How to Avoid Them

Mismatched package declaration and path

A file declaring package com.example.shop; should be organized under the corresponding package directory in a conventional source tree. A mismatch can make compilation and source discovery fail.

Fix: align the declaration, source path, and build configuration.

Launching a packaged class by its short name

If Main belongs to com.example.shop.app, java -cp out Main asks the JVM to find a default-package class.

Fix: use java -cp out com.example.shop.app.Main.

Confusing an import with package membership

Importing com.example.shop.Product does not make Product part of the importing package.

Fix: think of imports as name-resolution assistance.

Assuming subpackages share package-private access

com.example.shop.cart is distinct from com.example.shop.

Fix: choose an appropriate modifier or place tightly related implementation types in the same package when that makes sense architecturally.

Expecting wildcard imports to include subpackages

import java.util.*; does not import java.util.concurrent.*.

Fix: import the package or types actually required.

Using a keyword as a package segment

A segment such as new is invalid because it is a Java keyword.

Fix: choose a different valid identifier, such as newest.

Placing multiple public top-level classes in one file

A source file may contain multiple top-level types, but at most one public top-level type is permitted in a conventional source file, and its name must match the file name.

Fix: place public top-level types in appropriately named files.

13. A Practical Workflow for Package Design

Whiteboard
Loading diagram...


Recommended habits:

  1. Choose a stable package root associated with the project or organization.
  2. Group code by responsibility rather than placing everything in one large package.
  3. Use lowercase, meaningful package segments.
  4. Avoid the default package for code that will grow or be reused.
  5. Use package-private access for implementation details shared within a package.
  6. Keep declarations, folders, and build configuration aligned.
  7. Compile generated class files into a separate output directory.
  8. Use Maven or Gradle when dependencies, tests, and deployment make manual commands cumbersome.

14. Quick Revision

  • A package is a named namespace that organizes related Java types.
  • A fully qualified name includes both package and type, such as com.example.shop.Product.
  • A package declaration comes before imports and type declarations.
  • Package names are conventionally lowercase and often start with a reverse-domain prefix.
  • In a conventional source tree, dots in package names map to folder boundaries.
  • Imports help resolve simple names; they do not move types into another package.
  • java.lang is available without explicit imports.
  • The default package is suitable for tiny experiments, not most maintainable projects.
  • Package-private members are visible within the exact same package, not automatically in subpackages.
  • Packaged programs should be launched with the classpath root and fully qualified class name.

15. Conclusion

Packages are a foundational Java feature for organizing code as applications grow. They provide namespaces that prevent class-name collisions, encourage a consistent source-tree structure, and create useful boundaries for package-private implementation details.

To use packages reliably, keep declarations and folder paths aligned, follow conventional lowercase naming, understand how imports work, and launch packaged classes with fully qualified names. For larger projects, a build tool can automate compilation and classpath management while a thoughtful package structure keeps code easier to understand and maintain.