Java has four access levels: public, protected, default (package-private), private
public: visible to all classes everywhere
protected: visible to same package + subclasses (any package)
default: visible only to same package (no keyword)
private: visible only within the declaring class
Golden rule: default to private, open only when needed
Plain-English First
Imagine your house has different rooms. Your front garden is public — anyone walking past can see it. Your living room is protected — only family and close relatives can enter. Your bedroom is private — only you go in there. Your neighbour's shared driveway has no lock, but it's understood only people on that street use it. Java access modifiers work exactly like this: they control who is allowed to 'enter' a class, method, or variable.
Every piece of software you have ever used — your bank app, your favourite game, a weather app — is built on one golden rule: not everything should be accessible to everyone. When a banking app processes your PIN, it doesn't let any random part of the program read or change that number freely. That restriction is enforced in Java using access modifiers, and they are one of the very first tools a Java developer reaches for when designing anything serious.
Without access modifiers, every variable, method, and class in your program would be completely exposed to every other part of the codebase. That sounds harmless on a small project, but on a real-world application with hundreds of classes and multiple developers, it creates chaos. Someone accidentally modifies a value they shouldn't touch, a bug appears, and nobody knows where it came from. Access modifiers solve this by letting you draw clear boundaries — you decide exactly what is visible and what is locked away.
By the end of this article you'll know all four Java access modifiers, understand exactly when and why to use each one, be able to spot access-related compiler errors and fix them instantly, and feel confident answering access modifier questions in a Java interview. Let's build this up from scratch.
The Four Access Modifiers and What They Actually Mean
Java gives you four access levels. Think of them as four different lock types on a door. From most open to most locked, they are: public, protected, default (no keyword), and private.
'public' means absolutely anyone can access it — any class, in any package, anywhere in the project. It's the front door of a shop: open to the world.
'protected' means the class itself, any class in the same package, and any subclass (a class that inherits from this one) can access it. Think of a family recipe — you share it with family members and people in your household, but not strangers.
'default' (also called package-private) is what you get when you write NO modifier at all. Only classes inside the same package can access it. It's like an unwritten agreement between neighbours — people on the same street can use the shared path, but outsiders can't.
'private' is the strictest. Only the class that owns the member can access it. Nobody else — not even a subclass — can touch it directly. It's your diary with a lock.
These aren't just theory. They map directly to how Java enforces encapsulation, which is one of the four pillars of object-oriented programming. Getting these right is the difference between code that's easy to maintain and code that becomes a nightmare.
AccessModifierDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// This file demonstrates all four access modifiers in one place.// Run this as a single file (Java 11+) or create the classes in separate files.// A public class — visible to everyone, everywherepublicclassAccessModifierDemo {
// public field — any code in the entire project can read/change thispublicString brandName = "TheCodeForge";
// protected field — accessible in this class, same package, and subclassesprotectedint articleCount = 42;
// default (no keyword) field — only accessible within the same packageString internalCategory = "JavaBasics"; // no modifier = package-private// private field — ONLY this class can read or change this valueprivateString editorPassword = "s3cr3tP@ss";
// public method — anyone can call thispublicvoidshowBrandName() {
// This method is the 'front door' — safe to expose publiclySystem.out.println("Brand: " + brandName);
}
// private method — internal helper, no outside class should call thisprivateStringencryptPassword(String rawPassword) {
// Hiding the implementation detail — callers don't need to know HOWreturn"[ENCRYPTED]" + rawPassword.hashCode();
}
// public method that uses the private method internallypublicvoidprintEncryptedPassword() {
// We expose a safe action publicly, but keep the logic privateSystem.out.println("Encrypted: " + encryptPassword(editorPassword));
}
publicstaticvoidmain(String[] args) {
AccessModifierDemo demo = newAccessModifierDemo();
// Accessing public field directly — perfectly fineSystem.out.println("Public field: " + demo.brandName);
// Accessing protected field from within the SAME class — fineSystem.out.println("Protected field: " + demo.articleCount);
// Accessing default field from within the SAME package — fineSystem.out.println("Default field: " + demo.internalCategory);
// Accessing private field from within the SAME class — fineSystem.out.println("Private field: " + demo.editorPassword);
// Calling public method — fine from anywhere
demo.showBrandName();
// Calling public method that internally uses a private method
demo.printEncryptedPassword();
// NOTE: If you tried to call demo.encryptPassword() from OUTSIDE// this class, the compiler would throw an error. Try it and see!
}
}
Output
Public field: TheCodeForge
Protected field: 42
Default field: Java Basics
Private field: s3cr3tP@ss
Brand: TheCodeForge
Encrypted: [ENCRYPTED]-1722786358
Pro Tip: Default is Not a Keyword
You never write the word 'default' as an access modifier on a field or method — you simply omit any modifier entirely. Writing 'default int count = 0;' is a compiler error. The absence of a modifier IS the default access level.
Production Insight
Default access is often unintentional.
A field with no modifier is package-private — not 'no restriction'.
If you meant to hide it from other packages, that's fine. But if you forgot to add private, you've exposed it to every class in your package.
Rule: Always write private explicitly unless you have a reason to use default.
Key Takeaway
public: open to all
protected: subclasses + same package
default: same package only
private: class only
Start with private, widen only when needed.
private and public in Practice — The Bank Account Example
The most important pair to master first is private and public — and the classic teaching example is a bank account, because it maps perfectly to the real world.
Imagine a BankAccount class. It has a balance. Should the balance field be public? Absolutely not. If balance is public, any other class in your program can write something like: account.balance = 1000000; — with no checks, no history, no validation. That's terrifying.
Instead, you make balance private. Now nobody outside the class can touch it directly. You then provide public methods — called getters and setters — that control all access. The getter lets someone READ the balance. The setter (or a deposit/withdraw method) lets someone CHANGE it, but only after you've run whatever checks you need. This pattern is called encapsulation, and it's the whole point of private.
Public is the opposite philosophy — use it for things you genuinely want to expose as part of your class's contract with the rest of the world. Method names, constructors, anything another class legitimately needs to call. The golden rule: default to private first. Only make something public if you have a clear reason to.
BankAccount.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
publicclassBankAccount {
// private — no outside class can directly read or write the balance// This is the single most important use of 'private' you'll encounterprivatedouble balance;
// private — internal account identifier, nobody outside needs this rawprivateString accountNumber;
// public constructor — anyone who wants to create an account can do sopublicBankAccount(String accountNumber, double openingBalance) {
this.accountNumber = accountNumber;
// Validate even at construction time — private data, our rulesif (openingBalance < 0) {
thrownewIllegalArgumentException("Opening balance cannot be negative.");
}
this.balance = openingBalance;
}
// public getter — allows READ access to balance, but not direct WRITE accesspublicdoublegetBalance() {
return balance; // we return a copy of the value, not the field itself
}
// public method to deposit money — enforces our business rulespublicvoiddeposit(double amount) {
if (amount <= 0) {
System.out.println("Deposit amount must be positive. Ignoring.");
return;
}
balance += amount; // only THIS class modifies balance directlySystem.out.println("Deposited £" + amount + ". New balance: £" + balance);
}
// public method to withdraw money — enforces our business rulespublicvoidwithdraw(double amount) {
if (amount <= 0) {
System.out.println("Withdrawal amount must be positive. Ignoring.");
return;
}
if (amount > balance) {
// private data lets us protect this logic completelySystem.out.println("Insufficient funds. Balance is only £" + balance);
return;
}
balance -= amount;
System.out.println("Withdrew £" + amount + ". New balance: £" + balance);
}
// private helper — only used internally, no outside class needs to know this existsprivatebooleanisHighValueAccount() {
return balance > 10000;
}
// public method that uses the private helper — safe, controlled exposurepublicvoidprintAccountSummary() {
System.out.println("Account: " + accountNumber);
System.out.println("Balance: £" + balance);
System.out.println("High-value account: " + isHighValueAccount());
}
publicstaticvoidmain(String[] args) {
BankAccount myAccount = newBankAccount("ACC-00192", 500.00);
// Reading balance through the public getter — correct approachSystem.out.println("Initial balance: £" + myAccount.getBalance());
myAccount.deposit(250.00);
myAccount.withdraw(100.00);
myAccount.withdraw(800.00); // should fail — insufficient fundsSystem.out.println();
myAccount.printAccountSummary();
// The line below would cause a COMPILE ERROR if you uncommented it:// myAccount.balance = 999999; // ERROR: balance has private access in BankAccount// myAccount.isHighValueAccount(); // ERROR: isHighValueAccount() has private access
}
}
Output
Initial balance: £500.0
Deposited £250.0. New balance: £750.0
Withdrew £100.0. New balance: £650.0
Insufficient funds. Balance is only £650.0
Account: ACC-00192
Balance: £650.0
High-value account: false
Watch Out: Making Everything Public
New Java developers often make every field public 'to make things easier'. This destroys encapsulation. The moment your balance field is public, any class anywhere can set it to -99999 with no validation. Always start with private and only open things up when you have a specific, justified reason to.
Production Insight
Public fields are an invitation for data corruption.
If a field is public, any code can set it — no validation, no logging.
Teams often discover this during a security audit or after a production bug where a library overwrote a critical field.
Rule: Fields should be private. Expose behavior, not data.
Key Takeaway
Make fields private.
Provide public getters/setters or action methods.
Never expose raw data — always control access through methods.
This is encapsulation.
protected and Default — When Packages and Inheritance Come In
Once you understand private and public, the next step is understanding when you need something in between. That's where protected and default (package-private) come in — and they're the two most confused access modifiers for beginners.
Default (no keyword) is simpler: if two classes are in the same package — the same folder, essentially — they can see each other's default members. This is useful for utility classes or helper logic that belongs to a module but shouldn't be exposed as part of a public API. Think of it as 'internal' — visible within your team's workspace, invisible to the outside world.
Protected goes one step further: it adds inheritance to the mix. A protected member is visible in the same package AND in any subclass, even if that subclass is in a completely different package. This is specifically designed for the parent-child class relationship in object-oriented programming. You use protected when you're building a base class and you know subclasses will need to access or override something, but you still don't want the whole world touching it.
A good real-world analogy: a protected family recipe. Your children (subclasses) can have it and adapt it. Strangers (unrelated classes outside the package) cannot.
ProtectedAndDefaultDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// === FILE 1: Vehicle.java (in package: com.codeforge.vehicles) ===// Imagine this is in: com/codeforge/vehicles/Vehicle.javapackage com.codeforge.vehicles;
publicclassVehicle {
// public — any class anywhere can read the brandpublicString brand;
// protected — this class AND any subclass (even in other packages) can access this// It's the parent sharing something specifically with its childrenprotectedint engineCapacityCC;
// default (no modifier) — only classes in com.codeforge.vehicles can see thisString factoryLocation; // package-private// private — only Vehicle itself can touch thisprivateString internalSerialCode;
publicVehicle(String brand, int engineCapacityCC, String factoryLocation, String internalSerialCode) {
this.brand = brand;
this.engineCapacityCC = engineCapacityCC;
this.factoryLocation = factoryLocation;
this.internalSerialCode = internalSerialCode;
}
// protected method — subclasses can call and even override thisprotectedStringgetEngineDescription() {
return brand + " engine: " + engineCapacityCC + "cc";
}
// public method — the 'front door' for external callerspublicvoiddisplayInfo() {
System.out.println("Brand: " + brand);
System.out.println("Engine: " + engineCapacityCC + "cc");
System.out.println("Factory: " + factoryLocation);
// Internal serial code stays private — even displayInfo() is careful about itSystem.out.println("Serial: [RESTRICTED]");
}
}
// === FILE 2: ElectricCar.java (in package: com.codeforge.electric) ===// A DIFFERENT package — notice it can still access the protected member// Imagine this is in: com/codeforge/electric/ElectricCar.javapackage com.codeforge.electric;
import com.codeforge.vehicles.Vehicle; // importing the parent class
public class ElectricCar extends Vehicle { // 'extends' means ElectricCar is a subclass of Vehicleprivateint batteryRangeKm;
publicElectricCar(String brand, int batteryRangeKm) {
// Calling the parent constructor — electric cars have near-zero CC enginesuper(brand, 0, "Tesla Gigafactory", "EV-" + brand + "-001");
this.batteryRangeKm = batteryRangeKm;
}
@OverrideprotectedStringgetEngineDescription() {
// Subclass CAN access engineCapacityCC because it's protected// Even though ElectricCar is in a DIFFERENT package, inheritance allows thisreturn brand + " electric motor (no CC), range: " + batteryRangeKm + "km";
}
publicvoidshowElectricInfo() {
// We can call the protected method because we're a subclassSystem.out.println(getEngineDescription());
// We can access protected field engineCapacityCC directlySystem.out.println("Engine CC (should be 0 for EV): " + engineCapacityCC);
// We CANNOT access factoryLocation here — it's default/package-private// and ElectricCar is in a DIFFERENT package. Uncommenting this would fail:// System.out.println(factoryLocation); // COMPILE ERROR
}
}
// === FILE 3: Main.java (in package: com.codeforge.electric) ===package com.codeforge.electric;
publicclassMain {
publicstaticvoidmain(String[] args) {
ElectricCar tesla = newElectricCar("Tesla Model 3", 560);
// Public method — accessible from anywhere
tesla.displayInfo();
System.out.println();
// Calling the subclass-specific method
tesla.showElectricInfo();
// Accessing public field directly — fineSystem.out.println("\nBrand directly: " + tesla.brand);
// From OUTSIDE the class hierarchy, protected members are NOT accessible:// System.out.println(tesla.engineCapacityCC); // COMPILE ERROR from here
}
}
Output
Brand: Tesla Model 3
Engine: 0cc
Factory: Tesla Gigafactory
Serial: [RESTRICTED]
Tesla Model 3 electric motor (no CC), range: 560km
Engine CC (should be 0 for EV): 0
Brand directly: Tesla Model 3
Interview Gold: protected vs default
The key difference interviewers love to ask about: 'default' is visible within the same package only. 'protected' is visible within the same package AND in subclasses across any package. A subclass in a different package can access protected members but NOT default members of its parent.
Production Insight
protected is often overused.
Engineers sometimes make fields protected 'just in case' a future subclass needs them.
That's a leaky abstraction — now every subclass can depend on that field, and you can't change it without risking breakage.
Better: keep fields private, provide protected getters/setters if needed.
Rule: Design for inheritance explicitly, not 'just in case'.
Key Takeaway
protected = package + inheritance
default = package only
Use protected when you want subclasses (even in other packages) to access a member.
Prefer private + protected accessor methods over protected fields.
Access Modifiers on Classes — Not Just Fields and Methods
Everything we've covered so far applies to fields and methods inside a class. But access modifiers also control the class itself — and this trips up a lot of beginners.
A top-level class (a class not nested inside another class) can only be public or default (no modifier). You cannot make a top-level class private or protected — the Java compiler will throw an error immediately. This makes sense: a private class that nothing can see would be completely useless.
A public class is visible to everyone. A default class is visible only within its own package. This is useful when you want to create helper classes that are only relevant internally — you don't want other packages to depend on them.
However, inner classes (classes defined inside another class) can use all four access modifiers. A private inner class is a popular pattern for implementation details that are tightly coupled to their outer class.
One important Java rule: if a file contains a public class, the filename MUST match that class name exactly (including case). If your class is 'public class UserProfile', your file must be 'UserProfile.java'. Get this wrong and the compiler will refuse to compile. This is why you'll rarely see more than one public class per file.
ClassAccessModifierDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// This file demonstrates access modifiers applied at the CLASS level// A single .java file can have only ONE public class// The filename must match: ClassAccessModifierDemo.javapublicclassClassAccessModifierDemo {
// private inner class — completely hidden from outside// Only ClassAccessModifierDemo can create or use a Node// This is a common pattern in data structures (LinkedList nodes, tree nodes, etc.)privateclassNode {
int value;
Node nextNode; // points to the next node in a chainNode(int value) {
this.value = value;
this.nextNode = null;
}
}
// public inner class — anyone can use this if they have a ClassAccessModifierDemo instancepublicclassStatistics {
privateint totalNodes;
publicStatistics(int totalNodes) {
this.totalNodes = totalNodes;
}
publicvoidprintStats() {
System.out.println("Total nodes in chain: " + totalNodes);
}
}
// The outer class uses its private inner class freelyprivateNode firstNode;
privateint nodeCount;
// public method — builds an internal chain using private Node objectspublicvoidaddValue(int value) {
Node newNode = new Node(value); // Node is private, but we're inside the owner classif (firstNode == null) {
firstNode = newNode;
} else {
// Walk to the end of the chainNode currentNode = firstNode;
while (currentNode.nextNode != null) {
currentNode = currentNode.nextNode;
}
currentNode.nextNode = newNode; // attach the new node at the end
}
nodeCount++;
}
// public method — lets outsiders read values without knowing Node existspublicvoidprintAllValues() {
System.out.print("Chain: ");
Node currentNode = firstNode;
while (currentNode != null) {
System.out.print(currentNode.value);
if (currentNode.nextNode != null) System.out.print(" -> ");
currentNode = currentNode.nextNode;
}
System.out.println();
}
publicStatisticsgetStatistics() {
return new Statistics(nodeCount); // returns a public inner class instance
}
publicstaticvoidmain(String[] args) {
ClassAccessModifierDemo chain = newClassAccessModifierDemo();
chain.addValue(10);
chain.addValue(25);
chain.addValue(37);
chain.printAllValues();
Statistics stats = chain.getStatistics();
stats.printStats();
// The line below would cause a COMPILE ERROR:// ClassAccessModifierDemo.Node n = new ClassAccessModifierDemo.Node(5);// ERROR: Node has private access in ClassAccessModifierDemo
}
}
Output
Chain: 10 -> 25 -> 37
Total nodes in chain: 3
Watch Out: One Public Class Per File
In Java, a .java file can contain multiple classes, but only ONE of them can be public — and the filename must exactly match that public class name. If you have 'public class OrderProcessor' in a file named 'Processor.java', you'll get: 'class OrderProcessor is public, should be declared in a file named OrderProcessor.java'. This is one of the most common beginner compiler errors.
Production Insight
A helper class accidentally made public becomes part of your public API.
It can be imported by any other package, creating unwanted coupling.
Later you want to change it — but now other teams depend on it.
Rule: Make helper classes package-private (default) unless they're truly reusable across packages.
Key Takeaway
Top-level class: only public or default (package-private).
Inner classes: any of the four.
Filename must match public class name exactly.
Use package-private for internal helpers — avoid expanding your API surface unnecessarily.
Best Practices and Common Pitfalls with Access Modifiers
After understanding the mechanics, the real skill is applying them wisely. Here are patterns that separate clean code from fragile code.
First, the golden rule:minimize visibility. Start every member as private. Only increase visibility when there's a concrete need. This is called the Principle of Least Privilege applied to code.
Second, design interfaces, not innards. Public methods are your contract with the world. They should be stable. Keep implementation details private so you can change them without breaking callers.
Third, use protected sparingly. Protected creates a coupling between a parent class and its subclasses. That's intentional, but it should be a deliberate design choice, not a default. If you're writing a class that isn't meant to be extended, mark it final and keep everything private.
Fourth, be explicit about default access. Many bugs come from forgetting to add a modifier. A field that should be private but has no modifier is accidentally visible to the entire package. When you see a field without a modifier, you should be able to explain why it's package-private.
Finally, use access modifiers in tests. Unit tests in the same package can access default members. That's useful for testing package-private methods. But don't rely on it for production code — prefer testing through public APIs.
Production Insight
New engineers often make all methods public 'because testing is easier'.
That makes your API surface huge and fragile.
Every public method is a dependency risk — change it and you might break someone else's code.
Better: test through public entry points. If you must test internal logic, use package-private with @VisibleForTesting annotation.
Rule: Fewer public methods = easier to maintain.
Key Takeaway
Minimize visibility: start private, widen only when needed.
Public methods = stable contract. Private = changeable.
Use protected only when inheritance is by design.
Explicitly choose default — don't leave it by accident.
Test through public APIs, not internal visibility.
● Production incidentPOST-MORTEMseverity: high
The Case of the Exposed Account Balance
Symptom
Users were able to set negative balances on their accounts through a third-party API integration. The balance field was public, allowing direct write access from any class.
Assumption
The team assumed that because the UI validated inputs, the backend was safe. They never expected external code to modify the balance directly without going through the service layer.
Root cause
The balance field in Account was declared public double balance; instead of private. Any class in the project could read and write it. The third-party library's malicious code (or an internal misconfiguration) set it to -100.
Fix
Changed the field to private, added a getBalance() getter, and enforced all writes through deposit() and withdraw() methods with validation. Ran a full data integrity check to correct corrupted records.
Key lesson
Always start with private for fields — never expose raw data.
Use controlled public methods (getters/setters) to enforce invariants.
Code review should flag any public field that isn't a constant (static final).
Production debug guideWhen your code won't compile because of access restrictions, use this guide to identify the issue fast.4 entries
Symptom · 01
Compiler error: 'has private access in class'
→
Fix
Check if the member is truly private — if so, either add a public getter/setter or change the access to protected (if subclass) or default (if same package).
Symptom · 02
Compiler error: 'is not public in class; cannot be accessed from outside package'
→
Fix
The member is package-private (default). Either move the accessing class into the same package, or change the member's access to public or protected (if subclass).
Symptom · 03
Compiler error: 'cannot find symbol' for a method/field that exists
→
Fix
Check if the class is imported and if its access modifier allows access. A protected method won't be visible to a non-subclass in a different package.
Symptom · 04
Runtime exception: IllegalAccessError or NoSuchFieldError
→
Fix
Reflection is bypassing compile-time checks. Ensure the target class and member are at least public or that the calling code has the right module exports (Java 9+).
★ Access Modifier Debugging Quick ReferenceWhen compilation fails due to access modifiers, use this cheat sheet to pinpoint the fix in seconds.
Subclass can't access parent's field−
Immediate action
Check the access modifier of the field in the parent class.
Commands
javap -p ParentClass.class (shows all members, including private)
If private, add protected getter. If default, either move subclass to same package or change to protected.
Fix now
Change the field to protected or add a protected getter method.
Class in different package can't use a method+
Immediate action
Verify the method's access modifier and the relationship between classes.
Commands
Check the import statement and package declaration.
If the method is default, either move the calling class into the same package or change modifier to public.
Fix now
Change the method to public if it's meant for external use.
Compiler error: 'class is public, should be declared in a file named X.java'+
Immediate action
The filename must match the public class name exactly (case-sensitive).
Commands
ls -la *.java (list files and check names)
Rename the file or the class so they match.
Fix now
Rename the .java file to match the public class name, or remove the 'public' keyword from the class if it doesn't need to be public.
Access Level Comparison
Access Level
Same Class
Same Package
Subclass (diff. package)
Any Class (anywhere)
public
✅ Yes
✅ Yes
✅ Yes
✅ Yes
protected
✅ Yes
✅ Yes
✅ Yes
❌ No
default (no keyword)
✅ Yes
✅ Yes
❌ No
❌ No
private
✅ Yes
❌ No
❌ No
❌ No
Key takeaways
1
There are exactly four access levels in Java
public, protected, default (no keyword), and private — each one progressively more restrictive than the last.
2
Default access (no keyword) is NOT 'open to everything'
it means package-private. Only classes in the same package can see it. This surprises developers who assume 'no modifier = no restriction'.
3
protected is specifically designed for the inheritance relationship
subclasses in any package can access protected members, but unrelated classes outside the package cannot.
4
The golden rule of encapsulation
default to private for all fields. Only make something public or protected when you have a clear, deliberate reason. Opening things up is easy — locking them down later when other code depends on them is painful.
5
Top-level classes can only be public or default. Inner classes can use all four modifiers including private.
6
Access modifiers work hand-in-hand with Java modules (JPMS)
even a public class is hidden from other modules unless the package is exported.
Common mistakes to avoid
4 patterns
×
Making all fields public for convenience
Symptom
Other classes can change your object's data without any validation, causing hard-to-trace bugs (e.g., a negative age or a null username).
Fix
Always declare fields as private. Provide public getters for read access and carefully controlled setters or action methods for write access. The extra typing is worth every second.
×
Confusing protected with private when it comes to subclasses
Symptom
A developer makes a field private, then writes a subclass that needs to access it, and gets 'has private access' compiler error. They're confused because the subclass 'is a' version of the parent.
Fix
Use protected for fields or methods you specifically intend subclasses to access or override. Private means even child classes are locked out — by design.
×
Forgetting that default access is NOT 'no restriction'
Symptom
A class in a different package tries to use a default-access method and gets a compile error that says 'is not public in [class]; cannot be accessed from outside package', which surprises beginners who assumed no modifier means no restriction.
Fix
Understand that omitting a modifier is a deliberate choice meaning 'package-private'. If you want something universally accessible, you must explicitly write 'public'.
×
Using protected on fields when you should use public getters
Symptom
Subclasses directly access a protected field, and later the parent class changes internal representation — all subclasses break.
Fix
Keep fields private. Provide protected getter/setter methods. This allows you to change the implementation without affecting subclasses.
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01SENIOR
What is the difference between protected and default (package-private) a...
Q02SENIOR
Why would you ever use private access for a field when a getter method t...
Q03JUNIOR
If a class has no access modifier on its constructor, can you instantiat...
Q04JUNIOR
Can you make a top-level class private in Java? Explain.
Q05SENIOR
Explain the impact of access modifiers on the Java Module system (JPMS)....
Q01 of 05SENIOR
What is the difference between protected and default (package-private) access in Java? Can a subclass in a different package access a protected member? What about a default member?
ANSWER
Default (package-private) means the member is accessible only within the same package. Protected means the member is accessible within the same package AND also in any subclass, even if that subclass is in a different package. A subclass in a different package can access a protected member but NOT a default member of its parent.
Q02 of 05SENIOR
Why would you ever use private access for a field when a getter method that returns the same value is also public — isn't that the same thing?
ANSWER
No. Private + public getter gives you three things that a public field doesn't: 1) You can change the internal representation without changing the API (e.g., a field becomes computed). 2) You can add validation or logging in the getter. 3) You can control write access separately (no setter, or write via methods with checks). A public field exposes raw data with no control.
Q03 of 05JUNIOR
If a class has no access modifier on its constructor, can you instantiate it from a different package? What error would you see, and how would you fix it?
ANSWER
No, you cannot. A constructor with no access modifier (default/package-private) is only visible within the same package. From a different package you'll get: 'Class X has private access' (or similar compile error). To fix, either move the creation code into the same package, or make the constructor public.
Q04 of 05JUNIOR
Can you make a top-level class private in Java? Explain.
ANSWER
No. Top-level classes can only be public or default (package-private). 'private' would make it invisible to all other classes, rendering it useless. However, inner classes (nested classes) can be private — that's used to hide implementation details from outside the enclosing class.
Q05 of 05SENIOR
Explain the impact of access modifiers on the Java Module system (JPMS). How does 'public' differ in a module context?
ANSWER
In Java 9+ modules, even a public class is not automatically accessible from other modules. The module must explicitly export the package using 'exports' in module-info.java. Access modifiers still apply within a module (a public class inside the module is accessible to all other packages in that module), but crossing module boundaries requires both the export and the public modifier. This adds another layer of encapsulation.
01
What is the difference between protected and default (package-private) access in Java? Can a subclass in a different package access a protected member? What about a default member?
SENIOR
02
Why would you ever use private access for a field when a getter method that returns the same value is also public — isn't that the same thing?
SENIOR
03
If a class has no access modifier on its constructor, can you instantiate it from a different package? What error would you see, and how would you fix it?
JUNIOR
04
Can you make a top-level class private in Java? Explain.
JUNIOR
05
Explain the impact of access modifiers on the Java Module system (JPMS). How does 'public' differ in a module context?
SENIOR
FAQ · 5 QUESTIONS
Frequently Asked Questions
01
What is the default access modifier in Java?
The default access level in Java is called package-private. You get it by writing NO modifier at all before the field, method, or class. It means only classes within the same package can access that member. It's NOT a keyword — writing 'default int count = 0;' is actually a syntax error.
Was this helpful?
02
Can a subclass access the private members of its parent class in Java?
No. Private members are strictly limited to the class they're declared in — not even subclasses can access them directly. If a parent class has a private field that subclasses need to work with, the parent should provide a protected getter method or use protected access on the field itself.
Was this helpful?
03
What is the difference between private and protected in Java?
Private restricts access to the single class where the member is declared — no other class can touch it, including subclasses. Protected is less restrictive: it allows access from the same class, any class in the same package, AND any subclass regardless of package. Use private when you want to hide implementation details completely, and protected when you're designing a class that's meant to be extended.
Was this helpful?
04
Can I use 'default' as a keyword for access modifier?
No. 'default' is a reserved keyword used only in switch expressions and default methods in interfaces. To make a member package-private, simply omit any access modifier. Writing 'default int x;' will result in a compiler error.
Was this helpful?
05
What happens if I declare a class as private in Java?
You cannot declare a top-level class as private — it's a compile error. Inner classes (nested classes) can be private, meaning they are only accessible within the enclosing class. This is useful for implementation details like tree nodes.