一、get 和 set 方法的基础认知
1.1 定义与作用
get 方法(访问器)和 set 方法(修改器)是用于访问和修改类的私有成员变量的公共方法。在 Java 中,为了实现封装性,通常会将类的成员变量声明为 private,然后通过 public 的 get 和 set 方法来操作这些变量,从而控制变量的访问权限和修改规则。
例如一个简单的 User 类:
public class User {
private String name; // 私有成员变量
// get方法:获取name的值
public String getName() {
return name;
}
// set方法:设置name的值
public void setName(String name) {
this.name = name;
}
}
1.2 为什么需要 get 和 set 方法
直接将成员变量声明为 public 虽然可以简化代码,但会带来严重的问题:
- 无法控制变量的赋值规则(如年龄不能为负数)
- 当变量的内部实现改变时,会影响所有直接访问该变量的代码
- 破坏封装性,不符合面向对象设计原则
通过 get 和 set 方法,可以在不暴露内部实现的前提下,安全地提供变量的访问和修改通道。
二、get 和 set 方法的命名规范
遵循规范的命名方式是写出易读代码的基础,Java 中 get 和 set 方法有严格的命名约定:
2.1 基本命名规则
- get 方法:对于非布尔类型的变量,命名格式为get + 首字母大写的变量名,如getName()、getAge()
- set 方法:命名格式为set + 首字母大写的变量名,如setName()、setAge()
- 布尔类型特殊处理:对于布尔类型的变量,get 方法通常命名为is + 首字母大写的变量名,如isStudent()、isActive()
2.2 命名示例
public class Person {
private String name;
private int age;
private boolean married;
// 正确的get/set命名
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; }
public boolean isMarried() { return married; } // 布尔类型使用is前缀
public void setMarried(boolean married) { this.married = married; }
}
三、get 和 set 方法的实现细节
3.1 参数与返回值
- set 方法:通常返回 void,参数类型与对应的成员变量类型一致
- get 方法:没有参数,返回值类型与对应的成员变量类型一致
3.2 方法体中的常见操作
在实际开发中,get 和 set 方法的方法体往往不只是简单的赋值和返回,还可能包含:
3.2.1 参数验证
set 方法中可以对传入的参数进行合法性验证,避免无效值:
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年龄必须在0-150之间");
}
this.age = age;
}
3.2.2 触发事件
当变量值改变时,可以通过 set 方法触发特定的业务逻辑或事件:
private String address;
public void setAddress(String address) {
String oldAddress = this.address;
this.address = address;
// 地址改变时发送通知
if (!Objects.equals(oldAddress, address)) {
notifyAddressChanged();
}
}private void notifyAddressChanged() {
// 发送通知的业务逻辑}
3.2.3 延迟加载
get 方法中可以实现延迟加载,在真正需要使用变量时才初始化:
private List<String> hobbies;
public List<String> getHobbies() {
if (hobbies == null) {
hobbies = new ArrayList<>(); // 延迟初始化
}
return hobbies;
}
四、get 和 set 方法的使用场景
4.1 JavaBean 规范
get 和 set 方法是 JavaBean 规范的核心要求之一。JavaBean 要求:
- 类必须是公共的(public)
- 有一个无参的公共构造方法
- 通过 get/set 方法访问成员变量
遵循 JavaBean 规范的类可以被各种框架(如 Spring、Hibernate)自动处理,便于对象的序列化、反序列化和属性操作。
4.2 序列化与反序列化
在 JSON 序列化(如 Jackson、Gson)和 XML 序列化过程中,框架通常通过反射调用 get 和 set 方法来读写对象的属性。即使成员变量被标记为 transient,只要提供了 get 方法,依然可以被序列化。
4.3 框架集成
许多 Java 框架(如 Spring、MyBatis)依赖 get 和 set 方法进行属性注入和数据绑定。例如 Spring 的依赖注入和 MyBatis 的结果集映射,都需要通过 get/set 方法操作对象属性。
五、使用 get 和 set 方法的注意事项
5.1 不要过度使用
并非所有的成员变量都需要提供 get 和 set 方法:
- 只用于内部计算的临时变量,不需要对外暴露 get/set 方法
- 常量(final 修饰的变量)通常只需要 get 方法,不需要 set 方法
- 对于不希望被修改的变量,可以只提供 get 方法
5.2 避免在 get/set 方法中执行耗时操作
get 和 set 方法应该保持简洁高效,避免在其中执行复杂的计算或 IO 操作,否则会:
- 降低代码可读性(使用者通常认为 get/set 是轻量级操作)
- 影响性能,特别是在循环中调用时
5.3 注意封装性
- 不要在 set 方法中返回 this 对象来实现链式调用,这会破坏封装性
- 避免在 get 方法中返回私有集合的引用,应返回副本或不可修改的视图:
private List<String> tags;
// 错误方式:返回内部集合的引用,外部可以直接修改
public List<String> getTags() {
return tags;
}
// 正确方式:返回不可修改的视图
public List<String> getTags() {
return Collections.unmodifiableList(tags);
}
5.4 注意线程安全
在多线程环境下,get 和 set 方法需要考虑线程安全问题:
- 可以使用 volatile 关键字修饰成员变量
- 复杂操作可以使用 synchronized 关键字或 Lock 接口
六、get 和 set 方法的自动化工具
手动编写 get 和 set 方法不仅繁琐,还容易出错。在实际开发中,我们可以利用工具自动生成这些方法:
6.1 IDE 自带功能
- Eclipse:右键→Source→Generate Getters and Setters
- IntelliJ IDEA:Alt+Insert→Getters and Setters
6.2 Lombok 框架
Lombok 通过注解自动生成 get 和 set 方法,极大简化代码:
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class User {
private String name;
private int age;
}
使用 Lombok 需要在项目中引入相关依赖,并在 IDE 中安装 Lombok 插件。
七、常见问题与解决方案
7.1 命名错误导致框架无法识别
如果 get/set 方法命名不规范,许多依赖反射的框架会无法正确识别属性。解决方法:
- 严格遵循命名规范
- 使用 IDE 自动生成方法,避免手动输入错误
7.2 子类覆盖父类的 get/set 方法
子类覆盖父类的 get/set 方法时,需要注意返回值类型和参数类型必须兼容。在 Java 5 + 中,可以使用协变返回类型:
public class Parent {
public Number getValue() {
return 0;
}
}
public class Child extends Parent {
@Override
public Integer getValue() { // 协变返回类型,合法
return 1;
}
}
7.3 循环依赖问题
在 get/set 方法中调用其他对象的方法时,要避免形成循环依赖,否则可能导致栈溢出或死锁。
八、总结
get 和 set 方法看似简单,却是 Java 开发中不可或缺的基础。正确使用 get 和 set 方法不仅能保证代码的封装性和安全性,还能提高代码的可维护性和扩展性。掌握 get 和 set 方法的规范和技巧,需要在理解封装思想的基础上,结合实际开发场景灵活运用。同时,合理利用自动化工具可以减少重复劳动,让我们更专注于核心业务逻辑的实现。