Java 里五个“看着没问题”的写法

举报
yd_236430038 发表于 2026/10/19 21:18:42 2026/10/19
【摘要】 有些代码读起来毫无破绽,编译通过,测试也通过,直到线上某天才出问题。下面这五处,我在真实项目里都见过。 一、Integer 的 == 有时候是对的Integer a = 127;Integer b = 127;System.out.println(a == b); // trueInteger c = 128;Integer d = 128;System.out.println(c ==...

有些代码读起来毫无破绽,编译通过,测试也通过,直到线上某天才出问题。下面这五处,我在真实项目里都见过。

一、Integer 的 == 有时候是对的

Integer a = 127;
Integer b = 127;
System.out.println(a == b);   // true

Integer c = 128;
Integer d = 128;
System.out.println(c == d);   // false

Integer.valueOf 会缓存 -128 到 127 之间的对象,落在这个区间里的两个变量指向同一块内存,== 自然为真。超过 127 就各建各的。

可怕的地方在于:测试数据通常很小,127 以内的值一路绿灯;等生产环境的 ID 涨到四位数,比较结果开始随机出错。

结论很简单:包装类型一律用 equals,或者干脆用 long。

二、Arrays.asList 返回的不是 ArrayList

List<String> list = Arrays.asList("a", "b", "c");
list.add("d");   // UnsupportedOperationException

它返回的是 java.util.Arrays$ArrayList,一个内部类,底层是定长数组。add、remove、clear 全部不支持。

还有第二个坑:这个 List 和原数组共享同一块内存。改 List 就是改数组,反过来也一样。

String[] arr = {"a", "b"};
List<String> list = Arrays.asList(arr);
list.set(0, "x");
System.out.println(arr[0]);   // x

要一个真正可变的 List,就包一层:new ArrayList<>(Arrays.asList(...))。

三、subList 拿到的是视图,不是拷贝

List<String> list = new ArrayList<>(List.of("a", "b", "c", "d"));
List<String> sub = list.subList(1, 3);

sub.clear();
System.out.println(list);   // [a, d]

清空子列表,原列表跟着少了两个元素。因为 subList 返回的是原列表的一个窗口,不是新数组。

更隐蔽的是:对原列表做了结构修改(增删元素)之后,再操作这个 subList,会直接抛 ConcurrentModificationException。

需要独立数据就 new ArrayList<>(list.subList(from, to)),别省这一层。

四、try-with-resources 里 return,异常会盖掉返回值

static int read() throws IOException {
    try (var in = new FileInputStream("a.txt")) {
        return 1;
    }
}

看起来返回 1。但如果 close() 抛了异常,这个 1 会被丢弃,方法直接抛异常出去。

如果是 finally 里的 return 覆盖 try 里的 return,编译器至少还会警告;try-with-resources 这种隐式关闭,连提示都没有。

处理方式有两种:让方法签名声明异常,交给调用方;或者在 try 外面记一个结果变量,关闭完成之后再返回。

五、SimpleDateFormat 不是线程安全的

private static final SimpleDateFormat FMT = new SimpleDateFormat("yyyy-MM-dd");

这行代码在单线程里跑一万次都不会错。一旦放进 Spring 的单例 Bean,被多个请求线程共用,就会出现日期解析成乱码、年份对不上、甚至抛 NumberFormatException 的情况。

它内部用一个 Calendar 字段保存中间状态,多线程并发写同一个对象,结果取决于谁先谁后。

换成 DateTimeFormatter:

private static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

DateTimeFormatter 是不可变对象,可以安全地作为静态常量共享。

最后

这五个问题的共同点,是它们都不会在编译期暴露,也不容易在单元测试里复现。真正能挡住它们的,只有两件事:知道它们存在,以及在写代码时多问一句“这行返回的到底是什么类型”。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。