Senior 6 min · March 06, 2026

Java Access Modifiers—Public Field Allows Negative Balances

A public double balance field allowed setting -100 on accounts.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • 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, everywhere
public class AccessModifierDemo {

    // public field — any code in the entire project can read/change this
    public String brandName = "TheCodeForge";

    // protected field — accessible in this class, same package, and subclasses
    protected int articleCount = 42;

    // default (no keyword) field — only accessible within the same package
    String internalCategory = "Java Basics"; // no modifier = package-private

    // private field — ONLY this class can read or change this value
    private String editorPassword = "s3cr3tP@ss";

    // public method — anyone can call this
    public void showBrandName() {
        // This method is the 'front door' — safe to expose publicly
        System.out.println("Brand: " + brandName);
    }

    // private method — internal helper, no outside class should call this
    private String encryptPassword(String rawPassword) {
        // Hiding the implementation detail — callers don't need to know HOW
        return "[ENCRYPTED]" + rawPassword.hashCode();
    }

    // public method that uses the private method internally
    public void printEncryptedPassword() {
        // We expose a safe action publicly, but keep the logic private
        System.out.println("Encrypted: " + encryptPassword(editorPassword));
    }

    public static void main(String[] args) {
        AccessModifierDemo demo = new AccessModifierDemo();

        // Accessing public field directly — perfectly fine
        System.out.println("Public field: " + demo.brandName);

        // Accessing protected field from within the SAME class — fine
        System.out.println("Protected field: " + demo.articleCount);

        // Accessing default field from within the SAME package — fine
        System.out.println("Default field: " + demo.internalCategory);

        // Accessing private field from within the SAME class — fine
        System.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
public class BankAccount {

    // private — no outside class can directly read or write the balance
    // This is the single most important use of 'private' you'll encounter
    private double balance;

    // private — internal account identifier, nobody outside needs this raw
    private String accountNumber;

    // public constructor — anyone who wants to create an account can do so
    public BankAccount(String accountNumber, double openingBalance) {
        this.accountNumber = accountNumber;

        // Validate even at construction time — private data, our rules
        if (openingBalance < 0) {
            throw new IllegalArgumentException("Opening balance cannot be negative.");
        }
        this.balance = openingBalance;
    }

    // public getter — allows READ access to balance, but not direct WRITE access
    public double getBalance() {
        return balance; // we return a copy of the value, not the field itself
    }

    // public method to deposit money — enforces our business rules
    public void deposit(double amount) {
        if (amount <= 0) {
            System.out.println("Deposit amount must be positive. Ignoring.");
            return;
        }
        balance += amount; // only THIS class modifies balance directly
        System.out.println("Deposited £" + amount + ". New balance: £" + balance);
    }

    // public method to withdraw money — enforces our business rules
    public void withdraw(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 completely
            System.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 exists
    private boolean isHighValueAccount() {
        return balance > 10000;
    }

    // public method that uses the private helper — safe, controlled exposure
    public void printAccountSummary() {
        System.out.println("Account: " + accountNumber);
        System.out.println("Balance: £" + balance);
        System.out.println("High-value account: " + isHighValueAccount());
    }

    public static void main(String[] args) {
        BankAccount myAccount = new BankAccount("ACC-00192", 500.00);

        // Reading balance through the public getter — correct approach
        System.out.println("Initial balance: £" + myAccount.getBalance());

        myAccount.deposit(250.00);
        myAccount.withdraw(100.00);
        myAccount.withdraw(800.00); // should fail — insufficient funds

        System.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.java

package com.codeforge.vehicles;

public class Vehicle {

    // public — any class anywhere can read the brand
    public String brand;

    // protected — this class AND any subclass (even in other packages) can access this
    // It's the parent sharing something specifically with its children
    protected int engineCapacityCC;

    // default (no modifier) — only classes in com.codeforge.vehicles can see this
    String factoryLocation; // package-private

    // private — only Vehicle itself can touch this
    private String internalSerialCode;

    public Vehicle(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 this
    protected String getEngineDescription() {
        return brand + " engine: " + engineCapacityCC + "cc";
    }

    // public method — the 'front door' for external callers
    public void displayInfo() {
        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 it
        System.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.java

package com.codeforge.electric;

import com.codeforge.vehicles.Vehicle; // importing the parent class

public class ElectricCar extends Vehicle { // 'extends' means ElectricCar is a subclass of Vehicle

    private int batteryRangeKm;

    public ElectricCar(String brand, int batteryRangeKm) {
        // Calling the parent constructor — electric cars have near-zero CC engine
        super(brand, 0, "Tesla Gigafactory", "EV-" + brand + "-001");
        this.batteryRangeKm = batteryRangeKm;
    }

    @Override
    protected String getEngineDescription() {
        // Subclass CAN access engineCapacityCC because it's protected
        // Even though ElectricCar is in a DIFFERENT package, inheritance allows this
        return brand + " electric motor (no CC), range: " + batteryRangeKm + "km";
    }

    public void showElectricInfo() {
        // We can call the protected method because we're a subclass
        System.out.println(getEngineDescription());

        // We can access protected field engineCapacityCC directly
        System.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;

public class Main {
    public static void main(String[] args) {
        ElectricCar tesla = new ElectricCar("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 — fine
        System.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.java

public class ClassAccessModifierDemo {

    // 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.)
    private class Node {
        int value;
        Node nextNode; // points to the next node in a chain

        Node(int value) {
            this.value = value;
            this.nextNode = null;
        }
    }

    // public inner class — anyone can use this if they have a ClassAccessModifierDemo instance
    public class Statistics {
        private int totalNodes;

        public Statistics(int totalNodes) {
            this.totalNodes = totalNodes;
        }

        public void printStats() {
            System.out.println("Total nodes in chain: " + totalNodes);
        }
    }

    // The outer class uses its private inner class freely
    private Node firstNode;
    private int nodeCount;

    // public method — builds an internal chain using private Node objects
    public void addValue(int value) {
        Node newNode = new Node(value); // Node is private, but we're inside the owner class

        if (firstNode == null) {
            firstNode = newNode;
        } else {
            // Walk to the end of the chain
            Node 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 exists
    public void printAllValues() {
        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();
    }

    public Statistics getStatistics() {
        return new Statistics(nodeCount); // returns a public inner class instance
    }

    public static void main(String[] args) {
        ClassAccessModifierDemo chain = new ClassAccessModifierDemo();

        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 LevelSame ClassSame PackageSubclass (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.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the default access modifier in Java?
02
Can a subclass access the private members of its parent class in Java?
03
What is the difference between private and protected in Java?
04
Can I use 'default' as a keyword for access modifier?
05
What happens if I declare a class as private in Java?
🔥

That's Java Basics. Mark it forged?

6 min read · try the examples if you haven't

Previous
Java Keywords and Identifiers
11 / 13 · Java Basics
Next
Java Wrapper Classes