You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对按需初始化持有者模式的单例实例进行垃圾回收/字段清理?

实现GetItemQuery单例用于Retrofit API请求复用

既然你想通过单例模式复用GetItemQuery实例,避免每次API调用都新建对象,那静态内部类的懒加载实现是个不错的选择——它天然线程安全,还能实现懒加载(第一次调用才初始化实例)。我给你补全并优化了代码,同时附上使用示例和注意事项:

完整的GetItemQuery单例实现

public class GetItemQuery {
    // 请求数据字段
    private String itemId;
    private int numberToLoad;

    // 响应数据字段
    private Item item;

    // 私有构造方法,禁止外部直接new实例
    private GetItemQuery() {}

    // 静态内部类实现懒加载单例,JVM类加载机制保证线程安全
    private static class Loader {
        private static final GetItemQuery INSTANCE = new GetItemQuery();
    }

    // 获取全局唯一实例
    public static GetItemQuery getInstance() {
        return Loader.INSTANCE;
    }

    // 请求字段的Setter方法,用于动态设置API请求参数
    public void setItemId(String itemId) {
        this.itemId = itemId;
    }

    public void setNumberToLoad(int numberToLoad) {
        this.numberToLoad = numberToLoad;
    }

    // 请求字段的Getter方法(Retrofit可能需要通过反射读取这些字段)
    public String getItemId() {
        return itemId;
    }

    public int getNumberToLoad() {
        return numberToLoad;
    }

    // 响应字段的Setter/Getter,用于接收API返回数据
    public void setItem(Item item) {
        this.item = item;
    }

    public Item getItem() {
        return item;
    }

    // 可选但推荐:重置字段方法,避免上次请求的残留数据干扰下一次调用
    public void reset() {
        this.itemId = null;
        this.numberToLoad = 0;
        this.item = null;
    }
}

如何在Retrofit调用中使用

每次发起API请求前,先获取单例、重置字段、设置请求参数,再传入Retrofit的Call中:

// 1. 获取单例并重置字段(关键!避免旧数据残留)
GetItemQuery query = GetItemQuery.getInstance();
query.reset();

// 2. 设置本次请求的参数
query.setItemId("item_001");
query.setNumberToLoad(5);

// 3. 发起Retrofit请求
Call<GetItemQuery> call = yourApiService.getItem(query);
call.enqueue(new Callback<GetItemQuery>() {
    @Override
    public void onResponse(Call<GetItemQuery> call, Response<GetItemQuery> response) {
        if (response.isSuccessful()) {
            // 从响应中获取数据(也可以直接用response.body()获取整个实例)
            Item resultItem = response.body().getItem();
            // 处理业务逻辑...
        }
    }

    @Override
    public void onFailure(Call<GetItemQuery> call, Throwable t) {
        // 处理请求失败逻辑...
    }
});

注意事项

  • 线程安全问题:如果你的App存在多线程并发发起请求的场景,直接用全局单例可能会出现参数被覆盖的问题。解决办法有两个:
    1. 在设置参数+发起请求的代码块上加同步锁:
      synchronized (GetItemQuery.class) {
          GetItemQuery query = GetItemQuery.getInstance();
          query.reset();
          query.setItemId("item_001");
          // 发起请求...
      }
      
    2. 改用ThreadLocal为每个线程维护独立实例,彻底避免并发冲突:
      public class GetItemQuery {
          // 替换原来的Loader内部类和getInstance方法
          private static final ThreadLocal<GetItemQuery> THREAD_LOCAL = ThreadLocal.withInitial(GetItemQuery::new);
      
          public static GetItemQuery getInstance() {
              return THREAD_LOCAL.get();
          }
      
          // 可选:请求结束后移除当前线程的实例,避免内存泄漏
          public static void clear() {
              THREAD_LOCAL.remove();
          }
      
          // ... 其他字段和方法保持不变
      }
      
  • Retrofit序列化兼容:确保字段名和API的请求/响应字段一致,或者用@SerializedName注解指定映射关系(比如API字段是item_id,你的字段是itemId,就加@SerializedName("item_id"))。
  • 重置字段的必要性:一定要记得调用reset(),不然上次请求的itemId或响应数据可能会影响下一次请求的结果。

内容的提问来源于stack exchange,提问作者bycfly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:51:02