關於Unsupported major.minor version 49.0的毛病處理方法。本站提示廣大學習愛好者:(關於Unsupported major.minor version 49.0的毛病處理方法)文章只能為提供參考,不一定能成為您想要的結果。以下是關於Unsupported major.minor version 49.0的毛病處理方法正文
在裝2個分歧版本JDK時碰到了這個成績,在網上鉤了一吧!查到一個講授比擬好的材料。
一:要處理的成績
我們在嘗鮮 JDK1.5 的時刻,信任很多人碰到過 Unsupported major.minor version 49.0 毛病,其時定會茫然手足無措。由於剛開端那會兒,網上與此相干的中文材料還不多,如今好了,網上一找就曉得是若何處理,年夜多會告知你要應用 JDK 1.4 從新編譯。那末至於為何,誰人 major.minor 畢竟為什麼物呢?這就是本篇來說的內容,以使未錯而先知。
我認為我是比擬榮幸的,由於在碰到誰人毛病之前已研讀過《深刻 Java 虛擬機》第二版,英文原書名為《Inside the Java Virtual Machine》( Second Edition),看時已知曉 major.minor 躲藏於何處,但沒有親身領會,待到與 Unsupported major.minor version 49.0 真正會見試,正好是給我驗證了一個現實。
起首我們要對 Unsupported major.minor version 49.0 樹立的直接感到是:JDK1.5 編譯出來的類不克不及在 JVM 1.4 下運轉,必需編譯成 JVM 1.4 下能運轉的類。(固然,或許你用的照樣 JVM 1.3 或 JVM 1.2,那末就要編譯成目的 JVM 能承認的類)。這也處理成績的偏向。
二:major.minor 棲息於何處
何謂 major.minor,且又居身於何處呢?先理性熟悉並找到 major.minor 來。
寫一個 Java Hello World! 代碼,然後用 JDK 1.5 的編譯器編譯成,HelloWorld.java
package com.unmi; public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } package com.unmi;public class HelloWorld{ public static void main(String[] args) { System.out.println("Hello, World!"); }}
用 JDK 1.5 的 javac -d . HelloWorld.java 編譯出來的字節碼 HelloWorld.class 用 UltraEdit 翻開來的內容如圖所示:
從上圖中我們看出來了甚麼是 major.minor version 了,它相當於一個軟件的主次版本號,只是在這裡是標識的一個 Java Class 的主版本號和次版本號,同時我們看到 minor_version 為 0x0000,major_version 為 0x0031,轉換為十制數分離為0 和 49,即 major.minor 就是 49.0 了。
三:何謂 major.minor 和何用
Class 文件的第 5-8 字節為 minor_version 和 major_version。Java class 文件格局能夠會參加新特征。class 文件格局一旦產生變更,版本號也會隨之變更。關於 JVM 來講,版本號肯定了特定的 class 文件格局,平日只要給定主版本號和一系列次版本號後,JVM 能力夠讀取 class 文件。假如 class 文件的版本號超越了 JVM 所能處置的有用規模,JVM 將不會處置該 class 文件。
在 Sun 的 JDK 1.0.2 宣布版中,JVM 完成支撐從 45.0 到 45.3 的 class 文件格局。在一切 JDK 1.1 宣布版中的 JVM 都可以或許支撐版本從 45.0 到 45.65535 的 class 文件格局。在 Sun 的 1.2 版本的 SDK 中,JVM 可以或許支撐從版本 45.0 到46.0 的 class 文件格局。
1.0 或 1.2 版本的編譯器可以或許發生版本號為 45.3 的 class 文件。在 Sun 的 1.2 版本 SDK 中,Javac 編譯器默許發生版本號為 45.3 的 class 文件。但假如在 javac 敕令行中指定了 -target 1.2 標記,1.2 版本的編譯器將發生版本號為 46.0 的 class 文件。1.0 或 1.1 版本的 JVM 上不克不及運轉應用-target 1.2 標記所發生的 class 文件。
JVM 完成的 第二版中修正了對 class 文件主版本號和次版本號的說明。關於第二版而言,class 文件的主版本號與 Java 平台主宣布版的版本號堅持分歧(例如:在 Java 2 平台宣布版上,主版本號從 45 升至 46),次版本號與特定主平台宣布版的各個宣布版相干。是以,雖然分歧的 class 文件格局可以由分歧的版本號表現,但版本號紛歧樣其實不代表 class 文件格局分歧。版本號分歧的緣由能夠只是由於 class 文件由分歧宣布版本的 java 平台發生,能夠 class 文件的格局並沒有轉變。
下面三段節選自《深刻 Java 虛擬機》,煩瑣一堆,JDK 1.2 開啟了 Java 2 的時期,但誰人年月依然離我們很遠,我們傍邊許多少直接跳在 JDK 1.4 上的,我也差不多,只是項目請求不能不在一段時光裡冤枉在 JDK 1.3 上。不外年夜致我們可以獲得的信息就是每一個版本的 JDK 編譯器編譯出的 class 文件中都帶有一個版本號,分歧的 JVM 能接收一個規模 class 版本號,超越規模則要失足。不外普通都是能向後兼容的,曉得 Sun 在做 Solaris 的一句標語嗎?堅持對先前版本的 100% 二進制兼容性,這也是對客戶的投資掩護。
四:其他肯定 class 的 major.minor version 方法
1)Eclipse 中檢查
Eclipse 3.3 參加的新特點,當某個類沒有聯系關系到源代碼,翻開它會顯示比擬具體的類信息,固然還未到源碼級別了,看下圖是翻開 2.0 spring.jar 中 ClasspathXmlApplicationContext.class 顯示的信息
2)敕令 javap -verbose
關於編譯出的 class 文件用 javap -verbose 能顯示出類的 major.minor 版本,見下圖:
3) MANIFEST 文件
把 class 打成的 JAR 包中都邑有文件 META-INF\MANIFEST,這個文件普通會有編譯器的信息,上面列幾個包的 META-INF\MANIFEST 文件內容年夜家看看
·Velocity-1.5.jar 的 META-INFO\MANIFEST 部分內容 Manifest-Version: 1.0 Ant-Version: Apache Ant 1.7.0 Created-By: Apache Ant Package: org.apache.velocity Build-Jdk: 1.4.2_08 Extension-Name: velocity
我們看到是用 ant 打包,構建用的JDK是 1.4.2_08,用 1.4 編譯的類在 1.4 JVM 中固然能運轉。假如那人用 1.5 的 JDK 來編譯,然後用 JDK 1.4+ANT 來打包就太無聊了。
·2.0 spring.jar 的 META-INFO\MANIFEST 部分內容
Manifest-Version: 1.0 Ant-Version: Apache Ant 1.6.5 Created-By: 1.5.0_08-b03 (Sun Microsystems Inc.) Implementation-Title: Spring Framework
這下要留意啦,它是用的 JDK 1.5 來編譯的,那末它能否帶了 -target 1.4 或 -target 1.3 來編譯的呢?確切是的,可以檢查類的二進制文件,這是最保險的。地點 spring-2.0.jar 也能夠在 1.4 JVM 中加載履行。
·自已一個項目頂用 ant 打的 jar 包的 META-INFO\MANIFEST
Manifest-Version: 1.0 Ant-Version: Apache Ant 1.7.0 Created-By: 1.4.2-b28 (Sun Microsystems Inc.)
用的是 JDK 1.4 構建打包的。
第一第二種方法能明白曉得 major.minor version,而第三種辦法應當也沒成績,然則碰著失常構建就難說了,好比誰把誰人 META-INFO\MANIFEST 打包後換了也未可知。直接檢查類的二進制文件的辦法可以萬分包管,精確無誤,就是對象改動我也認了。
五:編譯器比擬及症節之地點
如今無妨從 JDK 1.1 到 JDK 1.7 編譯器編譯出的 class 的默許 minor.major version 吧。(又走到 Sun 的網站上翻滾出我歷來都沒用過的骨董來)
JDK 編譯器版本 target 參數 十六進制 minor.major 十進制 minor.major jdk1.1.8 不克不及帶 target 參數 00 03 00 2D 45.3 jdk1.2.2 不帶(默許為 -target 1.1) 00 03 00 2D 45.3 jdk1.2.2 -target 1.2 00 00 00 2E 46.0 jdk1.3.1_19 不帶(默許為 -target 1.1) 00 03 00 2D 45.3 jdk1.3.1_19 -target 1.3 00 00 00 2F 47.0 j2sdk1.4.2_10 不帶(默許為 -target 1.2) 00 00 00 2E 46.0 j2sdk1.4.2_10 -target 1.4 00 00 00 30 48.0 jdk1.5.0_11 不帶(默許為 -target 1.5) 00 00 00 31 49.0 jdk1.5.0_11 -target 1.4 -source 1.4 00 00 00 30 48.0 jdk1.6.0_01 不帶(默許為 -target 1.6) 00 00 00 32 50.0 jdk1.6.0_01 -target 1.5 00 00 00 31 49.0 jdk1.6.0_01 -target 1.4 -source 1.4 00 00 00 30 48.0 jdk1.7.0 不帶(默許為 -target 1.6) 00 00 00 32 50.0 jdk1.7.0 -target 1.7 00 00 00 33 51.0 jdk1.7.0 -target 1.4 -source 1.4 00 00 00 30 48.0 Apache Harmony 5.0M3 不帶(默許為 -target 1.2) 00 00 00 2E 46.0 Apache Harmony 5.0M3 -target 1.4 00 00 00 30 48.0
下面比擬是 Windows 平台下的 JDK 編譯器的情形,我們可以此作些總結:
1) -target 1.1 時 有次版本號,target 為 1.2 及今後都只用主版本號了,次版本號為 0
2) 從 1.1 到 1.4 說話差別比擬小,所以 1.2 到 1.4 默許的 target 都不是本身絕對應版本
3) 1.5 語法更改很年夜,所以直接默許 target 就是 1.5。也由於如斯用 1.5 的 JDK 要生成目的為 1.4 的代碼,光有 -target 1.4 不敷,必需同時帶上 -source 1.4,指定源碼的兼容性,1.6/1.7 JDk 生成目的為 1.4 的代碼也如斯。
4) 1.6 編譯器顯得較為保守,默許參數就為 -target 1.6。由於 1.6 和 1.5 的語法無差別,所以用 -target 1.5 時無需隨著 -source 1.5。
5) 留意 1.7 編譯的默許 target 為 1.6
6) 其他第三方的 JDK 生成的 Class 文件格局版本號同對應 Sun 版本 JDK
7) 最初一點最主要的,某個版本的 JVM 能接收 class 文件的最年夜主版本號不克不及跨越對應 JDK 帶響應 target 參數編譯出來的 class 文件的版本號。
下面那句話有點長,一口吻讀曩昔不是很好懂得,舉個例子:1.4 的 JVM 能接收最年夜的 class 文件的主版本號不克不及跨越用 1.4 JDK 帶參數 -target 1.4 時編譯出的 class 文件的主版本號,也就是 48。
由於 1.5 JDK 編譯時默許 target 為 1.5,出來的字節碼 major.minor version 是 49.0,所以 1.4 的 JVM 是沒法接收的,只要拋失足誤。
那末又為何從 1.1 到 1.2、從 1.2 到 1.3 或許從 1.3 到 1.4 的 JDK 進級不會產生 Unsupported major.minor version 的毛病呢,那是由於 1.2/1.3/1.4 都堅持了很好的二進制兼容性,看看 1.2/1.3/1.4 的默許 target 分離為 1.1/1.1/1.2 就曉得了,也就是默許情形下1.4 JDK 編譯出的 class 文件在 JVM 1.2 下都能加載履行,況且於 JVM 1.3 呢?(固然要去除應用了新版本擴大的 API 的身分)
六:找到成績處理的辦法
那末如今假如碰著這類成績該曉得若何處理了吧,還會像我所見到有些兄弟那樣,去找個 1.4 的 JDK 下載裝置,然後用其從新編譯一切的代碼嗎?其實年夜可不用如斯費心,我們必定還記得 javac 還有個 -target 參數,對啦,可以持續應用 1.5 JDK,編譯時帶上參數 -target 1.4 -source 1.4 就 OK 啦,不外你必定要對哪些 API 是 1.5 JDK 參加出去的管窺蠡測,不克不及你的 class 文件拿到 JVM 1.4 下就會 method not found。目的 JVM 是 1.3 的話,編譯選項就用 -target 1.3 -source 1.3 了。
響應的假如應用 ant ,它的 javac 義務也可對應的選擇 target 和 source
<javac target="1.4" source="1.4" ............................/>
假如是在開辟中,可以確定的是如今真正算得上是 JAVA IDE 關於工程也都有編譯選項設置目的代碼的。例如 Eclipse 的項目屬性中的 Java Compiler 設置,如圖
自已設定編譯選項,你會看到選擇分歧的 compiler compliance level 是,Generated class files compatibility 和 Source compatibility 也在變,你也能夠手動調劑那兩項,手動設置後你就不消很在意用的甚麼版本的編譯器了,只需求他生成我們願望的字節碼就好了,再引伸一下就是即便源代碼是用 VB 寫的,只需能編譯成 JVM 能履行的字節碼都不打緊。在其他的 IDE 也能找到響應的設置對話框的。
其他時刻,你必定要曉得以後的 JVM 是甚麼版本,能接收的字節碼主版本號是若干(可對比後面誰人表)。獲息以後 JVM 版本有兩種門路:
第一:假如你是直接用 java 敕令在掌握台履行法式,可以用 java -version 檢查以後的 JVM 版本,然後肯定能接收的 class 文件版本
第二:假如是在容器中履行,而不克不及明白曉得會應用哪一個 JVM,那末可以在容器中履行的法式中參加代碼 System.getProperty("java.runtime.version"); 或 System.getProperty("java.class.version"),取得 JVM 版本和能接收的 class 的版本號。
最初一絕招,假如你不想針對低版本的 JVM 用 target 參數從新編譯一切代碼;假如你依然想持續在代碼頂用新的 API 的話;更有甚者,你還用了 JDK 1.5 的新特征,比方泛型、主動拆裝箱、列舉等的話,那你用 -target 1.4 -source 1.4 就沒法編譯經由過程,不能不從新整頓代碼。那末告知你最初一招,不須要再從源代碼著手,直接轉換你所正常編譯出的字節碼,持續享用那些新的特征,新的 API,那就是:請參考之前的一篇日記:Retrotranslator讓你用JDK1.5的特征寫出的代碼能在JVM1.4中運轉 ,我就是這麼用的,做好測試就不會有成績的。
七:再議一個現實產生的相干成績
這是一個由於拷貝 Tomcat 而發生的 Unsupported major.minor version 49.0 毛病。情形是:我當地裝置的是 JDK 1.5,然後在網上找了一個 EXE 的 Tomcat 裝置文件裝置了而且可用。後來同事要一個 Tomcat,不想下載或裝置,因而依據我以往的經歷是把我的 Tomcat 全部目次拷給他應當就好了,成果是拿到他那邊閱讀 jsp 文件都湧現 Unsupported major.minor version 49.0 毛病,可以肯定的是他裝置的是 1.4 的 JDK,但我照樣有些疑惑,先前對這個成績還很有信念的我傻眼了。慣性思想是編譯好的 class 文件拿到低版本的 JVM 會湧現如是異常,可現並沒有效已 JDK 1.5 編譯好的類要履行啊。
後來細心看異常信息,終究發明了 %TOMCAT_HOME%\common\lib\tools.jar 這一端倪,由於 jsp 文件須要依附它來編譯,打來這個 tools.jar 中的一個 class 文件來看看,49.0,很快我就明確本來這個文件是在我的機械上裝置 Tomcat 時由 Tomcat 裝置法式從 %JDK1.5%\lib 目次拷到 Tomcat 的 lib 目次去的,形成在同事機械上編譯 JSP 時是 1.4 的 JVM 配搭著 49.0 的 tools.jar,那能不失足,因而找來 1.4 JDK 的 tools.jar 調換了 Tomcat 的就 OK 啦。
八:小結
其實懂得 major.minor 就像是我們可以這麼想像,異樣是微軟件的法式,32 位的運用法式不克不及拿到 16 位體系中履行那樣。
假如我們宣布前懂得到目的 JVM 版本,曉得怎樣從 java class 文件中看出 major.minor 版原來,就不消比及辦事器報出異常才著手去處理,也就可以預知到能夠產生的成績。
其他時刻碰到這個成績應詳細處理,總之成績的根由是低版本的 JVM 沒法加載高版本的 class 文件形成的,找到高版本的 class 文件處置一下就好了
上面是關於Tomcat 運轉第一個servlet時產生Unsupported major.minor version 52.0毛病的處理方法
應用Tomcat運轉第一個servlet法式是產生如圖毛病:
查找材料後發明是因為應用高版本的jdk編譯的項目運轉在低版本的jre上,決解方法是編譯的jdk和運轉的jre版本分歧。
檢查Tomcat應用的jre確切和編譯時應用的紛歧致(現在裝置了兩個jdk);
前面把Tomcat的jre改成1.8便可正常運轉:
no art, however minor, demands less than total dedication if you want to excel in it.