背景
最近遇到一道Java面试题, 感觉很有意思, 和大家分享一下.
是远程在线做题的, 可以使用自己的IDE.
题目
1 | private static void swap(Integer a, Integer b) { |
实际过程中(错误的)解法
分析:
第一感觉还是比较简单的, 由于Integer是不可变对象, 所以利用反射修改他们内部维护的那个’value’字段.
代码如下:
1 | private static void swap(Integer a, Integer b) { |
运行结果: a可以修改成功而b不可以.
调试
当发现下面的调试输出结果时我是有点崩溃的:1
2
3
4
5
6
7// 传入的val为1
private static void modifyViaReflection(Integer obj, int val, Field field) throws IllegalAccessException {
log.debug("before obj = {}", obj); // print 2
log.debug("val = " + val); // print 1
field.set(obj, val);
log.debug("after obj = {}", obj); // print 2 !!! (使用反射修改字段值失败)
}
当时调了好久, 明明传入的val是1, 为什么会修改不成功呢?
- 第一次修改是成功的, 第二次就不行, 调换了a和b的次序, 同样如此;
- 甚至怀疑是JDK版本的原因, 还从JDK8切换到了JDK6: 无果;
面试完之后, 继续探索:1
2
3
4
5
6
7
8// 传入的val为1
private static void modifyViaReflection(Integer obj, int val, Field field) throws IllegalAccessException {
log.debug("before obj = {}", obj); // print 2
log.debug("val = " + val); // print 1
log.debug("val = {}", val); // print 2 !!! (加了这一行调试语句后后发现了新大陆)
field.set(obj, val);
log.debug("after obj = {}", obj); // print 2 !!! (使用反射修改字段值失败)
}
正确的解法
1 | private static void modifyViaReflection(Integer obj, int val, Field field) throws IllegalAccessException { |
复盘
- 心态: 限时一小时, 总共两题, 第一题就卡主了, 有点紧张, 有点慌了.
- 知识储备: Integer的自动拆装箱, 前258位的缓存机制, 这些其实都懂, 但是做题的时候没有把这两个联系到一起;
- 调试原则: 遇到问题需要最先怀疑还是自己写的代码, 其次怀疑编译器, 操作系统, 社会环境之类的问题;
比如当时应该重点关注field.set(obj, val);
这句代码, set方法第二个参数是Object, 不是int, 是会发生自动装箱的,
当时要是能意识到这个就能很快定位到问题了.
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!