故事背景
自古以來,做買賣、特別是供大於求情況下,市場游戲總會出現乙方有求於甲方的現象。
在現在的市場經濟機制下,甲方和乙方的地位更難平等,小王是深有體會。小王是一家軟件外包公司的員工,他們為一家國企提供軟件服務,最近小王比較煩,因需求變更,甲方大爺軟件中引用的一個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