随笔 - 1683  文章 - 620 评论 - 440 trackbacks - 0

故事背景

自古以来,做买卖、特别是供大于求情况下,市场游戏总会出现乙方有求于甲方的现象。

 

 

 在现在的市场经济机制下,甲方和乙方的地位更难平等,小王是深有体会。小王是一家软件外包公司的员工,他们为一家国企提供软件服务,最近小王比较烦,因需求变更,甲方大爷软件中引用的一个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

posted on 2019-09-28 08:28  一天不进步,就是退步  阅读(...)  评论(... 编辑 收藏