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

Callerpackage-privateprotected
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.