Java Protected Access Modifier
Table of Contents + −
In the last lesson you learned about the Java private access modifier. But private creates a problem once you use inheritance: a child class often needs a field or helper from its parent, and private slams that door shut. Java has a modifier built exactly for this case, called protected. Let’s learn it.
🤔 The problem protected solves
Imagine a parent class with a helper that does some shared work. A child class wants to reuse it. With private, it cannot. Here is a parent with a private field and a private helper.
class Animal { private String name = "Rex"; // ❌ private: hidden from children too
private void describe() { // ❌ private: children cannot call this System.out.println("I am " + name); }}
class Dog extends Animal { void bark() { describe(); // ❌ ERROR: describe() has private access in Animal }}That call does not compile. Dog is a child of Animal, but private still locks it out and treats Dog like any outsider.
- We do not want the field fully public, because then any code anywhere could change it.
- But we do want children to use it.
- We need something in the middle that says “family only.” That is
protected.
🧩 What does protected mean?
The protected keyword gives a member two kinds of access at once.
- Any class in the same package can use it. This is the same reach that default (no modifier) gives.
- Any subclass can use it, even a subclass that lives in a different package. This is the extra power that
protectedadds on top.
So protected is the package access of default, plus a special pass for children no matter where they live. Think of a family membership card. People in your local club (the same package) get in. And your own children get in too, even if they moved to another city (another package). Here is the earlier example fixed by changing one word.
class Animal { protected String name = "Rex"; // ✅ children can see this now
protected void describe() { // ✅ children can call this now System.out.println("I am " + name); }}
class Dog extends Animal { void bark() { describe(); // ✅ works: Dog is a subclass of Animal System.out.println(name + " says woof"); }}Now it compiles, because Dog extends Animal. The child reaches into the parent’s protected members with no trouble. Run it:
public class Main { public static void main(String[] args) { Dog d = new Dog(); d.bark(); }}Output
I am RexRex says woof📦 The real test: a subclass in another package
The same-package part is easy. The interesting part is the cross-package rule. A subclass can reach a protected member even from a different package. First, the parent lives in a package called shapes.
package shapes;
public class Shape { protected String color = "red"; // ✅ open to subclasses everywhere
protected void show() { System.out.println("A " + color + " shape"); }}Now a child lives in a different package called drawing.
package drawing;
import shapes.Shape;
public class Circle extends Shape { public void draw() { color = "blue"; // ✅ allowed: Circle is a subclass of Shape show(); // ✅ allowed: protected method, reached by a child }}Even though Circle sits in another package, it still reaches the protected color field and show() method. Inheritance carries the access across the package boundary. Run it:
package drawing;
public class Main { public static void main(String[] args) { Circle c = new Circle(); c.draw(); }}Output
A blue shapeThis is the line that separates protected from default. With default access, the cross-package child would be blocked. With protected, it gets in.
🚫 A non-subclass in another package is still blocked
Protected is for subclasses, not for everyone in another package. A class in another package that is not a child cannot touch the protected member. Here a plain class in drawing tries to use Shape without extending it.
package drawing;
import shapes.Shape;
public class Outsider { public void test() { Shape s = new Shape(); s.color = "green"; // ❌ ERROR: color has protected access in Shape s.show(); // ❌ ERROR: show() has protected access in Shape }}Both lines fail to compile. Read the error closely.
Output
Outsider.java:7: error: color has protected access in Shape s.color = "green"; ^Outsider.java:8: error: show() has protected access in Shape s.show(); ^Outsider is in another package and does not extend Shape, so the protected pass does not apply. Protected opens the door for the family line, not for strangers in a far-off package.
🛠️ Where protected really shines: helpers for subclasses
The classic use of protected is a helper method or shared field that a parent provides for its children to build on. The parent does the common work once, and each child reuses it. Here a base class offers a protected helper, and two children lean on it.
class Payment { protected double amount;
Payment(double amount) { this.amount = amount; }
// a helper meant only for subclasses, not for the outside world protected double withTax() { return amount * 1.1; // add 10% tax }}
class CardPayment extends Payment { CardPayment(double amount) { super(amount); }
void process() { System.out.println("Card charge: " + withTax()); }}
class CashPayment extends Payment { CashPayment(double amount) { super(amount); }
void process() { System.out.println("Cash taken: " + withTax()); }}Both children call the same withTax() helper. The tax rule lives in one place, in the parent. Run it:
public class Main { public static void main(String[] args) { new CardPayment(100).process(); new CashPayment(50).process(); }}Output
Card charge: 110.00000000000001Cash taken: 55.00000000000001The helper is protected, so outside code cannot call withTax() directly and skip the real payment flow. But the children get full use of it. That is the sweet spot protected was made for.
Tip
A good rule of thumb: reach for protected on a method that you want subclasses to reuse or override. Be much more careful putting protected on a field, for reasons we will see in the mistakes section.
📊 protected vs default vs public quick recap
Here are the three “more open” levels side by side. private is strictest, then default, then protected, then public.
| Can access from… | default | protected | public |
|---|---|---|---|
| Same class | Yes | Yes | Yes |
| Same package | Yes | Yes | Yes |
| Subclass in another package | No | Yes | Yes |
| Non-subclass in another package | No | No | Yes |
Protected is exactly default plus one extra row: subclasses in other packages. That single extra row is the whole reason protected exists.
⚠️ Common Mistakes
Confusing protected with default. People think protected and “no modifier” are the same because both allow same-package access. The difference is the cross-package subclass.
// In package shapes:class Shape { String color; // ❌ default: cross-package child is blocked protected String size; // ✅ protected: cross-package child can use this}If a child in another package needs the member, default will not work. Mixing them up gives a confusing “has default access” error at compile time.
Making fields protected when a method would do. A protected field is open to every subclass forever. Once children write to it directly, you can never safely change how it works without breaking them all. That defeats the data hiding you learned with private.
class Account { protected double balance; // ❌ any subclass can set balance = -999}Prefer keeping the field private and exposing a protected method instead.
class Account { private double balance; // ✅ stays hidden and guarded
protected void deposit(double v) { // ✅ controlled access for subclasses if (v > 0) balance += v; }}Now subclasses get the access they need, the field stays guarded, and you keep the freedom to change it later.
Thinking protected means “anyone in another package.” It does not. Only subclasses get the cross-package pass. A regular class in another package gets locked out, just as you saw with Outsider.
✅ Best Practices
- Use
protectedon purpose, only when you truly want subclasses to reuse or override a member. It is not a “make it a bit more open” switch. - Prefer
protectedmethods overprotectedfields. A method gives you control. A bare field hands raw access to every child forever. - Keep most fields
private. Open them up only when a real subclass need appears, not “just in case.” - Remember the rule in one line. Protected equals same package plus subclasses anywhere.
- When a member should be visible to the whole world, use
public. When only your own class needs it, useprivate. Reach forprotectedfor the in-between family case.
📝 What You’ve Learned
protectedmakes a member visible inside the same package and to subclasses, even subclasses in other packages.- It sits between default and public. It is default access plus one bonus: cross-package children.
- A subclass in another package can use a protected member. A non-subclass in another package cannot.
- The best use of
protectedis a shared helper or field that a parent offers for its children to build on. - Favor protected methods over protected fields to keep your data safe.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
Who can access a protected member?
Why: protected allows same-package access plus access from subclasses, including subclasses in other packages.
- 2
What is the one extra power protected has over default access?
Why: Default and protected both allow same-package access. Only protected also lets a subclass in another package reach the member.
- 3
Can a non-subclass in another package use a protected member?
Why: The cross-package pass is only for subclasses. A plain class in another package cannot touch a protected member.
- 4
Why prefer a protected method over a protected field?
Why: A protected field hands raw access to every subclass. A protected method lets the parent control and validate what happens.
🚀 What’s Next?
You have now seen all four access levels, and protected showed how much packages matter for access. A package groups related classes together and shapes who can see what. The next lesson explains packages from the ground up, so you can organize real Java projects and control visibility with confidence. Let’s learn it.