Java 封装(Encapsulation)详细教学

1. 什么是封装?

封装(Encapsulation)是面向对象编程(OOP)的三大特性之一(另外两个是继承和多态)。它指的是将对象的状态(成员变量)私有化,同时提供公共的方法(getter 和 setter)来访问和修改这些变量,从而隐藏实现细节,保护数据安全

封装的核心思想:

  1. 隐藏对象的内部实现,防止外部直接访问。
  2. 通过方法提供受控访问,保证数据完整性和安全性。
  3. 提高代码的可维护性和可扩展性,修改内部代码不会影响外部调用。

2. 如何实现封装?

Java 中实现封装主要有以下几个步骤:

  1. 将类的成员变量(字段)声明为 private,使其无法被外部直接访问。
  2. 提供 public 方法(getter 和 setter)来访问和修改 private 变量
  3. 可以在 setter 方法中添加逻辑检查,确保数据合法性。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 创建一个封装的 Person 类
class Person {
// 私有字段,不能被外部直接访问
private String name;
private int age;

// 提供公共 getter 方法获取 name
public String getName() {
return name;
}

// 提供公共 setter 方法设置 name
public void setName(String name) {
this.name = name;
}

// 提供 getter 方法获取 age
public int getAge() {
return age;
}

// 提供 setter 方法设置 age,并添加合法性检查
public void setAge(int age) {
if (age >= 0 && age <= 150) { // 确保年龄在合理范围
this.age = age;
} else {
System.out.println("Invalid age! Please enter a value between 0 and 150.");
}
}
}

// 测试封装
public class TestEncapsulation {
public static void main(String[] args) {
Person p = new Person();

// 设置值
p.setName("Alice");
p.setAge(25);

// 获取值
System.out.println("Name: " + p.getName());
System.out.println("Age: " + p.getAge());

// 尝试设置非法年龄
p.setAge(-5); // 输出:Invalid age! Please enter a value between 0 and 150.
}
}

运行结果:

1
2
3
Name: Alice
Age: 25
Invalid age! Please enter a value between 0 and 150.

3. 封装的优点

1. 数据安全

  • 通过 private 关键字隐藏数据,防止外部直接修改,避免错误或非法数据。
  • 通过 setter 方法控制数据的合法性。

2. 降低耦合性,提高代码可维护性

  • 代码逻辑可以随时修改,而不影响外部代码。例如,可以修改 setAge() 方法的验证逻辑,而不影响 getAge()

3. 代码更加灵活

  • 可以在 gettersetter 方法中添加逻辑,如转换格式、校验数据等。

4. 只读和只写属性

在某些情况下,我们可能希望:

  • 属性只能读取,不能修改(只读)
  • 属性只能修改,不能读取(只写)

示例:只读属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Employee {
private String employeeId;

// 只提供 getter 方法,不提供 setter 方法
public String getEmployeeId() {
return employeeId;
}

// 只能通过构造方法初始化
public Employee(String employeeId) {
this.employeeId = employeeId;
}
}

public class TestReadOnly {
public static void main(String[] args) {
Employee emp = new Employee("E12345");
System.out.println("Employee ID: " + emp.getEmployeeId());

// emp.setEmployeeId("E67890"); // 错误:没有 setter 方法
}
}

输出:

1
Employee ID: E12345

示例:只写属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class BankAccount {
private double balance;

// 只提供 setter 方法,不提供 getter 方法
public void setBalance(double balance) {
if (balance >= 0) {
this.balance = balance;
} else {
System.out.println("Invalid balance!");
}
}

// 仅提供方法来增加余额,而不允许外部获取余额
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposit successful!");
} else {
System.out.println("Invalid deposit amount!");
}
}
}

public class TestWriteOnly {
public static void main(String[] args) {
BankAccount acc = new BankAccount();
acc.setBalance(1000);
acc.deposit(500);

// System.out.println(acc.getBalance()); // 错误:没有 getter 方法
}
}

5. this 关键字的作用

在封装中,this 关键字通常用于区分成员变量和方法参数。例如:

1
2
3
4
5
6
7
8
class Student {
private String name;

// this.name 代表成员变量,而 name 代表参数
public void setName(String name) {
this.name = name;
}
}

为什么要用 this 如果参数名称和成员变量名称相同,this 关键字可以消除歧义,保证操作的是当前对象的属性。


6. 访问控制修饰符

Java 提供 4 种访问控制修饰符,来控制类、变量和方法的访问权限:

修饰符 访问范围 适用场景
private 仅限本类访问 成员变量,隐藏实现
default(无修饰符) 同一包中的类可以访问 内部模块使用
protected 同一包中的类和子类可以访问 继承相关的封装
public 任何类都可以访问 对外提供的 API

示例:

1
2
3
4
5
6
class Test {
private int a = 10; // 仅本类可访问
int b = 20; // 包级访问(default)
protected int c = 30; // 子类和同包可访问
public int d = 40; // 任何地方都可访问
}

7. 封装与 JavaBean

JavaBean 是一种符合 Java 规范的类,通常用于数据封装。其特点:

  1. 所有成员变量 private
  2. 提供 publicgettersetter
  3. 必须有无参构造方法

JavaBean 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Student {
private String name;
private int age;

// 无参构造方法(必须)
public Student() {}

// getter 和 setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }

public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}

8. 总结

  • 封装通过 private 关键字隐藏数据,并提供 gettersetter 方法访问。
  • 封装可以提高数据安全性,防止数据被非法访问和修改
  • 只读属性:提供 getter,不提供 setter
  • 只写属性:提供 setter,不提供 getter
  • 使用 this 关键字区分成员变量和参数
  • 访问控制修饰符控制类、方法、变量的访问权限

封装是 Java 面向对象编程(OOP) 的核心概念之一,掌握封装有助于写出更安全、更易维护、更模块化的代码! 🚀

来源:

​ ChatGpt-4o