Java编程中的访问控制符详解
2021-02-287.1k 阅读
Java 访问控制符概述
在 Java 编程中,访问控制符(Access Modifiers)是一种重要的机制,用于控制类、方法和变量的访问级别。通过合理使用访问控制符,可以提高代码的安全性、封装性和可维护性。Java 中有四种访问控制符:public
、protected
、private
和默认(也称为包访问权限,即不使用任何关键字)。不同的访问控制符决定了类、方法或变量在不同的上下文中是否可访问。
public
访问控制符
- 含义与作用:
public
访问控制符表示具有最广泛的访问权限。被声明为public
的类、方法或变量可以在任何地方被访问,无论是在同一个包内还是不同的包中。这意味着任何其他类只要能够引用到包含public
成员的类,就可以访问这些public
成员。 - 类的
public
访问权限:- 当一个类被声明为
public
时,它可以被任何其他类访问。例如,我们创建一个public
类PublicClass
:
- 当一个类被声明为
public class PublicClass {
public String publicField;
public void publicMethod() {
System.out.println("This is a public method.");
}
}
- 在其他类中,即使不在同一个包,也可以通过创建
PublicClass
的实例来访问其public
成员:
package com.example.otherpackage;
public class AnotherClass {
public static void main(String[] args) {
com.example.PublicClass publicObj = new com.example.PublicClass();
publicObj.publicField = "Accessed from another package";
publicObj.publicMethod();
}
}
- 方法和变量的
public
访问权限:public
方法和变量通常用于提供对外的接口。比如,一个工具类可能有一些public
方法供其他类调用。以Math
类为例,Math
类中的public
方法sqrt
用于计算平方根,任何类都可以调用:
public class MathExample {
public static void main(String[] args) {
double result = Math.sqrt(16);
System.out.println("The square root of 16 is: " + result);
}
}
private
访问控制符
- 含义与作用:
private
访问控制符表示访问权限最为严格,被声明为private
的类成员(方法或变量)只能在声明它们的类内部被访问。这有助于实现数据的封装,隐藏类的内部实现细节,防止外部类直接访问和修改类的内部状态。 - 变量的
private
访问权限:假设我们有一个Person
类,其中有一个private
的age
变量来表示人的年龄:
public class Person {
private int age;
public void setAge(int newAge) {
if (newAge > 0 && newAge < 150) {
age = newAge;
} else {
System.out.println("Invalid age value.");
}
}
public int getAge() {
return age;
}
}
- 在这个例子中,外部类不能直接访问
age
变量。如果在另一个类中尝试直接访问age
变量,会导致编译错误:
public class TestPerson {
public static void main(String[] args) {
Person person = new Person();
// person.age = 30; // 这行代码会导致编译错误
person.setAge(30);
int age = person.getAge();
System.out.println("The person's age is: " + age);
}
}
- 方法的
private
访问权限:private
方法通常用于类内部的辅助操作,不希望外部类调用。例如,一个Calculator
类可能有一个private
方法用于执行内部的复杂计算:
public class Calculator {
private int performComplexCalculation(int a, int b) {
// 复杂的计算逻辑
return a * a + b * b;
}
public int calculateResult(int a, int b) {
int result = performComplexCalculation(a, b);
return result;
}
}
- 在这个例子中,
performComplexCalculation
方法是private
的,只有Calculator
类内部的其他方法(如calculateResult
)可以调用它,外部类无法直接调用。
默认访问控制符(包访问权限)
- 含义与作用:当一个类、方法或变量没有显式地使用任何访问控制符时,它就具有默认访问权限,也称为包访问权限。具有默认访问权限的成员可以被同一个包内的其他类访问,但不能被不同包中的类访问。这种访问权限在一定程度上实现了包内的封装,使得包内的类可以相互协作,同时对包外隐藏部分实现细节。
- 类的默认访问权限:假设有一个包
com.example.package1
,其中有一个默认访问权限的类DefaultClass
:
package com.example.package1;
class DefaultClass {
void defaultMethod() {
System.out.println("This is a default method.");
}
}
- 在同一个包内的其他类
SamePackageClass
可以访问DefaultClass
的默认方法:
package com.example.package1;
public class SamePackageClass {
public static void main(String[] args) {
DefaultClass defaultObj = new DefaultClass();
defaultObj.defaultMethod();
}
}
- 然而,如果在不同的包
com.example.package2
中的类DifferentPackageClass
尝试访问DefaultClass
,会导致编译错误:
package com.example.package2;
public class DifferentPackageClass {
public static void main(String[] args) {
// com.example.package1.DefaultClass defaultObj = new com.example.package1.DefaultClass();
// 这行代码会导致编译错误,因为DefaultClass具有默认访问权限,不同包无法访问
}
}
- 方法和变量的默认访问权限:同样,方法和变量具有默认访问权限时,遵循相同的规则。在一个包内,类之间可以相互访问具有默认访问权限的方法和变量,而不同包则无法访问。例如,在一个图形绘制的包中,可能有一些默认访问权限的方法用于包内的图形绘制辅助操作,这些方法对包外是不可见的。
protected
访问控制符
- 含义与作用:
protected
访问控制符介于private
和public
之间。被声明为protected
的成员可以被同一个包内的其他类访问,也可以被不同包中的子类访问。这一特性在实现继承关系时非常有用,它允许子类访问父类中需要继承和扩展的部分,同时对包外的非子类进行一定程度的隐藏。 - 不同包中子类的访问:假设有一个父类
Animal
在com.example.animals
包中,其中有一个protected
方法makeSound
:
package com.example.animals;
public class Animal {
protected void makeSound() {
System.out.println("The animal makes a sound.");
}
}
- 然后在
com.example.pets
包中有一个子类Dog
继承自Animal
:
package com.example.pets;
import com.example.animals.Animal;
public class Dog extends Animal {
@Override
protected void makeSound() {
System.out.println("The dog barks.");
}
public void performAction() {
makeSound();
}
}
- 在
Dog
类中,由于它是Animal
的子类,即使不在同一个包,也可以访问Animal
类中的protected
方法makeSound
。同时,Dog
类可以重写这个protected
方法来实现自己的行为。在Dog
类的performAction
方法中就调用了重写后的makeSound
方法。
- 同一个包内非子类的访问:在同一个包内,即使不是子类,也可以访问
protected
成员。例如,在com.example.animals
包中有一个ZooKeeper
类:
package com.example.animals;
public class ZooKeeper {
public void hearAnimalSound(Animal animal) {
animal.makeSound();
}
}
- 在这个例子中,
ZooKeeper
类不是Animal
类的子类,但由于它们在同一个包内,ZooKeeper
类可以访问Animal
类的protected
方法makeSound
。
访问控制符在类、方法和变量上的使用规则总结
- 类的访问控制符使用:
- 一个源文件中最多只能有一个
public
类,并且源文件的名称必须与public
类的名称完全相同(包括大小写)。 - 类不能被声明为
private
或protected
,因为这两个访问控制符主要用于类的成员(方法和变量)。类只能是public
或者具有默认访问权限。
- 一个源文件中最多只能有一个
- 方法和变量的访问控制符使用:
- 方法和变量可以使用
public
、private
、protected
或默认访问控制符。 - 通常,将类的内部状态(变量)声明为
private
,通过public
的访问器(getter)和修改器(setter)方法来访问和修改这些状态,以实现数据封装。 - 对于一些辅助方法,不希望外部类调用的,可以声明为
private
。 - 如果希望一个方法或变量在包内可见,并且在不同包的子类中也可见,可以声明为
protected
。
- 方法和变量可以使用
访问控制符与继承的关系
- 子类对父类成员的访问:
- 子类可以继承父类的
public
和protected
成员,无论子类与父类是否在同一个包内。 - 子类不能继承父类的
private
成员。例如,有一个父类Parent
:
- 子类可以继承父类的
public class Parent {
private int privateField;
protected int protectedField;
public int publicField;
private void privateMethod() {
System.out.println("This is a private method in Parent.");
}
protected void protectedMethod() {
System.out.println("This is a protected method in Parent.");
}
public void publicMethod() {
System.out.println("This is a public method in Parent.");
}
}
- 子类
Child
:
public class Child extends Parent {
public void accessParentMembers() {
// privateField = 10; // 编译错误,不能访问父类的private成员
protectedField = 20;
publicField = 30;
// privateMethod(); // 编译错误,不能访问父类的private方法
protectedMethod();
publicMethod();
}
}
- 子类重写方法的访问控制符规则:
- 子类重写父类的方法时,重写方法的访问控制符不能比父类中被重写方法的访问控制符更严格。例如,如果父类中的方法是
protected
,子类重写该方法时可以是protected
或public
,但不能是private
或默认访问权限。 - 假设父类
Shape
有一个protected
方法draw
:
- 子类重写父类的方法时,重写方法的访问控制符不能比父类中被重写方法的访问控制符更严格。例如,如果父类中的方法是
public class Shape {
protected void draw() {
System.out.println("Drawing a shape.");
}
}
- 子类
Circle
重写draw
方法:
public class Circle extends Shape {
@Override
public void draw() {
System.out.println("Drawing a circle.");
}
}
- 在这个例子中,
Circle
类重写draw
方法时使用了public
访问控制符,这是允许的,因为public
比protected
的访问权限更宽松。
访问控制符的最佳实践
- 数据封装:将类的成员变量声明为
private
,通过public
的getter
和setter
方法来访问和修改这些变量。这样可以控制对数据的访问,确保数据的一致性和安全性。例如,在一个BankAccount
类中,账户余额balance
应该是private
的:
public class BankAccount {
private double balance;
public double getBalance() {
return balance;
}
public void setBalance(double newBalance) {
if (newBalance >= 0) {
balance = newBalance;
} else {
System.out.println("Invalid balance value.");
}
}
}
- 隐藏实现细节:将类内部的辅助方法声明为
private
。这样可以避免外部类意外调用这些方法,同时也使得类的接口更加清晰。比如,在一个加密工具类中,可能有一些private
方法用于执行内部的加密算法步骤。 - 合理使用
protected
:在设计继承体系时,当需要子类访问父类的某些成员,但又不想让包外的非子类访问时,使用protected
。例如,在一个图形绘制的继承体系中,父类GraphicObject
可能有一些protected
方法用于设置图形的基本属性,子类Rectangle
和Circle
可以继承并使用这些方法来设置自身的属性。 - 使用默认访问权限实现包内协作:对于一些只在包内使用的类、方法和变量,可以使用默认访问权限。这样可以实现包内的协作,同时对包外隐藏这些实现细节。例如,在一个数据库访问包中,可能有一些默认访问权限的工具类用于包内的数据库连接管理等操作。
访问控制符与接口和抽象类
- 接口中的访问控制符:
- 接口中的成员变量默认是
public static final
的,不能修改其访问控制符。例如:
- 接口中的成员变量默认是
public interface MyInterface {
int DEFAULT_VALUE = 10; // 实际上是public static final int DEFAULT_VALUE = 10;
}
- 接口中的方法默认是
public abstract
的,同样不能修改其访问控制符。例如:
public interface MyInterface {
void doSomething(); // 实际上是public abstract void doSomething();
}
- 当一个类实现接口时,实现的方法必须是
public
的,因为接口方法默认是public
,子类重写时不能比父类(接口)的访问控制符更严格。例如:
public class MyClass implements MyInterface {
@Override
public void doSomething() {
System.out.println("Implementing doSomething method.");
}
}
- 抽象类中的访问控制符:
- 抽象类中的成员变量和方法可以使用各种访问控制符,与普通类类似。抽象方法可以是
protected
,这样只有子类(在同一个包或不同包)可以实现它。例如:
- 抽象类中的成员变量和方法可以使用各种访问控制符,与普通类类似。抽象方法可以是
public abstract class AbstractShape {
protected abstract void draw();
}
- 子类继承抽象类并实现抽象方法时,访问控制符要遵循重写规则。如果抽象方法是
protected
,子类可以用protected
或public
来实现;如果抽象方法是public
,子类只能用public
实现。例如:
public class Triangle extends AbstractShape {
@Override
public void draw() {
System.out.println("Drawing a triangle.");
}
}
通过深入理解和合理使用 Java 中的访问控制符,可以构建出更加健壮、安全和易于维护的程序。不同的访问控制符在不同的场景下发挥着重要作用,它们是 Java 面向对象编程中实现封装、继承和多态等特性的重要基础。无论是小型项目还是大型企业级应用,正确运用访问控制符都是编写高质量代码的关键。