故事背景
自古以来,做买卖、特别是供大于求情况下,市场游戏总会出现乙方有求于甲方的现象。
在现在的市场经济机制下,甲方和乙方的地位更难平等,小王是深有体会。小王是一家软件外包公司的员工,他们为一家国企提供软件服务,最近小王比较烦,因需求变更,甲方大爷软件中引用的一个jar中的常量发生了变化,他们更新了jar包,但甲方只同意将新jar包替换掉旧的jar包,导致系统出现执行异常!甲方限令必须尽快找到问题并解决掉!
为了防止公司信息泄露,我们模拟一下这个场景:
public class BinaryCompatibilityTest { public static void main(String[] args) { System.out.println(DefineConstants.FIRST + " " + DefineConstants.SECOND + " " + DefineConstants.THIRD); } }
其中DefineConstants来自甲方对乙方的引用:
import com.test.constants.Words; public class DefineConstants { private DefineConstants() { }; // Uninstantiable public static final String FIRST = Words.FIRST; public static final String SECOND = Words.SECOND; public static final String THIRD = Words.THIRD; }
其中,Words是引用的公用jar包
类实现如下:
package com.test.constants; public class Words { private Words() { }; // Uninstantiable public static final String FIRST = "the"; public static final String SECOND = null; public static final String THIRD = "set"; }
原先打印结果为
the null set
现在乙方小王修改了jar包后,代码变成了
package com.test.constants; public class Words { private Words() { }; // Uninstantiable public static final String FIRST = "physics"; public static final String SECOND = "chemistry"; public static final String THIRD = "biology"; }
他将重新打包后的jar包传给甲方,让甲方在tomcat上替换原来的jar包,结果运行后打印的结果却为:
the chemistry set
小王百思不得其解。
反复确认了jar包是否正确,都是最新的jar包。
万般无奈之下只好请出被辞退的中老年技术大神"老司机",并答应老司机1w/d的辛苦费。
老司机了解了情况后,就找到了原因,通过jd-gui反编译了代码给小王看:
替换了jar包后,DefineConstants并没有被重新编译,导致FIRST和THIRD的结果没有发生改变,
但因SECOND本身为null,在编译期常量表达式(compile-time constant expression)[JLS15.28]的精确定义中找到。它的定义太长了,就不在这里写出来了,但是理解这
个程序的行为的关键是null 不是一个编译期常量表达式。运行时就会执行新的结果:chemistry
解决办法是
1. 需要重新编译DefineConstants后,替换到新的class
2.重新编译整个项目的打包文件,提供新的包文件替换旧的打包文件
第一个方案
优点: 线上改动小,影响小,速度快
缺点:只能解决当前问题,如果项目中还有别的地方引用这个变量,将还会出错。
第二个方案
优点:从根本上解决问题
缺点:线上影响稍微大一些。
小王入司刚两年,是个勤奋好学的家伙,项目搞定后请老司机吃饭喝酒,趁老司机酒醉,趁机问解决这个问题的诀窍,老司机喝迷糊后道出了本质:
原来java考虑到升级的问题,有二进制兼容性规范,。。。。。。。。。
因老司机喝的有点多,描述的不是很清楚,小王只记住了在jsl规范了有明确的描述:jsl 13章,https://docs.oracle.com/javase/specs/jls/se12/html/index.html
参考资料:
【1】http://blog.sina.com.cn/s/blog_4c408e27010009ae.html
【2】java解惑
【3】https://docs.oracle.com/javase/specs/jls/se12/html/index.html
【4】https://docs.oracle.com/javase/specs/jls/se12/html/jls-15.html#jls-15.28