欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

學會Java字節(jié)碼指令,成為技術(shù)大佬

 更新時間:2021年08月12日 17:20:39   作者:沉默王二  
Java 字節(jié)碼指令是 JVM 體系中非常難啃的一塊硬骨頭,我估計有些讀者會有這樣的疑惑,“Java 字節(jié)碼難學嗎?我能不能學會啊?”本文帶領(lǐng)大家一探究竟,幫助大家搞懂java底層代碼如何執(zhí)行

Java 官方的虛擬機 Hotspot 是基于棧的,而不是基于寄存器的。

基于棧的優(yōu)點是可移植性更好、指令更短、實現(xiàn)起來簡單,但不能隨機訪問棧中的元素,完成相同功能所需要的指令數(shù)也比寄存器的要多,需要頻繁的入棧和出棧。

基于寄存器的優(yōu)點是速度快,有利于程序運行速度的優(yōu)化,但操作數(shù)需要顯式指定,指令也比較長。

Java 字節(jié)碼由操作碼和操作數(shù)組成。

  • 操作碼(Opcode):一個字節(jié)長度(0-255,意味著指令集的操作碼總數(shù)不可能超過 256 條),代表著某種特定的操作含義。
  • 操作數(shù)(Operands):零個或者多個,緊跟在操作碼之后,代表此操作需要的參數(shù)。

由于 Java 虛擬機是基于棧而不是寄存器的結(jié)構(gòu),所以大多數(shù)指令都只有一個操作碼。比如aload_0(將局部變量表中下標為 0 的數(shù)據(jù)壓入操作數(shù)棧中)就只有操作碼沒有操作數(shù),而 invokespecial #1(調(diào)用成員方法或者構(gòu)造方法,并傳遞常量池中下標為 1 的常量)就是由操作碼和操作數(shù)組成的。

01、加載與存儲指令

加載(load)和存儲(store)相關(guān)的指令是使用最頻繁的指令,用于將數(shù)據(jù)從棧幀的局部變量表和操作數(shù)棧之間來回傳遞。

1)將局部變量表中的變量壓入操作數(shù)棧中

  • xload_(x 為 i、l、f、d、a,n 默認為 0 到 3),表示將第 n 個局部變量壓入操作數(shù)棧中。
  • xload(x 為 i、l、f、d、a),通過指定參數(shù)的形式,將局部變量壓入操作數(shù)棧中,當使用這個指令時,表示局部變量的數(shù)量可能超過了 4 個

解釋一下

x 為操作碼助記符,表明是哪一種數(shù)據(jù)類型。見下表所示。

像 arraylength 指令,沒有操作碼助記符,它沒有代表數(shù)據(jù)類型的特殊字符,但操作數(shù)只能是一個數(shù)組類型的對象。

大部分的指令都不支持 byte、short 和 char,甚至沒有任何指令支持 boolean 類型。編譯器會將 byte 和 short 類型的數(shù)據(jù)帶符號擴展(Sign-Extend)為 int 類型,將 boolean 和 char 零位擴展(Zero-Extend)為 int 類型。

舉例來說:

private void load(int age, String name, long birthday, boolean sex) {
    System.out.println(age + name + birthday + sex);
}

通過 jclasslib 看一下 load() 方法(4 個參數(shù))的字節(jié)碼指令。

  • iload_1:將局部變量表中下標為 1 的 int 變量壓入操作數(shù)棧中。
  • aload_2:將局部變量表中下標為 2 的引用數(shù)據(jù)類型變量(此時為 String)壓入操作數(shù)棧中。
  • lload_3:將局部變量表中下標為 3 的 long 型變量壓入操作數(shù)棧中。
  • iload 5:將局部變量表中下標為 5 的 int 變量(實際為 boolean)壓入操作數(shù)棧中。

通過查看局部變量表就能關(guān)聯(lián)上了。

2)將常量池中的常量壓入操作數(shù)棧中

根據(jù)數(shù)據(jù)類型和入棧內(nèi)容的不同,此類又可以細分為 const 系列、push 系列和 Idc 指令。

const 系列,用于特殊的常量入棧,要入棧的常量隱含在指令本身。

push 系列,主要包括 bipush 和 sipush,前者接收 8 位整數(shù)作為參數(shù),后者接收 16 位整數(shù)。

Idc 指令,當 const 和 push 不能滿足的時候,萬能的 Idc 指令就上場了,它接收一個 8 位的參數(shù),指向常量池中的索引。

  • Idc_w:接收兩個 8 位數(shù),索引范圍更大。
  • 如果參數(shù)是 long 或者 double,使用 Idc2_w 指令。

舉例來說:

public void pushConstLdc() {
    // 范圍 [-1,5]
    int iconst = -1;
    // 范圍 [-128,127]
    int bipush = 127;
    // 范圍 [-32768,32767]
    int sipush= 32767;
    // 其他 int
    int ldc = 32768;
    String aconst = null;
    String IdcString = "沉默王二";
}

 通過 jclasslib 看一下 pushConstLdc() 方法的字節(jié)碼指令。

  • iconst_m1:將 -1 入棧。范圍 [-1,5]。
  • bipush 127:將 127 入棧。范圍 [-128,127]。
  • sipush 32767:將 32767 入棧。范圍 [-32768,32767]。
  • ldc #6 <32768>:將常量池中下標為 6 的常量 32768 入棧。
  • aconst_null:將 null 入棧。
  • ldc #7 <沉默王二>:將常量池中下標為 7 的常量“沉默王二”入棧。

3)將棧頂?shù)臄?shù)據(jù)出棧并裝入局部變量表中

主要是用來給局部變量賦值,這類指令主要以 store 的形式存在。

  • xstore_(x 為 i、l、f、d、a,n 默認為 0 到 3)
  • xstore(x 為 i、l、f、d、a)

明白了 xload_ 和 xload,再看 xstore_ 和 xstore 就會輕松得多,作用反了一下而已。

大家來想一個問題,為什么要有 xstore_ 和 xload_ 呢?它們的作用和 xstore n、xload n 不是一樣的嗎?

xstore_ 和 xstore n 的區(qū)別在于,前者相當于只有操作碼,占用 1 個字節(jié);后者相當于由操作碼和操作數(shù)組成,操作碼占 1 個字節(jié),操作數(shù)占 2 個字節(jié),一共占 3 個字節(jié)。

由于局部變量表中前幾個位置總是非常常用,雖然 xstore_<n> 和 xload_<n> 增加了指令數(shù)量,但字節(jié)碼的體積變小了!

舉例來說:

public void store(int age, String name) {
    int temp = age + 2;
    String str = name;
}

通過 jclasslib 看一下 store() 方法的字節(jié)碼指令。

  • istore_3:從操作數(shù)中彈出一個整數(shù),并把它賦值給局部變量表中索引為 3 的變量。
  • astore 4:從操作數(shù)中彈出一個引用數(shù)據(jù)類型,并把它賦值給局部變量表中索引為 4 的變量。

通過查看局部變量表就能關(guān)聯(lián)上了。

02、算術(shù)指令

算術(shù)指令用于對兩個操作數(shù)棧上的值進行某種特定運算,并把結(jié)果重新壓入操作數(shù)棧。可以分為兩類:整型數(shù)據(jù)的運算指令和浮點數(shù)據(jù)的運算指令。

需要注意的是,數(shù)據(jù)運算可能會導致溢出,比如兩個很大的正整數(shù)相加,很可能會得到一個負數(shù)。但 Java 虛擬機規(guī)范中并沒有對這種情況給出具體結(jié)果,因此程序是不會顯式報錯的。所以,大家在開發(fā)過程中,如果涉及到較大的數(shù)據(jù)進行加法、乘法運算的時候,一定要注意!

當發(fā)生溢出時,將會使用有符號的無窮大 Infinity 來表示;如果某個操作結(jié)果沒有明確的數(shù)學定義的話,將會使用 NaN 值來表示。而且所有使用 NaN 作為操作數(shù)的算術(shù)操作,結(jié)果都會返回 NaN。

舉例來說:

public void infinityNaN() {
    int i = 10;
    double j = i / 0.0;
    System.out.println(j); // Infinity

    double d1 = 0.0;
    double d2 = d1 / 0.0;
    System.out.println(d2); // NaN
}
  • 任何一個非零的數(shù)除以浮點數(shù) 0(注意不是 int 類型),可以想象結(jié)果是無窮大 Infinity 的。
  • 把這個非零的數(shù)換成 0 的時候,結(jié)果又不太好定義,就用 NaN 值來表示。

Java 虛擬機提供了兩種運算模式:

  • 向最接近數(shù)舍入:在進行浮點數(shù)運算時,所有的結(jié)果都必須舍入到一個適當?shù)木?,不是特別精確的結(jié)果必須舍入為可被表示的最接近的精確值,如果有兩種可表示的形式與該值接近,將優(yōu)先選擇最低有效位為零的(類似四舍五入)。
  • 向零舍入:將浮點數(shù)轉(zhuǎn)換為整數(shù)時,采用該模式,該模式將在目標數(shù)值類型中選擇一個最接近但是不大于原值的數(shù)字作為最精確的舍入結(jié)果(類似取整)。

我把所有的算術(shù)指令列一下:

  • 加法指令:iadd、ladd、fadd、dadd
  • 減法指令:isub、lsub、fsub、dsub
  • 乘法指令:imul、lmul、fmul、dmul
  • 除法指令:idiv、ldiv、fdiv、ddiv
  • 求余指令:irem、lrem、frem、drem
  • 自增指令:iinc

舉例來說:

public void calculate(int age) {
    int add = age + 1;
    int sub = age - 1;
    int mul = age * 2;
    int div = age / 3;
    int rem = age % 4;
    age++;
    age--;
}

通過 jclasslib 看一下 calculate() 方法的字節(jié)碼指令。

  • iadd,加法
  • isub,減法
  • imul,乘法
  • idiv,除法
  • irem,取余
  • iinc,自增的時候 +1,自減的時候 -1

 03、類型轉(zhuǎn)換指令

可以分為兩種:

1)寬化,小類型向大類型轉(zhuǎn)換,比如 int–>long–>float–>double,對應(yīng)的指令有:i2l、i2f、i2d、l2f、l2d、f2d。

  • 從 int 到 long,或者從 int 到 double,是不會有精度丟失的;
  • 從 int、long 到 float,或者 long 到 double 時,可能會發(fā)生精度丟失;
  • 從 byte、char 和 short 到 int 的寬化類型轉(zhuǎn)換實際上是隱式發(fā)生的,這樣可以減少字節(jié)碼指令,畢竟字節(jié)碼指令只有 256 個,占一個字節(jié)。

2)窄化,大類型向小類型轉(zhuǎn)換,比如從 int 類型到 byte、short 或者 char,對應(yīng)的指令有:i2b、i2s、i2c;從 long 到 int,對應(yīng)的指令有:l2i;從 float 到 int 或者 long,對應(yīng)的指令有:f2i、f2l;從 double 到 int、long 或者 float,對應(yīng)的指令有:d2i、d2l、d2f。

窄化很可能會發(fā)生精度丟失,畢竟是不同的數(shù)量級;

但 Java 虛擬機并不會因此拋出運行時異常。

舉例來說:

public void updown() {
    int i = 10;
    double d = i;
    
    float f = 10f;
    long ong = (long)f;
}

通過 jclasslib 看一下 updown() 方法的字節(jié)碼指令。

  • i2d,int 寬化為 doublef
  • 2l, float 窄化為 long

 04、對象的創(chuàng)建和訪問指令

Java 是一門面向?qū)ο蟮木幊陶Z言,那么 Java 虛擬機是如何從字節(jié)碼層面進行支持的呢?

1)創(chuàng)建指令

數(shù)組也是一種對象,但它創(chuàng)建的字節(jié)碼指令和普通的對象不同。創(chuàng)建數(shù)組的指令有三種:

  • newarray:創(chuàng)建基本數(shù)據(jù)類型的數(shù)組
  • anewarray:創(chuàng)建引用類型的數(shù)組
  • multianewarray:創(chuàng)建多維數(shù)組

普通對象的創(chuàng)建指令只有一個,就是 new,它會接收一個操作數(shù),指向常量池中的一個索引,表示要創(chuàng)建的類型。

舉例來說:

public void newObject() {
    String name = new String("沉默王二");
    File file = new File("無愁河的浪蕩漢子.book");
    int [] ages = {};
}

通過 jclasslib 看一下 newObject() 方法的字節(jié)碼指令。

  • new #13 <java/lang/String>,創(chuàng)建一個 String 對象。
  • new #15 <java/io/File>,創(chuàng)建一個 File 對象。
  • newarray 10 (int),創(chuàng)建一個 int 類型的數(shù)組。

2)字段訪問指令

字段可以分為兩類,一類是成員變量,一類是靜態(tài)變量(static 關(guān)鍵字修飾的),所以字段訪問指令可以分為兩類:

  • 訪問靜態(tài)變量:getstatic、putstatic。
  • 訪問成員變量:getfield、putfield,需要創(chuàng)建對象后才能訪問。

舉例來說:

public class Writer {
    private String name;
    static String mark = "作者";

    public static void main(String[] args) {
        print(mark);
        Writer w = new Writer();
        print(w.name);
    }

    public static void print(String arg) {
        System.out.println(arg);
    }
}

通過 jclasslib 看一下 main() 方法的字節(jié)碼指令。

  • getstatic #2 <com/itwanger/jvm/Writer.mark>,訪問靜態(tài)變量 mark
  • getfield #6 <com/itwanger/jvm/Writer.name>,訪問成員變量 name

 05、方法調(diào)用和返回指令

方法調(diào)用指令有 5 個,分別用于不同的場景:

  • invokevirtual:用于調(diào)用對象的成員方法,根據(jù)對象的實際類型進行分派,支持多態(tài)。
  • invokeinterface:用于調(diào)用接口方法,會在運行時搜索由特定對象實現(xiàn)的接口方法進行調(diào)用。
  • invokespecial:用于調(diào)用一些需要特殊處理的方法,包括構(gòu)造方法、私有方法和父類方法。
  • invokestatic:用于調(diào)用靜態(tài)方法。
  • invokedynamic:用于在運行時動態(tài)解析出調(diào)用點限定符所引用的方法,并執(zhí)行。

舉例來說:

public class InvokeExamples {
    private void run() {
        List ls = new ArrayList();
        ls.add("難頂");

        ArrayList als = new ArrayList();
        als.add("學不動了");
    }

    public static void print() {
        System.out.println("invokestatic");
    }

    public static void main(String[] args) {
        print();
        InvokeExamples invoke = new InvokeExamples();
        invoke.run();
    }
}

我們用 javap -c InvokeExamples.class 來反編譯一下。

Compiled from "InvokeExamples.java"
public class com.itwanger.jvm.InvokeExamples {
  public com.itwanger.jvm.InvokeExamples();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  private void run();
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #4                  // String 難頂
      11: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      16: pop
      17: new           #2                  // class java/util/ArrayList
      20: dup
      21: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
      24: astore_2
      25: aload_2
      26: ldc           #6                  // String 學不動了
      28: invokevirtual #7                  // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
      31: pop
      32: return

  public static void print();
    Code:
       0: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #9                  // String invokestatic
       5: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #11                 // Method print:()V
       3: new           #12                 // class com/itwanger/jvm/InvokeExamples
       6: dup
       7: invokespecial #13                 // Method "<init>":()V
      10: astore_1
      11: aload_1
      12: invokevirtual #14                 // Method run:()V
      15: return
}

InvokeExamples 類有 4 個方法,包括缺省的構(gòu)造方法在內(nèi)。

1)InvokeExamples() 構(gòu)造方法中

缺省的構(gòu)造方法內(nèi)部會調(diào)用超類 Object 的初始化構(gòu)造方法:

`invokespecial #1 // Method java/lang/Object."<init>":()V`

2)成員方法 run() 中

invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z

由于 ls 變量的引用類型為接口 List,所以 ls.add() 調(diào)用的是 invokeinterface 指令,等運行時再確定是不是接口 List 的實現(xiàn)對象 ArrayList 的 add() 方法。

invokevirtual #7 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z

由于 als 變量的引用類型已經(jīng)確定為 ArrayList,所以 als.add() 方法調(diào)用的是 invokevirtual 指令。

3)main() 方法中

invokestatic #11 // Method print:()V

print() 方法是靜態(tài)的,所以調(diào)用的是 invokestatic 指令。

方法返回指令根據(jù)方法的返回值類型進行區(qū)分,常見的返回指令見下圖。

06、操作數(shù)棧管理指令

常見的操作數(shù)棧管理指令有 pop、dup 和 swap。

  • 將一個或兩個元素從棧頂彈出,并且直接廢棄,比如 pop,pop2;
  • 復制棧頂?shù)囊粋€或兩個數(shù)值并將其重新壓入棧頂,比如 dup,dup2,dup_×1,dup2_×1,dup_×2,dup2_×2;
  • 將棧最頂端的兩個槽中的數(shù)值交換位置,比如 swap。

這些指令不需要指明數(shù)據(jù)類型,因為是按照位置壓入和彈出的。

舉例來說:

public class Dup {
    int age;
    public int incAndGet() {
        return ++age;
    }
}

通過 jclasslib 看一下 incAndGet() 方法的字節(jié)碼指令。

  • aload_0:將 this 入棧。
  • dup:復制棧頂?shù)?this。
  • getfield #2:將常量池中下標為 2 的常量加載到棧上,同時將一個 this 出棧。
  • iconst_1:將常量 1 入棧。
  • iadd:將棧頂?shù)膬蓚€值相加后出棧,并將結(jié)果放回棧上。
  • dup_x1:復制棧頂?shù)脑兀⑵洳迦?this 下面。
  • putfield #2: 將棧頂?shù)膬蓚€元素出棧,并將其賦值給字段 age。
  • ireturn:將棧頂?shù)脑爻鰲7祷亍?/li>

 07、控制轉(zhuǎn)移指令

控制轉(zhuǎn)移指令包括:

  • 比較指令,比較棧頂?shù)膬蓚€元素的大小,并將比較結(jié)果入棧。
  • 條件跳轉(zhuǎn)指令,通常和比較指令一塊使用,在條件跳轉(zhuǎn)指令執(zhí)行前,一般先用比較指令進行棧頂元素的比較,然后進行條件跳轉(zhuǎn)。
  • 比較條件轉(zhuǎn)指令,類似于比較指令和條件跳轉(zhuǎn)指令的結(jié)合體,它將比較和跳轉(zhuǎn)兩個步驟合二為一。
  • 多條件分支跳轉(zhuǎn)指令,專為 switch-case 語句設(shè)計的。
  • 無條件跳轉(zhuǎn)指令,目前主要是 goto 指令。

1)比較指令

比較指令有:dcmpg,dcmpl、fcmpg、fcmpl、lcmp,指令的第一個字母代表的含義分別是 double、float、long。注意,沒有 int 類型。

對于 double 和 float 來說,由于 NaN 的存在,有兩個版本的比較指令。拿 float 來說,有 fcmpg 和 fcmpl,區(qū)別在于,如果遇到 NaN,fcmpg 會將 1 壓入棧,fcmpl 會將 -1 壓入棧。

舉例來說。

public void lcmp(long a, long b) {
    if(a > b){}
}

通過 jclasslib 看一下 lcmp() 方法的字節(jié)碼指令。

lcmp 用于兩個 long 型的數(shù)據(jù)進行比較。

2)條件跳轉(zhuǎn)指令

這些指令都會接收兩個字節(jié)的操作數(shù),它們的統(tǒng)一含義是,彈出棧頂元素,測試它是否滿足某一條件,滿足的話,跳轉(zhuǎn)到對應(yīng)位置。

對于 long、float 和 double 類型的條件分支比較,會先執(zhí)行比較指令返回一個整形值到操作數(shù)棧中后再執(zhí)行 int 類型的條件跳轉(zhuǎn)指令。

對于 boolean、byte、char、short,以及 int,則直接使用條件跳轉(zhuǎn)指令來完成。

舉例來說。

public void fi() {
    int a = 0;
    if (a == 0) {
        a = 10;
    } else {
        a = 20;
    }
}

通過 jclasslib 看一下 fi() 方法的字節(jié)碼指令。

3 ifne 12 (+9) 的意思是,如果棧頂?shù)脑夭坏扔?0,跳轉(zhuǎn)到第 12(3+9)行 12 bipush 20。

3)比較條件轉(zhuǎn)指令

前綴“if_”后,以字符“i”開頭的指令針對 int 型整數(shù)進行操作,以字符“a”開頭的指令表示對象的比較。

舉例來說:

public void compare() {
    int i = 10;
    int j = 20;
    System.out.println(i > j);
}

通過 jclasslib 看一下 compare() 方法的字節(jié)碼指令。

11 if_icmple 18 (+7) 的意思是,如果棧頂?shù)膬蓚€ int 類型的數(shù)值比較的話,如果前者小于后者時跳轉(zhuǎn)到第 18 行(11+7)。

4)多條件分支跳轉(zhuǎn)指令

主要有 tableswitch 和 lookupswitch,前者要求多個條件分支值是連續(xù)的,它內(nèi)部只存放起始值和終止值,以及若干個跳轉(zhuǎn)偏移量,通過給定的操作數(shù) index,可以立即定位到跳轉(zhuǎn)偏移量位置,因此效率比較高;后者內(nèi)部存放著各個離散的 case-offset 對,每次執(zhí)行都要搜索全部的 case-offset 對,找到匹配的 case 值,并根據(jù)對應(yīng)的 offset 計算跳轉(zhuǎn)地址,因此效率較低。

舉例來說:

public void switchTest(int select) {
    int num;
    switch (select) {
        case 1:
            num = 10;
            break;
        case 2:
        case 3:
            num = 30;
            break;
        default:
            num = 40;
    }
}

通過 jclasslib 看一下 switchTest() 方法的字節(jié)碼指令。

case 2 的時候沒有 break,所以 case 2 和 case 3 是連續(xù)的,用的是 tableswitch。如果等于 1,跳轉(zhuǎn)到 28 行;如果等于 2 和 3,跳轉(zhuǎn)到 34 行,如果是 default,跳轉(zhuǎn)到 40 行。

5)無條件跳轉(zhuǎn)指令

goto 指令接收兩個字節(jié)的操作數(shù),共同組成一個帶符號的整數(shù),用于指定指令的偏移量,指令執(zhí)行的目的就是跳轉(zhuǎn)到偏移量給定的位置處。

前面的例子里都出現(xiàn)了 goto 的身影,也很好理解。如果指令的偏移量特別大,超出了兩個字節(jié)的范圍,可以使用指令 goto_w,接收 4 個字節(jié)的操作數(shù)。

想要走得更遠,Java 字節(jié)碼這塊就必須得硬碰硬地吃透,希望這些分享可以幫助到大家~

路漫漫其修遠兮,吾將上下而求索

除了以上這些指令,還有異常處理指令和同步控制指令,很多Java 方面的系列文章,例如 Java 核心語法、Java 集合框架、Java IO、Java 并發(fā)編程、Java 虛擬機等,持續(xù)更新中,希望大家多多關(guān)注支持腳本之家!

相關(guān)文章

  • JavaWeb學習筆記之Filter和Listener

    JavaWeb學習筆記之Filter和Listener

    這篇文章主要給大家介紹了關(guān)于JavaWeb學習筆記之Filter和Listener的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • 深入理解@component與@Configuration注解

    深入理解@component與@Configuration注解

    這篇文章主要介紹了深入理解@component與@Configuration注解,從Spring3.0,@Configuration用于定義配置類,可替換xml配置文件,被注解的類內(nèi)部包含有一個或多個被@Bean注解的方法,這些方法將會被掃描,并用于構(gòu)建bean定義,初始化Spring容器,需要的朋友可以參考下
    2023-11-11
  • SpringBoot實現(xiàn)異步消息處理的代碼示例

    SpringBoot實現(xiàn)異步消息處理的代碼示例

    在現(xiàn)代應(yīng)用程序中,異步消息處理是一項至關(guān)重要的任務(wù)。它可以提高應(yīng)用程序的性能、可伸縮性和可靠性,同時也可以提供更好的用戶體驗,本文將介紹如何使用Spring Boot實現(xiàn)異步消息處理,并提供相應(yīng)的代碼示例
    2023-06-06
  • Springboot接收POST請求,數(shù)據(jù)為json類型問題

    Springboot接收POST請求,數(shù)據(jù)為json類型問題

    在使用Spring框架中,當處理POST請求且內(nèi)容為JSON類型時,應(yīng)使用@RequestBody注解而非@RequestParam,通過@RequestBody可以將JSON數(shù)據(jù)綁定到一個Map對象中,然后通過Map的get方法來獲取需要的參數(shù)
    2022-10-10
  • Maven解決jar包版本沖突的4種方法詳解

    Maven解決jar包版本沖突的4種方法詳解

    這篇文章主要給大家介紹了關(guān)于Maven解決jar包版本沖突的4種方法代碼,maven工程要導入jar包的坐標,就必須要考慮解決jar包沖突,文中介紹的非常詳細,需要的朋友可以參考下
    2023-07-07
  • MyBatis入門實例教程之創(chuàng)建一個簡單的程序

    MyBatis入門實例教程之創(chuàng)建一個簡單的程序

    這篇文章主要介紹了MyBatis入門創(chuàng)建一個簡單的程序,在?MySQL?中創(chuàng)建數(shù)據(jù)庫?mybatisdemo,編碼為?utf8,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2022-02-02
  • 使用@EnableWebMvc輕松配置Spring MVC

    使用@EnableWebMvc輕松配置Spring MVC

    這篇文章主要為大家介紹了使用@EnableWebMvc輕松配置Spring MVC實現(xiàn)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • Java內(nèi)存管理垃圾回收基礎(chǔ)詳解

    Java內(nèi)存管理垃圾回收基礎(chǔ)詳解

    這篇文章主要為大家介紹了Java內(nèi)存管理垃圾回收基礎(chǔ)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • Mybatis-Plus的使用詳解

    Mybatis-Plus的使用詳解

    這篇文章主要介紹了Mybatis-Plus的使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-10-10
  • Java List 用法詳解及實例分析

    Java List 用法詳解及實例分析

    這篇文章主要介紹了Java List 用法詳解及實例分析的相關(guān)資料,需要的朋友可以參考下
    2017-07-07

最新評論