泛型方法写法全面辨析:从「能编译」到「能跑」到「能用」

文章目录

  • 泛型方法写法全面辨析:从「能编译」到「能跑」到「能用」
    • 背景:我们要写的是这样一个方法
    • 一、先建立判断框架(四步法)
    • 二、总览表(先看全貌)
    • 三、缺少 static 的后果(A 系列)
      • A1:`public Result<T> success(T data)` ------ 能编译,但工厂废了
      • A2:`public <T> Result<T> success(T data)` ------ 鸡生蛋 + 遮蔽
      • A3:`public <U> Result<U> success(U data)` ------ 鸡生蛋但不遮蔽
      • A 系列小结
    • 四、static 方法碰「类的 T」的后果(B 系列)
      • B1:`public static Result<T> success(T data)` ------ 编译直接报错
      • B2:`public static Result<T> success()` ------ 同样报错
      • B3:`private static T field;` ------ 静态字段也不能用类的 T
      • B 系列一句话法则
    • 五、类型参数声明/使用不当(C、D 系列)
      • C1:`public static <T> Result<U> success(T data)` ------ U 未声明
      • D1:`public static <T, U> Result<U> success(T data)` ------ 声明全了,但转换不安全
    • 六、能编译但语义不完美的写法(E、F 系列)
      • E1:`public static <T, U> Result<U> success(U data)` ------ 能跑,但 T 冗余
      • E2:`public static <T> Result<T> success(String data)` ------ T 和参数无关
      • F1:`public static Result success(T data)` ------ 编译不过
      • F2:`public static <T> Result<?> success(T data)` ------ 返回通配符
    • 七、错误分类总表
    • 八、正确写法逐项过关
    • 九、类型推断的两条路(为什么正解能自动推断)
      • 9.1 路 1:从实参推断(有参数时)
      • 9.2 路 2:从目标类型推断(Java 8+,无参数时)
      • 9.3 两条路都失败时,退回 Object
    • 十、口诀、排查清单与面试速查
      • 10.1 三句话总结
      • 10.2 写泛型方法时的排查清单
      • 10.3 速查
      • 10.4 三段代码记住所有区别
    • 十一、完整正确代码(本项目 Result 实现)

泛型方法写法全面辨析:从「能编译」到「能跑」到「能用」

本文以本项目 Result 的 success() 静态工厂方法为样本,穷举泛型方法的各种写法,讲清「能编译 / 能调用 / 语义正确」三个层次的区别。读完这篇,再遇到「要不要 static、要不要写 <T>、写 <T> 还是 <U>」这类问题,都能自己判断。

背景:我们要写的是这样一个方法

Result<T> 是一个泛型类,构造器是 private 的,对外只提供静态工厂方法:

java 复制代码
public class Result<T> {
    private Integer code;
    private String message;
    private T data;                       // ← 这里的 T 是「类的 T」

    private Result(Integer code, String message, T data) { ... }   // private 构造器

    public static <T> Result<T> success(T data) { ... }            // 我们要研究的方法
}

一个看似简单的 success,写法却有十几种变体,有的编译不过,有的能编译却调用不了,有的能调用却埋着运行期的雷。下面逐一辨析。


一、先建立判断框架(四步法)

拿到任意一个泛型方法写法,按这四步走:

复制代码
① 它是 static 吗?                 → 决定能不能碰「类的 T」
② 方法签名 <> 里声明了哪些类型参数?  → 决定方法内哪些 T 被遮蔽
③ 返回类型/参数类型用的是哪个 T?     → 决定类型之间的关系
④ 这些类型参数之间有关联吗?         → 决定调用时能否自动推断

后面每一种写法,都是在这四步里的某一步踩了坑。


二、总览表(先看全貌)

编号 写法 编译 能调用 语义正确 说明
A1 public Result<T> success(T data) ✅ ❌ ❌ 鸡生蛋(实例方法,外部无对象可调)
A2 public <T> Result<T> success(T data) ✅ ❌ ❌ 鸡生蛋 + 遮蔽
A3 public <U> Result<U> success(U data) ✅ ❌ ❌ 鸡生蛋(避开遮蔽仍不可调用)
B1 public static Result<T> success(T data) ❌ --- --- static 引用「类的 T」,编译报错
B2 public static Result<T> success() ❌ --- --- 同上
B3 private static T field; ❌ --- --- 静态字段不能用「类的 T」
C1 public static <T> Result<U> success(T data) ❌ --- --- U 未声明
D1 public static <T,U> Result<U> success(T data) ❌ --- --- T 不能转 U;强转则运行期埋雷
E1 public static <T,U> Result<U> success(U data) ✅ ✅ ⚠️ T 冗余
E2 public static <T> Result<T> success(String data) ✅ ⚠️ ⚠️ T 只能靠目标类型推断
F1 public static Result success(T data) ❌ --- --- static 里用了「类的 T」,编译报错
F2 public static <T> Result<?> success(T data) ✅ ⚠️ ⚠️ 返回通配符,调用方拿不到具体类型
✅ public static <T> Result<T> success(T data) ✅ ✅ ✅ 正解

图例:❌ = 编译直接报错 ⚠️ = 能编译但有隐患/语义不对 ✅ = 正确


三、缺少 static 的后果(A 系列)

A1:public Result<T> success(T data) ------ 能编译,但工厂废了

java 复制代码
public class Result<T> {
    private Result(Integer code, String message, T data) { ... }  // private!
    public Result<T> success(T data) {
        return new Result<>(200, "ok", data);
    }
}

编译 :✅ 通过。 能用吗:❌ 不能。原因是「鸡生蛋死锁」:

复制代码
要调用 success(data)  → 得先有一个 Result 对象
要有 Result 对象      → 得先 new
要 new                → 构造器 private,外部做不到
→ 外部写不出一行调用代码,方法废了

即使把构造器改成 public,也得先造一个「跳板对象」才能调用:

java 复制代码
Result<String> r0 = new Result<>(200, "ok", null);  // 先造一个跳板
Result<String> r1 = r0.success("abc");              // 再用它调 ------ 流程荒谬

判断依据:工厂方法的职责是「从无到有创建对象」,而实例方法的前提是「对象已存在」------两者天生矛盾。

类比:实例方法 =「我有一台面包机,用它烤面包」(得先有面包机);静态方法 =「直接去面包厂买面包」(不需要先有机器)。

A2:public <T> Result<T> success(T data) ------ 鸡生蛋 + 遮蔽

java 复制代码
public class Result<T> {
    private T data;                        // 这里的 T 是「类的 T」,记作 T1
    public <T> Result<T> success(T data) { // 方法自己声明了 <T>,记作 T2
        this.data = data;                  // ❌ 编译报错
        return new Result<>(200, "ok", data);
    }
}

为什么 this.data = data 报错 ?因为发生了遮蔽(Shadowing) :方法签名一旦声明 <T>,从签名开始,方法内所有 T 都指「方法的 T2」,类的 T1 被藏起来了。于是:

  • this.data 的类型是 T1(类字段)
  • 参数 data 的类型是 T2(方法声明)
  • T2 不能赋给 T1 → 编译报错

作用域示意:

复制代码
类体作用域(T = T1):
│
├── private T data;              ← T1(此时方法 T 还没生效)
│
├── public <T> Result<T> ...     ← 从这一刻起 T2 生效!
│            │
│            ├── Result<T>       ← T2(不是 T1!)
│            ├── T data          ← T2
│            └── 方法体内所有 T  ← T2
│
└── 方法结束后,恢复 T = T1

关键认知 :你以为返回类型 Result<T> 里的 T 是「类的 T1」?错,是方法的 T2 。方法签名的 <> 一旦声明了 T,从那里开始方法内所有 T 都指向方法的 T。

能用吗:❌ 还是不能------鸡生蛋问题依旧(同 A1)。

A3:public <U> Result<U> success(U data) ------ 鸡生蛋但不遮蔽

java 复制代码
public class Result<T> {
    public <U> Result<U> success(U data) { ... }  // U 和类的 T 不同名,没有遮蔽
}

编译 :✅ 通过。 能用吗:❌ 不能(鸡生蛋,同 A1)。

教学意义 :它证明了「去掉 static 才是根本问题」------你避开了遮蔽也没用,鸡生蛋问题依然让方法无法被调用。说明遮蔽是次要矛盾,static 是主要矛盾。

A 系列小结

写法 编译 能调用 主要问题
A1 ✅ ❌ 鸡生蛋
A2 ✅ ❌ 鸡生蛋 + 遮蔽(this.data = data 编译错)
A3 ✅ ❌ 鸡生蛋

核心结论 :「造对象的方法」必须在「没有对象时」就能调用 → 必须 static。这是硬性要求,不是优化建议。


四、static 方法碰「类的 T」的后果(B 系列)

B1:public static Result<T> success(T data) ------ 编译直接报错

java 复制代码
public class Result<T> {
    public static Result<T> success(T data) { ... }
    //                  ↑        ↑ 两处都用「类的 T」
}

编译错误:

复制代码
error: non-static type variable T cannot be referenced from a static context
(非静态类型变量 T 不能在静态上下文中被引用)

根本原因:static 方法不属于任何对象,而「类的 T」是「每个对象自己的标签」。推演一下就懂了:

java 复制代码
Result.success("张三");   // 此时 T 该是 String
Result.success(123);      // 此时 T 该是 Integer
Result.success(user);     // 此时 T 该是 User

static 方法只有一份代码,它不依附任何对象、读不到任何对象的 T → 无从确定该用哪个 → 编译器直接禁止。

类比:类的 T = 每个员工工位上的铭牌;实例方法 =「走到工位读他的铭牌」✅;static 方法 =「公司门口大喇叭广播」,不属于任何工位,读不到任何人的铭牌 ❌。

B2:public static Result<T> success() ------ 同样报错

和「正确写法」只差一个 <T> 的位置:

java 复制代码
public static <T> Result<T> success()   // ✅ <T> 在返回类型【前面】→ 声明
public static Result<T> success()       // ❌ <T> 只在返回类型【里面】→ 使用(但没地方声明)

初学者最容易忽略 :<T> 的位置决定了它是「声明」还是「使用」。写在返回类型前面才是声明一个方法级类型参数。

B3:private static T field; ------ 静态字段也不能用类的 T

java 复制代码
public class Result<T> {
    private static T field;   // ❌ non-static type variable T cannot be referenced from a static context
}

原因完全一样:static 字段属于类,「类的 T」属于对象;静态上下文里没有「对象」,自然没有「对象的 T」。

所以静态字段只能硬编码成某个具体类型:

java 复制代码
private static final Result<String> CACHE = ...;   // ✅ 用具体类型
private static final Result<T>      CACHE = ...;   // ❌ 编译错,static 字段不能用类的 T

B 系列一句话法则

凡是 static 修饰的成员(方法、字段),都不能引用「类的 T」。要么在 static 方法内自己声明 <T>,要么用具体类型。


五、类型参数声明/使用不当(C、D 系列)

C1:public static <T> Result<U> success(T data) ------ U 未声明

java 复制代码
public static <T> Result<U> success(T data) {
//            ↑声明了 T      ↑用了 U(没声明!)
    return new Result<>(200, "ok", data);
}

编译错误 :error: cannot find symbol symbol: class U

规则 :泛型类型参数必须先声明、后使用 。<> 是「声明清单」,声明了几个才能用几个。作用域从 < 开始到方法体结束;作用域外想用,得重新声明。

D1:public static <T, U> Result<U> success(T data) ------ 声明全了,但转换不安全

java 复制代码
public static <T, U> Result<U> success(T data) {
    return new Result<>(200, "ok", data);   // ❌ error: incompatible types: T cannot be converted to U
}

参数是 T,构造器需要 U,而 T 不一定是 U,编译器不敢让你塞。

有人会这样「绕过」编译错误:

java 复制代码
public static <T, U> Result<U> success(T data) {
    return new Result<>(200, "ok", (U) data);   // 强转,编译通过(有 unchecked 警告)
}

但埋下了运行期的雷:

java 复制代码
Result<Integer> r = Result.success("张三");
// 编译器推断:T=String(实参决定),U=Integer(目标类型决定)
// 方法内 (U) data 的意图是 (Integer) "张三"

它到底什么时候炸? 由于泛型擦除 ,方法内的 (U) data 在字节码里其实是 (Object) data,这一步不会抛异常 ,一个「data 实际装着 String」的 Result<Integer> 就这么返回了。真正的 ClassCastException 发生在之后取值时:

java 复制代码
Integer x = r.getData();   // 编译器在此处插入 checkcast Integer → 💥 ClassCastException

结论:这种「进 ≠ 出」的写法编译期不报错,运行期取值时才炸,比 C1 更隐蔽。

为什么正解 <T> Result<T> 是安全的?

java 复制代码
public static <T> Result<T> success(T data) {
    return new Result<>(..., data);
}
//     ↑声明 T   ↑返回装 T 的盒子   ↑盒子里装的就是 T
// 只有一个 T 贯穿头尾:进什么类型,出什么类型,严丝合缝,不需要强转,也不会炸

记忆口诀:

复制代码
进 == 出(同一个 T) → ✅ 安全
进 != 出            → ❌ 必须强转 → 运行期取值时可能炸

六、能编译但语义不完美的写法(E、F 系列)

E1:public static <T, U> Result<U> success(U data) ------ 能跑,但 T 冗余

java 复制代码
public static <T, U> Result<U> success(U data) {
    return new Result<>(200, "ok", data);   // ✅ 编译通过
}
Result<String> r = Result.success("abc");   // U=String(实参决定),T 从哪都推断不出 → Object

问题 :T 完全没用到,是纯粹的废话。要显式指定 T 得写 Result.<Integer, String>success("abc"),但 T=Integer 毫无意义。

对比正解:

java 复制代码
public static <T> Result<T> success(T data)   // 一个 T 就够,干净利落

结论 :这不是「错误」,是「冗余」。类型参数用不到就不该声明。判断依据:看它是否真的出现在参数或返回值里。

E2:public static <T> Result<T> success(String data) ------ T 和参数无关

java 复制代码
public static <T> Result<T> success(String data) {
    return new Result<>(200, "ok", null);   // ✅ 编译通过(data 甚至没用到)
}
Result<String> r = Result.success("abc");   // ✅ 靠【目标类型】推断 T=String
var r = Result.success("abc");              // ⚠️ T 推断为 Object,r 是 Result<Object>

问题 :T 完全由调用方的目标类型推断,实参 "abc" 反而帮不上忙,违背了「入参和返回值类型一致」的直觉。

典型误用:

java 复制代码
Result<Integer> r = Result.success("abc");
// T 被指定为 Integer,但方法里 data 塞的是 null
// 结果 r.getData() 拿到 null;若方法真把 "abc" 塞进去,取值时又会 ClassCastException

结论 :类型参数 T 必须出现在参数里或某个能推断的地方,否则编译器推断能力受限、语义反直觉。这是设计问题,不是语法错误。

F1:public static Result success(T data) ------ 编译不过

java 复制代码
public static Result success(T data) {   // 返回裸类型 Result + 参数用了「类的 T」
    ...
}

这个写法有两个问题:

  1. 返回类型 Result 是「原始类型(raw type)」,丢失所有泛型信息;
  2. 参数 T data 里的 T 是「类的 T」,而这是 static 方法 → 触发和 B1 完全相同的错误 non-static type variable T cannot be referenced from a static context,编译不通过。

要「合法地写裸类型」,必须把参数里的 T 去掉、改用 Object:

java 复制代码
public static Result success(Object data) {   // ✅ 编译通过(有 unchecked 警告)
    return new Result<>(200, "ok", data);
}
Result r = Result.success("abc");   // r 是裸类型,泛型信息全丢
Object o = r.getData();             // 只能拿到 Object
String s = (String) o;              // ⚠️ 又回到「必须强转」的原始状态

结论:裸类型 = 放弃泛型的所有好处,退回没有泛型的时代。不要这么写。

F2:public static <T> Result<?> success(T data) ------ 返回通配符

java 复制代码
public static <T> Result<?> success(T data) {
    return new Result<>(200, "ok", data);   // ✅ 编译通过(Result<T> 可赋给 Result<?>)
}
Result<?> r = Result.success("abc");        // ✅ 能接住,但只能当「未知类型」
Object o = r.getData();                     // 只能拿到 Object
String s = (String) r.getData();            // ⚠️ 又得强转

Result<String> r2 = Result.success("abc");  // ❌ 编译报错:Result<?> 不能赋给 Result<String>

问题 :返回 Result<?> 的意思是「返回一个装了某未知类型东西的盒子」。调用方拿到后只能当「未知」处理,拿不到具体类型,泛型的价值大打折扣(这是泛型的不变性 :Result<?> 与 Result<String> 互不兼容)。

复制代码
<T> Result<T>  → 「我告诉你装了 T」   → 调用方享受类型安全 ✅
<T> Result<?>  → 「我不告诉你装了什么」→ 调用方只能当 Object ⚠️

通配符 ? 应该用在「方法参数接收未知类型」的场景(如 List<?>),不该用在返回类型上。


七、错误分类总表

类别 编号 根本问题 判断方法
缺少 static A1 A2 A3 工厂方法必须能在「无对象时」调用 能否 Result.success(...) 直接调?
static 碰类的 T B1 B2 B3 静态上下文读不到「对象的 T」 static 后面是否直接用了类的 T?
类型参数没用对 C1 D1 声明 vs 使用不一致,或类型转换不安全 <T,U> 里的 U 是否真的能安全转换?
类型参数冗余/推断受限 E1 E2 类型参数没出现在关键位置 T 是否出现在参数或返回值里?
泛型退化 F1 F2 裸类型或通配符返回 返回类型是否是确定的 Result<T>?

八、正确写法逐项过关

java 复制代码
public static <T> Result<T> success(T data) {
    return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
}
检查项 结果
是 static 吗? ✅ 是 → 可以在无对象时调用(工厂职责)
static 碰「类的 T」吗? ✅ 没有 → 方法自己声明了 <T>
声明的 T 和参数/返回值匹配吗? ✅ 参数是 T、返回 Result<T>,同一个 T
有遮蔽问题吗? ⚠️ 有,但是「良性遮蔽」------类的 T 在 static 里本就不可用,遮蔽反而避免了混乱
类型推断可行吗? ✅ 实参 + 目标类型两条路都能推断

唯一的「瑕疵」是方法的 <T> 和类的 <T> 同名,但因为是 static 方法、类的 T 本就不可用,所以遮蔽是自然而然的。这也是 JDK 和各大框架的通用写法:Optional.of()、Stream.of()、Collections.emptyList() 都是这个模式。


九、类型推断的两条路(为什么正解能自动推断)

9.1 路 1:从实参推断(有参数时)

java 复制代码
public static <T> Result<T> success(T data) { ... }

Result<User> r = Result.success(user);   // 实参 user 是 User → T = User

因为参数就是 T data,实参类型直接决定 T。

9.2 路 2:从目标类型推断(Java 8+,无参数时)

java 复制代码
public static <T> Result<T> success() { ... }

Result<String> r = Result.success();   // 看左边声明了 Result<String> → T = String

编译器看左边变量的声明类型,反推 T,这叫目标类型推断。

9.3 两条路都失败时,退回 Object

java 复制代码
var r = Result.success();     // 没有目标类型 → T 推断为 Object
Result r = Result.success();  // 裸类型(警告)

启示 :无参调用 success() 时,最好显式声明接收变量的类型 (如 Result<String> r = Result.success();),给 T 一个明确的推断目标,避免它被推断成 Object 而丢失类型信息。


十、口诀、排查清单与面试速查

10.1 三句话总结

复制代码
① static 是工厂的「身份证」:造对象的方法必须 static,否则鸡生蛋。
② static 里不认「类的 T」  :必须自己声明 <T>,声明了才能用。
③ 进 == 出 才安全         :参数类型和返回类型是同一个 T,否则强转必埋雷。

10.2 写泛型方法时的排查清单

  1. 这个方法是「造对象」吗?→ 是则必须 static。
  2. 是 static 吗?→ 是则签名必须自己声明 <T>,不能裸用类的 T。
  3. <> 里声明的类型参数,方法体里用到的每个大写字母都声明了吗?
  4. 参数类型和返回类型是同一个 T 吗?→ 不是则想想转换是否安全(多半要强转埋雷)。
  5. T 出现在参数里了吗?→ 只出现在返回类型则推断受限(E2 问题)。
  6. 返回类型是 Result<T> 还是 Result<?> / 裸 Result?→ 只有 Result<T> 能让调用方享受类型安全。

10.3 速查

问题 答案
为什么工厂方法必须 static? 造对象时还没有对象可依附,只有 static 能在无对象时被调用
static 方法里为什么不能用类的 T? static 不依附对象,读不到「某个对象的 T」,编译器禁止
<T> 放返回类型前还是后? 必须在前 :public static <T> Result<T> ...(前=声明,里=使用)
方法 T 和类 T 同名会怎样? 方法内类的 T 被遮蔽,方法内所有 T 都指方法的 T
<T,U> Result<U> success(T data) 能编译吗? 不能,T 不能转 U;强转后运行期取值时会 ClassCastException
为什么强转 (U)data 不当场报错? 泛型擦除后 (U) 变成 (Object),异常推迟到取值处的 checkcast

10.4 三段代码记住所有区别

java 复制代码
// ❌ 错在没 static(鸡生蛋)
public Result<T> success(T data)

// ❌ 错在 static 直接碰类的 T(静态上下文)
public static Result<T> success(T data)

// ✅ 对:static + 自己声明 T + 参数和返回值用同一个 T
public static <T> Result<T> success(T data)

十一、完整正确代码(本项目 Result 实现)

把上面所有「正解」落实到实际代码,就是本项目的 Result.java:

java 复制代码
package com.mall.common;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.io.Serializable;

@Data
public class Result<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    private Integer code;
    private String message;
    private T data;
    private Long timestamp;

    private Result(Integer code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
        this.timestamp = System.currentTimeMillis();
    }

    /* ---------------- 成功 ---------------- */
    public static <T> Result<T> success() {
        return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), null);
    }
    public static <T> Result<T> success(T data) {
        return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
    }
    public static <T> Result<T> success(String message, T data) {
        return new Result<>(ResultCode.SUCCESS.getCode(), message, data);
    }
    public static <T> Result<T> success(ResultCode resultCode, T data) {
        return new Result<>(resultCode.getCode(), resultCode.getMessage(), data);
    }

    /* ---------------- 失败 ---------------- */
    public static <T> Result<T> error() {
        return new Result<>(ResultCode.ERROR.getCode(), ResultCode.ERROR.getMessage(), null);
    }
    public static <T> Result<T> error(String message) {
        return new Result<>(ResultCode.ERROR.getCode(), message, null);
    }
    public static <T> Result<T> error(Integer code, String message) {
        return new Result<>(code, message, null);
    }
    public static <T> Result<T> error(ResultCode resultCode) {
        return new Result<>(resultCode.getCode(), resultCode.getMessage(), null);
    }
    public static <T> Result<T> error(ResultCode resultCode, T data) {
        return new Result<>(resultCode.getCode(), resultCode.getMessage(), data);
    }
    public static <T> Result<T> error(Integer code, String message, T data) {
        return new Result<>(code, message, data);
    }

    /* ---------------- 便捷判断 ---------------- */
    // 加 @JsonIgnore:否则 Jackson 会把 isXxx() 当成属性序列化进 JSON,多出 success/error 字段
    @JsonIgnore
    public boolean isSuccess() {
        return ResultCode.SUCCESS.getCode().equals(this.code);
    }
    @JsonIgnore
    public boolean isError() {
        return !isSuccess();
    }

    public static <T> Result<T> judge(boolean flag) {
        return flag ? success() : error();
    }
}

每一个静态方法都符合第 1 节的「四步框架」,也都属于总览表里的「✅ 正解」。以后遇到任何泛型方法,按第 10 节的排查清单过一遍,就能自己判断对错。

相关推荐
LGL6030A26 分钟前
Java学习历程31——初识Spring Boot
java·spring boot·学习
Lansonli2 小时前
大数据Spark(八十八):Spark任务资源之Standalone集群测试
java·服务器·spark
budingxiaomoli3 小时前
Spring AOP
java·spring·java-ee
特立独行的猫A3 小时前
Ionic + 仓颉语言的 cj-tauri 开发框架实战:开发精美移动端 APP
java·后端·python
nice_lcj5203 小时前
LRU 缓存入门:它是什么、怎么用、面试怎么手写
java·数据结构·缓存·面试·lru
文丑颜不良啊4 小时前
LeetCode2741~2760题目解析
java·leetcode
金銀銅鐵13 小时前
带您看懂字节码指令(二):空循环里有什么
java·后端·ai编程
吴名氏.15 小时前
Java转AI Agent教程(第十三周)混合检索与重排
java·数据库·人工智能·ai·ai agent
.痞老板.16 小时前
java ee--多线程(进阶)
java·java-ee