Java Default Access Modifier
Table of Contents + β
In the last lesson you learned about Java access modifiers. Now letβs zoom in on the one that confuses people most: the level you get when you write nothing at all. That is the default access modifier, and it does not mean βopen to everyoneβ like many people assume. Letβs clear that up.
π€ What happens when you write no modifier?
Here is the trap. You write a class, you forget to add public, and the code still compiles. So you think it must be wide open. It is not.
class Box { int size; // β no modifier β this is NOT public}sizehas no access keyword in front of it.- Many beginners read that as βpublic by default.β It is not public.
- It got a quieter, in-between level called default access, also known as package-private.
- The pain shows up later. Split your code into separate folders and half of it stops compiling.
So letβs define it properly, then prove it with real code.
π§© What is default (package-private) access?
When you write a field, a method, or a class with no access modifier, it gets default access:
- The member is visible to every class in the same package.
- It is invisible to everything outside that package.
- A package is just a folder of related classes, declared with
package shop;at the top of a file. - People also call this package-private. Inside the package it acts almost public. Outside, almost private.
- There is no keyword for it. You get it by writing nothing.
Now letβs see both halves in action.
β Same package: it works
Letβs put two classes in the same shop package and have one use the otherβs default members. First, the class with default members:
package shop;
class Product { String name; // default field double price; // default field
void show() { // default method System.out.println(name + " costs " + price); }}Now a second class, in the same shop package, that reaches into Product directly:
package shop;
public class Store { public static void main(String[] args) { Product p = new Product(); p.name = "Pen"; // β
allowed, same package p.price = 12.5; // β
allowed, same package p.show(); // β
allowed, same package }}Store and Product share the shop package, so Store touches Productβs default fields and calls its default method as if they were public.
Output
Pen costs 12.5Inside one package, default access feels open. Now letβs cross the package line and watch it change.
β Different package: it fails to compile
Keep the same Product class in shop, but write the second class in a different package called web, importing it as usual:
package web;
import shop.Product; // import a class from another package
public class WebApp { public static void main(String[] args) { Product p = new Product(); // β Product itself is not public p.name = "Pen"; // β name has default access p.price = 12.5; // β price has default access p.show(); // β show() has default access }}This does not even compile. Product and all its members are default, so they are invisible from the web package. The compiler stops you before the program ever starts.
Compile error
WebApp.java: error: Product is not public in shop; cannot be accessed from outside package Product p = new Product(); ^WebApp.java: error: name has default access in Product p.name = "Pen"; ^WebApp.java: error: price has default access in Product p.price = 12.5; ^WebApp.java: error: show() has default access in Product p.show(); ^Read that first line carefully. βProduct is not public in shopβ. The class itself is hidden too, not just its fields. The key lesson: default access stops at the package boundary. Same package, yes. Other package, no.
π« Not even subclasses in another package
Here is the part that surprises people who already know protected. With protected, a subclass in another package can reach the inherited member. With default access, it cannot. A subclass gets no special pass across the package line. Letβs prove it with a subclass in the web package.
package web;
import shop.Product;
public class WebProduct extends Product { // β cannot even extend it void demo() { System.out.println(name); // β name is not visible here }}Even though WebProduct is a Product, it lives in a different package, so the default members stay hidden. Inheritance does not change the rule. The package boundary still wins.
Compile error
WebProduct.java: error: cannot inherit from a non-public class Product in a different packagepublic class WebProduct extends Product { ^WebProduct.java: error: name has default access in Product System.out.println(name); ^Default vs protected
This is the cleanest way to remember the difference. protected means same package plus subclasses anywhere. Default means same package only, with no exception for subclasses in other packages. So protected is strictly more open than default.
π¦ Default works on classes too
The no-modifier rule applies to classes, not just fields and methods. A class written without public is a default (package-private) class, usable only inside its own package. Here is a default class and a public class side by side.
package shop;
class Helper { // β default class β only the shop package can use it void run() { System.out.println("Helping inside the shop package."); }}
public class Catalog { // β
public class β any package can use it void load() { Helper h = new Helper(); // β
fine, same package h.run(); }}- Inside
shop,Catalogcreates and usesHelperwith no trouble. - Code in any other package cannot even name
Helper. - This lets you write helper classes that support one package without leaking to the whole program.
One public class per file
A Java source file can hold at most one public class, and its name must match the file name. But you can put several default classes in the same file alongside it. That is a common way to keep small package-internal helpers next to the class that uses them.
π‘ When should you use default access?
Sometimes a class, field, or method is meant to be shared within a feature but hidden from the rest of the program. Default access is the tool for that. Here a payments package has a public PaymentService and a default TaxCalculator that only the payment code should touch.
package payments;
class TaxCalculator { // default β internal to payments double addTax(double amount) { return amount * 1.1; // add 10% tax }}
public class PaymentService { // public β the outside entry point public double charge(double amount) { TaxCalculator tax = new TaxCalculator(); // β
same package double total = tax.addTax(amount); System.out.println("Charging " + total); return total; }}The rest of the program calls PaymentService.charge(...) and never sees TaxCalculator. You could rewrite, rename, or delete it, and no outside code would break, because none was ever allowed to depend on it. Reach for default access when:
- A helper class is only useful inside one package and should not be part of the public surface.
- A field or method is shared between a few classes in the same package but is not meant for the outside world.
- You want to keep your public surface small, so callers only see what they actually need.
β οΈ Common Mistakes
Default access trips people up in a few predictable ways.
Thinking no modifier means public. This is the big one. The code compiles, so it feels open, but it is only open inside the package.
// β wrong assumption: "no keyword means everyone can use it"class Order { int id; // this is package-private, NOT public}
// β
if you truly want it usable everywhere, say sopublic class Order { public int id;}Expecting cross-package access to work. You move a class into a new package, import it, and expect it to behave like before. If it had no modifier, the import does not help.
// β wrong: importing does not unlock default membersimport shop.Product;Product p = new Product(); // fails if Product is default
// β
right: make the class and the members you need public// (in shop) public class Product { public String name; ... }Assuming a subclass can reach inherited default members from another package. It cannot. Only protected or public cross that line for subclasses.
// β wrong: a subclass in another package still cannot see default fieldspackage web;class WebProduct extends shop.Product { void demo() { System.out.println(name); } // name is hidden}
// β
right: use protected in the parent if subclasses elsewhere need it// (in shop) protected String name;β Best Practices
Here are the habits that keep default access working for you, not against you.
- Be deliberate, not accidental. If you leave off a modifier, mean it. Do not let default access happen just because you forgot to type
public. - Use it to hide package internals. Keep helper classes and shared-but-internal methods package-private so your public surface stays small.
- Make a member
publicthe moment another package needs it. If you find yourself wanting to import a default member, that is your signal to promote it. - Prefer
protectedover default when subclasses elsewhere need access. Default blocks outside subclasses, so pickprotectedif inheritance across packages is the goal. - Keep related classes in the same package. Default access only helps when the classes that need each other actually share a package.
π§© What Youβve Learned
Nice work. Letβs recap the default access modifier.
- β Writing no modifier gives a member default (package-private) access.
- β Default members are visible to every class in the same package.
- β They are invisible from other packages, even after an import.
- β A subclass in another package still cannot reach inherited default members.
- β Classes can be default too, which hides them outside their package.
- β Use default access for package-internal helpers you do not want to expose.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What access level does a member with no modifier get?
Why: Writing no access modifier gives the member default access, also called package-private.
- 2
Who can use a default member?
Why: Default members are visible to every class in the same package and nothing outside it.
- 3
Can a subclass in a different package access an inherited default field?
Why: Default access stops at the package boundary, with no exception for subclasses in other packages. Use protected for that.
- 4
What does a class with no modifier mean?
Why: A class without public is package-private, so only classes in the same package can use it.
π Whatβs Next?
Default access keeps things inside a package. The opposite end of the scale opens a member to the entire program, every class in every package. That is the public modifier, and it is what you use for the methods and classes you want everyone to call. Letβs learn it.