
<T> 那么简单多数人只会用泛型,不懂泛型擦除带来的陷阱。
陷阱1:运行时无法获取泛型类型
javaList<String> list = new ArrayList<>();
// ❌ 运行时 list.getClass() 返回 ArrayList,不是 List<String>
// 泛型信息在编译后被擦除,反射拿不到陷阱2:泛型不能创建实例
java// ❌ 编译报错
T obj = new T();
// ✅ 正确做法:传 Class 对象
public <T> T createInstance(Class<T> clazz) throws Exception {
return clazz.getDeclaredConstructor().newInstance();
}陷阱3:通配符 ? extends vs ? super
这是 PECS 原则的核心:
场景 | 用什么 | 记忆口诀 |
|---|---|---|
只读取(消费) | ? extends T | Producer Extends |
只写入(生产) | ? super T | Consumer Super |
java// 只能读,不能加元素
List<? extends Number> nums = new ArrayList<Integer>();
nums.add(1); // ❌ 编译报错
// 只能加,读出来是 Object
List<? super Integer> ints = new ArrayList<Number>();
Object o = ints.get(0); // ✅ 只能当 Object 用filter().collect()Stream 真正的威力在于流水线式处理 + 短路优化。
实战:分组 + 聚合,一行搞定
java// 按部门分组,求每个部门最高薪资
Map<String, Optional<Employee>> result = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDept,
Collectors.maxBy(Comparator.comparing(Employee::getSalary))
));避坑:Stream 只能消费一次
javaStream<String> stream = list.stream();
stream.count(); // 第一次消费
stream.forEach(System.out::println); // ❌ 报错:stream 已关闭
// ✅ 重新创建,或用 collect 缓存
List<String> cached = stream.collect(Collectors.toList());性能坑:forEach 不一定比 for 循环快
小数据量下,Stream 的装箱拆箱开销反而更大。Stream 的优势在大数据量 + 并行流:
javalist.parallelStream() // 自动多核并行
.filter(...)
.map(...)
.collect(...);但并行流不是银弹——线程切换有成本,I/O 密集型任务才值得并行。
Lambda 的本质是把行为当参数传递,这是全栈开发中事件驱动、回调处理的基础。
自定义函数式接口的正确姿势
java@FunctionalInterface
public interface Try<T, R> {
R apply(T t) throws Exception;
}
// 使用:带异常的 Lambda
Try<String, Integer> parse = s -> Integer.parseInt(s);
try {
Integer result = parse.apply("123");
} catch (Exception e) {
// 统一处理
}全栈开发中大量的回调、事件处理、链式调用,底层全靠函数式接口。不理解这个,看 Spring 源码会非常吃力。
synchronized 之外你还得知道这些1. volatile 不是锁,是可见性保证
javavolatile boolean flag = false;
// 写线程修改 flag,读线程立刻可见
// 但 flag++ 这种复合操作,volatile 保不住2. ConcurrentHashMap 比 Hashtable 强在哪?
对比项 | Hashtable | ConcurrentHashMap |
|---|---|---|
锁粒度 | 整张表一把锁 | JDK8 后分段锁(CAS+synchronized) |
允许 null | ❌ | ❌ key 和 value 都不允许 null |
性能 | 高并发下几乎不可用 | 高并发场景首选 |
3. 线程池别手动 new Thread
java// ❌ 每来一个任务新建线程,OOM 预定
ExecutorService pool = Executors.newFixedThreadPool(10);
// ✅ 核心参数背下来
ThreadPoolExecutor pool = new ThreadPoolExecutor(
5, // corePoolSize
20, // maximumPoolSize
60L, TimeUnit.SECONDS, // keepAliveTime
new LinkedBlockingQueue<>(100), // 队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);全栈开发中,接口限流、异步任务、消息队列消费,全靠线程池。参数配错,线上直接崩。
catch (Exception e) 一把梭正确做法:精确捕获 + 早抛出 + 别吞异常
java// ❌ 最差写法:吞掉异常,问题排查无从下手
try {
doSomething();
} catch (Exception e) {
// 什么都不做
}
// ✅ 正确写法:要么处理,要么包装后抛出
try {
doSomething();
} catch (IOException e) {
throw new BusinessException("文件读取失败: " + e.getMessage(), e);
}全栈开发中,异常层层上抛到统一处理层,是后端架构的基本功。
Java 的核心语法不复杂,复杂的是那些"看着简单、用着踩坑"的细节。泛型擦除、Stream 消费、volatile 可见性、线程池参数——这些才是区分"会写 Java"和"懂 Java"的分水岭。
全栈开发的根基,不在框架,在语言本身。语法吃透了,Spring、MyBatis、Netty 才能真正看懂。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。