Java自增

来源:https://www.cnblogs.com/ytryhard/archive/2022/04/14/16145620.html
-Advertisement-
Play Games

Java自增 本文分為以下部分: 慄子 慄子解釋 來點複雜的 位元組碼解讀 總結 慄子 java存在一種神奇的操作符,,自增1,但是經常分不清楚 **i** 和**++i** 兩者的區別,雖然最後結果可能都是 i+1,但是在不同場景使用有不同效果。先上一段代碼。 public class Increa ...


Java自增

本文分為以下部分:

慄子

java存在一種神奇的操作符,++,自增1,但是經常分不清楚 i++++i 兩者的區別,雖然最後結果可能都是 i+1,但是在不同場景使用有不同效果。先上一段代碼。

public class IncreaseTest {

    public static void main(String[] args) {
        int i = 10;

        int j = i++;
        System.out.println(j);

        int k = ++i;
        System.out.println(k);
    }

}

看著腦袋都大,感覺 j、k 最後值都一樣,實際上不一樣。在講解原理之前,先簡單說明一下底層東西。

局部變數表

oracle java 局部變數表 中解釋

image-20220414142156180

其中第二段解釋,byte、char、short、int 等基本數據類型值會存在局部變數表中。

操作數棧

oracle java 操作數棧 中解釋

image-20220414142608968

簡單理解就是 主要用於保存計算過程的中間結果,同時作為計算過程中變數臨時的存儲空間。就是用於計算等操作。

舉個簡單的慄子簡單解釋上面兩個東西

/**
 * 操作數棧壓入 10 這個值
 * 然後將 10 保存到本地變數表賦值給 i
 * j同理
 *
 * 在進行 k = i + j 的操作
 * 操作數棧從本地變數表中讀取 i 的值壓入操作數棧
 * 操作數棧從本地變數表中讀取 j 的值壓入操作數棧
 * 然後對操作數棧中的兩個值進行相加操作
 * 將結果保存到本地變數表同時賦值給 k
 * 最後輸出 本地變數表中 讀取的數據
 */
public static void main(String[] args) {
    int i = 10;
    int j = 20;
    int k = i + j;
    
    System.out.println(k);
}

圖例如下:

image-20220414170111681

慄子解釋

回歸正題,最初的慄子,在計算 j 的時候,是先將 i 的本地變數表的值取出來壓入操作數棧,然後再進行 ++ 、賦值等操作,那是先賦值還是先 ++ 呢,其實已經不重要了,因為 ++ 操作操作的是本地變數表的值,而不是操作數棧中的值,所以當 i 的值已經壓入操作數棧後,那麼操作數棧中的值已經是 10 了,就算本地變數表的值再變化,不會改變操作數棧中的值。所以 j 為 10,本地變數表中 i 為11。(事實上是i的值取出來壓入操作數棧,然後i的本地變數表進行+1操作,然後操作數棧賦值給j)

image-20220414170702152

然後第二步,進行的是 int k = ++i;

首先看到的是 ++,所以操作的是本地變數表 i+1 ,然後再將 i 的本地變數表值壓入操作數棧,再賦值給 k。

image-20220414170935455

所以 i++ ,是先將 i 的值壓入操作數棧,再將本地變數表中 i 的值 +1 ,再將操作數棧中的數值賦值給要賦值的對象。

++i ,是先將本地變數表中 i 的值 +1,再壓入操作數棧中,再進行賦值給對象等操作。

簡記:先++就是先+1,後++就是後+1。

來點複雜的

public class IncreaseTest2 {

    public static void main(String[] args) {
        int i = 10;
        i = i++;
        System.out.println(i);

        i = ++i;
        System.out.println(i);
    }

}

首先看 i = i++;

通過簡單的慄子,我們知道,當 i++ 再後面時,為後 ++;那麼是 i 先賦值給自己 ,再 +1?

當然不是,仔細看上方賦值給 j 的圖會發現,先本地變數 +1 ,再進行賦值。

image-20220414155137003

然後再看 ++i,和第一個慄子差不多

image-20220414155511461

位元組碼解讀

第一個慄子

在終端中使用 javap -v -p xxx.class 文件可以看到該class文件的位元組碼文件。由於不好閱讀,所以追加覆蓋到 test1.txt 文件中。(採用idea的 jclasslib 插件也可以)

image-20220414155918804

在文件中找到熟悉的public static void main,下麵即為代碼的位元組碼,我貼一部分

image-20220414160203611

感覺有點晦澀難懂,我一一解釋吧。

bipush 將數值(當前10位byte,-128—127均為byte)壓入操作數棧。

istore 將操作數棧棧頂的值彈出返回給本地變數表(保存到本地變數)。

iload 從本地變數表中讀取數值壓入操作數棧。

iinc 本地變數表中的值+1

0: bipush        10	//操作數棧壓入10
2: istore_1		//將10存儲到本地變數i (這裡1表示i,可以從文件下方LocalVariableTable查看)
3: iload_1		//讀取i的值 10 壓入操作數棧
4: iinc          1, 1	//本地變數中 i 的值 +1(操作數棧值不變,依舊為10)
7: istore_2		//操作數棧棧頂(10)的值彈出返回本地變數 j ,所以 j 為10

11: iload_2		//有個列印輸出流,需要讀取 j 的值,刪除了相應位元組碼

15: iinc          1, 1	//本地變數 i 的值 +1(原本為11,現在變為12)
18: iload_1		//讀取i的值 12 壓入操作數棧
19: istore_3	        //保存至 k ,所以 k 為12

第二個慄子

使用同樣方法查看位元組碼

image-20220414161847608

其實和第一個慄子一樣,可以自行理解。

特慄

public class IncreaseTest3 {

    public static void main(String[] args) {
        int i = 10;

        i = (i++) * (++i);
	
        //120
        System.out.println(i);
    }

}

其實再理解上方位元組碼或者圖解,這個自然而然容易理解了

圖解

image-20220414171200169

位元組碼大概描述就是

iload 	//10 入操作數棧
iinc	//本地變數 +1 (操作數棧中依舊為10)
innc	//本地變數繼續 +1(此時為12)
iload	//12 入操作數棧
//然後進行乘法得到120
istore

總結

i++ ,是先將 i 的值壓入操作數棧,再將本地變數表中 i 的值 +1 ,最後操作數棧的數進行操作。

++i ,是先將本地變數表中 i 的值 +1,再壓入操作數棧中,最後操作數棧的數進行操作。

簡單記憶:先++就是先+1,後++就是後+1。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 1. JavaScript有哪些數據類型,它們的區別? JavaScript共有八種數據類型,分別是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。 其中 Symbol ...
  • 簡單開頭 首先技術面試官會根據簡歷里所寫的項目和個人掌握技術棧提問(我不知道已經改過多少次簡歷了,因為前期投簡歷是真的是沉在茫茫大海,撈漂流瓶都撈不到的那種) 我的技術棧:(Vue還在苦苦的自學當中,隨便推薦一下coderwhy老師B站的教學視頻,真的不錯,講得深入淺出,越聽越想聽)地址:https ...
  • 基本原則 原則一:價值為王 解析: 價值為王的另一種說法叫做YAGNI。YAGNI 是 You aren’t gonna need it 的縮寫。該原則的基本含義就是,不應該開發任何當前不使用的功能。因為這些占用開發成本的功能,可能根本沒有人用。而且不僅僅是開發成本打了水漂,你還要不斷投入維護成本, ...
  • package struct;import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory;public class SwitchDemo1 { public static void main(String[] args){ ...
  • Web-Servlet(2)--Thymeleaf 視圖模板技術,是做視圖渲染的一個技術(靜態頁面和數據柔和在一起) 基本流程 在伺服器端引入Thymeleaf環境 1.加入jar包 2.新建一個Servlet類ViewBaseServlet package src.com.wht.myssm.my ...
  • io流主要應用在各種腳本的開發列如,一個目錄爬行要去使用字典文件,還可以用來進行文件加密。後面可以深入研究一下序列化漏洞 ...
  • 在直播、語聊房、K 歌房場景中,為增加趣味性和互動性,玩家可以通過變聲來搞怪,通過混響烘托氣氛,通過立體聲使聲音更具立體感。ZegoExpress SDK 提供了多種預設的變聲、混響、混響回聲、立體聲效果,開發者可以靈活設置自己想要的聲音,在通話或直播過程中動態調整變聲、混響、混響回聲、虛擬立體聲,... ...
  • 一、profile的多文檔配置方式 1、profile文件方式:提供多個配置文件,每個代表一種環境 如: 1.application-dev.properties/yml 開發環境 2.application-test.properties/yml 測試環境 3.application-pro.pr ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...