1. 什么是 Getter 和 Setter
如果把对象想象成保险箱,它的私有字段就是保险箱的内容,而 getter 和 setter 是通往各个小格子的钥匙。Getter 用来查看里面有什么,Setter 则能把新的东西小心地放进去(当然,前提是不把比如说刺猬塞进文件夹里)。
Getter
Getter 是一个 public 方法,它返回 private 字段的值。它的名字通常以 get + 首字母大写的字段名开头。
public class Person {
private String name; // 私有字段
// 字段 name 的 getter
public String getName() {
return name;
}
}
Setter
Setter 是一个 public 方法,用于修改私有字段的值。它的名字以 set + 首字母大写的字段名开头。
public class Person {
private String name;
// 字段 name 的 setter
public void setName(String name) {
this.name = name;
}
}
针对 boolean 字段
对于 boolean 类型的字段,约定在 getter 中使用前缀 is:
private boolean active;
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
2. 语法与命名约定
Java 很严格,但并不古板。有一套行之有效的约定,使你的代码对其他开发者(以及一个月后的你自己)更易理解。
- Getter: public Type getImyaPolya()
- Setter: public void setImyaPolya(Type value)
- boolean 的 Getter: public boolean isImyaPolya()
字段名在方法名中以大写字母开头:如果字段是 age,则方法是 getAge() 和 setAge()。
这套约定遵循 JavaBeans 风格,因此 IDE、库和框架可以自动发现你的 getter 和 setter。比如在使用 Spring 或 JavaFX 时,需要的时候这些方法会被“魔法般地”调用。
3. 代码示例
我们来做一个教学项目——一个简单的“联系人”应用(类似电话本)——并为它添加正确的 getter 和 setter。
示例:带私有字段和 getter/setter 的 Contact 类
public class Contact {
private String name;
private String phone;
private int age;
private boolean favorite;
// Getter
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
public int getAge() {
return age;
}
public boolean isFavorite() {
return favorite;
}
// Setter
public void setName(String name) {
this.name = name;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setAge(int age) {
// 校验示例:年龄不能是负数
if (age < 0) {
System.out.println("年龄不能是负数!");
return;
}
this.age = age;
}
public void setFavorite(boolean favorite) {
this.favorite = favorite;
}
}
在应用中的使用
Contact friend = new Contact();
friend.setName("伊万·伊万诺夫");
friend.setPhone("+1-999-123-45-67");
friend.setAge(25);
friend.setFavorite(true);
System.out.println("姓名:" + friend.getName());
System.out.println("电话:" + friend.getPhone());
System.out.println("年龄:" + friend.getAge());
System.out.println("已收藏:" + (friend.isFavorite() ? "是" : "否"));
在 setter 中进行校验的示例
请注意,我们在 setAge 中添加了一个简单的检查:如果年龄为负,就不修改字段并输出警告。这是保护对象免受不正确数据影响的简单方式。
4. 最佳实践:如何做得更好
并非所有字段都需要 public setter
有时字段应为只读——例如在创建对象时设置、之后不再改变的唯一标识符。这种情况下就不要写 setter:
public class Contact {
private final int id; // final——初始化后无法修改
public Contact(int id) {
this.id = id;
}
public int getId() {
return id;
}
// 没有 setId!
}
使用 getter/setter 来控制访问与校验
public void setName(String name) {
if (name == null || name.trim().isEmpty()) {
System.out.println("姓名不能为空!");
return;
}
this.name = name;
}
不要直接暴露内部的可变对象
如果你有一个字段——比如电话号码列表:
private String[] phones;
不应该通过 getter 直接返回它:
public String[] getPhones() {
return phones; // 不推荐!
}
这样的代码允许外部代码随意修改列表——这会破坏封装!
更合理的做法:返回数组的副本:
public String[] getPhones() {
return Arrays.copyOf(phones, phones.length); // 返回副本
}
或者直接克隆:
public String[] getPhones() {
return phones.clone();
}
让 getter 和 setter 简洁明了
- 不要在 getter/setter 中编写复杂的业务逻辑——它们的职责很简单:控制访问,并在必要时进行校验。
- 如果字段不应该被修改——就不要提供 setter。
- 如果字段不应该被外部访问——就不要提供 getter。
5. 在 IDE 中自动生成 getter/setter
手写访问器并不有趣,尤其当类有十来个字段时。好在现代 IDE(例如 IntelliJ IDEA、Eclipse、带插件的 VS Code)都可以自动生成。
在 IntelliJ IDEA 中
- 打开类,把光标放在类体内部。
- 按下 Alt + Insert(或 Code -> Generate...)。
- 选择 Getter and Setter。
- 勾选需要的字段并点击 OK。
搞定! 你的 getter 和 setter 会像变魔法一样生成。
在 Eclipse 中
- 打开类。
- 右键 — Source — Generate Getters and Setters...
- 选择字段并点击 OK。
在 VS Code(配合 Java Extension Pack)
- 打开类文件。
- 在命令面板(Ctrl+Shift+P)中输入 Generate getters and setters。
- 按照提示操作。
6. 让你的应用更进一步:封装的实际运用
在前面的课程中,你构建了一个用于存储联系人的简单应用。现在我们可以改进它,把字段设为私有,并仅通过 getter/setter 提供访问。
改造前(反面示例):
public class Contact {
public String name;
public String phone;
public int age;
}
问题:任何代码都可以这样做:
Contact c = new Contact();
c.age = -1000; // 现在我们的通讯录里出现了吸血鬼!
改造后(正面示例):
public class Contact {
private String name;
private String phone;
private int age;
public void setAge(int age) {
if (age < 0) {
System.out.println("年龄不能是负数!");
return;
}
this.age = age;
}
public int getAge() {
return age;
}
// 其余的 getter/setter...
}
现在就不可能在外部不小心(或故意)把对象弄坏了。
7. 计算属性与不可变属性的 getter/setter
有时值不是存储在字段里,而是按需计算:
public class Rectangle {
private int width;
private int height;
public int getArea() {
return width * height;
}
}
面积不需要 setter —— 它不能被直接设置,只能通过修改宽或高来改变。
8. Getter/Setter 的常见错误
错误 1:Getter/Setter 破坏了封装。
如果 getter 返回对内部可变对象的引用(例如列表),外部代码就能绕过所有检查任意修改它。这会动摇封装的根本。
错误 2:Setter 不进行数据校验。
如果 setter 只是简单赋值而不检查,你可能会得到不正确的对象状态(例如负年龄或空姓名)。
错误 3:为所有字段自动生成 setter。
IDE 可以为所有字段生成 setter,但这并不总是对的!例如标识符(id)通常不需要 setter。
错误 4:在 getter/setter 中放入复杂逻辑。
Getter 和 Setter 应当保持简洁。如果出现复杂的业务逻辑,应将其提取到单独的方法中。
错误 5:违反命名约定。
如果把 getter 命名为 fetchName() 而不是 getName(),某些框架和库将无法识别它。
GO TO FULL VERSION