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() 方法。
这允许你在一个项目中有多个类的测试或独立执行,便于开发和调试。