-
我在 Java 里用 Arrays.equals 比较两个内容明明一模一样的二维数组结果它死活返回 false,我又用 Arrays.toString 想打出来看看却只看到一堆像 [I@1b6d3586 这样的乱码,折腾很久才搞懂 Arrays.equals 和 toString 只往下看一层对数组套数组这种嵌套结构根本不会递归深入比较的深度复盘
我有两个二维数组 int[][] a=1,2},{3,4 和 b 内容完全一样要判断相不相等,自然用了 Arrays.equals。结果一连串问号:a==b false(我懂是两个对象)、a.equals(b) false(数组没重写 equals)、所以我特意用 Arrays.equals(a,b) 可它对内容完全相同的二维数组还是返回 false;退一步测一维 Arrays.equal…- 47
- 0
-
我图省事把 Java 枚举的 ordinal 序号存进数据库当状态值、跑了一年风平浪静,直到产品要在枚举中间插一个新状态,我加完一上线数据库里成千上万条记录的状态全错位了、原本是已支付的变成了已取消,排查很久才反应过来 ordinal 是按声明顺序排的我一插队后面全跟着挪了位的深度复盘
我有个订单状态枚举 OrderStatus { CREATED, PAID, SHIPPED, COMPLETED, CANCELLED },存数据库时图省事直接存它的 ordinal()(CREATED=0,PAID=1,SHIPPED=2,COMPLETED=3,CANCELLED=4),读出来用 values()[storedInt] 还原,跑了一年一切正常。直到产品要在已支付之后已发货之前…- 0
- 0
-
我在 Java 里用 subList 截了一段子列表想单独拿去处理、结果对子列表的改动竟然莫名其妙影响到了原列表,后来又遇到取完子列表往原列表里加了个元素再用那个子列表时直接抛 ConcurrentModificationException,折腾很久才搞懂 subList 返回的根本不是拷贝而是原列表的一个视图的深度复盘
我有个大列表想取其中一段单独处理,很自然写 List sub = list.subList(1, 4),以为 sub 是独立子列表、对它怎么折腾都不影响原 list,然后对 sub 改了元素、或 sub.clear()。结果两件怪事:一是只动了 sub 可 list 里对应那段也跟着变了——sub.clear() 把 list 第 1~4 个元素一起删了;二是先取了 sub 再往 list add…- 0
- 0
-
我把一个可变对象当 HashMap 的 key 存了进去、后来改了这个对象里参与 hashCode 计算的一个字段,再用它去 map 里 get 竟然返回 null,可我遍历这个 map 明明能看到这条记录还在里面,我对着这个又在又取不出来的幽灵键排查了大半天才想通问题出在哪的深度复盘
我有个实体类 Key 重写了 equals 和 hashCode、都基于它的 id 字段,把一个 Key 对象当键连着一个值 put 进了 HashMap,一切正常。后来业务逻辑里我修改了这个 Key 对象的 id(它可变、有 setter),再拿这个对象去 map.get(key) 想取回当初存的值,结果返回 null。我懵了:这个 key 没删过啊,containsKey 也是 false,可…- 0
- 0
-
我用 Optional 包装返回值、想从此优雅地告别空指针,结果代码里满是 optional.get(),线上照样抛异常崩溃,排查后才明白我只是把空指针换了个名字、根本没用上 Optional 真正的价值的深度复盘
我读了不少用 Optional 优雅处理空值告别 NPE 的文章,深以为然,把一批可能返回空的方法都改成返回 Optional,心想这下调用方就得乖乖处理空值了。可上线后异常监控里照样躺着一堆崩溃,只是异常类型从 NullPointerException 变成 NoSuchElementException。翻调用代码顿时哭笑不得:调用方拿到 Optional 几乎全是直接 .get() 取值,只是…- 0
- 0
-
我在循环里用加号一段段拼接字符串,数据少时飞快、数据一多就慢得令人发指、CPU 还飙满,排查半天才明白 Java 的字符串是不可变的、我每拼一次都在悄悄复制一遍之前拼好的全部内容的深度复盘
我有段 Java 代码要把一大批数据拼成大字符串(几万行记录拼成报表/CSV/大 JSON),写得朴素:String result = "",循环里 result += 每条数据。测试数据量小跑得飞快,我觉得再直白不过。可一上真实数据量灾难就来了:数据从几百条涨到几万条,这段拼接慢得令人发指,本该几十毫秒的事跑了几十秒、CPU 飙满。我以为是数据处理逻辑慢,打点才傻眼:慢的就是…- 0
- 0
-
我在 Java 里用 == 比较两个字符串是否相等,测试时一直好好的,可一接上用户真实输入就时灵时不灵、明明看着一模一样却判为不等,排查半天发现 == 比的根本不是内容、而字符串常量池给我演了一出好戏的深度复盘
我有段 Java 代码要判断字符串是不是等于某个值(用户输入是不是 yes、状态码是不是 OK),图顺手直接写了 if (str == "yes")。自己测试时一直工作得很好,我便心安理得用了下去。可一接上真实场景就诡异:同样是 yes 有时判相等有时判不等,两个字符串打印出来一模一样 == 却咬定不相等;更抓狂的是毫无规律,硬编码测永远没问题,可一旦字符串是从用户输入、网络文…- 2
- 0
-
我用 ThreadLocal 存当前登录用户,本以为线程私有绝不会串,结果某个用户偶尔会看到另一个用户的数据,因为在线程池里线程是复用的、用完没清的 ThreadLocal 残留给了下一个请求的深度复盘
我用 ThreadLocal 存当前登录用户(请求进来时 set、后续各层从 ThreadLocal 取,避免层层传参),想当然以为 ThreadLocal 线程私有、每个请求一个线程各存各的绝不会串。结果线上出了最可怕的 bug:偶发地某个用户页面上显示出了另一个用户的数据(订单、隐私),用户身份串了、是严重越权。排查才定位到线程池:Web 服务器用线程池处理请求、线程是复用的(处理完一个请求会…- 0
- 0
-
我在 Java 的 finally 块里写了个 return 做收尾,结果 try 块里本该抛出的异常凭空消失了、本该返回的值也被悄悄换掉了,一个本该报错的方法竟然正常返回:一次 finally 里 return 吞掉了 try 结果的深度复盘
我有个方法 try 里做主要逻辑、finally 里做收尾,图省事在 finally 里也写了 return(或 finally 里调的清理方法会抛异常)。线上出了怪事:某个本该抛异常让上层感知失败的方法竟然正常返回了;有的方法 try 里明明 return 了算好的结果、实际返回的却是另一个值;异常和正确返回值都像凭空消失了,问题被掩盖直到下游拿着错误数据出更大乱子。复盘 try-finally…- 0
- 0
-
我在 Java 里用 Arrays.asList 把数组转成 List,以为得到了一个普通的可增删的列表,结果往里 add 一个元素就抛 UnsupportedOperationException,因为它根本不是 ArrayList 而是个固定大小的数组视图的深度复盘
我想把数组转成 List 后做些增删,顺手写了 List list = Arrays.asList("a","b","c"),然后 list.add("d")——Arrays.asList 返回 List、List 当然能 add,多自然。结果一运行就崩:UnsupportedOperationException,栈指…- 0
- 0
-
我用 BigDecimal 的 equals 判断两个金额是否相等,2.0 和 2.00 明明都是两块钱,它却告诉我不相等:一次 BigDecimal.equals 把小数位也算进相等的深度复盘
我用 BigDecimal 表示金额(为了精确不用 float,这是对的),某处用 equals 判断两个金额是否相等。可线上偶发:两个数值上明明相等的金额(一个 2.0、一个 2.00,都是两块钱)equals 却返回 false,导致金额一致才放行的逻辑卡住。查清 BigDecimal.equals 才明白:它不只比数值,还比 scale(小数位数)——BigDecimal 内部用无标度值+s…- 0
- 0
-
我从 Map 里取一个计数赋给 int,平时好好的,某次那个 key 不存在、map 返回 null,自动拆箱直接抛了 NullPointerException:一次 Java 自动拆箱 NPE 的深度复盘
我有个 Map counts 存计数,某处 int count = counts.get(key) 取出来用,平时好好的,可线上偶发崩溃,异常栈指向这行 NullPointerException。这就是个取值赋值,count 是 int、get 返回 Integer,哪来的空指针?查清才明白是自动拆箱:那个 key 不存在时 counts.get(key) 返回 null,而我把它赋给 int,J…- 2
- 0
-
我重写了 equals 让两个字段相同的对象相等,把它当 HashMap 的 key 存进去,再用一个一模一样的 key 去取却拿到了 null:一次 Java 只重写 equals 没重写 hashCode 的深度复盘
我用一个自定义的 OrderKey(含 userId 和 date)当 HashMap 的 key,为了让两个字段都相同的 key 被视为同一个 key,我重写了 equals。可线上诡异:map.put(key1, value) 存进去后,用一个字段完全相同的新 key2 去 get 居然返回 null,而 key1.equals(key2) 明明是 true。查清才明白:我只重写了 equal…- 0
- 0
-
我在 for-each 遍历一个 List 的过程中顺手删了几个元素,本地跑得好好的、线上却偶发抛 ConcurrentModificationException 崩溃:一次 Java 遍历时修改集合、迭代器 fail-fast 机制的深度复盘
我写了个清理逻辑遍历订单列表、把过期的删掉,for-each 里遇到过期就 orders.remove(order)。本地几条数据跑得好好的,上线后日志偶尔蹦出 java.util.ConcurrentModificationException、清理任务直接崩了。可这是单线程循环,哪来的并发?查清才发现:这异常名有误导,单线程一边遍历一边改集合也抛;for-each 底层是迭代器,迭代器靠 mod…- 0
- 0
-
一行 int count = map.get(key) 的赋值,在 key 不存在时悄悄触发了自动拆箱、抛出莫名其妙的空指针:一次 Java 自动装箱拆箱的深度复盘
一行 int count = countMap.get(userId) 偶尔抛 NullPointerException,可这行没有任何显式的 . 方法调用,哪来的空指针?根因是 Map.get 返回 Integer、key 不存在时返回 null,而赋给 int 会触发自动拆箱(实为调 .intValue()),对 null 调 .intValue() 就 NPE——这个 .intValue()…- 0
- 0
-
一个 JVM 服务因为默认永久缓存了 DNS 解析结果,在下游域名切换 IP 后还死连着早已下线的旧地址,连接全失败:一次 DNS 缓存的深度复盘
下游第三方做机房迁移,把域名解析到新 IP、旧 IP 下线,域名没变我们本该无感,结果服务大面积连接失败,而服务器上 nslookup 查的是新 IP、重启服务就好了。根因是 JVM 有独立于 OS 的 DNS 缓存,networkaddress.cache.ttl 默认可能是 -1(永久缓存)——进程首次把域名解析成旧 IP 后就永久记住、再也不重新解析,下游换 IP 后还死连旧地址、不重启不自…- 2
- 0
-
一段在 for-each 循环里直接 remove 元素的 Java 代码,跑起来就抛 ConcurrentModificationException,我和快速失败机制正面撞上了:一次集合遍历删除的深度复盘
一段单线程的 for-each 遍历 ArrayList、在循环里 remove 满足条件的元素,一运行就抛 ConcurrentModificationException——可明明是单线程,哪来的并发?根因是增强 for 底层用迭代器遍历,迭代器有 fail-fast 机制:记录集合的 modCount,每步检查它有没有被改;而 list.remove 绕过迭代器直接改集合让 modCount …- 0
- 0
-
一个重写了 equals 却忘了重写 hashCode 的 Java 对象,放进 HashSet 后既去不了重、也再也取不出来:一次违背 equals-hashCode 契约的深度复盘
给 User 重写了 equals(id 相同即相等),放进 HashSet 却没去重、用 id 相同的 User 去 map.get 竟返回 null——东西放进去了却取不出来。根因是只重写了 equals、没重写 hashCode:两个 equals 相等的对象,hashCode 却用 Object 默认实现(基于地址)而不同,违背了'equals 相等则 hashCode 必相等&…- 0
- 0
-
静态共享一个 SimpleDateFormat 给所有线程,高并发下偶发日期错乱甚至抛异常:一次线程不安全的深度排查与 DateTimeFormatter 正解
把 SimpleDateFormat 设成 static final 给所有线程共享,平时好好的,一上高并发就偶发日期错乱、甚至 NumberFormatException——SimpleDateFormat 内部用一个可变的 Calendar 暂存中间状态,并发 format/parse 时这个共享可变状态被互相覆盖,典型的数据竞争。本文从偶发难复现的现场讲起,剖析它非线程安全的根因,给出 Da…- 0
- 0
-
我在 for-each 循环里遍历一个 List 时顺手删掉了几个元素,明明是单线程却抛了个 ConcurrentModificationException,我对着增强 for 循环底层用迭代器的 fail-fast 机制这个坑排查了大半天的复盘
一个让我对 Java 遍历和修改不能同时进行彻底记牢的坑,困惑在那个异常的名字 ConcurrentModificationException(并发修改异常),可我的代码压根是单线程的根本没并发,凭什么报并发修改?要遍历列表把满足条件的元素删掉(删已取消订单),自然用增强 for 边遍历边删 for (Order o : orders) { if (o.isCancelled()) orders.…- 2
- 0
-
我用 == 比较两个 Integer,小数值时一切正常,某天数值一超过 127 判断就突然失效了,我对着 Java 自动装箱的 Integer 缓存只缓存 -128 到 127 这个坑排查了大半天的复盘
一个堪称 Java 最阴险的坑之一,阴险在绝大多数测试数据下表现正常让你深信不疑,然后在某个数值恰好够大的真实场景里毫无征兆突然失效。我有两个 Integer 值要判断是否相等,图省事用了 ==。Integer a=100,b=100; a==b 是 true 看着没问题,测试用的都是小数值一切正常就上线了。可某天数值大了:Integer x=200,y=200; x==y 竟是 false!更精…- 0
- 0
-
我给一个类重写了 equals 判断两个对象内容相等,用它做 HashMap 的 key 存了又取,取出来的竟然永远是 null,我对着只重写 equals 不重写 hashCode 这个坑排查大半天的复盘
一个堪称 Java 面试必问、实战必踩的经典坑,隐蔽在它看起来天经地义:我明明定义了两个对象在什么情况下相等,HashMap 却像没看见一样固执地说找不到。需求是个缓存:有个坐标点类 Point,我希望只要 x、y 相同两个 Point 就算相等,用 Point 做 key 把计算结果缓存进 HashMap。为此我认真重写了 equals(x、y 相同就相等),但没重写 hashCode。结果 p…- 0
- 0
-
我自定义对象重写了 equals 判断相等,放进 HashSet 却还是有重复、用它当 HashMap 的 key 也取不到值,我对着 equals 和 hashCode 排查了大半天的复盘
写去重逻辑,自定义类 User 重写了 equals 规定 id 相同就算同一用户,把一堆 User 放进 HashSet 去重、又用 User 当 HashMap 的 key。结果诡异:id 相同的两个 User 放进 HashSet 竟没去重、用 id 相同的 User 去 HashMap get 竟取不到值返回 null。盯着 equals 反复看明明重写了 id 相同返回 true 啊为啥…- 0
- 0
-
我用 Arrays.asList 把数组转成 List 然后往里 add 元素,运行时抛了 UnsupportedOperationException,代码编译明明全过,我对着这个"只读 List"排查了大半天的复盘
一段很普通的 Java 代码:用 Arrays.asList() 把数组转成 List,然后往里 add 几个元素,编译器全程绿灯,一运行却抛 java.lang.UnsupportedOperationException。满脸问号:类型明明是 List,List 不就该能 add 吗?编译都过了凭什么运行时说不支持?排查大半天才理解 Arrays.asList() 返回的根本不是 java.ut…- 0
- 0
Java
幸运之星正在降临...
点击领取今天的签到奖励!
恭喜!您今天获得了{{mission.data.mission.credit}}积分
我的优惠劵
-
¥优惠劵使用时效:无法使用使用时效:
之前
使用时效:永久有效优惠劵ID:×
没有优惠劵可用!
























