Package-Private (Default) Access in Java
A class, method or field with no access modifier is package-private: visible to every class in the same package and nothing else. It has no keyword β just the absence of public, protected or private.
Where it applies
package com.example.orders;
class OrderRepository { // package-private class
OrderRepository() {} // package-private constructor
Order findById(long id) { ... } // package-private method
List<Order> byCustomer(long cid) { ... }
}
The right default for internal collaborators
If two classes in the same package work closely together, making every member public exposes them to the whole codebase. Package-private keeps the collaboration confined and signals "internal detail".
package com.example.billing;
public class InvoiceService { // β public entry point
private final InvoicePrinter printer; // relies on a helper
...
}
class InvoicePrinter { // β package-private helper
String render(Invoice i) { ... }
}
Testability
JUnit tests placed in the same package (under src/test/java/com/example/billing/) can see package-private members without reflection. This preserves encapsulation for production code while letting tests exercise internals directly.
Interfaces and annotations
- Interface methods are implicitly
publicβ no package-private members in interfaces. - Annotation elements default to
public.
Difference from protected
| Caller | package-private | protected |
|---|---|---|
| Same package | β | β |
| Subclass, same package | β | β |
| Subclass, different package | β | β |
| Unrelated, different package | β | β |
Common mistakes
- Public by default β bloats the public API. Start restrictive.
- Splitting helpers across packages β if classes collaborate closely, keep them in the same package so package-private works.
- Mixing test sources with different package names β tests lose the package-private access they should have.
Related
Pillar: Access modifiers. See also protected, private.