簡而言之,之前沒有返回值的addUser()被改修改成返回一個User類的實例的方法。不過,應用的代碼沒有做任何修改,因為它沒有使用addUser()的返回值。
咋一看,com.nhn.user.UserAdmin.addUser()方法似乎仍然存在,如果存在的話,那么怎么還會出現NoSuchMethodError的錯誤呢?
原因
上面問題的原因是在于應用的代碼沒有用新的類庫來進行編譯。換句話來說,應用代碼似乎是調了正確的方法,只是沒有使用它的返回值而已。不管怎樣,編譯后的class文件表明了這個方法是有返回值的。你可以從下面的錯誤信息里看到答案。
1
|
java.lang.NoSuchMethodError: com.nhn.user.UserAdmin.addUser(Ljava/lang/String;)V |
NoSuchMethodError出現的原因是“com.nhn.user.UserAdmin.addUser(Ljava/lang/String;)V”方法找不到。注意一下”Ljava/lang/String;”和最后面的“V”。在Java字節碼的表達式里,”L;”表示的是類的實例。這里表示addUser()方法有一個java/lang/String的對象作為參數。在這個類庫里,參數沒有被改變,所以它是正常的。最后面的“V”表示這個方法的返回值。在Java字節碼的表達式里,”V”表示沒有返回值(Void)。綜上所述,上面的錯誤信息是表示有一個java.lang.String類型的參數,并且沒有返回值的com.nhn.user.UserAdmin.addUser方法沒有找到。
因為應用是用之前的類庫編譯的,所以返回值為空的方法被調用了。但是在修改后的類庫里,返回值為空的方法不存在,并且添加了一個返回值為“Lcom/nhn/user/User”的方法。因此,就出現了NoSuchMethodError。
注:
這個錯誤出現的原因是因為開發者沒有用新的類庫來重新編譯應用。不過,出現這種問題的大部分責任在于類庫的提供者。這個public的方法本來沒有返回值的,但是后來卻被修改成返回User類的實例。很明顯,方法的簽名被修改了,這也表明了這個類庫的后向兼容性被破壞了。因此,這個類庫的提供者應該告知使用者這個方法已經被改變了。
我們再回到Java字節碼上來。Java字節碼是JVM很重要的部分。JVM是模擬執行Java字節碼的一個模擬器。Java編譯器不會直接把高級語言(例如C/C++)編寫的代碼直接轉換成機器語言(CPU指令);它會把開發者可以理解的Java語言轉換成JVM能夠理解的Java字節碼。因為Java字節碼本身是平臺無關的,所以它可以在任何安裝了JVM(確切地說,是相匹配的JRE)的硬件上執行,即使是在CPU和OS都不相同的平臺上(在Windows PC上開發和編譯的字節碼可以不做任何修改就直接運行在Linux機器上)。編譯后的代碼的大小和源代碼大小基本一致,這樣就可以很容易地通過網絡來傳輸和執行編譯后的代碼。
Java class文件是一種人很難去理解的二進文件。為了便于理解它,JVM提供者提供了javap,反匯編器。使用javap產生的結果是Java匯編語言。在上面的例子中,下面的Java匯編代碼是通過javap -c對UserServiceadd()方法進行反匯編得到的。
1
2
3
4
5
6
7
|
public void add(java.lang.String); Code: 0 : aload_0 1 : getfield # 15 ; //Field admin:Lcom/nhn/user/UserAdmin; 4 : aload_1 5 : invokevirtual # 23 ; //Method com/nhn/user/UserAdmin.addUser:(Ljava/lang/String;)V 8 : return |
原文轉自:http://www.anti-gravitydesign.com