真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

java編譯無(wú)人機(jī)代碼 可編程的無(wú)人機(jī)

北大青鳥(niǎo)java培訓(xùn):Java代碼的優(yōu)化方法有哪些?

說(shuō)到代碼優(yōu)化,每個(gè)人或多或少都掌握一到兩種方法,但是這樣的方法對(duì)提升代碼運(yùn)行效率效果不大,最重要是對(duì)代碼的重視和了解,這樣才能提升代碼的運(yùn)行效率。

創(chuàng)新互聯(lián)2013年開(kāi)創(chuàng)至今,先為新鄉(xiāng)等服務(wù)建站,新鄉(xiāng)等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為新鄉(xiāng)企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問(wèn)題。

在進(jìn)行代碼優(yōu)化的過(guò)程中,方法是非常重要的,多掌握幾種方法,根據(jù)代碼的不同情況選擇適合的方法進(jìn)行優(yōu)化。

下面電腦培訓(xùn)為大家介紹Java代碼優(yōu)化的幾種方法。

1、使用指定類、方法的final修飾符具有final修飾符的類不可派生。

在Java核心API中,有許多最終應(yīng)用程序的例子,例如java.lang.String,整個(gè)類都是final。

為類指定final修飾符允許繼承類,并且為方法指定final修飾符允許覆蓋該方法。

如果將類指定為final,IT培訓(xùn)認(rèn)為該類的所有方法都是final。

Java編譯器將尋找內(nèi)聯(lián)所有最終方法的機(jī)會(huì)。

內(nèi)聯(lián)對(duì)于提高Java操作的效率非常重要。

這可以將性能平均提高50%。

2、重用對(duì)象String對(duì)象的使用是非常重要的,StringBuilder/StringBuffer并不是字符串連接。

由于Java虛擬機(jī)需要時(shí)間來(lái)生成對(duì)象,所以將來(lái)垃圾收集和處理這些對(duì)象可能需要一些時(shí)間。

因此,生成太多對(duì)象將對(duì)程序的性能產(chǎn)生很大影響。

3、使用局部變量調(diào)用方法時(shí)傳遞的參數(shù)以及在調(diào)用中創(chuàng)建的臨時(shí)變量都保存在堆棧中,速度更快。

其他變量(如靜態(tài)變量和實(shí)例變量)在堆中創(chuàng)建并且速度較慢。

此外,湖南北大青鳥(niǎo)發(fā)現(xiàn)在堆棧中創(chuàng)建的變量,當(dāng)方法完成運(yùn)行時(shí),內(nèi)容消失,不需要進(jìn)行額外的垃圾收集。

4、及時(shí)關(guān)閉流在Java編程過(guò)程中,在執(zhí)行數(shù)據(jù)庫(kù)連接和I/O流操作時(shí)要小心。

使用后,北大青鳥(niǎo)湖南嘉薈校區(qū)官網(wǎng)建議應(yīng)及時(shí)關(guān)閉以釋放資源。

因?yàn)檫@些大型物體的操作會(huì)導(dǎo)致系統(tǒng)的大量開(kāi)銷,稍微粗心會(huì)導(dǎo)致嚴(yán)重的后果。

java語(yǔ)言為什么使用的java虛擬機(jī)而不是直接翻譯成機(jī)器代碼(即和C語(yǔ)言一樣)?

Java語(yǔ)言的一個(gè)非常重要的特點(diǎn)就是與平臺(tái)的無(wú)關(guān)性。而使用Java虛擬機(jī)是實(shí)現(xiàn)這一特點(diǎn)的關(guān)鍵。一般的高級(jí)語(yǔ)言如果要在不同的平臺(tái)上運(yùn)行,至少需要編譯成不同的目標(biāo)代碼。而引入Java語(yǔ)言虛擬機(jī)后,Java語(yǔ)言在不同平臺(tái)上運(yùn)行時(shí)不需要重新編譯。Java語(yǔ)言使用模式Java虛擬機(jī)屏蔽了與具體平臺(tái)相關(guān)的信息,使得Java語(yǔ)言編譯程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可以在多種平臺(tái)上不加修改地運(yùn)行。Java虛擬機(jī)在執(zhí)行字節(jié)碼時(shí),把字節(jié)碼解釋成具體平臺(tái)上的機(jī)器指令執(zhí)行。 Java虛擬機(jī)的使用主體

誰(shuí)能簡(jiǎn)單闡述下java編譯執(zhí)行的過(guò)程?

Java虛擬機(jī)(JVM)是可運(yùn)行Java代碼的假想計(jì)算機(jī)。只要根據(jù)JVM規(guī)格描述將解釋器移植到特定的計(jì)算機(jī)上,就能保證經(jīng)過(guò)編譯的任何Java代碼能夠在該系統(tǒng)上運(yùn)行。本文首先簡(jiǎn)要介紹從Java文件的編譯到最終執(zhí)行的過(guò)程,隨后對(duì)JVM規(guī)格描述作一說(shuō)明。

一.Java源文件的編譯、下載、解釋和執(zhí)行

Java應(yīng)用程序的開(kāi)發(fā)周期包括編譯、下載、解釋和執(zhí)行幾個(gè)部分。Java編譯程序?qū)ava源程序翻譯為JVM可執(zhí)行代碼?字節(jié)碼。這一編譯過(guò)程同C/C++的編譯有些不同。當(dāng)C編譯器編譯生成一個(gè)對(duì)象的代碼時(shí),該代碼是為在某一特定硬件平臺(tái)運(yùn)行而產(chǎn)生的。因此,在編譯過(guò)程中,編譯程序通過(guò)查表將所有對(duì)符號(hào)的引用轉(zhuǎn)換為特定的內(nèi)存偏移量,以保證程序運(yùn)行。Java編譯器卻不將對(duì)變量和方法的引用編譯為數(shù)值引用,也不確定程序執(zhí)行過(guò)程中的內(nèi)存布局,而是將這些符號(hào)引用信息保留在字節(jié)碼中,由解釋器在運(yùn)行過(guò)程中創(chuàng)立內(nèi)存布局,然后再通過(guò)查表來(lái)確定一個(gè)方法所在的地址。這樣就有效的保證了Java的可移植性和安全性。

運(yùn)行JVM字節(jié)碼的工作是由解釋器來(lái)完成的。解釋執(zhí)行過(guò)程分三部進(jìn)行:代碼的裝入、代碼的校驗(yàn)和代碼的執(zhí)行。裝入代碼的工作由"類裝載器"(class loader)完成。類裝載器負(fù)責(zé)裝入運(yùn)行一個(gè)程序需要的所有代碼,這也包括程序代碼中的類所繼承的類和被其調(diào)用的類。當(dāng)類裝載器裝入一個(gè)類時(shí),該類被放在自己的名字空間中。除了通過(guò)符號(hào)引用自己名字空間以外的類,類之間沒(méi)有其他辦法可以影響其他類。在本臺(tái)計(jì)算機(jī)上的所有類都在同一地址空間內(nèi),而所有從外部引進(jìn)的類,都有一個(gè)自己獨(dú)立的名字空間。這使得本地類通過(guò)共享相同的名字空間獲得較高的運(yùn)行效率,同時(shí)又保證它們與從外部引進(jìn)的類不會(huì)相互影響。當(dāng)裝入了運(yùn)行程序需要的所有類后,解釋器便可確定整個(gè)可執(zhí)行程序的內(nèi)存布局。解釋器為符號(hào)引用同特定的地址空間建立對(duì)應(yīng)關(guān)系及查詢表。通過(guò)在這一階段確定代碼的內(nèi)存布局,Java很好地解決了由超類改變而使子類崩潰的問(wèn)題,同時(shí)也防止了代碼對(duì)地址的非法訪問(wèn)。

隨后,被裝入的代碼由字節(jié)碼校驗(yàn)器進(jìn)行檢查。校驗(yàn)器可發(fā)現(xiàn)操作數(shù)棧溢出,非法數(shù)據(jù)類型轉(zhuǎn)化等多種錯(cuò)誤。通過(guò)校驗(yàn)后,代碼便開(kāi)始執(zhí)行了。

Java字節(jié)碼的執(zhí)行有兩種方式:

1.即時(shí)編譯方式:解釋器先將字節(jié)碼編譯成機(jī)器碼,然后再執(zhí)行該機(jī)器碼。

2.解釋執(zhí)行方式:解釋器通過(guò)每次解釋并執(zhí)行一小段代碼來(lái)完成Java字節(jié)碼程 序的所有操作。

通常采用的是第二種方法。由于JVM規(guī)格描述具有足夠的靈活性,這使得將字節(jié)碼翻譯為機(jī)器代碼的工作

具有較高的效率。對(duì)于那些對(duì)運(yùn)行速度要求較高的應(yīng)用程序,解釋器可將Java字節(jié)碼即時(shí)編譯為機(jī)器碼,從而很好地保證了Java代碼的可移植性和高性能。

二.JVM規(guī)格描述

JVM的設(shè)計(jì)目標(biāo)是提供一個(gè)基于抽象規(guī)格描述的計(jì)算機(jī)模型,為解釋程序開(kāi)發(fā)人員提很好的靈活性,同時(shí)也確保Java代碼可在符合該規(guī)范的任何系統(tǒng)上運(yùn)行。JVM對(duì)其實(shí)現(xiàn)的某些方面給出了具體的定義,特別是對(duì)Java可執(zhí)行代碼,即字節(jié)碼(Bytecode)的格式給出了明確的規(guī)格。這一規(guī)格包括操作碼和操作數(shù)的語(yǔ)法和數(shù)值、標(biāo)識(shí)符的數(shù)值表示方式、以及Java類文件中的Java對(duì)象、常量緩沖池在JVM的存儲(chǔ)映象。這些定義為JVM解釋器開(kāi)發(fā)人員提供了所需的信息和開(kāi)發(fā)環(huán)境。Java的設(shè)計(jì)者希望給開(kāi)發(fā)人員以隨心所欲使用Java的自由。

JVM定義了控制Java代碼解釋執(zhí)行和具體實(shí)現(xiàn)的五種規(guī)格,它們是:

JVM指令系統(tǒng)

JVM寄存器

JVM棧結(jié)構(gòu)

JVM碎片回收堆

JVM存儲(chǔ)區(qū)

2.1JVM指令系統(tǒng)

JVM指令系統(tǒng)同其他計(jì)算機(jī)的指令系統(tǒng)極其相似。Java指令也是由 操作碼和操作數(shù)兩部分組成。操作碼為8位二進(jìn)制數(shù),操作數(shù)進(jìn)緊隨在操作碼的后面,其長(zhǎng)度根據(jù)需要而不同。操作碼用于指定一條指令操作的性質(zhì)(在這里我們采用匯編符號(hào)的形式進(jìn)行說(shuō)明),如iload表示從存儲(chǔ)器中裝入一個(gè)整數(shù),anewarray表示為一個(gè)新數(shù)組分配空間,iand表示兩個(gè)整數(shù)的"與",ret用于流程控制,表示從對(duì)某一方法的調(diào)用中返回。當(dāng)長(zhǎng)度大于8位時(shí),操作數(shù)被分為兩個(gè)以上字節(jié)存放。JVM采用了"big endian"的編碼方式來(lái)處理這種情況,即高位bits存放在低字節(jié)中。這同 Motorola及其他的RISC CPU采用的編碼方式是一致的,而與Intel采用的"little endian "的編碼方式即低位bits存放在低位字節(jié)的方法不同。

Java指令系統(tǒng)是以Java語(yǔ)言的實(shí)現(xiàn)為目的設(shè)計(jì)的,其中包含了用于調(diào)用方法和監(jiān)視多先程系統(tǒng)的指令。Java的8位操作碼的長(zhǎng)度使得JVM最多有256種指令,目前已使用了160多種操作碼。

2.2JVM指令系統(tǒng)

所有的CPU均包含用于保存系統(tǒng)狀態(tài)和處理器所需信息的寄存器組。如果虛擬機(jī)定義較多的寄存器,便可以從中得到更多的信息而不必對(duì)?;騼?nèi)存進(jìn)行訪問(wèn),這有利于提高運(yùn)行速度。然而,如果虛擬機(jī)中的寄存器比實(shí)際CPU的寄存器多,在實(shí)現(xiàn)虛擬機(jī)時(shí)就會(huì)占用處理器大量的時(shí)間來(lái)用常規(guī)存儲(chǔ)器模擬寄存器,這反而會(huì)降低虛擬機(jī)的效率。針對(duì)這種情況,JVM只設(shè)置了4個(gè)最為常用的寄存器。它們是:

pc程序計(jì)數(shù)器

optop操作數(shù)棧頂指針

frame當(dāng)前執(zhí)行環(huán)境指針

vars指向當(dāng)前執(zhí)行環(huán)境中第一個(gè)局部變量的指針

所有寄存器均為32位。pc用于記錄程序的執(zhí)行。optop,frame和vars用于記錄指向Java棧區(qū)的指針。

2.3JVM棧結(jié)構(gòu)

作為基于棧結(jié)構(gòu)的計(jì)算機(jī),Java棧是JVM存儲(chǔ)信息的主要方法。當(dāng)JVM得到一個(gè)Java字節(jié)碼應(yīng)用程序后,便為該代碼中一個(gè)類的每一個(gè)方法創(chuàng)建一個(gè)??蚣埽员4嬖摲椒ǖ臓顟B(tài)信息。每個(gè)??蚣馨ㄒ韵氯愋畔ⅲ?/p>

局部變量

執(zhí)行環(huán)境

操作數(shù)棧

局部變量用于存儲(chǔ)一個(gè)類的方法中所用到的局部變量。vars寄存器指向該變量表中的第一個(gè)局部變量。

執(zhí)行環(huán)境用于保存解釋器對(duì)Java字節(jié)碼進(jìn)行解釋過(guò)程中所需的信息。它們是:上次調(diào)用的方法、局部變量指針和操作數(shù)棧的棧頂和棧底指針。執(zhí)行環(huán)境是一個(gè)執(zhí)行一個(gè)方法的控制中心。例如:如果解釋器要執(zhí)行iadd(整數(shù)加法),首先要從frame寄存器中找到當(dāng)前執(zhí)行環(huán)境,而后便從執(zhí)行環(huán)境中找到操作數(shù)棧,從棧頂彈出兩個(gè)整數(shù)進(jìn)行加法運(yùn)算,最后將結(jié)果壓入棧頂。

操作數(shù)棧用于存儲(chǔ)運(yùn)算所需操作數(shù)及運(yùn)算的結(jié)果。

2.4JVM碎片回收堆

Java類的實(shí)例所需的存儲(chǔ)空間是在堆上分配的。解釋器具體承擔(dān)為類實(shí)例分配空間的工作。解釋器在為一個(gè)實(shí)例分配完存儲(chǔ)空間后,便開(kāi)始記錄對(duì)該實(shí)例所占用的內(nèi)存區(qū)域的使用。一旦對(duì)象使用完畢,便將其回收到堆中。

在Java語(yǔ)言中,除了new語(yǔ)句外沒(méi)有其他方法為一對(duì)象申請(qǐng)和釋放內(nèi)存。對(duì)內(nèi)存進(jìn)行釋放和回收的工作是由Java運(yùn)行系統(tǒng)承擔(dān)的。這允許Java運(yùn)行系統(tǒng)的設(shè)計(jì)者自己決定碎片回收的方法。在SUN公司開(kāi)發(fā)的Java解釋器和Hot Java環(huán)境中,碎片回收用后臺(tái)線程的方式來(lái)執(zhí)行。這不但為運(yùn)行系統(tǒng)提供了良好的性能,而且使程序設(shè)計(jì)人員擺脫了自己控制內(nèi)存使用的風(fēng)險(xiǎn)。

2.5JVM存儲(chǔ)區(qū)

JVM有兩類存儲(chǔ)區(qū):常量緩沖池和方法區(qū)。常量緩沖池用于存儲(chǔ)類名稱、方法和字段名稱以及串常量。方法區(qū)則用于存儲(chǔ)Java方法的字節(jié)碼。對(duì)于這兩種存儲(chǔ)區(qū)域具體實(shí)現(xiàn)方式在JVM規(guī)格中沒(méi)有明確規(guī)定。這使得Java應(yīng)用程序的存儲(chǔ)布局必須在運(yùn)行過(guò)程中確定,依賴于具體平臺(tái)的實(shí)現(xiàn)方式。

JVM是為Java字節(jié)碼定義的一種獨(dú)立于具體平臺(tái)的規(guī)格描述,是Java平臺(tái)獨(dú)立性的基礎(chǔ)。目前的JVM還存在一些限制和不足,有待于進(jìn)一步的完善,但無(wú)論如何,JVM的思想是成功的。

對(duì)比分析:如果把Java原程序想象成我們的C++原程序,Java原程序編譯后生成的字節(jié)碼就相當(dāng)于C++原程序編譯后的80x86的機(jī)器碼(二進(jìn)制程序文件),JVM虛擬機(jī)相當(dāng)于80x86計(jì)算機(jī)系統(tǒng),Java解釋器相當(dāng)于80x86CPU。在80x86CPU上運(yùn)行的是機(jī)器碼,在Java解釋器上運(yùn)行的是Java字節(jié)碼。

Java解釋器相當(dāng)于運(yùn)行Java字節(jié)碼的“CPU”,但該“CPU”不是通過(guò)硬件實(shí)現(xiàn)的,而是用軟件實(shí)現(xiàn)的。Java解釋器實(shí)際上就是特定的平臺(tái)下的一個(gè)應(yīng)用程序。只要實(shí)現(xiàn)了特定平臺(tái)下的解釋器程序,Java字節(jié)碼就能通過(guò)解釋器程序在該平臺(tái)下運(yùn)行,這是Java跨平臺(tái)的根本。當(dāng)前,并不是在所有的平臺(tái)下都有相應(yīng)Java解釋器程序,這也是Java并不能在所有的平臺(tái)下都能運(yùn)行的原因,它只能在已實(shí)現(xiàn)了Java解釋器程序的平臺(tái)下運(yùn)行。

什么是Java代碼的編譯與反編譯?

Java代碼的編譯與反編譯

2017-02-21Hollis數(shù)盟

一、什么是編譯

1、利用編譯程序從源語(yǔ)言編寫(xiě)的源程序產(chǎn)生目標(biāo)程序的過(guò)程。

2、用編譯程序產(chǎn)生目標(biāo)程序的動(dòng)作。編譯就是把高級(jí)語(yǔ)言變成計(jì)算機(jī)可以識(shí)別的2進(jìn)制語(yǔ)言,計(jì)算機(jī)只認(rèn)識(shí)1和0,編譯程序把人們熟悉的語(yǔ)言換成2進(jìn)制的。編譯程序把一個(gè)源程序翻譯成目標(biāo)程序的工作過(guò)程分為五個(gè)階段:詞法分析;語(yǔ)法分析;語(yǔ)義檢查和中間代碼生成;代碼優(yōu)化;目標(biāo)代碼生成。主要是進(jìn)行詞法分析和語(yǔ)法分析,又稱為源程序分析,分析過(guò)程中發(fā)現(xiàn)有語(yǔ)法錯(cuò)誤,給出提示信息。

二、什么是反編譯

計(jì)算機(jī)軟件反向工程(Reverseengineering)也稱為計(jì)算機(jī)軟件還原工程,是指通過(guò)對(duì)他人軟件的目標(biāo)程序(可執(zhí)行程序)進(jìn)行“逆向分析、研究”工作,以推導(dǎo)出他人的軟件產(chǎn)品所使用的思路、原理、結(jié)構(gòu)、算法、處理過(guò)程、運(yùn)行方法等設(shè)計(jì)要素,某些特定情況下可能推導(dǎo)出源代碼。反編譯作為自己開(kāi)發(fā)軟件時(shí)的參考,或者直接用于自己的軟件產(chǎn)品中。

三、Java類的編譯與反編譯

我們?cè)谧畛鯇W(xué)習(xí)Java的時(shí)候,會(huì)接觸到兩個(gè)命令:javac和java,那個(gè)時(shí)候我們就知道,javac是用來(lái)編譯Java類的,就是將我們寫(xiě)好的helloworld.java文件編譯成helloworld.class文件。

class文件打破了C或者C++等語(yǔ)言所遵循的傳統(tǒng),使用這些傳統(tǒng)語(yǔ)言寫(xiě)的程序通常首先被編譯,然后被連接成單獨(dú)的、專門支持特定硬件平臺(tái)和操作系統(tǒng)的二進(jìn)制文件。通常情況下,一個(gè)平臺(tái)上的二進(jìn)制可執(zhí)行文件不能在其他平臺(tái)上工作。而Javaclass文件是可以運(yùn)行在任何支持Java虛擬機(jī)的硬件平臺(tái)和操作系統(tǒng)上的二進(jìn)制文件。

那么反編譯呢,就是通過(guò)helloworld.class文件得到j(luò)ava文件(或者說(shuō)是程序員能看懂的Java文件)

四、什么時(shí)候會(huì)用到反編譯

1、我們只有一個(gè)類的class文件,但是我們又看不懂Java的class文件,那么我們可以把它反編譯成我們可以看得懂的文件。

2、學(xué)習(xí)Java過(guò)程中,JDK的每個(gè)版本都會(huì)加入越來(lái)越多的語(yǔ)法糖,有些時(shí)候我們想知道Java一些實(shí)現(xiàn)細(xì)節(jié),我們可以借助反編譯。

五、反編譯工具

1、javap

2、Jad:官網(wǎng)(墻裂推薦)

客戶端:

可以在官網(wǎng)下載可執(zhí)行文件,找到對(duì)應(yīng)的操作系統(tǒng)的對(duì)應(yīng)版本,然后進(jìn)行安裝使用。

因?yàn)槲沂褂玫氖莑inux操作系統(tǒng),所以我下載的是Linux版本的工具,這個(gè)工具下載好之后會(huì)有一個(gè)執(zhí)行文件,只要在執(zhí)行文件所在目錄執(zhí)行./jadhelloworld.class就會(huì)在當(dāng)前目錄下生成helloworld.jad文件,該文件里就是我們很熟悉的Java代碼

Eclipse插件:

下載地址在官網(wǎng)下載插件的jar包,然后將jar包放到eclipse的plugins目錄下‘在打開(kāi)Eclipse,Eclipse-Window-Preferences-Java,此時(shí)你會(huì)發(fā)現(xiàn)會(huì)比原來(lái)多了一個(gè)JadClipse的選項(xiàng),單擊,在Pathtodecompiler中輸入你剛才放置jad.exe的位置,也可以制定臨時(shí)文件的目錄。當(dāng)然在JadClipse下還有一些子選項(xiàng),如Debug,Directives等,按照默認(rèn)配置即可。基本配置完畢后,我們可以查看一下class文件的默認(rèn)打開(kāi)方式,Eclipse-Window-Preferences-General-Editors-FileAssociations我們可以看到class文件的打開(kāi)方式有兩個(gè),JadClipse和Eclipse自帶的ClassFileViewer,而JadClipse是默認(rèn)的。全部配置完成,下面我們可以查看源碼了,選擇需要查看的類,按F3即可查看源碼

如何用maven將java8寫(xiě)的代碼編譯為java6平臺(tái)的

在一般的Java應(yīng)用開(kāi)發(fā)過(guò)程中,開(kāi)發(fā)人員使用Java的方式比較簡(jiǎn)單。打開(kāi)慣用的IDE,編寫(xiě)Java源代碼,再利用IDE提供的功能直接運(yùn)行Java 程序就可以了。這種開(kāi)發(fā)模式背后的過(guò)程是:開(kāi)發(fā)人員編寫(xiě)的是Java源代碼文件(.java),IDE會(huì)負(fù)責(zé)調(diào)用Java的編譯器把Java源代碼編譯成平臺(tái)無(wú)關(guān)的字節(jié)代碼(byte code),以類文件的形式保存在磁盤(pán)上(.class)。Java虛擬機(jī)(JVM)會(huì)負(fù)責(zé)把Java字節(jié)代碼加載并執(zhí)行。Java通過(guò)這種方式來(lái)實(shí)現(xiàn)其“編寫(xiě)一次,到處運(yùn)行(Write once, run anywhere)” 的目標(biāo)。Java類文件中包含的字節(jié)代碼可以被不同平臺(tái)上的JVM所使用。Java字節(jié)代碼不僅可以以文件形式存在于磁盤(pán)上,也可以通過(guò)網(wǎng)絡(luò)方式來(lái)下載,還可以只存在于內(nèi)存中。JVM中的類加載器會(huì)負(fù)責(zé)從包含字節(jié)代碼的字節(jié)數(shù)組(byte[])中定義出Java類。在某些情況下,可能會(huì)需要?jiǎng)討B(tài)的生成 Java字節(jié)代碼,或是對(duì)已有的Java字節(jié)代碼進(jìn)行修改。這個(gè)時(shí)候就需要用到本文中將要介紹的相關(guān)技術(shù)。首先介紹一下如何動(dòng)態(tài)編譯Java源文件。

動(dòng)態(tài)編譯Java源文件

在一般情況下,開(kāi)發(fā)人員都是在程序運(yùn)行之前就編寫(xiě)完成了全部的Java源代碼并且成功編譯。對(duì)有些應(yīng)用來(lái)說(shuō),Java源代碼的內(nèi)容在運(yùn)行時(shí)刻才能確定。這個(gè)時(shí)候就需要?jiǎng)討B(tài)編譯源代碼來(lái)生成Java字節(jié)代碼,再由JVM來(lái)加載執(zhí)行。典型的場(chǎng)景是很多算法競(jìng)賽的在線評(píng)測(cè)系統(tǒng)(如PKU JudgeOnline),允許用戶上傳Java代碼,由系統(tǒng)在后臺(tái)編譯、運(yùn)行并進(jìn)行判定。在動(dòng)態(tài)編譯Java源文件時(shí),使用的做法是直接在程序中調(diào)用Java編譯器。

JSR 199引入了Java編譯器API。如果使用JDK 6的話,可以通過(guò)此API來(lái)動(dòng)態(tài)編譯Java代碼。比如下面的代碼用來(lái)動(dòng)態(tài)編譯最簡(jiǎn)單的Hello World類。該Java類的代碼是保存在一個(gè)字符串中的。

01 public class CompilerTest {

02 public static void main(String[] args) throws Exception {

03 String source = "public class Main { public static void main(String[] args) {System.out.println(\"Hello World!\");} }";

04 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

05 StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);

06 StringSourceJavaObject sourceObject = newCompilerTest.StringSourceJavaObject("Main", source);

07 Iterable extends JavaFileObject fileObjects = Arrays.asList(sourceObject);

08 CompilationTask task = compiler.getTask(null, fileManager, null,null, null, fileObjects);

09 boolean result = task.call();

10 if (result) {

11 System.out.println("編譯成功。");

12 }

13 }

14

15 static class StringSourceJavaObject extends SimpleJavaFileObject {

16

17 private String content = null;

18 public StringSourceJavaObject(String name, String content) ??throwsURISyntaxException {

19 super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE);

20 this.content = content;

21 }

22

23 public CharSequence getCharContent(boolean ignoreEncodingErrors) ??throws IOException {

24 return content;

25 }

26 }

27 }

如果不能使用JDK 6提供的Java編譯器API的話,可以使用JDK中的工具類com.sun.tools.javac.Main,不過(guò)該工具類只能編譯存放在磁盤(pán)上的文件,類似于直接使用javac命令。

另外一個(gè)可用的工具是Eclipse JDT Core提供的編譯器。這是Eclipse Java開(kāi)發(fā)環(huán)境使用的增量式Java編譯器,支持運(yùn)行和調(diào)試有錯(cuò)誤的代碼。該編譯器也可以單獨(dú)使用。Play框架在內(nèi)部使用了JDT的編譯器來(lái)動(dòng)態(tài)編譯Java源代碼。在開(kāi)發(fā)模式下,Play框架會(huì)定期掃描項(xiàng)目中的Java源代碼文件,一旦發(fā)現(xiàn)有修改,會(huì)自動(dòng)編譯 Java源代碼。因此在修改代碼之后,刷新頁(yè)面就可以看到變化。使用這些動(dòng)態(tài)編譯的方式的時(shí)候,需要確保JDK中的tools.jar在應(yīng)用的 CLASSPATH中。

下面介紹一個(gè)例子,是關(guān)于如何在Java里面做四則運(yùn)算,比如求出來(lái)(3+4)*7-10的值。一般的做法是分析輸入的運(yùn)算表達(dá)式,自己來(lái)模擬計(jì)算過(guò)程。考慮到括號(hào)的存在和運(yùn)算符的優(yōu)先級(jí)等問(wèn)題,這樣的計(jì)算過(guò)程會(huì)比較復(fù)雜,而且容易出錯(cuò)。另外一種做法是可以用JSR 223引入的腳本語(yǔ)言支持,直接把輸入的表達(dá)式當(dāng)做JavaScript或是JavaFX腳本來(lái)執(zhí)行,得到結(jié)果。下面的代碼使用的做法是動(dòng)態(tài)生成Java源代碼并編譯,接著加載Java類來(lái)執(zhí)行并獲取結(jié)果。這種做法完全使用Java來(lái)實(shí)現(xiàn)。

01 private static double calculate(String expr) throws CalculationException {

02 String className = "CalculatorMain";

03 String methodName = "calculate";

04 String source = "public class " + className

05 + " { public static double " + methodName + "() { return " + expr +"; } }";

06 //省略動(dòng)態(tài)編譯Java源代碼的相關(guān)代碼,參見(jiàn)上一節(jié)

07 boolean result = task.call();

08 if (result) {

09 ClassLoader loader = Calculator.class.getClassLoader();

10 try {

11 Class? clazz = loader.loadClass(className);

12 Method method = clazz.getMethod(methodName, new Class?[] {});

13 Object value = method.invoke(null, new Object[] {});

14 return (Double) value;

15 } catch (Exception e) {

16 throw new CalculationException("內(nèi)部錯(cuò)誤。");

17 }

18 } else {

19 throw new CalculationException("錯(cuò)誤的表達(dá)式。");

20 }

21 }

上面的代碼給出了使用動(dòng)態(tài)生成的Java字節(jié)代碼的基本模式,即通過(guò)類加載器來(lái)加載字節(jié)代碼,創(chuàng)建Java類的對(duì)象的實(shí)例,再通過(guò)Java反射API來(lái)調(diào)用對(duì)象中的方法。

Java字節(jié)代碼增強(qiáng)

Java 字節(jié)代碼增強(qiáng)指的是在Java字節(jié)代碼生成之后,對(duì)其進(jìn)行修改,增強(qiáng)其功能。這種做法相當(dāng)于對(duì)應(yīng)用程序的二進(jìn)制文件進(jìn)行修改。在很多Java框架中都可以見(jiàn)到這種實(shí)現(xiàn)方式。Java字節(jié)代碼增強(qiáng)通常與Java源文件中的注解(annotation)一塊使用。注解在Java源代碼中聲明了需要增強(qiáng)的行為及相關(guān)的元數(shù)據(jù),由框架在運(yùn)行時(shí)刻完成對(duì)字節(jié)代碼的增強(qiáng)。Java字節(jié)代碼增強(qiáng)應(yīng)用的場(chǎng)景比較多,一般都集中在減少冗余代碼和對(duì)開(kāi)發(fā)人員屏蔽底層的實(shí)現(xiàn)細(xì)節(jié)上。用過(guò)JavaBeans的人可能對(duì)其中那些必須添加的getter/setter方法感到很繁瑣,并且難以維護(hù)。而通過(guò)字節(jié)代碼增強(qiáng),開(kāi)發(fā)人員只需要聲明Bean中的屬性即可,getter/setter方法可以通過(guò)修改字節(jié)代碼來(lái)自動(dòng)添加。用過(guò)JPA的人,在調(diào)試程序的時(shí)候,會(huì)發(fā)現(xiàn)實(shí)體類中被添加了一些額外的 域和方法。這些域和方法是在運(yùn)行時(shí)刻由JPA的實(shí)現(xiàn)動(dòng)態(tài)添加的。字節(jié)代碼增強(qiáng)在面向方面編程(AOP)的一些實(shí)現(xiàn)中也有使用。


網(wǎng)站欄目:java編譯無(wú)人機(jī)代碼 可編程的無(wú)人機(jī)
URL地址:http://weahome.cn/article/hgcoeo.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部