Java的public, protected, default, private 这个几个关键字的概念和差异理解 【一文搞懂】
1.在Java中,public、protected、default(也称为包级私有)和 private 这四个关键字用来控制类、方法、变量等的访问权限。
以下是它们的通俗解释、概念和示例,最后还会给出一个对比表格,帮助你更直观地理解它们的异同。
1. public
- 概念:公开的,可以被任何地方的类访问。
- 通俗解释:如果一个方法、变量或类是
public的,那么不论这个方法或变量所在的类在什么包中,都可以自由访问。
示例:
public class Person { public String name; public void sayHello() { System.out.println("Hello, my name is " + name); } }
Person类和sayHello()方法都被标记为public,因此任何类都可以创建Person对象并调用sayHello()。
2. protected
- 概念:受保护的,可以被同一个包中的类访问,或者被继承该类的子类访问(即使子类在不同的包中)。
- 通俗解释:
protected允许当前包内的所有类访问,另外允许其他包中的子类访问。
示例:
class Animal { protected String type; protected void makeSound() { System.out.println("Animal is making a sound."); } } class Dog extends Animal { public void bark() { makeSound(); // 可以访问父类的 protected 方法 System.out.println("Dog is barking."); } }
makeSound()方法和type字段是protected的,因此可以在同一个包中的类或子类中被访问。
3. default(包级私有,未加修饰符)
- 概念:默认访问权限,也称包级私有,只有同一个包中的类可以访问,其他包中的类不能访问。
- 通俗解释:如果不加修饰符(例如没有写
public、protected、private),则为default。只有在同一个包内的类可以访问,外部包无法访问。
示例:
class Car { String model; // default 访问权限 void drive() { // default 访问权限 System.out.println("Driving the car."); } }
model字段和drive()方法是default访问权限,它们只能在同一个包中的类访问,其他包中的类无法访问。
4. private
- 概念:私有的,只能在该类的内部访问。
- 通俗解释:
private使得该方法或变量只能在自己所在的类内部使用,其他类甚至在同一个包中也无法访问。
示例:
class BankAccount { private double balance; // private 变量 private void updateBalance() { // private 方法 System.out.println("Updating balance..."); } public void deposit(double amount) { balance += amount; updateBalance(); // 类内部可以访问 private 方法 } }
balance和updateBalance()是private,因此只能在BankAccount类内部使用,其他类无法访问这些成员。
访问控制修饰符对比表
| 访问修饰符 | 同一个类内部 | 同一个包内 | 不同包的子类 | 不同包的非子类 |
|---|---|---|---|---|
public |
✅ | ✅ | ✅ | ✅ |
protected |
✅ | ✅ | ✅ | ❌ |
default |
✅ | ✅ | ❌ | ❌ |
private |
✅ | ❌ | ❌ | ❌ |
总结:
public:最宽松的访问权限,任何地方都可以访问。protected:允许同包内访问,此外允许子类(无论在哪个包)访问。default(未加修饰符):仅限于包内访问,其他包无法访问。private:最严格的访问权限,只能在类内部访问。
通过这些概念和示例,就可以清楚地看到每个访问控制修饰符的使用场景和具体区别。
2.下面举例加强理解:
| 访问修饰符 | 同一个类内部 | 同一个包内 | 不同包的子类 | 不同包的非子类 |
|---|---|---|---|---|
public |
✅ | ✅ | ✅ | ✅ |
protected |
✅ | ✅ | ✅ | ❌ |
default |
✅ | ✅ | ❌ | ❌ |
private |
✅ | ❌ | ❌ | ❌ |
为了清晰说明 public、protected、default、private 修饰符在 同一个类内部、同一个包内、不同包的子类、不同包的非子类 的16种情况,以下我将通过一个完整的例子进行讲解。
示例结构:
- 我们定义两个包:
com.example和com.other - 在
com.example包中,我们有一个父类Parent和一个子类Child。 - 在
com.other包中,我们定义一个类OtherChild,它继承自Parent,并且我们还有一个类NonChild,它不继承自Parent。 Parent类包含public、protected、default和private修饰的成员,展示它们在不同场景下的访问情况。
代码结构:
com.example.Parent.java(父类,包含所有修饰符示例)com.example.Child.java(同包中的子类)com.other.OtherChild.java(不同包中的子类)com.other.NonChild.java(不同包中的非子类)

我会添加 main() 函数作为入口,方便你更直观地运行和测试访问权限的差异。
下面我将为每个类添加 main() 方法,用来展示不同场景下如何访问不同修饰符定义的字段。【每个类都能够添加main()函数?见后面追问】
1. com.example.Parent.java(父类,包含main()入口):
package com.example; public class Parent { public String publicField = "Public Field"; protected String protectedField = "Protected Field"; String defaultField = "Default Field"; // default 访问权限 private String privateField = "Private Field"; public void accessFields() { // 同一个类内部,所有字段都能访问 System.out.println("Inside Parent class:"); System.out.println("Public Field: " + publicField); // ✅ System.out.println("Protected Field: " + protectedField); // ✅ System.out.println("Default Field: " + defaultField); // ✅ System.out.println("Private Field: " + privateField); // ✅ } public static void main(String[] args) { Parent parent = new Parent(); parent.accessFields(); // 测试同一个类内部访问 } }
2. com.example.Child.java(同包的子类,包含main()入口):
package com.example; public class Child extends Parent { public void testAccess() { System.out.println("Inside Child class (same package):"); // 同一个包的子类可以访问 public、protected 和 default 字段,但不能访问 private 字段 System.out.println("Public Field: " + publicField); // ✅ System.out.println("Protected Field: " + protectedField); // ✅ System.out.println("Default Field: " + defaultField); // ✅ // System.out.println(privateField); // ❌ 编译错误 } public static void main(String[] args) { Child child = new Child(); child.testAccess(); // 测试同包的子类访问 } }
3. com.other.OtherChild.java(不同包中的子类,包含main()入口):
package com.other; import com.example.Parent; public class OtherChild extends Parent { public void testAccess() { System.out.println("Inside OtherChild class (different package):"); // 不同包中的子类可以访问 public 和 protected 字段,不能访问 default 和 private 字段 System.out.println("Public Field: " + publicField); // ✅ System.out.println("Protected Field: " + protectedField); // ✅ // System.out.println(defaultField); // ❌ 编译错误 // System.out.println(privateField); // ❌ 编译错误 } public static void main(String[] args) { OtherChild otherChild = new OtherChild(); otherChild.testAccess(); // 测试不同包的子类访问 } }
4. com.other.NonChild.java(不同包的非子类,包含main()入口):
package com.other; import com.example.Parent; public class NonChild { public void testAccess() { Parent parent = new Parent(); System.out.println("Inside NonChild class (different package, non-subclass):"); // 不同包中的非子类只能访问 public 字段,不能访问 protected、default 和 private 字段 System.out.println("Public Field: " + parent.publicField); // ✅ // System.out.println(parent.protectedField); // ❌ 编译错误 // System.out.println(parent.defaultField); // ❌ 编译错误 // System.out.println(parent.privateField); // ❌ 编译错误 } public static void main(String[] args) { NonChild nonChild = new NonChild(); nonChild.testAccess(); // 测试不同包的非子类访问 } }
各类执行顺序:
Parent类的main()将展示同类内的所有字段访问,验证public、protected、default和private都可以访问。Child类的main()将展示同包子类中的字段访问,验证public、protected、default字段可以访问,private无法访问。OtherChild类的main()将展示不同包的子类访问,验证public和protected可以访问,default和private无法访问。NonChild类的main()将展示不同包的非子类访问,验证只有public字段可以访问,其他字段无法访问。
总结:
通过这些简单的 main() 方法,可以直接运行代码并查看输出,帮助你理解各个访问修饰符在不同场景中的权限控制效果。这也让你可以直观地看到每个类在不同情况下的访问权限差异。
3.追问:main()函数应该只能存在于一个.java文件中吧?
不,main() 函数可以存在于多个 .java 文件中。每个 Java 类都可以有一个 main() 方法,只要它们是独立的类。
每个类的 main() 方法都是程序的入口点,但只有一个 main() 方法会被 Java 虚拟机(JVM)调用,具体取决于你运行的类。
示例
假设有三个文件:
-
Parent.java
package com.example; public class Parent { public static void main(String[] args) { System.out.println("This is the Parent class."); } }
2.Child.java
package com.example; public class Child { public static void main(String[] args) { System.out.println("This is the Child class."); } }
3.OtherChild.java
package com.other; public class OtherChild { public static void main(String[] args) { System.out.println("This is the OtherChild class."); } }
运行示例
当你运行时,如果你在命令行中执行 java com.example.Parent,输出将是:
This is the Parent class.
如果你执行 java com.example.Child,输出将是:
This is the Child class.
而如果你执行 java com.other.OtherChild,输出将是:
This is the OtherChild class.
总结
- 多个
main()方法:在不同的类中可以有多个main()方法,每个方法都是独立的入口点。 - 选择运行:你可以选择运行任何一个类的
main()方法,而 JVM 只会执行你指定的那个类的main()方法。
这允许你在一个项目中有多个类的测试或独立执行,便于开发和调试。

浙公网安备 33010602011771号