Java面试-访问修饰符:public、protected、default、private 详解
👋 欢迎阅读《Java面试200问》系列博客!
🚀大家好,我是Jinkxs,一名热爱Java、深耕技术一线的开发者。在准备和参与了数十场Java面试后,我深知面试不仅是对知识的考察,更是对理解深度与表达能力的综合检验。
✨本系列将带你系统梳理Java核心技术中的高频面试题,从源码原理到实际应用,从常见陷阱到大厂真题,每一篇文章都力求深入浅出、图文并茂,帮助你在求职路上少走弯路,稳拿Offer!
🔍今天我们要聊的是:《访问修饰符:public、protected、default、private 详解》。准备好了吗?Let’s go!
🎯 引言:Java 世界的“门禁系统”大揭秘
“在Java的世界里,最像‘小区门禁’的,不是
SecurityManager
,而是——访问修饰符。”
想象一下你住的小区:
- public:小区大门,谁都能进(只要不违法)。
- protected:单元楼门禁,本楼住户和亲戚(子类)能进。
- default(包访问):家门口,只有你和家人(同包)知道密码。
- private:卧室门锁,只有你自己(本类)能进。
今天,我们就来扒一扒这四位“门神”的底细,看看它们如何守护Java世界的秩序。
📚 目录导航(别走丢了)
- 四大门神登场:public、protected、default、private
- public:小区大门,谁都能进
- protected:单元楼门禁,本楼住户和亲戚专用
- default(包访问):家门口,只有家人知道密码
- private:卧室门锁,只有我自己能进
- 继承中的访问权限“升级”规则
- 内部类的访问权限“特例”
- 面试常见陷阱大揭秘
- 使用场景总结:什么时候该用哪个?
1. 四大门神登场:public、protected、default、private
Java 提供了四种访问修饰符,用来控制类、方法、变量、构造器等成员的访问权限。
✅ 它们就像不同级别的门禁卡,决定了谁能“看到”和“使用”你的代码。
📊 四大访问修饰符权限对比表
修饰符 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ |
default (无修饰符) | ✅ | ✅ | ❌ | ❌ |
protected | ✅ | ✅ | ✅ | ❌(仅子类) |
public | ✅ | ✅ | ✅ | ✅ |
💡 记住这张表,你就掌握了Java世界的“门禁地图”。
2. public:小区大门,谁都能进
public
是最开放的访问级别,任何地方都可以访问。
✅ 用法:类、方法、变量、构造器
// public 类:任何包都能访问
public class PublicClass {// public 变量:任何地方都能读写public String name = "张三";// public 方法:任何地方都能调用public void sayHello() {System.out.println("你好,我是 " + name);}// public 构造器:任何地方都能 newpublic PublicClass(String name) {this.name = name;}
}
✅ 跨包访问示例
// com.example.utils 包
package com.example.utils;public class MathUtils {public static int add(int a, int b) {return a + b;}
}// com.example.app 包
package com.example.app;import com.example.utils.MathUtils;public class App {public static void main(String[] args) {// ✅ 可以访问 public 类和方法int sum = MathUtils.add(5, 3);System.out.println("和:" + sum);}
}
✅
public
是工具类、API接口的首选。
❌ 注意:public 类必须与文件名相同
// 文件名:MyClass.java
public class MyClass { } // ✅ 正确// public class YourClass { } // ❌ 编译错误!一个文件只能有一个 public 类,且名字必须匹配
3. protected:单元楼门禁,本楼住户和亲戚专用
protected
允许同包和不同包的子类访问。
✅ 用法:方法、变量、构造器(不能修饰类)
// com.example.parent 包
package com.example.parent;public class Parent {protected String familySecret = "祖传秘方";protected void familyMeeting() {System.out.println("召开家族会议,讨论:" + familySecret);}protected Parent() {System.out.println("Parent 构造器被调用");}
}
✅ 同包访问(本楼住户)
// com.example.parent 包
package com.example.parent;public class Neighbor {public void visit() {Parent p = new Parent();p.familySecret = "邻居知道了"; // ✅ 同包,可以访问p.familyMeeting(); // ✅ 同包,可以调用}
}
✅ 不同包子类访问(亲戚)
// com.example.child 包
package com.example.child;import com.example.parent.Parent;public class Child extends Parent {public void revealSecret() {// ✅ 子类,可以访问父类 protected 成员System.out.println("我知道的秘密:" + familySecret);familyMeeting();}
}
❌ 不同包非子类不能访问
// com.example.stranger 包
package com.example.stranger;import com.example.parent.Parent;public class Stranger {public void tryToAccess() {Parent p = new Parent();// p.familySecret = "我想知道"; // ❌ 编译错误!不是子类,也不是同包// p.familyMeeting(); // ❌ 编译错误!}
}
✅
protected
常用于父类希望子类继承的方法或变量。
4. default(包访问):家门口,只有家人知道密码
default
是没有显式修饰符时的默认访问级别,也叫包私有(package-private)。
✅ 用法:类、方法、变量、构造器
// com.example.internal 包
package com.example.internal;// default 类:只能同包访问
class InternalClass {// default 变量:只能同包访问String internalData = "内部数据";// default 方法:只能同包访问void processData() {System.out.println("处理数据:" + internalData);}// default 构造器:只能同包访问InternalClass() {System.out.println("InternalClass 创建");}
}
✅ 同包访问
// com.example.internal 包
package com.example.internal;public class Manager {public void manage() {InternalClass ic = new InternalClass();ic.internalData = "更新数据"; // ✅ 同包,可以访问ic.processData(); // ✅ 同包,可以调用}
}
❌ 不同包不能访问
// com.example.external 包
package com.example.external;import com.example.internal.InternalClass; // ❌ 编译错误!InternalClass 不是 publicpublic class External {public void access() {// InternalClass ic = new InternalClass(); // 即使能导入,也无法访问}
}
✅
default
适合“包内协作”的类,对外隐藏实现细节。
5. private:卧室门锁,只有我自己能进
private
是最严格的访问级别,只有本类内部可以访问。
✅ 用法:方法、变量、构造器、内部类(不能修饰外部类)
public class BankAccount {private String accountNumber; // 账号,私有private double balance = 0.0; // 余额,私有// private 构造器:只能本类或内部类调用private BankAccount(String accountNumber) {this.accountNumber = accountNumber;}// 提供 public 方法来安全访问public void deposit(double amount) {if (amount > 0) {balance += amount;logTransaction("存款", amount); // ✅ 本类内可以调用 private 方法}}public void withdraw(double amount) {if (amount > 0 && amount <= balance) {balance -= amount;logTransaction("取款", amount);}}// private 方法:只在本类内部使用private void logTransaction(String type, double amount) {System.out.println("交易记录:" + type + " " + amount + ",余额:" + balance);}// public 方法获取余额(只读)public double getBalance() {return balance;}
}
✅ 使用场景:封装核心数据和逻辑
public class PasswordManager {private String masterPassword = "123456"; // 主密码,绝不暴露public boolean verifyPassword(String input) {// 在这里进行加密比较等操作return input.equals(masterPassword); // ✅ 本类内可以访问}// ❌ 外部无法直接获取 masterPassword
}
✅
private
是实现封装的关键,保护对象的内部状态。
6. 继承中的访问权限“升级”规则
子类重写父类方法时,访问权限不能更严格,但可以更宽松。
✅ 正确示例:权限升级
class Parent {protected void doWork() {System.out.println("Parent work");}
}class Child extends Parent {@Overridepublic void doWork() { // ✅ 正确:protected → public(更宽松)System.out.println("Child work");}
}
❌ 错误示例:权限降级
class Parent {public void doWork() {System.out.println("Parent work");}
}class Child extends Parent {@Override// private void doWork() { } // ❌ 编译错误!public → private(更严格)// protected void doWork() { } // ✅ 正确:public → protected(更宽松或相同)
}
✅ 规则:
private
→package
→protected
→public
,只能向右或持平,不能向左。
7. 内部类的访问权限“特例”
内部类可以拥有所有四种访问修饰符,而外部类只能是 public
或 default
。
✅ private 内部类
public class Outer {// private 内部类:只有 Outer 类能访问private class PrivateInner {public void display() {System.out.println("我是私有内部类");}}public void createInner() {PrivateInner inner = new PrivateInner(); // ✅ Outer 类内可以创建inner.display();}
}// 其他类
class Other {public void tryCreate() {// Outer.PrivateInner inner = new Outer().new PrivateInner(); // ❌ 编译错误!}
}
✅ protected 内部类
public class Base {protected class ProtectedInner {public void show() {System.out.println("受保护的内部类");}}
}class Sub extends Base {public void accessInner() {ProtectedInner inner = new ProtectedInner(); // ✅ 子类可以访问inner.show();}
}
✅ public 内部类
public class Container {public class PublicInner {public void info() {System.out.println("公开的内部类");}}
}// 其他类
class Client {public void use() {Container.PublicInner inner = new Container().new PublicInner(); // ✅ 可以访问inner.info();}
}
8. 面试常见陷阱大揭秘
❓ 面试题1:default 和 protected 的区别?
答:
default
:同包内可访问。protected
:同包内可访问,且不同包的子类也可访问。
// 包A
package A;
class Parent {String def = "default";protected String pro = "protected";
}// 包B
package B;
import A.Parent;
class Child extends Parent {void test() {// def = "x"; // ❌ default 成员,不同包非同包子类不能访问pro = "y"; // ✅ protected 成员,子类可以访问}
}
❓ 面试题2:private 方法能被继承吗?
❌ 不能!
private
方法对子类不可见,子类无法继承,也无法重写。
class Parent {private void secret() { }
}class Child extends Parent {// public void secret() { } // 这不是重写,是一个新方法// Child 类根本不知道 Parent 有个 secret() 方法
}
❓ 面试题3:为什么外部类不能是 private 或 protected?
答:因为外部类需要被其他类导入(import)。
private
:只有本类能访问,外部类无法被导入。protected
:只有同包和子类能访问,但子类还没创建,无法导入。所以外部类只能是
public
(任何地方可导入)或default
(同包可导入)。
❓ 面试题4:构造器可以是 private 吗?有什么用?
✅ 可以! 常用于:
- 单例模式:防止外部
new
- 工具类:防止实例化
- 构建器模式:配合静态工厂方法
public class Singleton {private static Singleton instance;// private 构造器private Singleton() { }public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
❓ 面试题5:protected 变量在子类中可以直接访问吗?
✅ 可以!
protected
成员在子类中就像自己的成员一样,可以直接访问。
class Parent {protected String name;
}class Child extends Parent {void introduce() {name = "小明"; // ✅ 直接访问父类 protected 变量System.out.println("我是 " + name);}
}
9. 使用场景总结:什么时候该用哪个?
场景 | 推荐修饰符 | 原因 |
---|---|---|
API 接口、工具类 | public | 需要被广泛调用 |
类的核心数据和敏感逻辑 | private | 封装,防止外部破坏 |
希望子类继承的方法或变量 | protected | 给子类扩展空间 |
包内协作的实现类 | default | 对外隐藏,对内开放 |
单例类的构造器 | private | 防止外部创建实例 |
工具类的方法 | public static | 方便调用 |
内部帮助类 | private 或 static | 仅本类使用 |
需要被不同包子类继承的方法 | protected | 跨包继承 |
✅ 黄金法则:尽可能使用最小的访问权限。
从private
开始,如果不够用,再逐步放宽,直到public
。
📈 附录:访问修饰符速查表
位置 | private | default | protected | public |
---|---|---|---|---|
外部类 | ❌ | ✅ | ❌ | ✅ |
内部类 | ✅ | ✅ | ✅ | ✅ |
构造器 | ✅ | ✅ | ✅ | ✅ |
方法 | ✅ | ✅ | ✅ | ✅ |
字段 | ✅ | ✅ | ✅ | ✅ |
同包访问 | ❌ | ✅ | ✅ | ✅ |
不同包子类访问 | ❌ | ❌ | ✅ | ✅ |
不同包非子类访问 | ❌ | ❌ | ❌ | ✅ |
💡 记住:
private
是你的“卧室”,default
是你的“家”,protected
是你的“家族”,public
是你的“朋友圈”。
管好你的“门”,Java世界才安全!
🎯 总结一下:
本文深入探讨了《访问修饰符:public、protected、default、private 详解》,从原理到实践,解析了面试中常见的考察点和易错陷阱。掌握这些内容,不仅能应对面试官的连环追问,更能提升你在实际开发中的技术判断力。
🔗 下期预告:我们将继续深入Java面试核心,带你解锁《自动装箱与拆箱机制解析》 的关键知识点,记得关注不迷路!
💬 互动时间:你在面试中遇到过类似问题吗?或者对本文内容有疑问?欢迎在评论区留言交流,我会一一回复!
如果你觉得这篇文章对你有帮助,别忘了 点赞 + 收藏 + 转发,让更多小伙伴一起进步!我们下一篇见 👋