闰年判断必须显式加括号:(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0),否则因&&优先级高于||易出错;同时需校验年份范围,如year > 0。

闰年判断的数学规则必须严格对应代码逻辑
Java里写错闰年判断,不是因为不会写 if,而是把“能被4整除且不能被100整除,或能被400整除”这个条件,直接硬翻译成 year % 4 == 0 && year % 100 != 0 || year % 400 == 0 却没加括号——这会因运算符优先级出错。
实际执行时 && 优先级高于 ||,等价于 (year % 4 == 0 && year % 100 != 0) || year % 400 == 0,看起来对,但一旦有人为了“简洁”去掉括号又改顺序(比如写成 year % 400 == 0 || year % 4 == 0 && year % 100 != 0),就容易误以为可以省略括号,其实仍依赖优先级——可读性差,维护时极易翻车。
- 永远显式加括号:
(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) - 负数年份、0年、极大年份(如
Integer.MAX_VALUE)不参与闰年定义,业务中应先校验输入范围,比如year > 0 && year - Java 8+ 推荐用
java.time.Year.isLeap(),它内部已处理边界和时区无关性,比手写更可靠
用 Year.isLeap() 避开手工逻辑的所有坑
很多人坚持手写判断,是没意识到 java.time.Year 这个工具类从 Java 8 就存在,且专为年份操作设计。它不依赖系统默认时区,不涉及 Calendar 的可变状态,也不会因 GregorianCalendar 的历史补丁行为产生歧义。
常见错误是试图用 LocalDate.of(year, 2, 29) 看是否抛异常来判断——这在非闰年会直接抛 DateTimeException,属于运行时异常,不便于静态分析,也增加不必要的对象创建开销。
立即学习“Java免费学习笔记(深入)”;
- 正确写法:
boolean isLeap = Year.of(year).isLeap(); - 注意
Year.of()会校验年份范围(-999,999,999 到 999,999,999),越界抛DateTimeException,比手写更早暴露非法输入 - 如果项目还在用 Java 7 或更低版本,别强行引入
threetenbp,老老实实加括号写逻辑,比引入兼容层更轻量
测试时最容易漏掉的三个年份
只测 2000、2004、2100 是不够的。这三个年份才真正区分逻辑是否完整:
-
1900:能被4和100整除,但不能被400整除 → 不是闰年(常被误判为闰年) -
2000:能被4、100、400整除 → 是闰年(验证“或能被400整除”分支) -
2024:能被4整除、不能被100整除 → 是闰年(验证主分支)
少一个,就说明括号没包对,或者条件写反了。别用随机年份覆盖,就盯死这三个。
性能差异几乎可以忽略,但语义清晰度差很多
手写表达式和调用 Year.isLeap() 在百万次调用下,差距不到 1ms。真正在意性能的场景(比如天文历法计算引擎),早就不用 int 年份了,会用更底层的儒略日或纪元偏移。
真正影响交付质量的是:接手你代码的人,能不能一眼看懂“这里是在判断闰年”,而不是停顿三秒去括号里数 && 和 || 的结合关系。
所以不是“该不该用封装”,而是“有没有理由拒绝一个语义明确、经过 JDK 测试、还自带输入校验的标准方法”。没有合理理由。










