Back to Blog
Java

Java Package Declaration: Syntax and Naming Rules

java package declaration: Understand the syntax, placement, and naming rules of Java package declarations, and how they affect directory structure, imports, and access...

Javapackage namingaccess controlimportsdirectory structure
Illustration of a Java package declaration mapping to a directory structure with a folder tree and a code snippet.

A Java package declaration is the first statement in a source file, before any imports or class definitions. It tells the compiler which package the classes in that file belong to. The syntax is package com.example.myapp; followed by a semicolon. Only one package declaration is allowed per source file, and it must be the first non-comment line. If you place anything before it, such as an import or a class definition, the compiler rejects the file with an error.

package com.example.myapp; import java.util.List; public class UserService { // ... }

The package name is a sequence of identifiers separated by dots. Each identifier must be a valid Java identifier: it cannot start with a digit and cannot be a reserved keyword. The declaration ends with a semicolon, just like any other Java statement. The package name becomes part of the fully qualified name of every class in the file. For example, the class UserService above has the fully qualified name com.example.myapp.UserService.

How Package Names Affect the Directory Structure

The Java compiler and the JVM expect the directory structure to mirror the package name. A class in the package com.example.myapp must be stored in a directory path com/example/myapp/. This is not a rule enforced by the language itself, but it is required by the standard tools. The javac compiler, when given a source file with a package declaration, will place the generated .class file in the corresponding subdirectory if you use the -d option.

For example, compiling UserService.java with:

javac -d classes src/com/example/myapp/UserService.java

produces classes/com/example/myapp/UserService.class. If you omit the -d option, the .class file lands in the same directory as the source file, which can cause runtime ClassNotFoundException errors if you later run the program from a different base directory. Keeping the source tree aligned with the package structure is a convention that every build tool, from Maven to Gradle, follows automatically.

Naming Conventions and Reverse Domain Names

Developers typically use the reverse domain name of an organization as the root of the package name. If your company owns example.com, the package root becomes com.example. This reduces collisions between libraries from different organizations. The convention is not enforced by the compiler, but it is widely accepted because it makes fully qualified names unique across the ecosystem.

ComponentExamplePurpose
Reverse domaincom.exampleGlobal uniqueness
Project or productmyappSeparates one application from another
Module or layerservice, model, controllerOrganizes code by responsibility

The Java Language Specification does not mandate any particular naming scheme. You can use a single identifier like utils, but that risks conflicts when multiple libraries are combined. A multi-level name that begins with a reversed domain is the safest choice for code that may be distributed or reused.

How Packages Control Access to Classes and Members

Packages are not just a naming mechanism; they also define a visibility boundary. A class or member declared without an access modifier is package-private, meaning it is accessible only from classes in the same package. This is a deliberate design choice that lets you hide implementation details from other packages while still sharing them within a cohesive group of classes.

package com.example.myapp.service; class UserRepository { // package-private class, visible only within com.example.myapp.service }

A public class is accessible from anywhere, but its package-private members are still restricted. This distinction is important when designing an API. You can expose a public entry point while keeping helper classes and methods package-private. If you later need to share those helpers with another package, you must make them public, which widens the API surface and makes future changes harder to manage.

Importing Classes from Other Packages

To use a class from a different package, you either use its fully qualified name or import it. The import statement appears after the package declaration and before the class definition. Importing does not change the access level; the target class must be public to be visible outside its package.

package com.example.myapp; import java.util.ArrayList; import java.util.List; public class Inventory { private List<String> items = new ArrayList<>(); }

You can also import a specific static member with import static, but that is a separate feature. The key point is that the package declaration must come first. A common error is to place an import before the package declaration, which produces a compile-time error. The order is fixed: package, imports, then type declarations.

Common Mistakes and Their Consequences

One frequent mistake is forgetting the package declaration entirely. A class without a package declaration is placed in the default package, which has no name. The default package is convenient for small examples, but it has serious limitations. Classes in the default package cannot be imported by named packages, and they cannot be referenced from other packages. This makes the default package unsuitable for anything beyond a quick test.

Another mistake is mismatching the package name and the directory path. If you declare package com.example.myapp but store the file in src/main/java/myapp/, the compiler will not complain during compilation, but the runtime classpath resolution will fail. Tools like Maven and Gradle enforce the directory structure by convention, so this error usually surfaces immediately in a build.

A third issue is using reserved keywords as package segments. For example, package com.example.int; is invalid because int is a keyword. You must choose an identifier that is not reserved.

Package Declarations in the Java Module System

Since Java 9, the module system adds another layer of organization. A module is a collection of packages, declared in a module-info.java file. The package declaration itself does not change, but the module descriptor controls which packages are exported and which modules they are accessible to. A package that is not exported is effectively invisible outside its module, even if its classes are public.

module com.example.myapp { exports com.example.myapp.service; }

This means that when you work with modules, you must consider both the package-level access and the module-level export. A public class in a non-exported package cannot be accessed from other modules. This is a stricter boundary than package-private, and it is worth understanding if you are building a library or a large application with explicit module boundaries.

java package declaration: Practical Usage and Code Examples | RYUSLOG DEV