Java 实体类:什么时候走 getter、什么时候直接访问字段?

一句话结论:外部代码默认调用 getter,本类内部默认直接访问字段,再结合场景细分。


一、先分清两种访问方式

假设实体:

java

运行

public class User {
    // 私有字段
    private String username;

    // getter
    public String getUsername() {
        System.out.println("执行了 getUsername()");
        return username;
    }
    
    // setter
    public void setUsername(String username) {
        this.username = username;
    }
}
  1. 直接访问字段user.username
  2. 调用 getteruser.getUsername()

二、场景 1:类外部访问(99% 情况走 getter)

1. 普通业务代码 / 自己写的 Java 代码

字段被 private 修饰,外部根本无法直接访问,只能调用 getter

java

运行

// 外部类
User user = new User();
// user.username;  // 编译报错!private 外部不可见
user.getUsername(); // 必须走 getter

2. 框架 / 工具自动取值(最常用场景)

主流框架默认优先调用 getter,不会直接读字段:

  • Spring MVC / Spring Boot(参数绑定、返回 JSON)
  • Jackson / Fastjson / Gson(序列化 JSON)
  • MyBatis / Hibernate(ORM 数据库映射)
  • Lombok、BeanUtils、反射工具
举例:JSON 序列化

java

运行

User user = new User();
user.setUsername("张三");
// Jackson 底层调用 getUsername()
String json = new ObjectMapper().writeValueAsString(user);

控制台会打印:执行了 getUsername()

只有特殊配置,框架才会改为直接访问字段


三、场景 2:类内部访问(默认直接读字段)

User 类自己的方法里:

java

运行

public class User {
    private String username;

    public String getUsername() {
        return username;
    }

    // 本类内部方法
    public void test() {
        // 1. 直接访问字段(不走 getter)
        String s1 = this.username; 

        // 2. 主动调用 getter(才会执行方法逻辑)
        String s2 = this.getUsername();
    }
}

规则

  • 本类内写 字段名 → 直接读内存字段
  • 本类内写 getXxx() → 才会执行 getter 方法

特例:Lombok @Data / @Getter

Lombok 只是自动生成 getter/setter,访问规则不变:

  • 外部:只能 getter
  • 内部:默认直读字段

四、哪些情况框架会「直接访问字段」不走 getter?

框架可以通过配置绕过 getter,直接反射读取字段

1. Jackson 全局 / 单独配置直接访问字段

java

运行

// 类上加注解:强制序列化直接读字段,不走 getter
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY,
                getterVisibility = JsonAutoDetect.Visibility.NONE)
public class User {
    private String username;
    public String getUsername(){...}
}

2. MyBatis 配置 accessType="FIELD"

xml

<result property="username" column="name" accessType="FIELD"/>

MyBatis 会直接反射赋值 / 取值,跳过 getter/setter

3. 纯反射代码(手动反射字段)

java

运行

// 手动反射获取字段,不走 getter
Field field = User.class.getDeclaredField("username");
field.setAccessible(true);
String val = (String) field.get(user);

五、高频面试 / 踩坑点

1. getter 里加了业务逻辑(格式化、默认值、计算)

外部框架取值一定会执行这段逻辑,内部直读字段不会。

java

运行

public String getUsername() {
    return username == null ? "匿名用户" : username;
}
  • JSON 序列化、前端接收 → 看到「匿名用户」
  • 本类方法里 this.username → 拿到原始 null

2. 字段 public 公开了

java

运行

public String username;
  • 外部代码可以二选一user.usernameuser.getUsername()
  • 不推荐:破坏封装,业务混乱。

3. 继承场景

子类访问父类 private 字段:依然只能用父类 getter


六、极简总结表

表格

访问位置 默认行为
外部 普通代码 只能调用 getter(private 限制)
内部 自身方法 直接读取字段,不走 getter
JSON/ORM 等框架(默认) 调用 getter
手动反射字段 / 框架特殊配置 直接读字段,跳过 getter

补充小建议

  1. 业务逻辑不要在 getter/setter 写复杂业务,容易因「访问方式不同」出现诡异 bug;
  2. 实体字段一律 private,严格遵循 Java 封装;
  3. 如果需要统一格式化,放在业务层 / 工具类,而非 getter。

更多推荐