一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - 入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

2023-12-22 01:13未知服務(wù)器之家 Java教程

前言:眾所周知, i++ 和 ++i 的區(qū)別是: i++ 先將 i 的值賦值給變量,再將 i 的值自增1;而 ++i 則是先將 i 的值自增1,再將結(jié)果賦值給變量。因此,二者最終都給 i 自增了1,只是方式不同而已。 當然,如果在面試過程中面試官問你

前言:眾所周知,i++++i的區(qū)別是:i++先將i的值賦值給變量,再將i的值自增1;而++i則是先將i的值自增1,再將結(jié)果賦值給變量。因此,二者最終都給i自增了1,只是方式不同而已。

當然,如果在面試過程中面試官問你這個問題,只回答出上述內(nèi)容,只能說明你對這方面的知識了解的還是太淺顯。那么i++++i到底有什么不同之處呢?

一、局部變量表與操作數(shù)棧簡介

《深入理解Java虛擬機》第八章對棧幀結(jié)構(gòu)有如下描述Java虛擬機以方法作為最基本的執(zhí)行單元,“棧幀”(Stack Frame)則是用于支持虛擬機進行方法調(diào)用和方法執(zhí)行背后的數(shù)據(jù)結(jié)構(gòu),它也是虛擬機運行時數(shù)據(jù)區(qū)中的虛擬機棧的棧元素。

在一個活動線程中,可能會執(zhí)行多個方法,因此會存在多個棧幀,和“棧”(先進后出)一樣,處于棧頂?shù)臈攀钦嬲\行的,處于棧頂?shù)臈Q作“當前棧幀”(Current Stack Frame),這個棧幀所屬的方法稱作“當前方法”(Current Method)。

在執(zhí)行main方法時,main方法所屬的線程主線程,假設(shè)在主線程中調(diào)用了一個method1()方法,在method1()內(nèi)部調(diào)用了method2()方法,在method2()方法執(zhí)行兩個整數(shù)運算,示例如下:

/**
 * 方法調(diào)用
 *
 * @author iCode504
 * @date 2023-10-23 22:05
 */
public class StackFrameDemo1 {
    public static void main(String[] args) {
        System.out.println("main開始執(zhí)行");
        method1();
        System.out.println("main執(zhí)行完成");
    }

    private static void method1() {
        System.out.println("method1開始執(zhí)行");
        int result = method2();
        System.out.println("result = " + result);
        System.out.println("method1執(zhí)行結(jié)束");
    }

    private static int method2() {
        int var1 = 10;
        int var2 = 20;
        return var1 + var2;
    }
}

運行結(jié)果:

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

由代碼我們可以看出,main方法最先執(zhí)行一個輸出,然后進入method1執(zhí)行第一個輸出,再完整執(zhí)行method2method2執(zhí)行完成以后,再執(zhí)行method1,最后執(zhí)行main方法,由于這段代碼中只涉及一個主線程,并且最先完整執(zhí)行方法的是method2,因此method2對應(yīng)的棧幀就是當前棧幀,main方法最后執(zhí)行完畢,因此main方法對應(yīng)的棧幀在method2method1之下。以下是這段代碼對應(yīng)的棧幀概念圖:

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

在每一個棧幀中存儲了方法的局部變量表、操作數(shù)棧、動態(tài)鏈接和方法返回地址等信息

1.1 局部變量表

局部變量表(Local variable Table)是一組變量值的存儲空間,用于存放方法參數(shù)和方法內(nèi)部定義的局部變量。

局部變量表的容量是以變量槽(Variable Slot)為最小單位,每個變量槽能存儲基本數(shù)據(jù)類型和引用數(shù)據(jù)類型的數(shù)據(jù)。為了盡可能節(jié)省棧幀消耗的內(nèi)存空間,局部變量表中的變量槽是可以重用的。

JVM使用索引定位的方式使用索引變量表,索引值的范圍是從0開始到局部變量表最大變量槽的數(shù)量(類似數(shù)組結(jié)構(gòu))。

當一個方法被調(diào)用的時候,JVM會使用局部變量表來完成參數(shù)值到參數(shù)變量列表的傳遞,即實參到形參的傳遞。

1.2 操作數(shù)棧

操作數(shù)棧(Operand Stack)也稱作操作數(shù)棧,它是一個棧結(jié)構(gòu)(后進先出,例如手槍的彈夾,先打出去的子彈是最頂上的子彈)。

在方法開始執(zhí)行的時候,這個方法對應(yīng)的操作數(shù)棧是空的,在方法執(zhí)行過程中,會有各種字節(jié)碼指令向操作數(shù)棧中寫入或讀取內(nèi)容,即出棧和入棧操作,例如:兩數(shù)相加運算時,就需要將兩個數(shù)壓入棧頂后調(diào)用運算指令。

操作數(shù)棧中的元素的數(shù)據(jù)類型必須和字節(jié)碼指令序列嚴格匹配,在編譯程序代碼的時候編譯器必須要嚴格保證這一點,在類的校驗階段的數(shù)據(jù)流分析時候還需要再次校驗。例如:執(zhí)行加法iaddiint類型,add是兩個數(shù)相加)命令時,就需要保證兩個操作數(shù)必須是int類型,不能出現(xiàn)其他類型相加的情況。

二、字節(jié)碼分析(圖解)

我們可以從字節(jié)碼的角度進一步對i++++i的執(zhí)行過程做進一步的分析。以下面代碼為例:

/**
 * i++和++i的深入分析
 * 
 * @author iCode504
 * @date 2023-10-17 5:58
 */
public class IncrementAndDecrementOperators2 {
    public static void main(String[] args) {
        int intValue1 = 2;
        int intValue2 = 2;
        int result1 = intValue1++;
        int result2 = ++intValue2;
        System.out.println("result1 = " + result1);
        System.out.println("result2 = " + result2);
    }
}

我們需要查看編譯后的字節(jié)碼文件,字節(jié)碼文件不能直接使用記事本打開,但是我們可以使用javap -verbose 文件名.class命令,以IncrementAndDecrementOperators2.class為例:

javap -verbose IncrementAndDecrementOperators2.class

此時就會打開所有的字節(jié)碼文件,我們只需要關(guān)注main方法內(nèi)的執(zhí)行過程即可:

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

首先來解釋一下這四行代碼的含義:

0: iconst_2
1: istore_1
2: iconst_2
3: istore_2
  • iconst_2一共有兩部分組成,i指的是int類型(源代碼中我們定義的確實是int類型),const代表常量(數(shù)字2是整型常量),iconst_2的含義是將2入操作數(shù)棧。
  • istore_1中的store代表的是存儲,istore_1的含義是將操作數(shù)棧中的數(shù)值2出棧,存入到局部變量表1的位置。同理,i_store2表示將操作數(shù)棧中的數(shù)值2出棧,存儲到局部變量表2的位置。

以下是前面四行代碼存儲過程圖(存儲過程全部流程圖點擊此鏈接下載:點我下載):

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

此時我們繼續(xù)觀察4-8行代碼:

4: iload_1
5: iinc			1, 1
8: istore_3
  • iload_1的作用是將局部變量表1號位置存儲的值移動到操作數(shù)棧的棧頂。
  • 第5行的iinc有兩個參數(shù),第一個參數(shù)1是局部變量表的位置,另一個參數(shù)1的含義是在該位置存儲一個1,如果這個位置存在值,那么這個值的結(jié)果是已存在值 + 參數(shù)值
  • istore_3將操作數(shù)棧中的數(shù)移動到局部變量表的3號位置。

以下是這三行代碼的示意圖:

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

9-12行的字節(jié)碼的作用原理和4-8行的作用原理基本相同:

9: iinc			2, 1
12: iload_2
13: istore		4

istore 4的作用是將操作數(shù)棧中的值存儲到局部變量表4號位置。

以下是這三行代碼的示意圖:

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

接下來15-30行是和系統(tǒng)輸出有關(guān)的。其中第30行iload_3在局部變量表中(這個值為2)值移動到操作數(shù)棧頂供系統(tǒng)輸出,事實上iload_3的值正好對應(yīng)源代碼中變量result1的值。也就是說,result1輸出結(jié)果就是iload_3的數(shù)值2。

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

同理,iload 4就是第二個要輸出的值,在局部變量表中第4個位置存儲的值正好是3,而輸出的變量名是result2,因此result2的輸出結(jié)果是3。

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

三、i++++i性能分析

i++++i主要用在普通for循環(huán)上,那么我們就將二者用在for循環(huán)上,循環(huán)相同的次數(shù),從字節(jié)碼的角度進行分析。

以下是使用i++++i的兩個for循環(huán)文件:

/**
 * i++在for循環(huán)的使用
 *
 * @author ZhaoCong
 * @date 2023-10-21 16:14:33
 */
public class LoopTest1 {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {

        }
    }
}
/**
 * ++i在for循環(huán)的使用
 *
 * @author ZhaoCong
 * @date 2023-10-21 16:15:17
 */
public class LoopTest2 {
    public static void main(String[] args) {
        for (int i = 0; i < 100; ++i) {

        }
    }
}

執(zhí)行編譯命令以后,我們來查看兩個文件的字節(jié)碼:

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

入門篇-其之六-附錄一-以Java字節(jié)碼的角度分析i++和++i

仔細觀察這兩個字節(jié)碼文件內(nèi)容,我們發(fā)現(xiàn)在兩個文件main方法的字節(jié)碼內(nèi)容完全相同。由此可見,兩種方式執(zhí)行for循環(huán)的效率是相同的。

延伸 · 閱讀

精彩推薦
  • Java教程SpringBoot 緩存 Caffeine使用解析

    SpringBoot 緩存 Caffeine使用解析

    這篇文章主要介紹了SpringBoot 緩存 Caffeine使用解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教...

    西涼的悲傷9442022-01-07
  • Java教程Maven工程搭建spring boot+spring mvc+JPA的示例

    Maven工程搭建spring boot+spring mvc+JPA的示例

    本篇文章主要介紹了Maven工程搭建spring boot+spring mvc+JPA的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧...

    涼城4272021-03-25
  • Java教程eclipse導(dǎo)入IntelliJ IDEA的maven項目的示例

    eclipse導(dǎo)入IntelliJ IDEA的maven項目的示例

    本篇文章主要介紹了eclipse導(dǎo)入IntelliJ IDEA的maven項目的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧...

    yulsh7672021-03-02
  • Java教程IDEA Debug模式下改變各類型變量值的方法

    IDEA Debug模式下改變各類型變量值的方法

    這篇文章主要介紹了IDEA Debug模式下改變各類型變量值的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧...

    須木一瓜15762021-04-22
  • Java教程spring集成redis cluster詳解

    spring集成redis cluster詳解

    這篇文章主要介紹了spring集成redis cluster詳解,分享了maven依賴,Spring配置,增加connect-redis.properties 配置文件等相關(guān)內(nèi)容,具有一定參考價值,需要的朋友可...

    cenmin16422021-02-19
  • Java教程Java 21 新特性:Record Patterns

    Java 21 新特性:Record Patterns

    Record Patterns 第一次發(fā)布預(yù)覽是在JDK 19、隨后又在JDK 20中進行了完善。現(xiàn)在,Java 21開始正式推出該特性優(yōu)化。下面我們通過一個例子來理解這個新特性。 ...

    未知1532023-09-29
  • Java教程Java 無參數(shù)構(gòu)造函數(shù)的應(yīng)用

    Java 無參數(shù)構(gòu)造函數(shù)的應(yīng)用

    本篇文章主要介紹了Java 無參數(shù)構(gòu)造函數(shù)的應(yīng)用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧...

    文戲那瞥8802021-04-22
  • Java教程Flutter瀑布流仿寫原生的復(fù)用機制詳解

    Flutter瀑布流仿寫原生的復(fù)用機制詳解

    這篇文章主要給大家介紹了關(guān)于Flutter瀑布流仿寫原生的復(fù)用機制的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用flutter具有一定的參考學...

    頭疼腦脹的代碼搬運工7512021-11-01
主站蜘蛛池模板: 国产大乳美女挤奶视频 | 女张腿男人桶羞羞漫画 | 韩日一区二区三区 | 精品国产免费一区二区三区 | 久久99国产精品二区不卡 | 亚洲骚图 | 色婷婷久久综合中文久久一本` | 久久er99热精品一区二区 | 王小军怎么了最新消息 | 色播开心网 | 精品国产品国语在线不卡丶 | 太粗 好紧 使劲舒服 | 免费观看国产视频 | 国产在线播放91 | 国产精品永久免费10000 | 色综合色狠狠天天综合色hd | 精品一区二区高清在线观看 | 亚洲精品一区二区久久这里 | 久久久久嫩草影院精品 | 香蕉成人999视频 | www.色小妹| 香蕉国产成版人视频在线观看 | 国产91精选在线观看麻豆 | nhdta系列媚药系列 | 青草视频网址 | 超碰成人在线播放 | 日岳母小说 | 免费369看片入口 | 国产美女屁股直流白浆视频无遮挡 | 日韩小视频在线观看 | 亚洲国产精品综合久久一线 | 2022超帅男同gayxxx | 天天拍天天色 | 成人免费一区二区三区在线观看 | 亚洲国产综合精品 | 亚洲天堂一区二区在线观看 | 日本韩国在线 | 国产午夜精品一区二区 | 成人福利影院 | 成人国产网站v片免费观看 成人国产精品视频 | 四虎影院久久久 |