Back to Blog
Java

Java Package Declaration: Syntax and Naming Rules

Learn the syntax and placement rules for Java package declarations, plus naming conventions, directory structure, imports, and package-private 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 tells the compiler which package the classes in a source file belong to. It appears at the top of the file, before any imports or class definitions. The syntax is package com.example.myapp;. Only one package declaration is allowed per source file, and it must be the first code element. If you place an import or a class definition before it, 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. 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

When class files are stored on the file system, the JVM expects the directory structure to mirror the package name. A class in the package com.example.myapp must be loadable from a directory path com/example/myapp/ under some classpath root. This is not a rule enforced by the Java language itself, but it is how the standard classloader works. The javac compiler, when given a source file with a package declaration, places 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. That is useful only if that directory is on the classpath as the package root; otherwise you will get a runtime ClassNotFoundException. In practice, keep source files in directories that match their package, as Maven and Gradle do by default with roots such as src/main/java.

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. For a top-level class or a member of a class, the absence of an access modifier means package-private: it is accessible only from classes in the same package. (Interface members are implicitly public.) 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 confusing source-file location with the package path. If you declare package com.example.myapp but store the file in src/main/java/myapp/, the compiler does not reject the source, but the generated class file only works if the runtime classpath points to a directory containing the matching com/example/myapp/ path. Follow the conventional layout src/main/java/com/example/myapp/UserService.java so source and compiled output stay aligned.

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: Syntax, Naming, and Access Control | RYUSLOG DEV