封装
在理解 set 和 get 之前,你必须先理解面向对象编程中的一个核心概念:封装。

封装 的思想是:
- 隐藏内部状态:一个对象不应该直接暴露其内部的数据(成员变量),外部代码不应该能随意地修改对象的内部状态,这会导致对象变得不稳定和不可预测。
- 提供公共接口:对象应该通过一组公共的方法(
public methods)来与外界交互,这些方法就像是对象的“门卫”或“服务员”,负责控制对内部数据的访问和修改。
set 和 get 方法就是实现封装最经典、最常见的方式。
- Getter (或 Accessor):用于获取(读取)私有成员变量的值,方法名通常以
get开头。 - Setter (或 Mutator):用于设置(修改)私有成员变量的值,方法名通常以
set开头。
为什么需要使用 set 和 get?
直接让成员变量 public 看起来很方便,但会带来很多问题:
// 不好的实践:public 成员变量
public class BadUser {
public String name;
public int age;
}
public class Main {
public static void main(String[] args) {
BadUser user = new BadUser();
user.name = "John";
user.age = -5; // 问题:年龄可以被设置为负数!
user.name = null; // 问题:名字可以被设置为 null!
}
}
你看,age 可以被设置为负数,name 可以被设置为 null,这显然不符合现实逻辑,对象的状态被破坏了。

使用 set 和 get 可以完美地解决这些问题。
set 和 get 的基本语法
一个标准的 JavaBean 类通常遵循以下命名和结构规范:
- 成员变量必须是
private的。 - 每个私有成员变量都有一个对应的
public的getter方法。 - 对于可变的成员变量,还需要一个
public的setter方法。
语法示例
public class User {
// 1. 私有成员变量,对外隐藏
private String name;
private int age;
// 2. Getter 方法:用于获取 name 的值
public String getName() {
return this.name;
}
// 3. Setter 方法:用于设置 name 的值
public void setName(String name) {
// 在这里可以添加逻辑检查
if (name != null && !name.trim().isEmpty()) {
this.name = name;
} else {
System.out.println("名字不能为空或空字符串!");
}
}
// 4. Getter 方法:用于获取 age 的值
public int getAge() {
return this.age;
}
// 5. Setter 方法:用于设置 age 的值
public void setAge(int age) {
// 在这里可以添加逻辑检查,确保年龄是合理的
if (age > 0 && age < 150) {
this.age = age;
} else {
System.out.println("年龄必须在 1 到 149 之间!");
}
}
}
如何使用
public class Main {
public static void main(String[] args) {
User user = new User();
// 使用 setter 方法设置值
user.setName("Alice");
user.setAge(30);
// 使用 getter 方法获取值
String userName = user.getName();
int userAge = user.getAge();
System.out.println("用户名: " + userName); // 输出: 用户名: Alice
System.out.println("年龄: " + userAge); // 输出: 年龄: 30
// 尝试设置非法值
user.setAge(-100); // 输出: 年龄必须在 1 到 149 之间!
System.out.println("年龄: " + user.getAge()); // 输出: 年龄: 30 (值没有被修改)
}
}
从上面的例子可以看出,通过 setter 方法,我们可以在赋值前加入任何我们想要的逻辑(如校验、格式化、触发事件等),从而保证了对象数据的有效性和一致性。
特殊情况:布尔类型的 Getter
对于 boolean 类型的成员变量,getter 方法的命名有一个小小的不同,它不以 get 开头,而是以 is 开头。
public class Product {
private String productName;
private boolean inStock;
// 标准的 getter
public String getProductName() {
return productName;
}
// 布尔类型的 getter
public boolean isInStock() {
return inStock;
}
// setter 保持不变
public void setInStock(boolean inStock) {
this.inStock = inStock;
}
}
// 使用
Product p = new Product();
p.setInStock(true);
boolean isAvailable = p.isInStock(); // 使用 is...() 方法
现代 Java 的演进:final、record 和 var
随着 Java 语言的发展,set/get 的使用方式也在变化。
不可变对象
如果一个对象创建后其状态不应该被改变,我们称之为不可变对象,对于这种对象,我们只需要提供 getter 方法,而不提供 setter 方法,成员变量通常用 final 修饰。
public final class ImmutablePoint {
private final int x;
private final int y;
public ImmutablePoint(int x, int y) {
this.x = x;
this.y = y;
}
// 只有 getter,没有 setter
public int getX() {
return x;
}
public int getY() {
return y;
}
}
不可变对象是线程安全的,易于理解和维护,是现代 Java 编程中推荐的设计模式。
record (Java 14+ 引入,Java 16+ 正式发布)
record 是 Java 为创建简单的、不可变的数据载体而引入的一种新类型,它自动为你生成 private final 的成员变量、构造器、getter 方法、equals(), hashCode() 和 toString() 方法。
使用 record,你不再需要手动编写 set 和 get,代码极其简洁。
// 使用 record 定义一个与上面 ImmutablePoint 功能相同的类
public record Point(int x, int y) {
// 编译器会自动生成所有必要的代码,包括 getX() 和 getY()
}
// 使用
Point p = new Point(10, 20);
int x = p.x(); // 直接通过访问器访问,就像 getter 一样
int y = p.y();
System.out.println("Point is: " + p); // toString() 也自动生成了
Lombok 库
Lombok 是一个非常流行的库,它通过注解来自动生成 setter、getter、toString()、equals() 等样板代码,这极大地减少了重复的代码编写。
import lombok.Getter;
import lombok.Setter;
@Getter // 为所有成员变量生成 getter
@Setter // 为所有成员变量生成 setter
public class LombokUser {
private String name;
private int age;
}
// 使用方式与普通类完全一样
public class Main {
public static void main(String[] args) {
LombokUser user = new LombokUser();
user.setName("Bob");
user.setAge(25);
System.out.println(user.getName());
}
}
var (Java 10+ 引入)
var 是一个局部变量类型推断关键字,它不会改变 set/get 的工作原理,但可以让你的代码更简洁。
// 传统写法
User user1 = new User();
user1.setName("Charlie");
// 使用 var
var user2 = new User(); // 编译器自动推断 user2 的类型是 User
user2.setName("David");
| 特性 | 描述 | 优点 |
|---|---|---|
private 成员变量 |
对外隐藏对象的内部状态。 | 保护数据,防止外部直接、不安全的修改。 |
public Getter |
提供读取私有成员变量的安全通道。 | 控制对数据的读取,可以在读取时进行计算或转换。 |
public Setter |
提供修改私有成员变量的安全通道。 | 核心价值:可以在修改前进行数据校验,确保对象状态的有效性和一致性。 |
| 封装 | 通过 private + public 方法组合实现。 |
提高了代码的安全性、可维护性和灵活性。 |
| 不可变对象 | 只有 getter,没有 setter,成员变量为 final。 |
线程安全,易于设计和使用。 |
record / Lombok |
现代 Java 提供的简化 set/get 编写的工具。 |
减少样板代码,提高开发效率。 |
最佳实践建议:
- 永远优先将成员变量设为
private。 - 除非有特殊原因(如需要修改对象状态),否则总是为
private成员变量提供getter和setter。 - 在
setter方法中尽可能地加入校验逻辑,这是封装最重要的体现。 - 对于不会改变状态的数据,考虑创建不可变对象或使用
record。 - 在团队项目中,统一使用 Lombok 可以显著提升开发效率和代码整洁度。
