最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java代碼編譯和反編譯的那些事兒

 更新時(shí)間:2019年05月09日 10:58:06   作者:HollisChuang  
這篇文章主要給大家介紹了關(guān)于Java代碼編譯和反編譯的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

編程語言

在介紹編譯和反編譯之前,我們先來簡(jiǎn)單介紹下編程語言(Programming Language)。編程語言(Programming Language)分為低級(jí)語言(Low-level Language)和高級(jí)語言(High-level Language)。

機(jī)器語言(Machine Language)和匯編語言(Assembly Language)屬于低級(jí)語言,直接用計(jì)算機(jī)指令編寫程序。

而C、C++、Java、Python等屬于高級(jí)語言,用語句(Statement)編寫程序,語句是計(jì)算機(jī)指令的抽象表示。

舉個(gè)例子,同樣一個(gè)語句用C語言、匯編語言和機(jī)器語言分別表示如下:

計(jì)算機(jī)只能對(duì)數(shù)字做運(yùn)算,符號(hào)、聲音、圖像在計(jì)算機(jī)內(nèi)部都要用數(shù)字表示,指令也不例外,上表中的機(jī)器語言完全由十六進(jìn)制數(shù)字組成。最早的程序員都是直接用機(jī)器語言編程,但是很麻煩,需要查大量的表格來確定每個(gè)數(shù)字表示什么意思,編寫出來的程序很不直觀,而且容易出錯(cuò),于是有了匯編語言,把機(jī)器語言中一組一組的數(shù)字用助記符(Mnemonic)表示,直接用這些助記符寫出匯編程序,然后讓匯編器(Assembler)去查表把助記符替換成數(shù)字,也就把匯編語言翻譯成了機(jī)器語言。

但是,匯編語言用起來同樣比較復(fù)雜,后面,就衍生出了Java、C、C++等高級(jí)語言。

什么是編譯

上面提到語言有兩種,一種低級(jí)語言,一種高級(jí)語言。可以這樣簡(jiǎn)單的理解:低級(jí)語言是計(jì)算機(jī)認(rèn)識(shí)的語言、高級(jí)語言是程序員認(rèn)識(shí)的語言。

那么如何從高級(jí)語言轉(zhuǎn)換成低級(jí)語言呢?這個(gè)過程其實(shí)就是編譯。

從上面的例子還可以看出,C語言的語句和低級(jí)語言的指令之間不是簡(jiǎn)單的一一對(duì)應(yīng)關(guān)系,一條a=b+1;語句要翻譯成三條匯編或機(jī)器指令,這個(gè)過程稱為編譯(Compile),由編譯器(Compiler)來完成,顯然編譯器的功能比匯編器要復(fù)雜得多。用C語言編寫的程序必須經(jīng)過編譯轉(zhuǎn)成機(jī)器指令才能被計(jì)算機(jī)執(zhí)行,編譯需要花一些時(shí)間,這是用高級(jí)語言編程的一個(gè)缺點(diǎn),然而更多的是優(yōu)點(diǎn)。首先,用C語言編程更容易,寫出來的代碼更緊湊,可讀性更強(qiáng),出了錯(cuò)也更容易改正。

將便于人編寫、閱讀、維護(hù)的高級(jí)計(jì)算機(jī)語言所寫作的源代碼程序,翻譯為計(jì)算機(jī)能解讀、運(yùn)行的低階機(jī)器語言的程序的過程就是編譯。負(fù)責(zé)這一過程的處理的工具叫做編譯器

現(xiàn)在我們知道了什么是編譯,也知道了什么是編譯器。不同的語言都有自己的編譯器,Java語言中負(fù)責(zé)編譯的編譯器是一個(gè)命令:javac

javac是收錄于JDK中的Java語言編譯器。該工具可以將后綴名為.java的源文件編譯為后綴名為.class的可以運(yùn)行于Java虛擬機(jī)的字節(jié)碼。

當(dāng)我們寫完一個(gè)HelloWorld.java文件后,我們可以使用javac HelloWorld.java命令來生成HelloWorld.class文件,這個(gè)class類型的文件是JVM可以識(shí)別的文件。通常我們認(rèn)為這個(gè)過程叫做Java語言的編譯。其實(shí),class文件仍然不是機(jī)器能夠識(shí)別的語言,因?yàn)闄C(jī)器只能識(shí)別機(jī)器語言,還需要JVM再將這種class文件類型字節(jié)碼轉(zhuǎn)換成機(jī)器可以識(shí)別的機(jī)器語言。

什么是反編譯

反編譯的過程與編譯剛好相反,就是將已編譯好的編程語言還原到未編譯的狀態(tài),也就是找出程序語言的源代碼。就是將機(jī)器看得懂的語言轉(zhuǎn)換成程序員可以看得懂的語言。Java語言中的反編譯一般指將class文件轉(zhuǎn)換成java文件。

有了反編譯工具,我們可以做很多事情,最主要的功能就是有了反編譯工具,我們就能讀得懂Java編譯器生成的字節(jié)碼。如果你想問讀懂字節(jié)碼有啥用,那么我可以很負(fù)責(zé)任的告訴你,好處大大的。比如我的博文幾篇典型的原理性文章,都是通過反編譯工具得到反編譯后的代碼分析得到的。如深入理解多線程(一)——Synchronized的實(shí)現(xiàn)原理、深度分析Java的枚舉類型—-枚舉的線程安全性及序列化問題、Java中的Switch對(duì)整型、字符型、字符串型的具體實(shí)現(xiàn)細(xì)節(jié)、Java的類型擦除等。我最近在GitChat寫了一篇關(guān)于Java語法糖的文章,其中大部分內(nèi)容都用到反編譯工具來洞悉語法糖背后的原理。

Java反編譯工具

本文主要介紹3個(gè)Java的反編譯工具:javap、jad和cfr

javap

javap是jdk自帶的一個(gè)工具,可以對(duì)代碼反編譯,也可以查看java編譯器生成的字節(jié)碼。javap和其他兩個(gè)反編譯工具最大的區(qū)別是他生成的文件并不是java文件,也不像其他兩個(gè)工具生成代碼那樣更容易理解。拿一段簡(jiǎn)單的代碼舉例,如我們想分析Java 7中的switch是如何支持String的,我們先有以下可以編譯通過的源代碼:

public class switchDemoString {
 public static void main(String[] args) {
 String str = "world";
 switch (str) {
  case "hello":
  System.out.println("hello");
  break;
  case "world":
  System.out.println("world");
  break;
  default:
  break;
 }
 }
}

執(zhí)行以下兩個(gè)命令:

javac switchDemoString.java
javap -c switchDemoString.class

生成代碼如下:

public class com.hollis.suguar.switchDemoString {
 public com.hollis.suguar.switchDemoString();
 Code:
 0: aload_0
 1: invokespecial #1   // Method java/lang/Object."<init>":()V
 4: return

 public static void main(java.lang.String[]);
 Code:
 0: ldc  #2   // String world
 2: astore_1
 3: aload_1
 4: astore_2
 5: iconst_m1
 6: istore_3
 7: aload_2
 8: invokevirtual #3   // Method java/lang/String.hashCode:()I
 11: lookupswitch { // 2
  99162322: 36
  113318802: 50
  default: 61
  }
 36: aload_2
 37: ldc  #4   // String hello
 39: invokevirtual #5   // Method java/lang/String.equals:(Ljava/lang/Object;)Z
 42: ifeq  61
 45: iconst_0
 46: istore_3
 47: goto  61
 50: aload_2
 51: ldc  #2   // String world
 53: invokevirtual #5   // Method java/lang/String.equals:(Ljava/lang/Object;)Z
 56: ifeq  61
 59: iconst_1
 60: istore_3
 61: iload_3
 62: lookupswitch { // 2
   0: 88
   1: 99
  default: 110
  }
 88: getstatic #6   // Field java/lang/System.out:Ljava/io/PrintStream;
 91: ldc  #4   // String hello
 93: invokevirtual #7   // Method java/io/PrintStream.println:(Ljava/lang/String;)V
 96: goto  110
 99: getstatic #6   // Field java/lang/System.out:Ljava/io/PrintStream;
 102: ldc  #2   // String world
 104: invokevirtual #7   // Method java/io/PrintStream.println:(Ljava/lang/String;)V
 107: goto  110
 110: return
}

我個(gè)人的理解,javap并沒有將字節(jié)碼反編譯成java文件,而是生成了一種我們可以看得懂字節(jié)碼。其實(shí)javap生成的文件仍然是字節(jié)碼,只是程序員可以稍微看得懂一些。如果你對(duì)字節(jié)碼有所掌握,還是可以看得懂以上的代碼的。其實(shí)就是把String轉(zhuǎn)成hashcode,然后進(jìn)行比較。

個(gè)人認(rèn)為,一般情況下我們會(huì)用到j(luò)avap命令的時(shí)候不多,一般只有在真的需要看字節(jié)碼的時(shí)候才會(huì)用到。但是字節(jié)碼中間暴露的東西是最全的,你肯定有機(jī)會(huì)用到,比如我在分析synchronized的原理的時(shí)候就有是用到j(luò)avap。通過javap生成的字節(jié)碼,我發(fā)現(xiàn)synchronized底層依賴了ACC_SYNCHRONIZED標(biāo)記和monitorenter、monitorexit兩個(gè)指令來實(shí)現(xiàn)同步。

jad

jad是一個(gè)比較不錯(cuò)的反編譯工具,只要下載一個(gè)執(zhí)行工具,就可以實(shí)現(xiàn)對(duì)class文件的反編譯了。還是上面的源代碼,使用jad反編譯后內(nèi)容如下:

命令:jad switchDemoString.class

public class switchDemoString
{
 public switchDemoString()
 {
 }
 public static void main(String args[])
 {
 String str = "world";
 String s;
 switch((s = str).hashCode())
 {
 default:
  break;
 case 99162322:
  if(s.equals("hello"))
  System.out.println("hello");
  break;
 case 113318802:
  if(s.equals("world"))
  System.out.println("world");
  break;
 }
 }
}

看,這個(gè)代碼你肯定看的懂,因?yàn)檫@不就是標(biāo)準(zhǔn)的java的源代碼么。這個(gè)就很清楚的可以看到原來字符串的switch是通過equals()和hashCode()方法來實(shí)現(xiàn)的。

但是,jad已經(jīng)很久不更新了,在對(duì)Java7生成的字節(jié)碼進(jìn)行反編譯時(shí),偶爾會(huì)出現(xiàn)不支持的問題,在對(duì)Java 8的lambda表達(dá)式反編譯時(shí)就徹底失敗。

CFR

jad很好用,但是無奈的是很久沒更新了,所以只能用一款新的工具替代他,CFR是一個(gè)不錯(cuò)的選擇,相比jad來說,他的語法可能會(huì)稍微復(fù)雜一些,但是好在他可以work。

如,我們使用cfr對(duì)剛剛的代碼進(jìn)行反編譯。執(zhí)行一下命令:

java -jar cfr_0_125.jar switchDemoString.class --decodestringswitch false

得到以下代碼:

public class switchDemoString {
 public static void main(String[] arrstring) {
 String string;
 String string2 = string = "world";
 int n = -1;
 switch (string2.hashCode()) {
  case 99162322: {
  if (!string2.equals("hello")) break;
  n = 0;
  break;
  }
  case 113318802: {
  if (!string2.equals("world")) break;
  n = 1;
  }
 }
 switch (n) {
  case 0: {
  System.out.println("hello");
  break;
  }
  case 1: {
  System.out.println("world");
  break;
  }
 }
 }
}

通過這段代碼也能得到字符串的switch是通過equals()和hashCode()方法來實(shí)現(xiàn)的結(jié)論。

相比Jad來說,CFR有很多參數(shù),還是剛剛的代碼,如果我們使用以下命令,輸出結(jié)果就會(huì)不同:

java -jar cfr_0_125.jar switchDemoString.class

public class switchDemoString {
 public static void main(String[] arrstring) {
 String string;
 switch (string = "world") {
  case "hello": {
  System.out.println("hello");
  break;
  }
  case "world": {
  System.out.println("world");
  break;
  }
 }
 }
}

所以--decodestringswitch表示對(duì)于switch支持string的細(xì)節(jié)進(jìn)行解碼。類似的還有--decodeenumswitch、--decodefinally、--decodelambdas等。在我的關(guān)于語法糖的文章中,我使用--decodelambdas對(duì)lambda表達(dá)式警進(jìn)行了反編譯。 源碼:

public static void main(String... args) {
 List<String> strList = ImmutableList.of("Hollis", "公眾號(hào):Hollis", "博客:www.hollischuang.com");

 strList.forEach( s -> { System.out.println(s); } );
}

java -jar cfr_0_125.jar lambdaDemo.class --decodelambdas false反編譯后代碼:

public static /* varargs */ void main(String ... args) {
 ImmutableList strList = ImmutableList.of((Object)"Hollis", (Object)"\u516c\u4f17\u53f7\uff1aHollis", (Object)"\u535a\u5ba2\uff1awww.hollischuang.com");
 strList.forEach((Consumer<String>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());
}

private static /* synthetic */ void lambda$main$0(String s) {
 System.out.println(s);
}

CFR還有很多其他參數(shù),均用于不同場(chǎng)景,讀者可以使用java -jar cfr_0_125.jar --help進(jìn)行了解。這里不逐一介紹了。

如何防止反編譯

由于我們有工具可以對(duì)Class文件進(jìn)行反編譯,所以,對(duì)開發(fā)人員來說,如何保護(hù)Java程序就變成了一個(gè)非常重要的挑戰(zhàn)。但是,魔高一尺、道高一丈。當(dāng)然有對(duì)應(yīng)的技術(shù)可以應(yīng)對(duì)反編譯咯。但是,這里還是要說明一點(diǎn),和網(wǎng)絡(luò)安全的防護(hù)一樣,無論做出多少努力,其實(shí)都只是提高攻擊者的成本而已。無法徹底防治。

典型的應(yīng)對(duì)策略有以下幾種:

  • 隔離Java程序
    • 讓用戶接觸不到你的Class文件
  • 對(duì)Class文件進(jìn)行加密
    • 提到破解難度
  • 代碼混淆
    • 將代碼轉(zhuǎn)換成功能上等價(jià),但是難于閱讀和理解的形式

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • java通過反射創(chuàng)建對(duì)象并調(diào)用方法

    java通過反射創(chuàng)建對(duì)象并調(diào)用方法

    這篇文章主要介紹了java通過反射創(chuàng)建對(duì)象并調(diào)用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 使用Aop的方式實(shí)現(xiàn)自動(dòng)日志記錄的方式詳細(xì)介紹

    使用Aop的方式實(shí)現(xiàn)自動(dòng)日志記錄的方式詳細(xì)介紹

    這篇文章主要介紹了使用Aop的方式實(shí)現(xiàn)自動(dòng)日志記錄,通過監(jiān)聽器去監(jiān)聽,當(dāng)訪問到具體的類方法,通過aop切面去獲取訪問的方法,然后將日志記錄下來,就這種方式給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • MyBatis?Plus?導(dǎo)入IdType失敗的解決

    MyBatis?Plus?導(dǎo)入IdType失敗的解決

    這篇文章主要介紹了MyBatis?Plus?導(dǎo)入IdType失敗的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • hadoop client與datanode的通信協(xié)議分析

    hadoop client與datanode的通信協(xié)議分析

    本文主要分析了hadoop客戶端read和write block的流程. 以及client和datanode通信的協(xié)議, 數(shù)據(jù)流格式等
    2012-11-11
  • 解決idea不顯示Services工具欄的問題

    解決idea不顯示Services工具欄的問題

    這篇文章主要介紹了解決idea不顯示Services工具欄的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • 詳解SpringBoot中JdbcTemplate的事務(wù)控制

    詳解SpringBoot中JdbcTemplate的事務(wù)控制

    JdbcTemplate是spring-jdbc提供的數(shù)據(jù)庫核心操作類,那對(duì)JdbcTemplate進(jìn)行事務(wù)控制呢,本文就詳細(xì)的介紹一下
    2021-09-09
  • java selenium Selenium IDE介紹及用法

    java selenium Selenium IDE介紹及用法

    本文主要介紹java selenium Selenium IDE,這里整理了相關(guān)資料和介紹如何安裝 Selenium IDE和使用方法,有需要的小伙伴可以參考下
    2016-08-08
  • Java文件讀寫IO/NIO及性能比較詳細(xì)代碼及總結(jié)

    Java文件讀寫IO/NIO及性能比較詳細(xì)代碼及總結(jié)

    這篇文章主要介紹了Java文件讀寫IO/NIO及性能比較詳細(xì)代碼及總結(jié),具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • 聊聊Spring data jpa @query使用原生SQl,需要注意的坑

    聊聊Spring data jpa @query使用原生SQl,需要注意的坑

    這篇文章主要介紹了Spring data jpa@query使用原生SQl,需要注意的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • IDEA利用jclasslib 修改class文件的實(shí)現(xiàn)

    IDEA利用jclasslib 修改class文件的實(shí)現(xiàn)

    這篇文章主要介紹了IDEA利用jclasslib 修改class文件的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02

最新評(píng)論

平遥县| 郁南县| 临武县| 武山县| 福海县| 萝北县| 沅江市| 张家港市| 东安县| 麟游县| 观塘区| 睢宁县| 酒泉市| 武义县| 绿春县| 从江县| 江永县| 永善县| 镇远县| 昌图县| 图木舒克市| 武平县| 日土县| 夏邑县| 毕节市| 北海市| 闵行区| 乾安县| 托里县| 玛纳斯县| 英吉沙县| 永登县| 沐川县| 建阳市| 桐乡市| 东明县| 东方市| 上蔡县| 衡阳市| 斗六市| 南安市|