Recent from talks
Contribute something
Nothing was collected or created yet.
Java package
View on WikipediaA Java package organizes Java classes into namespaces,[1] providing a unique namespace for each type it contains. Classes in the same package can access each other's package-private and protected members.
In general, a package can contain the following kinds of types: classes, interfaces, enumerations, records and annotation types. A package allows a developer to group classes (and interfaces) together. These classes will all be related in some way – they might all have to do with a specific application or perform a specific set of tasks. Programmers also typically use packages to organize classes belonging to the same category or providing similar functionality.
Using packages
[edit]In a Java source file, the package that this file's class or classes belong to is specified with the
package keyword. This keyword is usually the first keyword in the source file. At most one package declaration can appear in a source file.
package java.awt.event;
To use a package's classes inside a Java source file, it is convenient to import the classes from the package with an import declaration, which dequalifies the namespaces of the class into scope. The following declaration
import java.awt.event.*;
imports all classes from the java.awt.event package, while the next declaration
import java.awt.event.ActionEvent;
imports only the ActionEvent class from the package. After either of these import declarations, the ActionEvent class can be referenced using its simple class name:
ActionEvent myEvent = new ActionEvent();
Classes can also be used directly without an import declaration by using the fully qualified name of the class. For example,
java.awt.event.ActionEvent myEvent = new java.awt.event.ActionEvent();
does not require a preceding import declaration.
Package-wide Javadoc & annotations
[edit]Documentation explaining the package as a whole is written as Javadoc in a file named exactly package-info.java. That file is also the place for annotations to be used across all classes of the package.[2]
The unnamed package
[edit]If a package declaration is not used, classes are placed in an unnamed package. Classes in an unnamed package cannot be imported by classes in any other package.[3] The official Java Tutorial advises against this:
- Generally speaking, an unnamed package is only for small or temporary applications or when you are just beginning the development process. Otherwise, classes and interfaces belong in named packages.[4]
Package access protection
[edit]Public members and classes are visible everywhere and private members are visible only in the same class. Classes within a package can access classes and members declared with default (package-private) access as well as class members declared with the protected access modifier. Default (package-private) access is enforced when a class or member has not been declared as public, protected or private. By contrast, classes in other packages cannot access classes and members declared with default access. However, class members declared as protected can be accessed from the classes in the same package as well as classes in other packages that are subclasses of the declaring class.[5]
Creation of JAR files
[edit]JAR files are created with the jar command-line utility. The command
jar cf myPackage.jar *.class
compresses all .class files into the JAR file myPackage.jar. The 'c' option on the command line tells the jar command to "create new archive." The ' f ' option tells it to create a file. The file's name comes next before the contents of the JAR file.
Package naming conventions
[edit]Packages are usually defined using a hierarchical naming pattern, with some levels in the hierarchy separated by periods (., pronounced "dot"). Although packages lower in the naming hierarchy are often referred to as "subpackages" of the corresponding packages higher in the hierarchy, there is almost no semantic relationship between packages. The Java Language Specification establishes package naming conventions to avoid the possibility of two published packages having the same name. The naming conventions describe how to create unique package names, so that packages that are widely distributed will have unique namespaces. This allows packages to be separately, easily and automatically installed and catalogued.
In general, a package name begins with the top level domain name of the organization and then the organization's domain and then any subdomains, listed in reverse order. The organization can then choose a specific name for its package. Subsequent components of the package name vary according to an organization's own internal naming conventions.[6]
For example, if an organization in Canada called MySoft creates a package to deal with fractions, naming the package ca.mysoft.fractions distinguishes the fractions package from another similar package created by another company. If a German company named MySoft also creates a fractions package, but names it de.mysoft.fractions, then the classes in these two packages are defined in a unique and separate namespace. There is no requirement that the TLD must be the name of the country of the company. For instance, many packages also use other TLDs such as org, com, etc. The Java standard library puts all symbols in the java.*, javax.*, or jdk.* namespace, though some classes associated with other technologies may reside in other namespaces. Some projects, such as Project Lombok, may not use TLDs at all (all symbols are located in namespace lombok).
Using TLDs in package names is a convention primarily associated with Java, but seldom used in other languages. In other languages such as C++ and C# it is sufficient to just prefix package/namespace names with the company name.
Complete conventions for disambiguating package names and rules for naming packages when the Internet domain name cannot be directly used as a package name are described in section 7.7 of the Java Language Specification.[7]
Core packages in Java SE 8
[edit]java.lang
|
Basic language functionality and fundamental types. Implicitly imported by every program. |
|---|---|
java.util
|
Collection data structure classes |
java.io
|
File operations |
java.math
|
Multiprecision arithmetics |
java.nio
|
The Non-blocking I/O framework for Java |
java.net
|
Networking operations, sockets, DNS lookups, ... |
java.security
|
Key generation, encryption and decryption |
java.sql
|
Java Database Connectivity (JDBC) to access databases |
java.awt
|
Basic hierarchy of packages for native GUI components |
java.text
|
Provides classes and interfaces for handling text, dates, numbers, and messages in a manner independent of natural languages. |
java.rmi
|
Provides the RMI package. |
java.time
|
The main API for dates, times, instants, and durations. |
java.beans
|
The java.beans package contains classes and interfaces related to JavaBeans components. |
java.applet
|
This package provides classes and methods to create and communicate with the applets. |
Modules
[edit]In Java 9 (released on September 21, 2017) support for "modules", a kind of collection of packages, was implemented as a result of the development effort of Project Jigsaw. The "modules" were earlier called "superpackages" and originally planned for Java 7.
Modules describe their dependencies in a declaration placed in a file named module-info.java at the root of the module's source-file hierarchy. Since Java 9, the JDK is able to check the module dependencies both at compile time and runtime. The JDK itself is modularized for Java 9.[8][9] For example, the majority of the Java standard library is exported by the module java.base.
As an example, the following module declaration declares that the module com.foo.bar depends on another com.foo.baz module, and exports the following packages: com.foo.bar.alpha and com.foo.bar.beta:
module com.foo.bar {
requires com.foo.baz;
exports com.foo.bar.alpha;
exports com.foo.bar.beta;
}
See also
[edit]References
[edit]- ^ James Gosling, Bill Joy, Guy Steele, Gilad Bracha, The Java Language Specification, Third Edition, ISBN 0-321-24678-0, 2005. In the Introduction, it is stated "Chapter 7 describes the structure of a program, which is organized into packages similar to the modules of Modula."
- ^ "Chapter 7. Packages and Modules". docs.oracle.com. Retrieved 2021-12-10.
- ^ "Chapter 7. Packages". Docs.oracle.com. Retrieved 2013-09-15.
- ^ "Creating and Using Packages (The Java™ Tutorials > Learning the Java Language > Packages)". docs.oracle.com.
- ^ "Controlling Access to Members of a Class (The Java™ Tutorials > Learning the Java Language > Classes and Objects)". docs.oracle.com.
- ^ Code Conventions for the Java Programming Language: 9. Naming Conventions
- ^ "Packages". docs.oracle.com.
- ^ "JDK Module Summary". Oracle Corporation. 2015-10-23. Archived from the original on 2015-12-08. Retrieved 2015-11-29.
- ^ "Understanding Java 9 Modules". Oracle Corporation. October 1, 2017. Retrieved 2022-10-04.
External links
[edit]Java package
View on Grokipediacom.example.graphics), ensuring unique identification of types in large-scale software development.[3] This structure allows developers to declare types within a package using a package statement at the top of a compilation unit, which organizes source files into directories mirroring the package hierarchy.[2] Access modifiers like public, protected, and package-private (default) further control visibility, permitting unrestricted use within the package while restricting external access to protect implementation details.[4]
Introduced in Java 1.0, packages form the foundation of the Java platform's standard library, with core examples including java.lang for fundamental classes like Object and String, java.util for utilities and collections, and java.io for input/output operations.[1][2] Since Java 9, packages integrate with the module system, where modules encapsulate packages to enhance modularity, reliability, and security by explicitly declaring exported and accessible packages.[5] This evolution maintains backward compatibility while supporting modern application architectures, such as those in enterprise and cloud environments.[1]
Overview and Purpose
Definition
In Java, a package serves as a namespace mechanism that organizes a set of related classes and interfaces, preventing naming conflicts across different parts of a program and facilitating the logical grouping of types based on their functionality or domain.[6] This structure allows developers to encapsulate related code, making large-scale software development more manageable by isolating namespaces. Packages were introduced with the initial release of Java 1.0 in 1996, as an integral component of the language's object-oriented design to support modular programming from the outset. The concept is formally defined in the first edition of The Java Language Specification, which outlines packages as the primary organizational unit for programs. While packages are logical constructs, they are typically mapped to a corresponding directory structure in the filesystem for compilation and execution, and correspond to a directory structure in the filesystem for compilation and execution, where the compiler requires source files to be placed in directories mirroring the package hierarchy to ensure correct compilation. though this mapping is not strictly required and serves primarily to enforce organization during development.[2][1] For instance, to declare that a class belongs to a package, the following syntax is placed at the top of the source file:package com.example.myapp;.[2] This declaration associates all public types in the file with the specified namespace.
Benefits and Organization
Java packages provide a fundamental mechanism for organizing code in Java applications, enhancing maintainability by grouping related classes and interfaces into logical namespaces that mirror the structure of directories. This logical grouping simplifies the management of large codebases, allowing developers to navigate and modify code more efficiently without sifting through unrelated components.[6] Furthermore, packages serve as a foundational element for larger-scale software architecture, enabling the construction of modular systems where components can be developed, tested, and integrated independently.[1] A primary advantage of packages is the reduction of naming collisions, as each package establishes a unique namespace that prevents conflicts between class names across different parts of a project or when integrating third-party libraries. For instance, thejava.util.Vector class can coexist with a custom vector.Vector without ambiguity, ensuring clarity in large projects.[1] Packages also promote reusability by facilitating the distribution and reuse of code libraries, such as the extensive Java API, where thousands of pre-built classes are organized into packages for easy importation and application.[6] This structure supports separation of concerns in object-oriented programming, as related functionality is encapsulated within packages, allowing developers to focus on specific domains while maintaining overall code clarity and reducing complexity.[6]
In team development environments, packages divide code into independent units that align with team responsibilities, fostering collaboration by enabling parallel work on distinct modules without interference. This division not only streamlines version control but also supports the versioning and distribution of code libraries, as packages can be sealed and bundled into JAR files for sharing across projects or organizations.[6] Overall, these organizational benefits make packages essential for scalable, maintainable Java software.[1]
Declaration and Basic Usage
Declaring Packages
In Java, a package is declared using thepackage keyword followed by the package name and a semicolon, as in package com.example;. This declaration must appear as the first statement in the source file, preceding any import statements or class declarations, and may be preceded only by comments.[7]
A single source file can contain multiple class or interface declarations, but all must belong to the same package specified by the declaration; mixing classes from different packages in one file is not permitted. If no package declaration is present, the classes belong to the unnamed package, which serves as a default for top-level classes without a specified namespace.[7][8]
During compilation with the javac command, the Java compiler interprets the package declaration to organize the resulting .class files into a directory hierarchy that mirrors the package name's dot-separated components. For instance, a declaration of package com.example; requires the source file to be placed in a com/example/ subdirectory relative to the compilation root, and the compiler will output the .class file to the corresponding com/example/ directory. This structure ensures that the fully qualified class name, such as com.example.MyClass, aligns with the filesystem path for loading at runtime.[2][8]
To execute a class from a package, the java command requires the fully qualified class name, prefixed by the package path, and often specifies the classpath to locate the directory structure. For example, assuming the .class file is in the current directory's com/example/ subdirectory, the command java -cp . com.example.MyClass runs the class by searching the classpath (here, the current directory) for the matching package hierarchy.[2]
Importing Packages
In Java, importing packages enables the use of classes, interfaces, and other types from other packages without always specifying their fully qualified names, facilitating code readability and modularity. Import declarations are placed at the beginning of a compilation unit, after the optional package declaration and before any class or interface declarations. They do not instantiate or load classes but merely provide shortcuts for name resolution during compilation.[9] Single-type import declarations allow the import of a specific class or interface from a named package, using the syntaximport package.name.ClassName;. For example, import java.util.[List](/page/List); permits the use of List instead of java.util.List throughout the file. This form is precise and avoids importing unnecessary types, reducing the risk of namespace pollution. Type-import-on-demand declarations, on the other hand, import all accessible public types from a package using import package.name.*;, such as import java.util.*;, which brings in classes like ArrayList and HashMap under their simple names. However, this wildcard form can lead to ambiguities if multiple packages define types with the same simple name, requiring fully qualified names to resolve conflicts; the compiler issues errors for unresolved references in such cases.[10][11]
Static imports, introduced in Java 5.0 (J2SE 5.0, released in 2004), extend this mechanism to static members of classes or interfaces, allowing their use without qualifying the containing type. The syntax is import static package.name.ClassName.staticMember; for a single member, like import static java.lang.Math.PI;, or import static package.name.ClassName.*; for all accessible static members of a type, such as import static java.lang.Math.*;. This enables direct invocation, e.g., double area = PI * r * r;, but should be used judiciously to maintain code clarity, as overuse can obscure the origin of members and complicate maintenance.[12][13]
Imports are unnecessary for types within the same package or for any public type in the java.lang package, which is implicitly imported in every compilation unit as if import java.lang.*; were declared. As an alternative to imports, fully qualified names can always be used, such as java.util.List<String> list = new java.util.ArrayList<>();, which bypasses import statements entirely and is recommended for occasional references to avoid compilation dependencies on external packages.[14][10]
During compilation with javac, import declarations guide the resolution of unqualified names: the compiler maps simple names to their canonical (fully qualified) equivalents based on the imports, checking accessibility and module boundaries before substituting them in the generated bytecode. Unresolved or ambiguous names trigger compile-time errors, ensuring type safety; for instance, shadowing by local variables takes precedence over imports, and duplicate imports of the same simple name from different packages are disallowed for single-type imports but permitted (with qualification required) for on-demand imports. This process does not affect runtime loading, which occurs only when classes are first referenced.[9][15]
Special Package Features
The Unnamed Package
In Java, the unnamed package, also known as the default or root package, is the implicit container for any compilation unit that lacks apackage declaration. This setup places such classes directly in the current working directory during compilation and execution, without establishing any namespace separation from other classes in the same directory.
Key characteristics of the unnamed package include its simplicity and lack of hierarchical structure; it supports no subpackages, as package declarations always reference a named top-level package.[16] Classes within it can access each other without import statements, but importing types from named packages is possible via standard import declarations, though the reverse—exporting or referencing unnamed package types from named packages—is restricted in modular contexts.
The unnamed package has notable limitations, particularly since Java 9's introduction of the module system, where packages in the unnamed module cannot be explicitly referenced or imported by packages in named modules.[17] This restriction enhances modularity but renders the unnamed package unsuitable for integration with modern, module-based applications. Additionally, it is generally discouraged for non-trivial projects due to the absence of namespace organization, which can lead to naming conflicts and maintenance challenges in larger codebases.[8]
Common use cases for the unnamed package are limited to simple scripts, applets, prototypes, or unit tests where minimal setup is preferred.[8] For example, a basic class like HelloWorld.java without a package statement compiles directly with javac HelloWorld.java and runs from the current directory using java HelloWorld, avoiding any folder hierarchy.[8] This approach suits educational examples or quick experiments but should be avoided in production code to leverage the full benefits of package organization.[8]
Package-Wide Documentation and Annotations
In Java, package-wide documentation and annotations are primarily managed through a special source file namedpackage-info.java, introduced in Java SE 5 to serve as the central location for package-level declarations, annotations, and descriptive comments. This file must reside in the root directory of the package it describes and contains only the package declaration, optionally preceded by annotations and followed by a Javadoc comment block. Unlike regular class files, package-info.java does not define classes, interfaces, or other members; its sole purpose is to encapsulate metadata applicable to the entire package.
The Javadoc comment in package-info.java provides an overview of the package's purpose, usage, and contents, which the javadoc tool processes to generate the package-summary.html file in the API documentation.[18] This summary page includes the package description—derived from the first sentence of the comment for brevity, with the full text appearing below—along with lists of classes, interfaces, and other package members. Standard Javadoc tags supported at the package level include @author, @since, @see, @version, and @deprecated, enabling structured documentation such as authorship credits or deprecation notices. For instance, the following example demonstrates a basic package-info.java file:
/**
* Utility classes for [data processing](/page/Data_processing) in the com.example.util package.
* <p>
* This package provides helper methods for [string](/page/String) manipulation and validation.
* @author [John Doe](/page/John_Doe)
* @since 1.0
* @see com.example.data
*/
package com.example.util;
/**
* Utility classes for [data processing](/page/Data_processing) in the com.example.util package.
* <p>
* This package provides helper methods for [string](/page/String) manipulation and validation.
* @author [John Doe](/page/John_Doe)
* @since 1.0
* @see com.example.data
*/
package com.example.util;
javadoc tool is run on a source tree containing this file (e.g., via javadoc -d docs src), it incorporates the comment into the generated package-summary.html, ensuring developers can access package-level insights without examining individual class files.[18]
Annotations at the package level are declared immediately before the package statement in package-info.java, applying their effects to all elements within the package. Built-in annotations like @Deprecated can mark an entire package as discouraged for use, signaling to developers and tools that its contents should be phased out in favor of alternatives; this generates appropriate warnings in IDEs and documentation.[19] Custom annotations, defined elsewhere in the codebase or libraries, can also target packages for purposes such as versioning, security policies, or serialization configurations (e.g., JAXB's @XmlSchema for namespace mapping).[20] The @Documented meta-annotation, when applied to a custom annotation type, ensures that package-level annotations appear in the generated Javadoc output, enhancing API visibility. These annotations are compiled into a package-info.class file, which is included in JAR archives alongside other package resources, allowing runtime and compile-time processors to access them.
Access Control Mechanisms
Visibility Modifiers
In Java, visibility modifiers, also known as access modifiers, control the accessibility of classes, interfaces, methods, constructors, and fields, thereby enforcing encapsulation and managing interactions across packages. There are four primary modifiers:public, protected, default (also called package-private), and private. These modifiers determine whether code in the same class, same package, subclasses (potentially in other packages), or any code worldwide can access the declared element.[21][22]
The public modifier grants the broadest visibility, making the element accessible from any class in any package. It is typically used for elements intended as part of a public API, such as classes or methods exposed for general use. For example, a public class can be imported and instantiated from unrelated packages:
[public](/page/Public) class MyPublicClass {
[public](/page/Public) void myMethod() {
// [Implementation](/page/Implementation)
}
}
[public](/page/Public) class MyPublicClass {
[public](/page/Public) void myMethod() {
// [Implementation](/page/Implementation)
}
}
protected modifier allows access within the same package and also to subclasses, even if those subclasses reside in different packages, facilitating inheritance while restricting general external access. It applies to members (methods, fields, constructors) and inner (non-top-level) classes but not to top-level classes or interfaces. An example is a protected field in a superclass:
public class Superclass {
protected int protectedField;
}
public class Superclass {
protected int protectedField;
}
protectedField through inheritance, but unrelated classes cannot. This modifier balances reusability in hierarchies with package boundaries.[21][24]
Default (package-private) access, which occurs when no modifier is specified, restricts visibility to the same package only, preventing access from other packages. It is suitable for internal package utilities not meant for external consumption. For instance:
class MyDefaultClass {
int defaultField;
void defaultMethod() {
// Implementation
}
}
class MyDefaultClass {
int defaultField;
void defaultMethod() {
// Implementation
}
}
MyDefaultClass and its members are invisible outside the package, promoting modular design by hiding implementation details. Private access, the most restrictive, limits visibility to the declaring class itself, applying only to members (methods, fields, constructors) and inner classes, not top-level declarations. An example is:
private String privateField;
private MyPrivateClass() {
// Private constructor
}
private String privateField;
private MyPrivateClass() {
// Private constructor
}
| Modifier | Same Class | Same Package | Subclass (Any Package) | Any Class (Any Package) |
|---|---|---|---|---|
public | Yes | Yes | Yes | Yes |
protected | Yes | Yes | Yes | No |
| default | Yes | Yes | No | No |
private | Yes | No | No | No |
public elements are essential for API exposure, allowing libraries to provide reusable components across projects, while default access supports internal package use without risking external interference. Protected enables controlled extension via inheritance across packages, but private and default minimize unintended access. These modifiers apply uniformly to classes (except private for top-level), methods, fields, and constructors, guiding developers to use the most restrictive level possible for security and maintainability.[21][22]
Access modifiers, including the default package-private level, were formalized in Java 1.0, released in 1996, as part of the original language design to support object-oriented principles. Subsequent versions refined protected access, such as clarifications for nested classes in Java 1.1 and integration with module systems in Java 9, though the core package-based semantics remain consistent.[25][22]
Package-Private Access
Package-private access, also known as default access, is the visibility level applied to classes, interfaces, methods, and fields in Java when no explicit access modifier is specified.[21] This default level restricts access to only within the same package, allowing related classes to collaborate internally without exposing elements to external code.[21] No keyword is required to declare package-private access; it is implicitly enforced by omitting modifiers likepublic, protected, or private.[21]
The mechanics of package-private access are handled primarily by the Java compiler during compilation, which generates errors for attempts to access default members from outside the package, and by the Java Virtual Machine (JVM) at runtime for dynamic resolutions.[26] For instance, field access and method invocations are resolved during the linking phase, where the JVM checks accessibility; if a class attempts to access a package-private method or field from another package, it throws an IllegalAccessError.[27] This dual enforcement ensures that package boundaries are respected, promoting encapsulation by limiting visibility to the declaring package's namespace.[21]
A common use case for package-private access is hiding implementation details within a package to facilitate internal collaboration, such as utility classes that support core package functionality without needing public exposure.[21] For example, consider a package com.example.utils containing a class Helper with a default method processData(); classes within com.example.utils can invoke this method, but attempts from com.other.pkg will fail at compile time with an error like "processData() has private access in Helper".[21] This approach allows developers to build modular packages where internal APIs remain shielded, reducing the risk of unintended dependencies from external code.[21]
Best practices recommend using package-private access for elements intended solely for intra-package use, as it aligns with the principle of employing the most restrictive visibility level possible to enhance maintainability and security.[21] Developers should prefer this over broader modifiers like public for non-exported utilities, ensuring that only necessary interfaces are exposed while keeping helper methods and fields internal to avoid bloating the public API.[21] In the context of broader visibility modifiers, package-private serves as a middle ground between private (class-only) and protected (subclass-inclusive), but it strictly confines access to the package scope without inheritance considerations.[21]
Naming and Structure
Naming Conventions
Java package names adhere to strict conventions defined by Oracle (formerly Sun Microsystems) to promote uniqueness, avoid conflicts, and enhance readability in collaborative and distributed development environments. These names consist exclusively of lowercase ASCII letters, separated by periods (.), without underscores or other special characters in standard cases.[28][29]
The recommended structure derives from reversing an organization's Internet domain name, forming a hierarchical prefix that ensures global uniqueness by tying names to registered domains. For instance, a company with the domain example.com would use com.example as the base, extended to subcomponents like com.example.project.module for specific modules or features. This domain-based approach prevents naming collisions, as no two entities can own the same domain, and facilitates easy identification of the originating organization. A prominent example is the Apache Software Foundation's use of org.apache.commons for its shared utility libraries.[30][29][28]
For developers without a registered domain, such as in personal or hobby projects, a common variation is to employ a generic or self-descriptive prefix like com.example or personal., though the latter is discouraged for code intended for wider distribution to maintain consistency with domain-based uniqueness. Crucially, certain prefixes are reserved for the Java platform: names beginning with java. or javax. are exclusively allocated to standard library packages, and using them in custom code can lead to runtime errors or compatibility issues. Additionally, internal Oracle packages may start with sun., which should also be avoided by third-party developers.[29][30]
In cases where domain names contain invalid characters (e.g., hyphens or digits at the start of components), an underscore (_) may be inserted as a workaround, such as com.example._invalid for a domain like invalid-example.com, but this is an exception rather than the norm to preserve simplicity.[29]
These guidelines trace their origins to Java's early development in the mid-1990s, with formal documentation in the 1996 Java Code Conventions and refinements through the Java Language Specification starting from Java 1.0. The conventions received their last major revision in 1999 but continue to serve as the authoritative standard, unchanged in principle across subsequent Java versions, including Java SE 25 (released September 2025).[31][3][32]
Hierarchical Packages
In Java, packages can be organized hierarchically to form multi-level namespaces, allowing for structured grouping of related classes and interfaces. The fully qualified name of a package uses dot-separated components to denote this hierarchy; for example, ifP is a package and Q is a subpackage of P, then P.Q represents the fully qualified name of the subpackage.[33] This structure maps directly to the file system, where the package name is transformed into a directory path by concatenating components with file name separators, such as com/company/project/submodule corresponding to the directory com/company/project/submodule/.[34] During compilation, source files in nested directories are automatically assigned to the corresponding hierarchical package based on their location relative to the source root.[34]
This hierarchical organization provides deeper logical grouping for complex libraries and applications, enabling developers to treat subpackages as independent units while maintaining overall coherence.[33] For instance, in the Java Development Kit (JDK), the top-level package java contains subpackages such as util, io, and net, where java.util further includes classes like ArrayList and subpackages like concurrent.[33] Such nesting supports scalable namespace management without imposing dependencies between levels.
Importantly, subpackages are treated as entirely separate from their parent packages in terms of access control. There is no automatic inheritance of package-private (default) visibility across hierarchy levels; for example, a class in package oliver cannot access package-private members of a class in subpackage oliver.twist without explicit public exposure.[35] Access between them requires the use of public modifiers, ensuring encapsulation and preventing unintended coupling in hierarchical structures.[35] This independence reinforces the hierarchical model as a naming convention rather than a functional inheritance mechanism.[33]
Distribution and Packaging
Creating JAR Files
A JAR file serves as a platform-independent archive format for distributing Java packages, bundling class files organized by package directories along with resources and metadata into a single, compressed file based on the ZIP standard.[36] This structure preserves the hierarchical organization of packages, where class files in subdirectories (e.g.,com/example/mypackage/) are archived with their relative paths intact, enabling seamless loading via the Java classpath.[37]
To create a JAR file, the jar command-line tool is used with the basic syntax jar cf jar-file input-file(s), where c specifies creation of a new archive, f directs output to a file, and input files or directories are listed recursively to include package structures.[37] For instance, the command jar cf myapp.[jar](/page/Jar) -C build/classes . changes to the build/classes directory (using the -C option) and archives all contents, capturing .class files within their package subdirectories without including the build path itself.[38] Verbose output can be enabled with v for monitoring, such as jar cvf myapp.[jar](/page/Jar) -C build/classes ., and compression is applied by default unless disabled with 0.[37]
The manifest file, located at META-INF/MANIFEST.MF within the JAR, provides essential metadata and is automatically generated with basic entries during creation, but can be customized using the m option, as in jar cmf manifest.mf myapp.jar -C build/classes ..[39] It specifies attributes like Main-Class: com.example.MyApp to designate the entry point for executable JARs, allowing invocation via java -jar myapp.jar, and supports package versioning through headers such as Implementation-Version: 1.0 to indicate compatibility within the bundled packages.[39] These manifest entries influence package loading by integrating with the classpath, ensuring that classes from sealed or versioned packages are resolved correctly at runtime.[39]
For enhanced security, JAR files can be signed using external tools like jarsigner after creation, which generates signature files in META-INF to verify the integrity of packaged classes and prevent tampering with package contents.[36] Packages can also be sealed via manifest attributes, such as adding Name: com/example/mypackage/ Sealed: true to the manifest before archiving, which restricts all classes in that package to originate solely from this JAR, blocking additions from other sources during execution.[36] This sealing mechanism, applied at the JAR or per-package level, promotes encapsulation without affecting unnamed packages.[36]
Since the early 2000s, JAR creation has been automated in build processes using tools like Ant and Maven, which invoke the jar command to package directory-based structures into distributable archives.
Integration with Build Tools
Java packages integrate seamlessly with modern build tools such as Apache Maven and Gradle, which automate the organization of source code, dependency management, and compilation processes. In Maven, the standard project layout places Java source files under thesrc/main/java directory, where the package hierarchy directly mirrors the directory structure—for instance, a package named com.example corresponds to the path src/main/java/com/example. This convention ensures that the compiler can resolve package declarations automatically during the build phase, as defined in the project's pom.xml file, which specifies source directories and compiler settings via plugins like maven-compiler-plugin. Similarly, Gradle adopts the same convention-over-configuration approach, using src/main/java for production sources and enforcing package-directory alignment through its Java plugin, configurable in the build.gradle file.[40][41]
Build tools handle package resolution by managing transitive dependencies, which are libraries required indirectly by direct dependencies, ensuring all necessary packages are available on the classpath without manual intervention. Maven resolves these by traversing the dependency tree and applying mediation rules, such as selecting the nearest version in the tree (e.g., preferring a direct dependency's version over a deeper transitive one), while scopes like compile or runtime control transitivity to avoid bloating the build. Gradle similarly resolves transitive dependencies by default in configurations like implementation, constructing a dependency graph and using strict version conflict resolution to select compatible package versions, with options to exclude unwanted transitives via exclude rules. This process supports package imports across projects, where tools fetch artifacts from repositories like Maven Central, compiling and linking them during the build to resolve import statements in Java code.[42][43]
Best practices for integrating packages with build tools emphasize automation in continuous integration/continuous deployment (CI/CD) pipelines to maintain consistency and quality. In Maven and Gradle projects, enforce package naming conventions—such as reverse-domain notation (e.g., com.company.project)—by integrating static analysis tools like Checkstyle, configured via plugins in pom.xml or build.gradle, to validate directory structures and package declarations during builds; this catches violations early in CI workflows on platforms like Jenkins or GitHub Actions. Additionally, plugins like the package-info-maven-plugin can automatically generate package-info.java files for documentation and annotations (e.g., @NonNullByDefault), inserting them into package directories based on templates, which is particularly useful for large-scale projects to standardize package-level metadata without manual effort. These practices reduce errors in package organization and ensure reproducible builds across environments.[44]
As of Java 21 (the 2023 LTS release), build tools like Maven 3.9 and later versions have optimized support for modular packages under the Java Platform Module System (JPMS), allowing seamless compilation of module-info.java files alongside traditional packages. Maven's maven-compiler-plugin (version 3.11+) handles JPMS options such as --module-path for resolving modular dependencies, while Gradle’s Java toolchain support enables targeting Java 21 runtimes for builds, ensuring packages in modular projects are encapsulated and exported correctly during dependency resolution. This integration enhances scalability for modern Java applications by combining traditional package management with module boundaries.[45]
Standard Library Packages
Core Packages in Java SE
The Java Standard Edition (SE) runtime library includes 183 packages in version 25 (released in 2025), providing foundational APIs for developing and running Java applications.[46] These packages are grouped by functional areas, such as language fundamentals, data structures, input/output, networking, and database access, with many organized under modules introduced in Java 9 for improved modularity and encapsulation.[47] Prior to Java 9, packages prefixed withjavax served as extensions to the core java packages, offering optional or advanced features like graphical user interfaces and security; in modern Java SE, these have been integrated or migrated into standard modules.[46]
All core packages in Java SE are automatically available through the runtime classpath, ensuring seamless access without additional configuration in standard deployments.[47] The java.lang package is uniquely accessible without explicit import statements, allowing direct use of its classes in any Java code.[48]
Among the essential packages, several stand out for their ubiquity and criticality:
-
java.lang: This package supplies the core classes indispensable to the Java language, includingObjectas the superclass of all classes,Stringfor immutable text handling, primitive wrappers likeIntegerandDouble, and exception classes such asThrowable. It resides in thejava.basemodule and underpins object-oriented programming, reflection, and runtime operations.[48] -
java.util: Focused on utility functions, this package includes the Collections Framework with interfaces and implementations likeList(e.g.,ArrayList),Set(e.g.,HashSet), andMap(e.g.,HashMap), alongside legacy date/time classes likeDateandCalendar. Modern date/time handling is provided by the separatejava.timepackage (introduced in Java 8). It is also part of thejava.basemodule.[49] -
java.io: Providing input/output capabilities, this package defines streams for byte and character data (e.g.,InputStream,OutputStream,Reader,Writer), file operations viaFile, and serialization support throughSerializable. It enables reading from and writing to files, consoles, and network sources, and is included in thejava.basemodule. -
java.net: This package supports networking protocols and operations, offering classes for URLs (URL), sockets (Socket,ServerSocket), URI handling (URI), and HTTP clients (viajava.net.httpsince Java 11). It facilitates communication over TCP/IP and other transports, and is housed in thejava.basemodule. -
java.sql: Known as the JDBC (Java Database Connectivity) API, this package enables interaction with relational databases through interfaces likeConnection,Statement,PreparedStatement, andResultSet, allowing SQL execution, transaction management, and data retrieval. It forms a separatejava.sqlmodule for database-specific functionality.
Evolution in Recent Versions
Prior to Java 9, the Java Standard Edition (SE) platform relied on a monolithic classpath model, where all standard library packages were loaded collectively into a single runtime environment. Java SE 8, released in March 2014, encompassed approximately 204 packages, providing a comprehensive set of core functionalities from basic language support to advanced networking and internationalization features.[50] With the release of Java 9 in September 2017, the introduction of the Java Platform Module System (JPMS) marked a significant shift, reorganizing the JDK into over 95 modules while preserving the underlying package structure. Packages continued to serve as the primary organizational unit for classes and interfaces, but they were now explicitly grouped within modules for better encapsulation and dependency management; for example, thejava.util package, containing essential collection classes, was placed in the foundational java.base module.[51]
Subsequent long-term support (LTS) releases further refined the package ecosystem. Java 17, released in September 2021, removed several deprecated internal APIs, including elements of the sun.* packages that had long been discouraged for public use due to their non-portable and unsupported nature, thereby strengthening security and promoting reliance on stable public APIs.[52] Java 21, released in September 2023, finalized virtual threads via JEP 444, extending the java.lang.Thread class to support lightweight, JVM-managed threads that enhance concurrency in the java.lang package without the overhead of traditional platform threads.[53] By Java 25, released in September 2025, these concurrency and language features continued to evolve with enhancements such as primitive types in patterns (JEP 507) and further optimizations in virtual thread support, maintaining focus on performance and scalability in core packages like java.lang and java.util without altering the package structure significantly.[54] Throughout these changes, backward compatibility has been prioritized to ensure existing applications using legacy packages continue to function, often through migration aids like the --add-opens flag for controlled access to internals. The jdeps tool, available since JDK 8, plays a crucial role in this evolution by enabling developers to statically analyze package dependencies in class files or JARs, helping identify reliance on deprecated elements and guiding modularization efforts.[55]
Packages in the Modular System
Compatibility with Modules
The Java Platform Module System, introduced through Project Jigsaw in Java 9 in September 2017, builds upon the existing package system without altering its fundamental structure.[56][57] Packages, which have organized classes and interfaces since Java's inception, remain the primary unit for namespace management and access control, but they are now logically contained within modules to provide a higher level of organization and security.[58] A module groups one or more packages, allowing developers to define boundaries that prevent unintended access to internal elements, while public APIs in exported packages remain accessible as before.[59] To ensure backward compatibility with legacy code, non-modular JAR files placed on the module path are automatically treated as automatic modules.[57] These automatic modules derive their name from the JAR file (e.g., via theAutomatic-Module-Name manifest attribute or JAR filename), and they implicitly read all other modules in the configuration, exposing all their public packages without requiring any modifications.[60] This mechanism allows existing applications and libraries relying solely on packages to run seamlessly in a modular environment, bridging the classpath and module path during the transition.[61]
Migrating traditional package-based code to the modular system involves creating a module-info.java file at the root of the source tree to declare the module's name, dependencies, and exported packages.[58] This file explicitly lists the packages belonging to the module and uses directives like requires to specify dependencies on other modules, enabling compile-time verification of access.[57] Notably, named modules prohibit the use of the unnamed package (the default package for classes without a package declaration), as all code in a named module must reside in named packages to enforce clear boundaries; unnamed packages are confined to the unnamed module, which encompasses all classpath elements.[58]
This integration enhances encapsulation by treating packages as the foundational building blocks for module boundaries, restricting access to non-exported packages to only within the same module.[59] By default, internal packages and their public types are inaccessible from outside the module, reducing the risk of reliance on implementation details and promoting reliable, maintainable software architectures.[57] Tools like --add-exports and --add-reads provide temporary overrides for migration, but the long-term goal is stronger isolation to improve security and performance in large-scale applications.[57]
Exporting and Encapsulation
In the Java Platform Module System, introduced to enhance modularity and security, packages within a module can be selectively exported to control visibility to other modules.[57] Theexports directive in the module-info.java file specifies which packages are accessible outside the module, allowing public types and members within those packages to be used by dependent modules at both compile time and runtime.[62] For example, the syntax exports com.example.pkg; makes the entire com.example.pkg package available to any module that requires the current module.[62]
Qualified exports provide finer-grained control by limiting access to specific modules, promoting stronger encapsulation.[62] The syntax exports com.example.pkg to com.ally.module; restricts the package to only the named target module, ensuring that other modules cannot access it even if they depend on the exporting module.[62] This mechanism allows module authors to define "friend" relationships, where sensitive packages remain hidden from the broader ecosystem while being shared with trusted dependencies.[57]
Encapsulation is a core principle of the module system, where non-exported packages are inherently inaccessible to code outside the module, regardless of the public visibility of their classes or members.[63] This design enforces a boundary around the module, preventing unintended access and reducing the risk of coupling to internal implementations.[57] It strengthens the traditional package-private access modifier by aligning it with module boundaries; package-private elements are now confined not just to their package but to the entire module, enhancing reliability and maintainability in large-scale applications.[62]
To access exported packages from another module, the requires directive must be used in the consuming module's module-info.java.[64] The basic syntax is requires module.name;, which declares a dependency on the named module and enables reading its exported packages.[64] Optional modifiers like transitive propagate the dependency to downstream modules, while static indicates an optional runtime dependency.[64] Without a corresponding requires clause, attempts to reference types from another module's exported packages will result in compilation or linkage errors.[57]
The module system's exporting and encapsulation features have been standard since Java 9, providing a robust framework for modular Java applications.[57] In Java 21 and later, the system continues to enforce these boundaries more rigorously through the module path, discouraging reliance on the legacy classpath for new development and promoting explicit dependencies to minimize accessibility issues.[65]
Starting in Java 25, module import declarations provide an additional mechanism for accessing exported packages. The syntax import module M; allows on-demand import of all public top-level classes and interfaces from module M's exported packages, simplifying usage in certain contexts without a full requires directive, while maintaining encapsulation. This feature, introduced via JEP 511, enhances expressiveness for modular code.[66]