前言
在 Java 编程的世界里,访问修饰符就像是一个个神奇的钥匙,它们决定了我们能否访问类、方法和变量。合理使用这些访问修饰符,不仅能提高代码的安全性,还能让代码的结构更加清晰,易于维护。
一、生活场景类比
为了更好地理解这四种访问修饰符,我们可以把它们想象成不同类型的房间。
1. private
:私人保险柜
私人保险柜只有主人才能打开,其他人无法触碰里面的东西。在 Java 里,private
修饰的成员就如同私人保险柜里的物品,只有定义它的类内部可以访问。
2. 缺省(默认):小区内的花园
小区内的花园只对小区居民开放,外来人员不能随意进入。在 Java 中,缺省访问权限的成员就像小区内的花园,只有同一个包内的类可以访问。
3. protected
:家族的传家宝
家族的传家宝不仅家族里的人可以看到,家族分支的后代(子类)即便住在不同的地方(不同包),也有一定的权限接触。在 Java 里,protected
修饰的成员同一个包内的类能访问,不同包的子类也能访问。
4. public
:广场
广场是完全开放的,任何人都可以自由出入。在 Java 中,public
修饰的成员可以在任何地方被访问。
二、private
修饰符:数据的严密守护者
1. 访问权限范围
private
是 Java 中访问权限最严格的修饰符。当你用 private
修饰一个类成员(比如字段、方法)时,这个成员就被牢牢地限制在定义它的类内部,其他任何类都不能直接访问。
2. 作用
private
主要用于封装数据,隐藏类的内部实现细节。这样做可以避免外部代码对类的内部数据进行非法修改,提高代码的安全性和可维护性。就像把重要文件锁在保险柜里,只有特定的人(类内部)才能操作。
3. 示例代码
// 定义一个名为Person的类
class Person {
// 用private修饰的字段,代表人的年龄
private int age;
// 公共的设置年龄的方法
public void setAge(int newAge) {
if (newAge > 0 && newAge < 150) {
age = newAge;
} else {
System.out.println("输入的年龄不合法");
}
}
// 公共的获取年龄的方法
public int getAge() {
return age;
}
}
public class PrivateExample {
public static void main(String[] args) {
Person person = new Person();
// 不能直接访问person的age字段,以下代码会编译错误
// System.out.println(person.age);
// 通过公共方法设置年龄
person.setAge(25);
// 通过公共方法获取年龄
System.out.println("这个人的年龄是: " + person.getAge());
}
}
在这个例子中,age
字段被声明为 private
,在 PrivateExample
类中不能直接访问 age
。但我们提供了 setAge
和 getAge
这两个公共方法,通过它们可以间接地操作 age
字段,同时还能对输入的年龄进行合法性检查。
4. 使用场景
- 封装敏感数据:比如用户的密码、银行卡号等信息,这些数据非常敏感,不能被外部随意访问,只能通过类提供的公共方法进行操作。
- 隐藏实现细节:当类的某个方法的实现细节不希望被外部知晓时,可以将其声明为
private
。例如,一个类中有一个复杂的计算方法,这个方法的具体实现对外部没有意义,只需要提供一个公共方法返回计算结果即可。
三、缺省(默认)修饰符:包内的友好交流通道
1. 访问权限范围
当你定义类成员时不使用任何访问修饰符,这个成员就具有缺省访问权限,也叫包访问权限。这意味着只有同一个包内的类可以访问这些成员,不同包的类无法访问。
2. 作用
缺省访问权限有助于将相关的类组织在同一个包中,方便包内的类之间进行交互,同时对包外的类隐藏实现细节。就像小区内的居民可以自由使用小区花园,但外人不能随意进入,提高了代码的模块化程度。
3. 示例代码
// 假设我们有一个包叫做com.example.package1
package com.example.package1;
// 定义一个名为Book的类
class Book {
// 缺省访问权限的字段,代表书的价格
double price;
// 缺省访问权限的方法,用于显示书的价格
void showPrice() {
System.out.println("这本书的价格是: " + price + " 元");
}
}
// 同一个包内的另一个类,用于测试Book类
class BookTest {
public static void main(String[] args) {
Book book = new Book();
// 可以直接访问book的price字段
book.price = 29.9;
// 可以直接调用book的showPrice方法
book.showPrice();
}
}
如果我们在另一个包中创建一个类来访问 Book
类的成员,会发现无法访问,因为它们不在同一个包内。
4. 使用场景
- 包内协作:当多个类需要在同一个包内进行协作,但不希望外部包的类访问这些类的某些成员时,可以使用缺省访问权限。例如,一个包内有多个工具类,这些工具类之间需要相互调用某些方法,但这些方法不希望被其他包的类使用。
- 内部实现:对于一些只在包内部使用的辅助类和方法,可以使用缺省访问权限。
四、protected
修饰符:继承体系的桥梁
1. 访问权限范围
protected
修饰符的访问权限介于缺省和 public
之间。protected
修饰的成员同一个包内的类可以访问,不同包的子类也有访问权限。
2. 作用
protected
常用于父类希望子类能够访问其某些成员,但又不希望其他无关类随意访问的情况。它在继承体系中提供了一种灵活控制成员访问权限的方式,既保证了一定程度的封装性,又方便了子类对父类成员的使用。
3. 示例代码
// 定义一个父类Animal
package com.example.animal;
public class Animal {
// 用protected修饰的字段,代表动物的名字
protected String name;
// 构造方法,用于初始化动物的名字
public Animal(String animalName) {
name = animalName;
}
// 用protected修饰的方法,用于显示动物的名字
protected void showName() {
System.out.println("这只动物的名字是: " + name);
}
}
// 同一个包内的类,用于测试Animal类
package com.example.animal;
class SamePackageTest {
public static void main(String[] args) {
Animal animal = new Animal("小狗");
// 可以直接访问animal的name字段
System.out.println("动物的名字是: " + animal.name);
// 可以直接调用animal的showName方法
animal.showName();
}
}
// 不同包的子类Dog
package com.example.pet;
import com.example.animal.Animal;
public class Dog extends Animal {
public Dog(String dogName) {
super(dogName);
}
public void introduce() {
// 可以访问父类的protected字段和方法
System.out.println("我是一只狗,我的名字是: " + name);
showName();
}
public static void main(String[] args) {
Dog dog = new Dog("旺财");
dog.introduce();
}
}
在这个例子中,Animal
类的 name
字段和 showName
方法被声明为 protected
。在同一个包内的 SamePackageTest
类可以直接访问,不同包的子类 Dog
也可以通过实例方法访问。
4. 使用场景
- 继承体系中的数据共享:当父类有一些数据或方法希望子类能够使用,但又不希望外部类随意访问时,可以使用
protected
修饰符。例如,一个图形类有一些属性和方法,它的子类(如圆形、矩形等)需要访问这些属性和方法来实现自己的功能,但这些属性和方法不希望被其他无关类访问。 - 扩展父类功能:子类可以通过访问父类的
protected
成员来扩展父类的功能。
五、public
修饰符:开放的公共资源
1. 访问权限范围
public
是 Java 中访问权限最宽松的修饰符。用 public
修饰的类、方法和变量可以在任何地方被访问,只要能获取到该类的实例或引用。
2. 作用
public
常用于定义可以被其他类广泛使用的公共接口、工具类等。通过将这些类和成员声明为 public
,可以提高代码的复用性,让不同的模块之间能够方便地进行交互。
3. 示例代码
// 定义一个公共类Calculator
public class Calculator {
// 公共的加法方法
public int add(int a, int b) {
return a + b;
}
// 公共的减法方法
public int subtract(int a, int b) {
return a - b;
}
}
public class PublicExample {
public static void main(String[] args) {
Calculator calculator = new Calculator();
// 可以在任何地方访问public方法
int result1 = calculator.add(5, 3);
int result2 = calculator.subtract(5, 3);
System.out.println("5 + 3 的结果是: " + result1);
System.out.println("5 - 3 的结果是: " + result2);
}
}
在这个例子中,Calculator
类的 add
和 subtract
方法被声明为 public
,在 PublicExample
类中可以直接调用这些公共方法。
4. 使用场景
- 定义公共接口:当你需要定义一个供其他类实现的接口时,接口中的方法和常量通常都是
public
的。例如,Java 中的Runnable
接口,它的run
方法就是public
的,任何实现该接口的类都需要实现这个public
方法。 - 工具类:一些通用的工具类,如日期处理类、字符串处理类等,它们的方法和字段通常也会声明为
public
,方便其他类使用。
六、总结
为了更清晰地对比这四种访问修饰符的区别,我们可以用一个表格来总结它们的访问权限范围:
修饰符 | 同一类中 | 同一包中的类 | 不同包的子类 | 不同包的非子类 |
---|---|---|---|---|
private | √ | × | × | × |
缺省 | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |