从写Java到现在,最让我记忆犹新的,反而是些一开始觉得很聪明、后来却碰了一鼻子灰的用法。
就说这个双括号初始化吧,早年我也觉得挺优雅:
List<String> list = new ArrayList<String>() {{
add("A");
add("B");
}};直到有一次排查一个内存泄漏,发现是因为这个匿名内部类隐式持有了外部类的引用,在某个长生命周期的对象里用多了,GC都收不掉。还有序列化的时候,经常报一些莫名其妙的错误。现在除非写临时测试代码,否则一律改用List.of()或者老老实实add。
这个坑估计不少人都踩过。当时在做用户上下文传递,图省事用了ThreadLocal存登录信息:
private static final ThreadLocal<User> context = new ThreadLocal<>();
// 登录后塞进去
context.set(currentUser);
// 业务里直接取
User user = context.get();在单元测试里一切正常,一上线用了Tomcat线程池就出灵异事件——用户A偶尔会看到用户B的数据。原因就是线程被复用了,上次的ThreadLocal没清。后来学乖了,一定配套写try-finally清理:
try {
context.set(user);
doBusiness();
} finally {
context.remove(); // 这句不能少
}刚出Optional那会儿,我也喜欢到处套:
public Optional<String> findName() {
return Optional.ofNullable(someNullableValue);
}但后来看到有人这么写:
Optional<User> user = Optional.ofNullable(getUser());
if (user.isPresent()) {
// ...
}这不就是变相的if (user != null)吗?完全没发挥Optional的意图。我现在觉得,Optional的核心意义是声明“这个方法可能没有返回值”,逼着调用方处理空情况,而不是在业务逻辑里到处判断isPresent()。
以前关资源是每个Java人的噩梦:
InputStream is = null;
try {
is = new FileInputStream("file");
// 读文件
} finally {
if (is != null) {
try { is.close(); } catch (IOException e) { /* 吞了? */ }
}
}现在一句搞定:
try (InputStream is = new FileInputStream("file")) {
// 自动关,异常也不会漏
}而且从JDK 9开始,还能用已有的变量:
Reader reader = new StringReader("text");
try (reader) { // 不用再声明新变量
// ...
}这种改进才是实实在在的福利。
以前的switch写得我头疼,特别是忘了break的时候:
本文系作者在时代Java发表,未经许可,不得转载。
如有侵权,请联系nowjava@qq.com删除。