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

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

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

服務器之家 - 編程語言 - Java教程 - Java虛擬機運行時棧的棧幀

Java虛擬機運行時棧的棧幀

2022-01-17 11:40沒頭腦遇到不高興 Java教程

本節(jié)將會介紹一下Java虛擬機棧中的棧幀,會對棧幀的組成部分(局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口)分別進行介紹,最后還會通過javap命令反解析編譯后的.class文件,進行分析方法執(zhí)行時的局部變量表、操作數(shù)棧等

 

Java虛擬機棧概述

Java虛擬機棧(Java Virtual Machine Stacks)是線程私有的,它的生命周期與線程相同。虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:棧幀(Stack Frame)是用于支持Java虛擬機進行方法調(diào)用和執(zhí)行的數(shù)據(jù)結(jié)構(gòu),它是虛擬機棧中的棧元素。每個方法在執(zhí)行的同到都會創(chuàng)建一個棧幀用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。

在編譯程序代碼的時候,棧幀中需要多大的局部變量表,多深的操作數(shù)棧都已經(jīng)完全確定了,并且寫入到方法表的Code屬性之中,因此一個棧幀需要分配多少內(nèi)存,不會受到程序運行期變量數(shù)據(jù)的影響,而僅僅取決于具體的虛擬機實現(xiàn)。

每一個方法從調(diào)用直至執(zhí)行完成的過程,就對應著一個棧幀在虛擬棧中從入棧到出棧的過程(說人話就是要執(zhí)行一個方法,將該方法的棧幀壓入棧頂,方法執(zhí)行完成其棧幀出棧)。在JVM里面,棧幀的操作只有兩種:出棧和入棧。正在被線程執(zhí)行的方法稱為當前線程方法,而該方法的棧幀就稱為當前幀,執(zhí)行引擎運行時只對當前棧幀有效。

Java虛擬機運行時棧的棧幀

下面對棧幀的每個組成部分分別介紹一下。

 

局部變量表

局部變量表(Local Variable Table)是一組變量值存儲空間,用于存放方法參數(shù)和方法內(nèi)部定義的局部變量。在Java程序編譯為Class文件時就在方法的code屬性的max_locals數(shù)據(jù)項中確定了該方法所需要分配的局部變量表的最大容量。

局部變量表的容量以變量槽(Variable Slot,下稱Slot)為最小單位,虛擬機規(guī)范中并沒有明確指明一個Slot應占用的內(nèi)存空間大小,只是很有導向性地說到每個引都應該能存放一個boolean、byte、char、short,int,float、reference或returnAddress類型的數(shù)據(jù),這8種數(shù)類都可以使用32位或更小的物理存來存放,但這種描述與明確指出 "每個Slot占用32位長度的內(nèi)存空間" 是有一些差別的,它運行Slot的長度可以隨著處理器、操作系統(tǒng)或虛擬機的不同而發(fā)生變化。只要保證使在64位虛擬機中使用了64位的物理內(nèi)存空間去實現(xiàn)一個Slot,虛擬機仍要使用對齊和補白的手段讓Slot在外觀上看起來與32位擬機中的一致。

一個Slot可以存放一個32位以內(nèi)的數(shù)據(jù)類型,Java中占用32位以內(nèi)的數(shù)據(jù)類型有boolean、byte、char、short、float、reference和returnAddress8種類型。第7種reference類表示對一個對象實例的引用,虛擬機規(guī)范既沒有說明它的長度,也沒有明確指出這種引用應有怎樣的結(jié)構(gòu)。但是一般來說,虛擬機實現(xiàn)至少都應當能通過這個引用做到兩點,一是從此引用直接或間接地查找到對象在Java堆中的數(shù)據(jù)存放的起始地址索引,二是此引用中直接或間接地查找到對象所屬數(shù)據(jù)類型在方法區(qū)中的存儲的類型信息。第8種即returnAddress類型目前已經(jīng)很少見了,現(xiàn)在已經(jīng)由異常表代替。

對于64位的數(shù)據(jù)類型,虛擬機會以高位對齊的方式為其分配兩個連續(xù)的引Slot空間。Java語言中明確的(reference類型則可能是32位也可能是64位),64位的數(shù)據(jù)類型只有l(wèi)ong和double兩種。虛擬機通過索引定位的方式使用局部變量表,索引值的范圍是從0開始至局部變量表最大的Slot數(shù)量。如果訪問的是32位數(shù)據(jù)類型的變量,索引 n 就代表了使用第n個Slot,如果是64位數(shù)據(jù)類型的變量,則說明會同時使用n和n+1兩個Slot對于兩個相鄰的共同存放一個64位數(shù)據(jù)的兩個Slot,不允許采用任何方式單獨訪問其中的某一個,Java虛擬機規(guī)范中明確要求了如果遇到進行這種操作的字節(jié)碼序列,虛擬機應該在類加載的校驗階段拋出異常。

如果是實例方法(非static的方法),那么局部變量表中第0位索引的Slot默認是用于傳遞方法所屬對象實例的引用"this"。其余參數(shù)則按照參數(shù)表的順序來排列,占用從1開始的局部變量Slot,參數(shù)表分配完畢后,再根據(jù)方法體內(nèi)部定義的變量順序和作用域分配其余的Slot(比如方法method(int a1,inta2),參數(shù)表為a1和a2,則局部變量表索引0、1、2則分別存儲了this指針、a1、a2,如果方法內(nèi)部有其他內(nèi)部變量,則在局部變量表中存在a2之后的位置)。

Java虛擬機運行時棧的棧幀

為了盡可能節(jié)省棧幀空間,局部變量表中的Slot是可以重用的,方法體中定義的變量,其作用域并不一定會覆蓋整個方法體,如果當前字節(jié)碼PC計數(shù)器的值已經(jīng)超出了某個變量的作用域,那這個變量對應的Slot就可以交給其他變量使用。

局部變量不像的類成員變量那樣存在"準備階段"。我們知道類變量有兩次賦初始值的過程,一次在準備階段,賦予系統(tǒng)初始值;另外一次在初始化階段,賦予程序員定義的初始值。因此,即使在初始化階段程序員沒有為類變量賦值也沒有關(guān)系,類變量仍然具有一個確定的初始值。但局部變量就不一樣,如果一個局部變量定義了但沒有賦初始值是不能使用的,不要認為Java中任何情況下都存在諸如整型變量默認為0,布爾型變量默認為false等這樣的默認值。

 

操作數(shù)棧

操作數(shù)棧(Operand Stack)也常稱為操作棧,它是一個后入先出(Last In First out,LIFO)棧。同局部變量表一樣,操作數(shù)棧的最大深度也在編譯的時候?qū)懭氲絚ode屬性的max_stacks數(shù)據(jù)項中。操作數(shù)棧的每一個元素可以是任意的Java數(shù)據(jù)類型,包括long和double。32位數(shù)據(jù)類型所占的棧容量為1,64位數(shù)據(jù)類型所占的棧容量為2。在方法執(zhí)行的任何時候,操作數(shù)棧的深度都不會超過在maxstacks數(shù)據(jù)項中設(shè)定的最大值。

當一個方法剛剛開始執(zhí)行的時候,這個方法的操作數(shù)棧是空的,在方法的執(zhí)行過程中,會有各種字節(jié)碼指令往操作數(shù)棧中寫入和提取內(nèi)容,也就是出棧/入棧操作。例如,在做算術(shù)運算的時候是通過操作數(shù)棧來進行的,又或者在調(diào)用其他方法的時候是通過操作數(shù)棧來進行參數(shù)傳遞的。舉個例子,整數(shù)加法的字節(jié)碼指令iadd在運行的時候操作數(shù)棧中最接近棧頂?shù)膬蓚€元素已經(jīng)存入了兩個int型的數(shù)值,當執(zhí)行這個指令時,會將這兩個int值出棧并相加,然后將相加的結(jié)果入棧。

Java虛擬機的解釋執(zhí)行引擎稱為“基于棧的執(zhí)行引擎”,其中所指的“棧”就是操作數(shù)棧。如果當前線程請求的棧深度大于虛擬機所允許的最大深度,將拋出StackOverflowError異常。

 

動態(tài)連接

每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支持方法調(diào)用過程中的動態(tài)連接(Dynamic Linking)。Class文件的常量池中存有大量的符號引用,字節(jié)碼中的方法調(diào)用指令就以常量池中指向方法的符號引用作為參數(shù)。這些符號引用一部分會在類加載階段或者第一次使用的時候就轉(zhuǎn)化為直接引用,這種轉(zhuǎn)化稱為靜態(tài)解析。另外一部分將在每一次運行期間轉(zhuǎn)化為直接引用,這部分稱為動態(tài)連接。

Java代碼在進行Javac編譯的時候,并不像C和C++那樣有“連接”這一步驟,而是在虛擬機加載Class文件的時候進行動態(tài)連接。也就是說,在Class文件中不會保存各個方法、字段的最終內(nèi)存布局信息,因此這些字段、方法的符號引用不經(jīng)過運行期轉(zhuǎn)換的話無法得到真正的內(nèi)存入口地址,也就無法直接被虛擬機使用。當虛擬機運行時,需要從常量池獲得對應的符號引用,再在類創(chuàng)建時或運行時解析、翻譯到具體的內(nèi)存地址之中。

Math math=new Math();
math.compute();//調(diào)用實例方法compute()

以上面兩行代碼為例,解釋一下動態(tài)連接:math.compute()調(diào)用時compute()叫符號,需要通過compute()這個符號去到常量池中去找到對應方法的符號引用,運行時將通過符號引用找到方法的字節(jié)碼指令的內(nèi)存地址。

 

方法的返回地址

當一個方法開始執(zhí)行后,只有兩種方式可以退出這個方法。第一種方式是執(zhí)行引擎遇任意一個方法返回的字節(jié)碼指令,這時候可能會有返回值傳遞給上層的方法調(diào)用者(調(diào)用當前方法的方法稱為調(diào)用者),這種退出方法的方式稱為正常完成出口(Normal Method Invocation Completion)。另外一種退出方式是,在方法執(zhí)行過程中遇到了異常,并且這個異常沒有在方法體內(nèi)得到處理,無論是Java虛擬機內(nèi)部產(chǎn)生的異常,還是代碼中使用athrow字節(jié)碼指令產(chǎn)生的異常,只要在本方法的異常表中沒有搜索到匹配的異常處理器,就會導致方法退出,這種退出方法的方式稱為異常完成出口(Abrupt Method Invocation Completion)。一個方法使用異常完成出口的方式退出,是不會給它的上層調(diào)用者產(chǎn)生任何返回值的。

無論采用何種退出方式,在方法退出之后,都需要返回到方法被調(diào)用的位置,程序才能繼續(xù)執(zhí)行,方法返回時可能需要在棧幀中保存一些信息,用來幫助恢復它的上層方法的執(zhí)行狀態(tài)。一般來說,方法正常退出時,調(diào)用者的PC計數(shù)器的值可以作為返回地址,棧幀中很可能會保存這個計數(shù)器值。而方法異常退出時,返回地址是要通過異常處理器表來確定的,棧幀中一般不會保存這部分信息。方法退出的過程實際上就等同于把當前棧幀出棧,因此退出時可能執(zhí)行的操作有:恢復上層方法的局部變量表和操作數(shù)棧,把返回值(如果有的話)壓入調(diào)用者棧幀的操作數(shù)棧中,調(diào)整pc計數(shù)器的值以指向方法調(diào)用指令后面的一條指令等。

 

結(jié)合javap命令理解棧幀

上面進行了大段的文文字介紹,還是不太好理解,下面我們通過javap命令來分析一下方法中的操作指令、局部變量表、操作數(shù)棧等。

javap是jdk自帶的反解析工具。它的作用就是根據(jù)class字節(jié)碼文件,反解析出當前類對應的code區(qū)(匯編指令)、本地變量表、異常表和代碼行偏移量映射表、常量池等等信息。下面是其用法說明:

D:wyonlinemyworkspacesframeworkTestincomwkpjvm>javap
用法: javap <options> <classes>
其中, 可能的選項包括:
  -help  --help  -?        輸出此用法消息
  -version                 版本信息
  -v  -verbose             輸出附加信息
  -l                       輸出行號和本地變量表
  -public                  僅顯示公共類和成員
  -protected               顯示受保護的/公共類和成員
  -package                 顯示程序包/受保護的/公共類
                           和成員 (默認)
  -p  -private             顯示所有類和成員
  -c                       對代碼進行反匯編
  -s                       輸出內(nèi)部類型簽名
  -sysinfo                 顯示正在處理的類的
                           系統(tǒng)信息 (路徑, 大小, 日期, MD5 散列)
  -constants               顯示最終常量
  -classpath <path>        指定查找用戶類文件的位置
  -cp <path>               指定查找用戶類文件的位置
  -bootclasspath <path>    覆蓋引導類文件的位置

下面我們寫一個簡單的Java程序:

package com.wkp.jvm;
 
public class Math {
 
	public static final Integer CONSTANT=666;
	
	public int compute() {//一個方法對應一塊棧幀內(nèi)存區(qū)域
		int a=3;
		int b=5;
		int c=(a+b)*10;
		return c;
	}
	
	public static void main(String[] args) {
		Math math=new Math();
		math.compute();
	}
}

然后進入到Math.class所在目錄執(zhí)行: javap -c Math.class > Math.txt 命令,將Math.class字節(jié)碼文件反匯編然后輸出到Math.txt文件中:

Java虛擬機運行時棧的棧幀

然后我們查看Math.txt的內(nèi)容如下:我們重點分析下compute方法內(nèi)的指令,其內(nèi)部的指令后面我加了注釋(這里我是參考上一節(jié)的《JVM字節(jié)碼指令集大全及其介紹》,感興趣的話可以看一看),注釋中的棧就是指的棧幀中的操作數(shù)棧,本地變量表就是指的局部變量表。

Compiled from "Math.java"
public class com.wkp.jvm.Math {
  public static final java.lang.Integer CONSTANT;
 
  static {};
    Code:
       0: sipush        666
       3: invokestatic  #10                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       6: putstatic     #16                 // Field CONSTANT:Ljava/lang/Integer;
       9: return
 
  public com.wkp.jvm.Math();
    Code:
       0: aload_0
       1: invokespecial #21                 // Method java/lang/Object."<init>":()V
       4: return
 
  public int compute();
    Code:
       0: iconst_3			//將int類型的3推送至棧頂
       1: istore_1			//將棧頂int類型數(shù)值(上面的3)出棧并存入第二個本地變量
       2: iconst_5			//將int類型的5推送至棧頂
       3: istore_2			//將棧頂int類型數(shù)值(上面的5)出棧并存入第三個本地變量
       4: iload_1			//將第二個int型本地變量(上面的3)推送至棧頂
       5: iload_2			//將第三個int型本地變量(上面的5)推送至棧頂
       6: iadd				//將棧頂兩int型數(shù)值出棧,然后相加并將結(jié)果壓入棧頂
       7: bipush        10	        //將常量值10推送至棧頂
       9: imul				//將棧頂兩int型數(shù)值出棧,然后相乘并將結(jié)果壓入棧頂
      10: istore_3			//將棧頂int類型數(shù)值(上面的乘積)出棧并存入第四個本地變量
      11: iload_3			//將第四個int類型本地變量推送至棧頂
      12: ireturn			//從當前方法返回int類型值
 
  public static void main(java.lang.String[]);
    Code:
       0: new           #1                  // class com/wkp/jvm/Math
       3: dup
       4: invokespecial #33                 // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #34                 // Method compute:()I
      12: pop
      13: return
}

下面我們通過圖示簡單表示一下上面compute方法中指令操作時關(guān)于本地變量表、操作數(shù)棧的情況:

我們先看下第一行 0: iconst_3 //將int類型的3推送至棧頂,可以看到下圖3已經(jīng)被入棧到操作數(shù)棧的棧頂。

Java虛擬機運行時棧的棧幀

我們再看下第二行 1: istore_1 //將棧頂int類型數(shù)值(上面的3)出棧并存入第二個本地變量,將上圖中棧頂?shù)?出棧然后存入本地表中第二個位置,如下圖所示:

Java虛擬機運行時棧的棧幀

第三行、第四行跟上面的一二行指令類似,第四行指令執(zhí)行后變成如下所示:

Java虛擬機運行時棧的棧幀

第五行、第六行中 4: iload_1 //將第二個int型本地變量(上面的3)推送至棧頂; 5: iload_2 //將第三個int型本地變量(上面的5)推送至棧頂,即將局部變量表中的3和5依次壓入棧頂,如下圖所示:

Java虛擬機運行時棧的棧幀

然后第七行執(zhí)行iadd操作,將棧頂?shù)膬蓚€int類型數(shù)據(jù)5和3出棧相加,將得到的和壓入棧頂,得到如下結(jié)果:

Java虛擬機運行時棧的棧幀

后面的指令操作過程與上面類似,執(zhí)行完第12行的iload_3指令之后,會得到如下圖所示:

Java虛擬機運行時棧的棧幀

關(guān)于局部變量表的信息,還可以通過javap -l 命令查看如下圖所示,另外還可以通過Idea中的jclasslib 查看。

LocalVariableTable表示的就是局部變量表的信息:

public int compute();
    LineNumberTable:
      line 8: 0
      line 9: 2
      line 10: 4
      line 11: 11
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      13     0  this   Lcom/wkp/jvm/Math;
          2      11     1     a   I
          4       9     2     b   I
         11       2     3     c   I

我們還可以通過 javap -c Math.class > Math.txt 查看更多的信息如下:我們可以看到 9: invokevirtual #34 // Method compute:() 中的 #34 可以在常量池中找到 #34 = Methodref #1.#35 // com/wkp/jvm/Math.compute:()也就是方法的符號引用,運行時通過符號引用解析出來方法的執(zhí)行指令的內(nèi)存地址,這個其實就是動態(tài)連接。

Classfile /D:/wyonline/myworkspaces/framework/Test/bin/com/wkp/jvm/Math.class
  Last modified 2019-8-24; size 761 bytes
  MD5 checksum be0cdf4bcd037929d3fe0af86d44a837
  Compiled from "Math.java"
public class com.wkp.jvm.Math
  minor version: 0
  major version: 52		//魔數(shù)
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:		//常量池
   #1 = Class              #2             // com/wkp/jvm/Math
   #2 = Utf8               com/wkp/jvm/Math
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               CONSTANT
   #6 = Utf8               Ljava/lang/Integer;
   #7 = Utf8               <clinit>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Methodref          #11.#13        // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  #11 = Class              #12            // java/lang/Integer
  #12 = Utf8               java/lang/Integer
  #13 = NameAndType        #14:#15        // valueOf:(I)Ljava/lang/Integer;
  #14 = Utf8               valueOf
  #15 = Utf8               (I)Ljava/lang/Integer;
  #16 = Fieldref           #1.#17         // com/wkp/jvm/Math.CONSTANT:Ljava/lang/Integer;
  #17 = NameAndType        #5:#6          // CONSTANT:Ljava/lang/Integer;
  #18 = Utf8               LineNumberTable
  #19 = Utf8               LocalVariableTable
  #20 = Utf8               <init>
  #21 = Methodref          #3.#22         // java/lang/Object."<init>":()V
  #22 = NameAndType        #20:#8         // "<init>":()V
  #23 = Utf8               this
  #24 = Utf8               Lcom/wkp/jvm/Math;
  #25 = Utf8               compute
  #26 = Utf8               ()I
  #27 = Utf8               a
  #28 = Utf8               I
  #29 = Utf8               b
  #30 = Utf8               c
  #31 = Utf8               main
  #32 = Utf8               ([Ljava/lang/String;)V
  #33 = Methodref          #1.#22         // com/wkp/jvm/Math."<init>":()V
  #34 = Methodref          #1.#35         // com/wkp/jvm/Math.compute:()I
  #35 = NameAndType        #25:#26        // compute:()I
  #36 = Utf8               args
  #37 = Utf8               [Ljava/lang/String;
  #38 = Utf8               math
  #39 = Utf8               SourceFile
  #40 = Utf8               Math.java
{
  public static final java.lang.Integer CONSTANT;
    descriptor: Ljava/lang/Integer;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
 
  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: sipush        666
         3: invokestatic  #10                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         6: putstatic     #16                 // Field CONSTANT:Ljava/lang/Integer;
         9: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
 
  public com.wkp.jvm.Math();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #21                 // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/wkp/jvm/Math;
 
  public int compute();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_3
         1: istore_1
         2: iconst_5
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: bipush        10
         9: imul
        10: istore_3
        11: iload_3
        12: ireturn
      LineNumberTable:
        line 8: 0
        line 9: 2
        line 10: 4
        line 11: 11
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      13     0  this   Lcom/wkp/jvm/Math;
            2      11     1     a   I
            4       9     2     b   I
           11       2     3     c   I
 
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #1                  // class com/wkp/jvm/Math
         3: dup
         4: invokespecial #33                 // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #34                 // Method compute:()I
        12: pop
        13: return
      LineNumberTable:
        line 15: 0
        line 16: 8
        line 17: 13
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      14     0  args   [Ljava/lang/String;
            8       6     1  math   Lcom/wkp/jvm/Math;
}
SourceFile: "Math.java"

參考:《深入理解Java虛擬機第二版》、《Java虛擬機規(guī)范 JavaSE8版》

到此這篇關(guān)于Java虛擬機運行時棧的棧幀的文章就介紹到這了,更多相關(guān)Java 虛擬機 棧幀內(nèi)容請搜索服務器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務器之家!

原文鏈接:https://blog.csdn.net/u012988901/article/details/100043857

延伸 · 閱讀

精彩推薦
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經(jīng)有好久沒有升過級了。升級完畢重啟之后,突然發(fā)現(xiàn)好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發(fā)項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java實現(xiàn)搶紅包功能

    Java實現(xiàn)搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現(xiàn)搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程xml與Java對象的轉(zhuǎn)換詳解

    xml與Java對象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發(fā)現(xiàn)了對于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7482021-02-04
主站蜘蛛池模板: 国产成人免费观看在线视频 | 国产欧美日韩在线不卡第一页 | 日韩一级生活片 | 亚洲系列国产精品制服丝袜第 | 91精品手机国产露脸 | 欧美亚洲国产一区二区三区 | 久久精品国产色蜜蜜麻豆国语版 | 日本制服丝袜 | 欧美另类亚洲 | h卡通第一页 | 新版孕妇bbwbbwbbw | 97色伦图片7778久久 | x8x8在线观看 | 2020国语对白露脸 | 香蕉91| 青草社区视频 | 亚久久伊人精品青青草原2020 | 欧美午夜寂寞影院安卓列表 | 嫩草影院地址一地址二 | 亚洲图片一区二区三区 | 成年人免费观看 | 亚洲AV午夜福利精品香蕉麻豆 | 蜜桃视频一区二区 | 亚洲国产精品久久网午夜小说 | 好姑娘在线视频观看免费 | 国产目拍亚洲精品一区二区三区 | 国产-第1页-草草影院 | 男人的j伸到女人的屁股眼 男人吃奶动态图 | 欧美a级v片在线观看一区 | 风间由美在线 | 亚洲第一天堂无码专区 | 婷婷综合在线 | 美女黑人做受xxxxxⅹ | 精品91一区二区三区 | 日朝欧美亚洲精品 | www.尤物视频| 午夜片神马影院福利 | 毛片在线观看网站 | 亚洲精品福利在线 | 免费在线观看中文字幕 | 高清欧美videossexo免费 |