Java的public, protected, default, private 这个几个关键字的概念和差异理解 【一文搞懂】

1.在Java中,publicprotecteddefault(也称为包级私有)和 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(包级私有,未加修饰符)

  • 概念:默认访问权限,也称包级私有,只有同一个包中的类可以访问,其他包中的类不能访问。
  • 通俗解释:如果不加修饰符(例如没有写 publicprotectedprivate),则为 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 方法
    }
}
  • balanceupdateBalance()private,因此只能在 BankAccount 类内部使用,其他类无法访问这些成员。

 

访问控制修饰符对比表

访问修饰符同一个类内部同一个包内不同包的子类不同包的非子类
public
protected
default
private

总结:

  1. public:最宽松的访问权限,任何地方都可以访问。
  2. protected:允许同包内访问,此外允许子类(无论在哪个包)访问。
  3. default(未加修饰符):仅限于包内访问,其他包无法访问。
  4. private:最严格的访问权限,只能在类内部访问。

通过这些概念和示例,就可以清楚地看到每个访问控制修饰符的使用场景和具体区别。

 

 

 

2.下面举例加强理解:

访问修饰符同一个类内部同一个包内不同包的子类不同包的非子类
public
protected
default
private

为了清晰说明 publicprotecteddefaultprivate 修饰符在 同一个类内部同一个包内不同包的子类不同包的非子类 的16种情况,以下我将通过一个完整的例子进行讲解。

示例结构:

  • 我们定义两个包:com.examplecom.other
  • com.example 包中,我们有一个父类 Parent 和一个子类 Child
  • com.other 包中,我们定义一个类 OtherChild,它继承自 Parent,并且我们还有一个类 NonChild,它不继承自 Parent
  • Parent 类包含 publicprotecteddefaultprivate 修饰的成员,展示它们在不同场景下的访问情况。

代码结构:

  1. com.example.Parent.java(父类,包含所有修饰符示例)
  2. com.example.Child.java(同包中的子类)
  3. com.other.OtherChild.java(不同包中的子类)
  4. 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();  // 测试不同包的非子类访问
    }
}

各类执行顺序:

  1. Parent 类的 main() 将展示同类内的所有字段访问,验证 publicprotecteddefaultprivate 都可以访问。
  2. Child 类的 main() 将展示同包子类中的字段访问,验证 publicprotecteddefault 字段可以访问,private 无法访问。
  3. OtherChild 类的 main() 将展示不同包的子类访问,验证 publicprotected 可以访问,defaultprivate 无法访问。
  4. NonChild 类的 main() 将展示不同包的非子类访问,验证只有 public 字段可以访问,其他字段无法访问。

 

总结:

通过这些简单的 main() 方法,可以直接运行代码并查看输出,帮助你理解各个访问修饰符在不同场景中的权限控制效果。这也让你可以直观地看到每个类在不同情况下的访问权限差异。

 

3.追问:main()函数应该只能存在于一个.java文件中吧?

不,main() 函数可以存在于多个 .java 文件中。每个 Java 类都可以有一个 main() 方法,只要它们是独立的类。

每个类的 main() 方法都是程序的入口点,但只有一个 main() 方法会被 Java 虚拟机(JVM)调用,具体取决于你运行的类。

示例

假设有三个文件:

  1. 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() 方法。

这允许你在一个项目中有多个类的测试或独立执行,便于开发和调试。

 

posted @ 2024-10-14 11:38  AlphaGeek  阅读(5743)  评论(0)    收藏  举报