Java Multiple class declarations in one file
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Java allows multiple class declarations in a single .java file, but only one of them can be public, and that public class must match the filename. All other classes in the file must be package-private (no access modifier). The compiler generates a separate .class file for each class regardless of how they are organized in source files.
The One-Public-Class Rule
The Java Language Specification enforces a strict rule: if a file contains a public class, the file must be named after that class. This means at most one public class per file.
Compiling OrderProcessor.java produces three .class files:
OrderProcessor.classOrderValidator.classOrder.class
Each class is fully independent at the bytecode level. The fact that they share a source file is purely a source-code organization choice.
What Happens If You Break the Rule
Attempting to declare two public classes in one file causes a compilation error:
The compiler enforces this because it uses the filename to locate public classes during compilation. Without this convention, the compiler would need to scan every file to find a class definition.
Four Types of Classes in a Single File
Java supports four distinct kinds of class declarations within a file, each with different visibility and use cases:
1. Top-Level Public Class
One per file, visible to all packages. This is the standard approach.
2. Top-Level Package-Private Class
Visible only within the same package. Useful for implementation details that support the public class.
3. Inner Class (Non-Static Nested)
Tied to an instance of the enclosing class. Has access to all fields and methods of the outer class, including private members.
4. Static Nested Class
Acts like a regular class but scoped inside another. Commonly used for builders, iterators, and entry types (like Map.Entry).
Comparison of Declaration Types
| Declaration Type | Access Modifier | Visibility | Separate .class File | Can Access Outer Instance |
| Public top-level | public | All packages | Yes | N/A |
| Package-private top-level | (none) | Same package only | Yes | N/A |
| Inner class | Any | Depends on modifier | Yes (Outer$Inner.class) | Yes |
| Static nested class | Any | Depends on modifier | Yes (Outer$Nested.class) | No |
| Local class (in method) | (none) | Enclosing method only | Yes (Outer$1Local.class) | Yes (if non-static method) |
| Anonymous class | (none) | Expression only | Yes (Outer$1.class) | Yes (if non-static context) |
When Multiple Top-Level Classes Make Sense
There are a few legitimate scenarios for putting multiple classes in one file:
Small, Tightly Coupled Helper Classes
When a helper class is only a few lines and only used by the primary class in the file:
Sealed Class Hierarchies (Java 17+)
Sealed classes often define their permitted subclasses in the same file for cohesion:
This pattern keeps the entire type hierarchy visible in one place, which is particularly natural for algebraic data types used with pattern matching.
Prototyping and Competitive Programming
During prototyping or competitive programming, keeping everything in one file reduces friction:
When to Avoid Multiple Classes in One File
For production code, the standard practice is one class per file. Here is why:
- IDE navigation. IDEs map filenames to class names. If
OrderValidatoris insideOrderProcessor.java, pressing "Go to Class" forOrderValidatordoes not take you to the right file intuitively. - Version control. Changes to
OrderValidatorshow up as diffs onOrderProcessor.java, making commit history harder to follow. - Code review. Reviewers expect to find a class in a file with the matching name. Hunting through files for package-private classes slows down reviews.
- Refactoring. Extracting a package-private class into its own file later requires updating every import in the package.
Common Pitfalls
Assuming package-private classes are invisible. Package-private classes are accessible to every class in the same package, not just the class they share a file with. If another class in the package references your "helper" class, you have created an unintended coupling.
Forgetting that each class gets its own .class file. Even though three classes share one .java file, the compiler outputs three .class files. Deployment tools and classloaders operate on .class files, so the source file organization is invisible at runtime.
Using multiple classes to avoid proper package structure. If you find yourself putting many related classes in one file, that is usually a sign you need a sub-package instead. Create a new package and give each class its own file.
Inner classes holding references to the outer instance. Non-static inner classes implicitly hold a reference to the enclosing object, which prevents garbage collection of the outer instance. Use static nested classes unless you specifically need access to the outer instance.
Sealed class permits across files. In Java 17+, sealed classes can permit subclasses in other files (using the permits clause), but those subclasses must be in the same package. You do not need to put them in the same file.
Summary
Java allows multiple class declarations in one file, with the constraint that at most one can be public (and must match the filename). Package-private top-level classes, inner classes, static nested classes, local classes, and anonymous classes all provide different scoping mechanisms. For production code, prefer one class per file for maintainability and tooling support. Use multiple classes in one file for sealed type hierarchies, small tightly-coupled helpers, and prototyping scenarios where single-file convenience outweighs organizational overhead.

