MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java类的访问修饰符

2023-09-082.3k 阅读

Java 类的访问修饰符

访问修饰符的概念与作用

在 Java 编程中,访问修饰符是一种重要的语言特性,它用于控制类、类的成员(包括字段、方法、构造函数等)的访问权限。通过合理使用访问修饰符,可以实现数据封装、信息隐藏,从而提高代码的安全性、可维护性和可扩展性。访问修饰符就像是一把钥匙,决定了哪些代码块能够访问特定的类或类成员,不同的访问修饰符赋予了不同级别的访问权限。

Java 中的访问修饰符种类

Java 提供了四种主要的访问修饰符,分别是 publicprotectedprivate 和默认(也称为包访问权限,即不使用任何修饰符)。每种修饰符都有其特定的访问规则,接下来我们将详细介绍每种访问修饰符。

public 修饰符

  1. 访问权限描述public 修饰符是访问权限最宽泛的修饰符。被 public 修饰的类、字段、方法或构造函数可以被任何其他类访问,无论这些类是否在同一个包中,甚至可以在不同的 Java 项目中访问。这意味着 public 成员就像是对整个 Java 世界公开的接口,任何代码都可以自由地使用它们。
  2. 应用场景:通常用于定义 API(应用程序编程接口)。例如,当开发一个供其他开发者使用的库时,那些希望外部开发者能够调用的类、方法和字段就应该使用 public 修饰符。这样可以确保外部代码能够方便地使用库提供的功能,同时也保证了库的功能可以被广泛复用。
  3. 代码示例
// 定义一个 public 类
public class PublicClass {
    // public 字段
    public int publicField;

    // public 方法
    public void publicMethod() {
        System.out.println("This is a public method.");
    }

    // public 构造函数
    public PublicClass() {
        publicField = 10;
    }
}

// 在另一个类中访问 PublicClass
public class AnotherClass {
    public static void main(String[] args) {
        PublicClass publicObj = new PublicClass();
        publicObj.publicMethod();
        System.out.println("Public field value: " + publicObj.publicField);
    }
}

在上述代码中,PublicClass 类及其成员(字段 publicField、方法 publicMethod 和构造函数)都被声明为 public。因此,AnotherClass 类可以在 main 方法中创建 PublicClass 的实例,并访问其 public 成员。

private 修饰符

  1. 访问权限描述private 修饰符是访问权限最严格的修饰符。被 private 修饰的字段、方法或构造函数只能在其所在的类内部被访问,其他任何类(即使是同一个包中的类)都无法直接访问这些 private 成员。这实现了数据的高度封装,使得类的内部状态和实现细节对外界不可见,从而保护了数据的完整性和安全性。
  2. 应用场景:常用于隐藏类的内部实现细节,确保类的使用者只能通过类提供的公共接口来操作类的状态。例如,一个类可能有一些辅助方法,这些方法只在类内部使用,不希望外部类调用,就可以将这些方法声明为 private。另外,对于类的敏感数据字段,如密码、用户敏感信息等,也应该使用 private 修饰符进行保护。
  3. 代码示例
public class PrivateClass {
    // private 字段
    private int privateField;

    // private 方法
    private void privateMethod() {
        System.out.println("This is a private method.");
    }

    // public 方法用于间接访问 private 字段和调用 private 方法
    public void publicMethod() {
        privateField = 20;
        privateMethod();
        System.out.println("Private field value accessed via public method: " + privateField);
    }
}

public class TestPrivateClass {
    public static void main(String[] args) {
        PrivateClass privateObj = new PrivateClass();
        privateObj.publicMethod();
        // 以下代码会报错,因为 privateField 和 privateMethod 是 private 的
        // System.out.println(privateObj.privateField);
        // privateObj.privateMethod();
    }
}

在上述代码中,PrivateClass 类中的 privateFieldprivateMethod 只能在 PrivateClass 类内部被访问。publicMethod 作为一个公共接口,允许外部类间接访问 privateField 和调用 privateMethod。而在 TestPrivateClass 类的 main 方法中,直接访问 privateField 和调用 privateMethod 会导致编译错误。

protected 修饰符

  1. 访问权限描述protected 修饰符的访问权限介于 publicprivate 之间。被 protected 修饰的字段、方法或构造函数可以被同一包中的其他类访问,同时也可以被不同包中的子类访问。这意味着 protected 成员对于同一包内的代码和子类来说是可见的,但对于其他无关的类是不可见的。
  2. 应用场景:常用于实现继承关系时,希望子类能够访问父类的某些成员,但又不想将这些成员完全暴露给所有类。例如,当设计一个基类,其中有一些成员是为子类提供扩展和定制功能所需要的,但对于其他非子类的类没有直接使用的必要,就可以将这些成员声明为 protected
  3. 代码示例
// 定义一个包
package com.example;

// 父类
public class ParentClass {
    // protected 字段
    protected int protectedField;

    // protected 方法
    protected void protectedMethod() {
        System.out.println("This is a protected method.");
    }
}

// 同一包中的子类
package com.example;

public class SamePackageChildClass extends ParentClass {
    public void accessProtectedMembers() {
        protectedField = 30;
        protectedMethod();
        System.out.println("Protected field value in same package child class: " + protectedField);
    }
}

// 不同包中的子类
package com.anotherpackage;

import com.example.ParentClass;

public class DifferentPackageChildClass extends ParentClass {
    public void accessProtectedMembers() {
        protectedField = 40;
        protectedMethod();
        System.out.println("Protected field value in different package child class: " + protectedField);
    }
}

// 测试类
package com.example;

public class TestProtected {
    public static void main(String[] args) {
        SamePackageChildClass samePackageChild = new SamePackageChildClass();
        samePackageChild.accessProtectedMembers();

        DifferentPackageChildClass differentPackageChild = new DifferentPackageChildClass();
        differentPackageChild.accessProtectedMembers();
    }
}

在上述代码中,ParentClass 中的 protectedFieldprotectedMethod 可以被同一包中的 SamePackageChildClass 访问,也可以被不同包中的 DifferentPackageChildClass 访问。通过继承,子类可以利用父类的 protected 成员进行功能扩展。

默认访问修饰符(包访问权限)

  1. 访问权限描述:当一个类、字段、方法或构造函数没有使用任何访问修饰符时,它具有默认的访问权限,也称为包访问权限。具有包访问权限的成员只能被同一包中的其他类访问,不同包中的类无法访问这些成员,即使这些类是子类关系也不行(除非通过继承和 protected 修饰符等方式间接访问)。
  2. 应用场景:通常用于只想在同一个包内使用的类和成员。例如,当开发一个相对独立的模块,模块内部的类之间需要相互协作,但不希望模块外部的类直接访问这些内部类和成员时,可以使用默认访问修饰符。这样可以将模块的内部实现细节封装在包内,只对外暴露必要的公共接口。
  3. 代码示例
// 包 com.example
package com.example;

// 具有默认访问权限的类
class DefaultClass {
    // 默认访问权限的字段
    int defaultField;

    // 默认访问权限的方法
    void defaultMethod() {
        System.out.println("This is a default method.");
    }
}

// 同一包中的另一个类
package com.example;

public class AnotherDefaultClass {
    public static void main(String[] args) {
        DefaultClass defaultObj = new DefaultClass();
        defaultObj.defaultField = 50;
        defaultObj.defaultMethod();
        System.out.println("Default field value: " + defaultObj.defaultField);
    }
}

// 不同包中的类(无法直接访问 DefaultClass 的成员)
package com.anotherpackage;

// 以下代码会报错,因为 DefaultClass 具有包访问权限
// import com.example.DefaultClass;
// public class TestDefaultFromAnotherPackage {
//     public static void main(String[] args) {
//         DefaultClass defaultObj = new DefaultClass();
//         defaultObj.defaultField = 60;
//         defaultObj.defaultMethod();
//     }
// }

在上述代码中,DefaultClass 及其成员具有默认访问权限,因此 AnotherDefaultClass 类(在同一包内)可以访问 DefaultClass 的成员。而在 com.anotherpackage 包中的 TestDefaultFromAnotherPackage 类尝试访问 DefaultClass 的成员时会导致编译错误。

访问修饰符对类的限制

  1. 类的访问修饰符限制:类只能使用 public 或默认访问修饰符。使用 privateprotected 修饰类是不允许的,因为这会导致类无法被外部代码实例化或访问,从而失去了类作为代码组织和复用单元的意义。public 类可以被任何包中的类访问,而默认访问权限的类只能被同一包中的类访问。
  2. 内部类的特殊情况:内部类(定义在另一个类内部的类)可以使用所有四种访问修饰符。这是因为内部类的访问权限可以根据其所在的外部类的需求进行更细粒度的控制。例如,一个 private 内部类只能在其外部类内部被访问,常用于实现一些仅在外部类内部使用的辅助类,进一步隐藏实现细节。

访问修饰符对继承的影响

  1. 子类对父类成员访问权限的继承:子类继承父类时,会继承父类中所有非 private 的成员。对于 protected 成员,子类可以直接访问,无论子类与父类是否在同一包中。对于具有默认访问权限的成员,子类只有在与父类处于同一包中时才能访问。如果父类成员是 public,子类当然可以访问,并且子类在重写这些方法时,重写方法的访问权限不能比父类更严格(例如,父类方法是 public,子类重写方法不能是 protected 或默认访问权限)。
  2. 重写方法的访问权限规则:当子类重写父类的方法时,重写方法的访问权限必须至少与父类方法的访问权限相同。这是为了保证多态性的正确实现,确保在使用父类引用指向子类对象时,能够正确调用子类重写的方法。例如,如果父类方法是 protected,子类重写方法可以是 protectedpublic,但不能是 private 或默认访问权限。

合理使用访问修饰符的最佳实践

  1. 最小化访问权限原则:在设计类和类成员时,应遵循最小化访问权限原则,即只赋予类和成员必要的访问权限。尽可能使用最严格的访问修饰符,只有在确实需要更宽泛的访问权限时才放宽限制。这样可以减少代码的耦合度,提高代码的安全性和可维护性。例如,如果一个字段只在类内部使用,就应该将其声明为 private,而不是使用更宽泛的访问修饰符。
  2. 根据类的角色和功能确定访问权限:如果一个类是作为公共 API 的一部分,供外部广泛使用,那么该类及其关键的操作方法应该使用 public 修饰符。如果一个类是内部辅助类,只在特定模块内部使用,那么可以使用默认访问修饰符。对于类中的一些实现细节,如辅助方法和内部状态字段,应根据其使用范围选择合适的访问修饰符,尽量将其限制在最小范围内。
  3. 考虑继承和扩展性:当设计一个可继承的类时,要合理使用 protected 修饰符。对于那些希望子类能够扩展和定制的行为和状态,使用 protected 修饰符可以在保护内部实现细节的同时,为子类提供必要的访问权限。同时,要注意重写方法的访问权限规则,确保继承体系中的访问权限一致性和多态性的正确实现。

通过深入理解和合理运用 Java 类的访问修饰符,可以构建出更加健壮、安全和易于维护的 Java 程序。不同的访问修饰符在不同的场景下发挥着重要作用,它们是实现面向对象编程中数据封装和信息隐藏的关键工具。在实际编程中,应根据具体需求仔细选择合适的访问修饰符,以达到最佳的代码设计效果。