写了几年代码,还是会踩 ArrayIndexOutOfBoundsException 的坑
从异常信息到排查思路,梳理 Java 数组越界的常见原因和处理方式,附带一段边界检查的写法。
支持通配符SSL证书、多域名证书、IP证书。适配ACME接口, 支持Zerossl、Let's Encrypt和Google等渠道。登录已有账号
2026-07-28 07:15:44 Java Instant 时间处理
有次线上报警,同事写的接口接收一个时间字符串,说好了是 ISO 8601 格式,用 Instant.parse() 解析,自测时好好的,上线就跑不动了。翻开日志一看,DateTimeParseException。传过来的字符串是 "2023-10-21T12:34:56",末尾少了那个 Z。就差一个字符,整个链路就断了。
Instant.parse() 这个方法,对字符串格式卡得很死。它只接受一种写法:日期部分四位年、两位月、两位日,中间加一个字母 T,后面跟时间——小时、分钟、秒都必须是两位,秒后面可以带纳秒,0 到 9 位都行,最后一定要用 Z 收尾。
Z 在这里表示 UTC 时区,相当于 +00:00,但你写成 +00:00 它不认,写成 +08:00 更不行。哪怕你把 T 写成空格,或者漏掉秒,它都会直接抛异常,没得商量。
正常的解析语句很简单:
Instant instant = Instant.parse("2023-10-21T12:34:56.123456Z");
得到的就是 UTC 时间轴上的一个点。如果接收到的字符串是 "2023-10-21T12:34:56.123456+08:00",虽然这也是合法的 ISO 8601,Instant.parse 就是会失败,因为它只处理末尾带 Z 的 UTC 字面量。
遇到这种带偏移量的场景,我们一般得先转成 ZonedDateTime 再取 Instant,但这是额外处理了。
所以代码里一定要用 try-catch 把 Instant.parse() 包住,抓到 DateTimeParseException 就给一个明确的错误提示,别把堆栈直接甩给前端。
我习惯在工具类里加一层校验,比如先看一下字符串是不是以 Z 结尾,长度是否合理,不过这也是防君子不防小人的做法。
分布式系统里,很多外部 API 返回的时间戳都会严格按这种 ISO 8601 带 Z 的格式来,比如 Kafka 消息里的 create_time 字段,直接 Instant.parse() 就能拿到一个不可变的 Instant 对象。这个对象线程安全,可以在多个地方共享。
做审计日志的时候也一样,用 Instant.now() 拿当前 UTC 时间写到日志,后续用 ELK 之类工具捞出来,也能直接用 Instant.parse 还原回去,省掉日期格式化来回折腾。
还有个常见场景是拿用户输入或配置文件里的 UTC 时间字符串,转成东八区时间展示:
String input = "2023-10-21T12:34:56Z";
Instant instant = Instant.parse(input);
LocalDateTime beijingTime = instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
这里就引出一个很容易绕进去的点:Instant 和 LocalDateTime 的区别。Instant 永远是 UTC 时间轴上的一个瞬间,不带时区概念。LocalDateTime 是“年月日时分秒”这样的本地日期时间,没有时区信息,你没法直接把它对应到一个绝对时刻。要转换成带时区的时间,必须通过 ZonedDateTime 或者给一个 ZoneId。比如你把同一个 LocalDateTime 分别挂到 Asia/Shanghai 和 America/New_York,得到的 Instant 能差十几个小时。
日常开发里,如果需要记录某个事件发生的绝对时间,用 Instant;如果要展示给用户看,就转成对应时区的 ZonedDateTime 再格式化成字符串。
直接用 Instant 输出,用户看到的是 UTC 时间,国内时间得加八小时,很多人会愣一下。
还有一个几乎不用在意的细节:Instant 精度是纳秒,但操作系统时钟给不出那么高精度,多数时候微秒都够了,不影响使用。
说到底,用 Instant.parse() 只要记住三个点就够了:字符串格式必须分毫不差,末尾带 Z,中间用 T;解析必须 catch 异常;搞清楚 Instant 是不带时区的时间戳,往 LocalDateTime 转的时候一定得补上时区。这些地方没弄错,线上因为时间处理出的诡异 bug 会少很多。
从异常信息到排查思路,梳理 Java 数组越界的常见原因和处理方式,附带一段边界检查的写法。
一个Java后端在选型本地缓存时的实际对比,覆盖HashMap、Guava Cache、Caffeine和Ehcache的性能、功能与适用场景,没有标准答案,只有取舍。
记录使用 MinIO Java SDK 生成预签名 URL 的实践,包含下载和上传场景,以及一些容易出错的点。
记录一次排序 NPE 的排查和解决,顺便说下 IDEA 调试集合时 null 元素不显示的问题怎么调整。