Packages
Packages are Java's namespace and access-control mechanism — they organize related classes into hierarchical, dot-separated names and define exactly what "package-private" visibility means.
Every Java class belongs to a package, whether declared explicitly or left in the unnamed default package. Packages prevent naming collisions between unrelated classes, mirror a project's directory structure, and form one of the four access-control boundaries alongside public, protected, and private.
The package Declaration
A source file's package declaration, if present, must be the very first non-comment statement in the file — before any imports or type declarations. It states which package all the types declared in that file belong to.
package com.example.billing;
public class Invoice {
// ...
}Package Naming Conventions
Convention (not a compiler rule) is to use all-lowercase, dot-separated names, typically starting with a reversed internet domain the organization controls — com.example.projectname — to keep package names globally unique across companies. Segments should be short, meaningful nouns; avoid uppercase letters (which would visually collide with class names) and Java keywords as segment names.
com.example.billing.invoice
com.example.billing.payment
org.apache.commons.lang3
java.util
java.util.concurrentPackage Structure Mirrors Directory Structure
The Java compiler and tooling expect a class's package name to correspond exactly to its directory path relative to a source root: a class in package com.example.billing must live in a directory path com/example/billing/ under the source root. This mapping is how javac and the classpath locate .class files at compile and run time.
src/
com/
example/
billing/
Invoice.java <- package com.example.billing;
Payment.java <- package com.example.billing;The Unnamed (Default) Package
A source file with no package declaration belongs to the unnamed (default) package. This is fine for quick scripts, tutorials, or single-file demos, but is discouraged for any real project: classes in the unnamed package cannot be imported by name from a named package, which severely limits reuse and organization at any meaningful scale.
Package-Private Access
A class, field, method, or constructor declared with no access modifier at all (neither public, protected, nor private) has package-private (also called "default") access — visible only to other code within the same package. This is Java's way of grouping tightly-collaborating classes that shouldn't expose their coupling to the rest of the codebase.
InvoiceCalculator and its method are visible to any other class in com.example.billing, but invisible to classes in other packages, even if those packages import com.example.billing.*
package com.example.billing;
class InvoiceCalculator { // package-private: no modifier
double subtotal(Invoice inv) { /* ... */ return 0.0; } // also package-private
}Package-Private as an Access-Control Tool
Package-private access lets a package expose a small public API while keeping helper classes and implementation details hidden from outside code, without needing separate modules. It's a natural fit for grouping a public interface with a package-private implementation class that outside code never needs to see directly.
package com.example.billing;
public interface PriceCalculator {
double calculate(Invoice inv);
}
class DefaultPriceCalculator implements PriceCalculator { // package-private impl
@Override public double calculate(Invoice inv) { return 0.0; }
}
public class PriceCalculators {
public static PriceCalculator standard() {
return new DefaultPriceCalculator(); // visible here, hidden from callers outside the package
}
}Fully Qualified Names
A fully qualified name combines a type's package and simple name, unambiguously identifying it anywhere: java.util.List, com.example.billing.Invoice. You can always use a fully qualified name in place of an import, at the cost of verbosity — this is exactly what happens implicitly for any type you don't import.
public class Main {
public static void main(String[] args) {
java.util.List<String> names = new java.util.ArrayList<>();
names.add("Ada");
System.out.println(names);
}
}Two Classes, Same Simple Name, Different Packages
Because packages form separate namespaces, two completely unrelated classes can share the same simple name as long as they live in different packages — java.util.Date and java.sql.Date coexist without conflict. Code that needs both in the same file must use at least one fully qualified name, or import one and fully qualify the other.
import java.util.Date;
public class Main {
public static void main(String[] args) {
Date utilDate = new Date(); // java.util.Date, via import
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime()); // fully qualified
}
}Static Imports
A static import brings a specific static member (or all static members, via wildcard) of a class into scope without qualification, letting you write, for example, sqrt(x) instead of Math.sqrt(x). This is covered fully in the Imports topic; it's mentioned here because static imports still respect ordinary package/access-modifier visibility rules — a private static member cannot be statically imported from outside its class regardless of package.
import static java.lang.Math.sqrt;
import static java.lang.Math.PI;
public class Main {
public static void main(String[] args) {
System.out.println(sqrt(2)); // instead of Math.sqrt(2)
System.out.println(PI); // instead of Math.PI
}
}Subpackages Are Not Automatically Related
Despite the dot-separated naming that visually suggests nesting, Java packages have no hierarchical containment relationship for access purposes. com.example.billing and com.example.billing.invoice are entirely separate packages: a package-private member in one is not visible from the other, even though the names look nested. Only exact package-name matches share package-private access.
Practical Usage: Organizing a Real Project
Real projects typically group packages by feature or layer — com.example.app.billing, com.example.app.billing.model, com.example.app.web — keeping related classes together and unrelated concerns apart. Package-private classes within a feature package hide internal helpers, while a small number of public types form that feature's actual API surface for the rest of the application.
Edge Cases
A single .java file can declare at most one public top-level class/interface/enum/record, and its name must exactly match the file name; other, non-public top-level types may share the file, and all of them belong to the same package declared at the top.
java.lang is automatically available in every source file without an import — it is the one package the compiler implicitly imports; every other package (including the rest of java.util, java.io, and so on) requires an explicit import or a fully qualified name.
Best Practices
Always declare a real package for anything beyond a throwaway script. Use reverse-domain-based naming to avoid collisions with other organizations' code. Keep implementation-detail classes package-private, exposing only the minimal public surface a package's consumers actually need. Group by cohesive feature/responsibility rather than by technical layer alone when it improves navigability.
Key Takeaways
The package declaration, if present, must be the first statement in a source file and determines the class's namespace.
Package names conventionally use reverse-domain, all-lowercase, dot-separated segments.
A class's directory location must match its package name for standard compilation and classpath resolution.
No access modifier means package-private: visible only within the exact same package, hiding implementation details from outside code.
Fully qualified names (package.ClassName) unambiguously identify a type and let same-named classes from different packages coexist.
Dot-separated package names do not imply any nested access relationship — com.a and com.a.b are unrelated packages for visibility purposes.
import com.example.billing; public class Main { package com.example.app; // package declaration must be first! } package com.example.app; import com.example.billing.Invoice; public class Main { }
The package declaration must be the very first non-comment statement in the file, before any import statements.
// file: com/example/billing/Invoice.java package com.billing; // does not match the directory path -- won't resolve correctly
// file: com/example/billing/Invoice.java package com.example.billing; // matches the directory path exactly
Standard Java tooling requires a class's package name to correspond exactly to its directory path relative to the source root; a mismatch causes compilation or classpath resolution failures.
package com.example.billing; class Helper { /* package-private */ } package com.example.billing.invoice; class Client { void use() { Helper h = new Helper(); // does not compile: different package, despite the name nesting } } package com.example.billing; public class Helper { /* now public, genuinely shared */ } com.example.billing and com.example.billing.invoice are entirely separate packages despite the dot-notation suggesting nesting. A package-private class in one is invisible to the other; make it public if it must be shared.
Where must a package declaration appear in a Java source file, relative to imports and class declarations?
It must be the very first non-comment statement in the file, before any import statements and before any type declarations.
What access level does a class or member get when declared with no access modifier at all?
Package-private (default) access: visible only to other code within the exact same package, and invisible to code in any other package, including subpackages.
Why can java.util.Date and java.sql.Date both exist with the same simple name?
Because packages form separate namespaces; two types can share a simple name as long as they live in different packages. Their fully qualified names (java.util.Date vs. java.sql.Date) remain unambiguous.
Do com.example.app and com.example.app.util share package-private access with each other?
No. Despite the dot-notation suggesting nesting, Java treats them as two completely separate packages for access-control purposes; a package-private member in one is invisible to the other.
What must be true about a class's directory location relative to its package declaration for standard compilation?
The directory path (relative to the source root) must exactly match the package name, with each dot-separated segment becoming a directory level, e.g. package com.example.app maps to com/example/app/.
Which package is automatically available in every Java source file without an explicit import?
java.lang -- it is implicitly imported everywhere; every other package requires an explicit import or a fully qualified name.
Interview-style: why does Java use package-private visibility instead of forcing every type to choose only between public and private?
Package-private lets a group of tightly-collaborating classes share implementation details freely with each other while still hiding those details from the rest of the codebase, without requiring a separate module boundary -- it's a middle ground for cohesive internal collaboration within one namespace.
Write a package declaration and a package-private helper class alongside a public class that uses it, both in package com.example.util.
// file: com/example/util/StringHelper.java package com.example.util; class StringHelper { // package-private static String reverse(String s) { return new StringBuilder(s).reverse().toString(); } } // file: com/example/util/TextUtils.java package com.example.util; public class TextUtils { public static String reversed(String s) { return StringHelper.reverse(s); // visible: same package } }
StringHelper has no access modifier, so it is package-private and visible only within com.example.util. TextUtils, in the same package, can call it directly, while code in any other package can only see and call TextUtils.reversed().
Does this compile? Explain why or why not.
It does not compile. Box has no access modifier, making it package-private and visible only within package a. Package b's Main class cannot import or reference it at all, even with an explicit import statement -- import only works for accessible types.
Write two classes named Point, one in package geometry.plane and one in package geometry.space, and a Main class that uses both via fully qualified names.
// file: geometry/plane/Point.java package geometry.plane; public class Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } // file: geometry/space/Point.java package geometry.space; public class Point { public int x, y, z; public Point(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } } // file: Main.java public class Main { public static void main(String[] args) { geometry.plane.Point p2d = new geometry.plane.Point(1, 2); geometry.space.Point p3d = new geometry.space.Point(1, 2, 3); System.out.println(p2d.x + ", " + p3d.z); } }
Because each Point lives in a different package, both can share the same simple name without conflict. Main must distinguish them using fully qualified names (or import one and fully qualify the other) since both cannot be imported unqualified into the same file.
From a class in a different package (e.g. package app;), can code create a new shapes.Circle(5.0) after importing shapes.Circle?
No. Circle has no access modifier, so it is package-private -- accessible only within the shapes package itself. Even importing shapes.Circle from package app would fail to compile, since the type isn't accessible outside its own package at all. It would need to be declared public to be usable from app.
// file: a/Box.java
package a;
class Box { int value = 1; }
// file: b/Main.java
package b;
import a.Box;
public class Main {
public static void main(String[] args) {
Box box = new Box();
}
}package shapes;
class Circle {
double radius;
Circle(double radius) { this.radius = radius; }
double area() { return Math.PI * radius * radius; }
}