Access Modifiers in Java – public, protected, default and private
Access modifiers control who can use a class, field, method or constructor. They let you expose a clean public interface while hiding the internal details that other code should not touch, which is the heart of encapsulation.
Java has four access levels: public, protected, default (no keyword, also called package-private) and private. This tutorial explains each one with a multi-package example, including the real compiler errors you get when rules are broken.
1. The Four Access Levels
| Modifier | Same class | Same package | Subclass in another package | Any other class |
|---|---|---|---|---|
public | Yes | Yes | Yes | Yes |
protected | Yes | Yes | Yes (through inheritance) | No |
| default (no keyword) | Yes | Yes | No | No |
private | Yes | No | No | No |
From most open to most restricted: public → protected → default → private.
2. A Multi-Package Example
To see the rules in action we need two packages. The shop.core package contains Product, with one field at each access level, and Inventory:
package shop.core;
public class Product {
public String name; // visible everywhere
protected double price; // same package + subclasses
int stock; // (default) same package only
private double costPrice; // this class only
public Product(String name, double price, int stock, double costPrice) {
this.name = name;
this.price = price;
this.stock = stock;
this.costPrice = costPrice;
}
public double profitPerUnit() {
return price - costPrice; // private field is fine inside the class
}
}
package shop.core;
// Same package as Product
public class Inventory {
public static String check(Product p) {
return p.name + ": price " + p.price + ", stock " + p.stock;
// p.costPrice would not compile: private
}
}
The shop.app package contains a subclass, DiscountedProduct, and an unrelated class, Store:
package shop.app;
import shop.core.Product;
// Different package, but a subclass of Product
public class DiscountedProduct extends Product {
public DiscountedProduct(String name, double price) {
super(name, price, 10, price * 0.6);
}
public double salePrice() {
return price * 0.9; // protected field is inherited: allowed
// stock would not compile: default access, different package
}
}
package shop.app;
import shop.core.Inventory;
import shop.core.Product;
// Different package, NOT a subclass
public class Store {
public static void main(String[] args) {
Product pen = new Product("Pen", 20.0, 100, 12.0);
System.out.println("public name: " + pen.name);
System.out.println("public method: " + pen.profitPerUnit());
System.out.println("via same package: " + Inventory.check(pen));
// pen.price, pen.stock, pen.costPrice would not compile here
DiscountedProduct bag = new DiscountedProduct("Bag", 1000.0);
System.out.println("subclass method: " + bag.salePrice());
}
}
public name: Pen public method: 8.0 via same package: Pen: price 20.0, stock 100 subclass method: 900.0
Compile all four files with javac -d out *.java and run with java -cp out shop.app.Store. Each class used only the members it is allowed to see.
3. What Happens When You Break the Rules
If Store-like code in another package tries to read the hidden fields directly, the compiler refuses:
Peek.java:8: error: price has protected access in Product
System.out.println(pen.price);
^
Peek.java:9: error: stock is not public in Product; cannot be accessed from outside package
System.out.println(pen.stock);
^
Peek.java:10: error: costPrice has private access in Product
System.out.println(pen.costPrice);
^
3 errors
Access is checked at compile time, so mistakes are caught before the program ever runs.
4. public
public members are visible to all code everywhere. Use it for the API that other classes are meant to call: public classes, their important constructors and methods, and constants. A public class must be declared in a file with the same name.
5. protected
protected members are visible within the same package and to subclasses in other packages. In another package, a subclass can use the protected member through inheritance, as DiscountedProduct.salePrice() does with price, but not on an arbitrary Product object it does not own. Use protected for hooks that subclasses are designed to use or override.
6. Default (Package-Private)
If you write no modifier, the member is visible only inside its own package. Inventory can read stock, but DiscountedProduct cannot, even though it is a subclass, because it lives in another package. Package-private is a good choice for helper classes and methods that are shared inside one feature package but are not part of its public API.
default keyword for access. Writing default before a method means something else entirely: a default method in an interface.7. private
private members are visible only inside the class that declares them (including its nested classes). Fields should almost always be private, with access through methods that can enforce rules:
public class Account {
private double balance; // hidden: cannot be set to an invalid value from outside
public double getBalance() { return balance; }
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("Deposit must be positive");
balance += amount;
}
public static void main(String[] args) {
Account acc = new Account();
acc.deposit(500);
try {
acc.deposit(-50);
} catch (IllegalArgumentException e) {
System.out.println("Rejected: " + e.getMessage());
}
System.out.println("Balance: " + acc.getBalance());
}
}
Rejected: Deposit must be positive Balance: 500.0
Because balance is private, no other class can set it to a negative value; every change goes through deposit().
8. Private Constructors
A private constructor stops other classes from creating objects with new. It is used for singletons, utility classes with only static methods, and classes that offer static factory methods:
public class AppConfig {
private static final AppConfig INSTANCE = new AppConfig();
private final String environment;
private AppConfig() { // private constructor: no "new AppConfig()" outside
environment = "production";
}
public static AppConfig getInstance() { // the only way to get the object
return INSTANCE;
}
public String getEnvironment() { return environment; }
public static void main(String[] args) {
AppConfig a = AppConfig.getInstance();
AppConfig b = AppConfig.getInstance();
System.out.println("Environment: " + a.getEnvironment());
System.out.println("Same object: " + (a == b));
}
}
Environment: production Same object: true
See Private Constructor in Java.
9. Access Modifiers for Classes
- A top-level class can be
publicor default (package-private) only. It cannot beprivateorprotected. - A nested class can use any of the four modifiers.
- Interface members are implicitly
public(methods may also beprivatesince Java 9). - Enum constructors are always private.
10. Access Modifiers and Overriding
An overriding method cannot reduce visibility. If a parent method is protected, the child's version can be protected or public, but not default or private. Private methods are not inherited, so they cannot be overridden at all.
| Parent method | Allowed in overriding method |
|---|---|
public | public |
protected | protected, public |
| default | default, protected, public |
private | Not inherited; a method with the same name is a new method |
11. Beyond Classes: Modules
Since Java 9, the module system adds another layer: a module's module-info.java decides which packages it exports. Even public classes in a non-exported package are hidden from other modules. Most applications on the class path are not affected, but libraries use this to hide internal packages.
12. Best Practices
- Start with the most restrictive level and open up only when needed.
- Make fields
private; expose behaviour through methods. - Keep helper classes package-private instead of public.
- Use
protecteddeliberately, for an API you design for subclasses. - Avoid public mutable fields; public
static finalconstants of immutable types are fine.
13. Summary
public: everywhere.protected: package plus subclasses. Default: package only.private: class only.- Access rules are enforced by the compiler.
- Top-level classes can only be
publicor package-private. - Overriding methods cannot reduce visibility.
- Private fields and controlled methods are the foundation of encapsulation.
Related Tutorials
Frequently Asked Questions (FAQ)
Q1. What are access modifiers in Java?
Keywords that set the visibility of classes, fields, methods and constructors. Java has public, protected, private, and default (package-private) access when no keyword is used.
Q2. What is the default access modifier in Java?
When no modifier is written, the member is package-private: visible only to classes in the same package. Interface members are an exception; they are implicitly public.
Q3. What is the difference between private and protected?
private members are visible only inside their own class. protected members are visible in the same package and to subclasses in other packages.
Q4. What is the difference between protected and default access?
Both allow access within the same package. protected also allows access from subclasses in other packages; default access does not.
Q5. Can a top-level class be private or protected?
No. A top-level class can only be public or package-private. Nested classes can use all four access levels.
Q6. Can a constructor be private?
Yes. A private constructor prevents other classes from creating objects with new. It is used in singletons, utility classes and classes with static factory methods.
Q7. Can I override a private method?
No. Private methods are not inherited, so a subclass method with the same signature is a completely separate method, not an override.
Q8. Can an overriding method have weaker access?
No. It can keep the same visibility or make it wider, but not narrower. Overriding a public method with a protected one is a compile error.
Q9. Why should fields be private?
So that other classes cannot put an object into an invalid state, and so you can change the internal representation later without breaking other code. Access goes through methods that can validate values.
Q10. Can a subclass in another package access a protected member of any parent object?
Only through references of its own type (or subtypes). It can use this.price or otherDiscounted.price, but not someProduct.price on a plain Product reference.
Q11. Are access modifiers checked at runtime?
Primarily at compile time. The JVM also checks access when linking classes, and reflection respects access rules unless code explicitly calls setAccessible(true), which modules can restrict.
Q12. Can local variables have access modifiers?
No. Local variables are visible only inside their block, so access modifiers do not apply. Only final is allowed on them.
Q13. What access modifiers can interface methods have?
Interface methods are public by default. Since Java 9 they can also be private (helper methods for default methods). They cannot be protected.
Q14. Is "default" a keyword for access?
No. Package-private access is expressed by writing no modifier. The default keyword is used for switch labels and default methods in interfaces.
Q15. Can nested classes access private members of the outer class?
Yes. Nested and inner classes can access all members of their enclosing class, including private ones, and vice versa.
Q16. What is package-private access good for?
Sharing helpers between classes of one package while keeping them out of the public API. Unit tests placed in the same package can also access package-private members.
Q17. What is the most restrictive access modifier?
private. It limits access to the declaring class only.
Q18. Do access modifiers apply to static members?
Yes, in the same way. A private static method can only be called inside its class; a public static method can be called from anywhere.
Q19. How do Java modules affect public classes?
In a modular application, a public class is visible to other modules only if its package is listed in an exports clause of module-info.java. This hides internal packages even when their classes are public.
Q20. What is the difference between access modifiers and non-access modifiers?
Access modifiers (public, protected, private) control visibility. Non-access modifiers such as static, final, abstract, synchronized and transient change other properties of a member.