導航:首頁 > 文件教程 > class文件常量池變數個數

class文件常量池變數個數

發布時間:2023-04-02 14:59:50

Ⅰ 常量池的具體結構

java程序中,有很多的東西是永恆的,不會在運行過程中變化。比如一個類的名字,一個類欄位的名字/所屬類型,一個類方法的名字/返回類型/參數名與所屬類型,一個常量,還有在程序中出現的大量的字面值。
比如下面小段源碼中粗體代碼顯示的部分:
public class ClassTest {
private String itemS ="我們 ";
private final int itemI =100 ;
public void setItemS (String para ){...}
}
而這些在JVM解釋執行程序的時候是非常重螞余要的。那麼編譯器將源程序編譯成class文件後,會用一部分位元組分類存儲這些粗體代碼。而這些位元組我們就稱為常量池。事實上,只有JVM載入class後,在方法區中為它們開辟了空間才更像一個「池」。
正如上面所示,一個程序中有很多永恆的類似粗體代碼顯示的部分。每一個都是常量池中的一個常量表(常量項)。而這些常量表之間又有不同,class文件共有11種常量表,如下所示: 常量表類型 標志值(佔1 byte) 描述 CONSTANT_Utf8 1 UTF-8編碼的Unicode字元串 CONSTANT_Integer 3 int類型的字面值 CONSTANT_Float 4 float類型的字面值 CONSTANT_Long 5 long類型的字面值 CONSTANT_Double 6 double類型的字液弊面值 CONSTANT_Class 7 對一個類或介面的符號引用 CONSTANT_String 8 String類型字面值的引用 CONSTANT_Fieldref 9 對一個欄位的符號引用 CONSTANT_Methodref 10 對一個類中方法的符號引用 CONSTANT_InterfaceMethodref 11 對一個介面中方法的符號引用 CONSTANT_NameAndType 12 對一個欄位或方法的部分符號引用 (1) CONSTANT_Utf8 用UTF-8編碼方式來表示程序中所有的重要常量字元串。這些字元串悶埋滾包括: ①類或介面的全限定名, ②超類的全限定名,③父介面的全限定名, ④類欄位名和所屬類型名,⑤類方法名和返回類型名、以及參數名和所屬類型名。⑥字元串字面值
表格式: tag(標志1:佔1byte) length(字元串所佔位元組的長度,佔2byte) bytes(字元串位元組序列)
(2) CONSTANT_Integer、 CONSTANT_Float、 CONSTANT_Long、 CONSTANT_Double 所有基本數據類型的字面值。比如在程序中出現的1用CONSTANT_Integer表示。3.1415926F用 CONSTANT_Float表示。
表格式: tag bytes(基本數據類型所需使用的位元組序列)
(3) CONSTANT_Class 使用符號引用來表示類或介面。我們知道所有類名都以 CONSTANT_Utf8表的形式存儲。但是我們並不知道 CONSTANT_Utf8表中哪些字元串是類名,那些是方法名。因此我們必須用一個指向類名字元串的符號引用常量來表明。
表格式: tag name_index(給出表示類或介面名的CONSTANT_Utf8表的索引)
(4) CONSTANT_String 同 CONSTANT_Class,指向包含字元串字面值的 CONSTANT_Utf8表。
表格式: tag string_index(給出表示字元串字面值的CONSTANT_Utf8表的索引)
(5) CONSTANT_Fieldref 、 CONSTANT_Methodref、 CONSTANT_InterfaceMethodref 指向包含該欄位或方法所屬類名的 CONSTANT_Utf8表,以及指向包含該欄位或方法的名字和描述符的 CONSTANT_NameAndType 表
表格式: tag class _index(給出包含所屬類名的CONSTANT_Utf8表的索引) name_and_type_index(包含欄位名或方法名以及描述符的 CONSTANT_NameAndType表 的索引)
(6) CONSTANT_NameAndType 指向包含欄位名或方法名以及描述符的 CONSTANT_Utf8表。
表格式: tag name_index(給出表示欄位名或方法名的CONSTANT_Utf8表的索引) type_index(給出表示描述符的CONSTANT_Utf8表的索引)
在Java源代碼中的每一個字面值字元串,都會在編譯成class文件階段,形成標志號為8(CONSTANT_String_info)的常量表 。 當JVM載入 class文件的時候,會為對應的常量池建立一個內存數據結構,並存放在方法區中。同時JVM會自動為CONSTANT_String_info常量表中的字元串常量的字面值 在堆中創建新的String對象(intern字元串對象 ,又叫拘留字元串對象)。然後把CONSTANT_String_info常量表的入口地址轉變成這個堆中String對象的直接地址(常量池解析)。
拘留字元串對象
源代碼中所有相同字面值的字元串常量只可能建立唯一 一個拘留字元串對象。 實際上JVM是通過一個記錄了拘留字元串引用的內部數據結構來維持這一特性的。在Java程序中,可以調用String的intern()方法來使得一個常規字元串對象成為拘留字元串對象。
(1)String s=new String("Hello world"); 編譯成class文件後的指令(在myeclipse中查看):
事實上,在運行這段指令之前,JVM就已經為"Hello world"在堆中創建了一個拘留字元串( 值得注意的是:如果源程序中還有一個"Hello world"字元串常量,那麼他們都對應了同一個堆中的拘留字元串)。然後用這個拘留字元串的值來初始化堆中用new指令創建出來的新的String對象,局部變數s實際上存儲的是new出來的堆對象地址。
(2)String s="Hello world";
這跟(1)中創建指令有很大的不同,此時局部變數s存儲的是早已創建好的拘留字元串的堆地址。
java常量池技術 java中的常量池技術,是為了方便快捷地創建某些對象而出現的,當需要一個對象時,就可以從池中取一個出來(如果池中沒有則創建一個),則在需要重復創建相等變數時節省了很多時間。常量池其實也就是一個內存空間,常量池存在於方法區中。
String類也是java中用得多的類,同樣為了創建String對象的方便,也實現了常量池的技術。
測試代碼如下:
public class Test{
public static void main(String[] args){
//s1,s2分別位於棧中,指向堆中不同的空間
String s1=new String("hello");
String s2=new String("hello");
System.out.println(s1==s2);//輸出false
//s3,s4位於池中同一空間
String s3="hello" String s4="hello";
System.out.println(s3==s4);//輸出true
}
}
用new String()創建的字元串不是常量,不能在編譯期就確定,所以new String()創建的字元串不放入常量池中,他們有自己的地址空間。
String 對象(內存)的不變性機制會使修改String字元串時,產生大量的對象,因為每次改變字元串,都會生成一個新的String。 java 為了更有效的使用內存,常量池在編譯期遇見String 字元串時,它會檢查該池內是否已經存在相同的String 字元串,如果找到,就把新變數的引用指向現有的字元串對象,不創建任何新的String 常量對象,沒找到再創建新的。所以對一個字元串對象的任何修改,都會產生一個新的字元串對象,原來的依然存在,等待垃圾回收。
代碼:
String a = 「test」;
String b = 「test」;
String b = b+"java";
a,b同時指向常量池中的常量值"test",b=b+"java"之後,b原先指向一個常量,內容為"test」,通過對b進行+"java" 操作後,b之前所指向的那個值沒有改變,但此時b不指向原來那個變數值了,而指向了另一個String變數,內容為」test java「。原來那個變數還存在於內存之中,只是b這個變數不再指向它了。
八種基本類型的包裝類和對象池 java中基本類型的包裝類的大部分都實現了常量池技術,這些類是Byte,Short,Integer,Long,Character,Boolean,另外兩種浮點數類型的包裝類則沒有實現。另外Byte,Short,Integer,Long,Character這5種整型的包裝類也只是在對應值小於等於127時才可使用常量池,也即對象不負責創建和管理大於127的這些類的對象。 一些對應的測試代碼:
public class Test{ public static void main(String[] args){
//5種整形的包裝類Byte,Short,Integer,Long,Character的對象,
//在值小於127時可以使用常量池
Integer i1=127;
Integer i2=127;
System.out.println(i1==i2); //輸出true
//值大於127時,不會從常量池中取對象
Integer i3=128;
Integer i4=128;
System.out.println(i3==i4); //輸出false
//Boolean類也實現了常量池技術
Boolean bool1=true;
Boolean bool2=true;
System.out.println(bool1==bool2); //輸出true
//浮點類型的包裝類沒有實現常量池技術
Double d1=1.0;
Double d2=1.0;
System.out.println(d1==d2); //輸出false
}
}
對Integer對象的代碼補充
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) {
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
當你直接給一個Integer對象一個int值的時候,其實它調用了valueOf方法,然後你賦的這個值很特別,是128,那麼沒有進行cache方法,相當於new了兩個新對象。所以問題中定義a、b的兩句代碼就類似於:
Integer a = new Integer(128);
Integer b = new Integer(128);
這個時候再問你,輸出結果是什麼?你就知道是false了。如果把這個數換成127,再執行:
Integer a = 127;
Integer b = 127;
System.out.println(a == b);
結果就是:true
進行對象比較時最好還是使用equals,便於按照自己的目的進行控制。這里引出equals()和==,equals比較的是字元串字面值即比較內容,==比較引用。
看一下IntegerCache這個類裡面的內容:
private static class IntegerCache {
private IntegerCache() {
}
static final Integer cache[] = new Integer[-(-128) + 127 + 1];
static {
for (int i = 0; i < cache.length; i++)
cache[i] = new Integer(i - 128);
}
}
由於cache[]在IntegerCache類中是靜態數組,也就是只需要初始化一次,即static{......}部分,所以,如果Integer對象初始化時是-128~127的范圍,就不需要再重新定義申請空間,都是同一個對象---在IntegerCache.cache中,這樣可以在一定程度上提高效率。
針對String方面的補充
在同包同類下,引用自同一String對象.
在同包不同類下,引用自同一String對象.
在不同包不同類下,依然引用自同一String對象.
在編譯成.class時能夠識別為同一字元串的,自動優化成常量,所以也引用自同一String對象.
在運行時創建的字元串具有獨立的內存地址,所以不引用自同一String對象.
String的intern()方法會查找在常量池中是否存在一份equal相等的字元串,
如果有則返回一個引用,沒有則添加自己的字元串進入常量池,注意:只是字元串部分。
所以這時會存在2份拷貝,常量池的部分被String類私有並管理,自己的那份按對象生命周期繼續使用。
返回字元串對象的規范化表示形式
一個初始值為空的字元串池,它由類 String 私有地維護。
當調用 intern 方法時,如果池已經包含一個等於此 String 對象的字元串(該對象由 equals(Object) 方法確定),則返回池中的字元串引用。否則,將此 String 對象添加到池中,並且返回此 String 對象的引用。
它遵循對於任何兩個字元串 s 和 t,當且僅當 s.equals(t) 為 true 時,s.intern() == t.intern() 才為 true。
所有字面值字元串和字元串賦值常量表達式都是內部的。
------------------------------------代碼演示補充-------------------------------------
String s0= "java";
String s1=new String("java");
String s2=new String("java");
s1.intern();
s2=s2.intern(); //把常量池中"java"的引用賦給s2
System.out.println( s0==s1);//false 「 intern返回的引用沒有引用變數接收~ s1.intern();等於廢代碼.」
System.out.println( s0==s1.intern() );//true
System.out.println( s0==s2 );//true
------------------------------------代碼演示補充-------------------------------------
String s1=new String("java");
String s2=s1.intern();//s1 檢查常量池,發現沒有就拷貝自己的字元串進去
//s2 引用該字元串常量池的地址
System.out.println(s2 == s1);//false
System.out.println( s2==s1.intern());//true
System.out.println( s1==s1.intern());// false

Ⅱ 常量池是什麼

可以理解為class文件之中的資源倉庫,它是class文件結構中與其他項目關聯最多的數據類型,也是佔用class文件空間最大的數早芹高據項目之一,同時它還是class文件中第一個出現表類型的數據項目。

由於常量池的數量是不固定的,所以在常量池入口需要放置一項u2(即2個位元組)類型的數據,代表常量池容量計數值(constant-pool-count)(從1開始,將0表示不引用任何常量)。

常量池中主要存放兩大類常量:

    ●    字面量(Literal):比較接近首隱於Java語言層面的常量概念,如文本字元串,聲明為final的常量值。

    ●    符號引用(Synbolic Reference):包括如下三類常陸尺量:

          ①    類和介面的全限定名(Fully Qualified Name)

          ②    欄位的名稱和描述符(Descriptor)

          ③    方法的名稱和描述符

Ⅲ 手機java虛擬機能運行.class文件嗎

jre下也有bin\java.exe這個程序

java GreedSnake
這樣就可以,如果你這個類有包的話就要麻煩一點。
要在包的頂層目錄中執行。
java packages.packages.GreedSnake
這樣

packages是你具體的包名。

Ⅳ [轉載]Class文件在JVM中如何存儲

JDK6 HotSpot VM用instanceKlass來記錄類的元數據,每個Java類有一個對應的instanceKlass。
每個instanceKlass上引用著一個constantPoolOopDesc對象,然後間接引用著一個constantPoolCacheOopDesc對象。前者跟Class文件里記錄的常量池的結構類似,而後者是為了讓解釋器運行得更高效的一個緩存。

舉例的話,用VisualVM里的 SA Plugin 來演示,java.lang.String的狀況。
這里我用JDK 7的一個預覽版,build 96來運行VisualVM 1.3和一個groovysh,並且用VisualVM里的SA Plugin來觀察groovysh的運行狀態:

圖1:java.lang.String對應的一個instanceKlass

留意到instanceKlass里有個_constants欄位,引用著一個constantPoolOopDesc對象(後面簡稱constantPool對象)。

圖2:觀察constantPool對象的內容:

留意到它是一個類似數組的對象,裡面有_length欄位描述常量池內容的個數,後面就是常量池項了。
各個類型的常量是混在一起放在常量池裡的,跟Class文件里的基本上一樣。
最不同的是在這個運行時常量池裡,symbol是在類之間共享的;而在Class文件的常量池裡每個Class文件都有自皮做己的一份symbol內容,沒共享。

圖3:觀察constantPool里其中一個Utf8常量的內容:

這張圖的關注點是位於0x180188a8的一個symbol對象(內容是"intern"),它的結構跟數組類似,有_length來記錄長度,後面是UTF-8編碼的位元組。

這些Utf8常量在HotSpot VM里以symbolOopDesc對象(下面簡稱symbol對象)來表現;它們可以通過一個全局的SymbolTable對象找到。注意:constantPool對象並不「包含」這些symbol對象,而只是引用著它們而已;或者說,constantPool對象只存了對symbol對象的引用,而沒有存它們的內容。

讓我們來看看原本的Class文件里內容是怎樣的:

再對比圖2看看,是不是正好對應上的?

圖2里constantPool的第一個常量池項的內容是:

這個26738818數字是怎麼來的呢?
實際上是:26738818 = 408 << 16 | 130
而原本Class文件里常量池的第一項內容正是#130.#408,也就是由一個Class_index和一個NameAndType_index組成的Methodref。

圖2里還有個細節,可以看到原本Class文件里常量池第7項是一個Class,但在圖2里顯示的是一個「UnresolvedClass」。這正是動態類載入/鏈接的一個表現。這個項所指向的Class還沒被String里的方法使用過,所以還沒跟String鏈接起祥握漏來,所以這里看到是unresolved。
我們可以故意在那個groovysh里執行一句:

這樣會引發String.charAt()方法執行的過程中拋出一個java.lang.異常,那麼就必須要完成鏈接的步驟。
然後再去看看String的常量池的樣子:

就可以看到常量池的第7項已經解析(resolve)好了,從原本的符號引用變成了一個直接引用。

在JDK7以後的更新版中,HotSpot VM會逐漸去除謹爛PermGen,原本一些放在GC堆里的元數據會搬到GC管理之外的堆空間里。所以上面描述的實現會有些變化。具體會變成怎樣還沒真相。

至於其它JVM,其實運行時常量池想怎麼組織都可以的,反正Java層面上看不出來JVM內部組織這些元數據的方式的差異。

原文地址: https://hllvm-group.iteye.com/group/topic/26412#post-187861

Ⅳ JVM_位元組碼文件(ClassFile)詳解

我們知道 javac 命令可以將 .java 文件編譯成 .class 文件,而這個 Class 文件 中包含了 Java虛擬機 指令集、符號表以及若干其他輔助信息;最終將在 Java虛擬機 運行。

本文是以 JVM8 為例的。

每一個 Class文件 都有如下的 ClassFile 文件結構:

先簡單介紹一下 ClassFile 文件結構各部分含義:

描述符是表示欄位或方法類型的字元串。

欄位描述符表示類、實例或局部變數的類型。

從上面文法可以看出,欄位描述符中一共有三個類型:

方法描述符包含 0 個或者多個參數描述符以及一個返回值描述符。

看了描述符,可能大家有點疑惑,泛型信息怎麼表示啊?

常量池的通用格式如下:

目前 JVM8 中一共用 14 種常量類型,分別如下:

我們知道要使用一個欄位或者調用一個方法,就必須知道欄位或者方法所屬類符號引用,和欄位的名字和類型,方法的名字和方法參數類型以及方法返回值類型。
但是我們知道類是能繼承的,那麼子類調用父類的方法或者欄位,這里的所屬類符號引用,到底是子類本身還是父類的呢?

我們知道類,方法,欄位都有不同的訪問標志,在 Class 文件 中使用一個 u2 類型數據項來存儲,也就是最多可以有 16 個不同標志位。
在類,方法,欄位中有相同的標志,也有不同的標志,總體規劃,我們可以藉助 Modifier 類的源碼來了解:

在 Modifier 類中,類的訪問標志:

我們知道在 java 中類可以用的修飾符有: public , protected , private , abstract , static , final , strictfp 。

但是我們再看 Class 文件 中類的訪問標志:

仔細看,你會發現有些不同點:

在 Modifier 類中,欄位的訪問標志:

我們知道在 java 中欄位可以用的修飾符有: public , protected , private , static , final , transient 和 volatile 。

但是我們再看 Class 文件 中欄位的訪問標志:

Class 文件 中欄位的訪問標志和 java 中欄位的修飾符差不多,只是多了 ACC_SYNTHETIC 和 ACC_ENUM 兩個標志。

在 Modifier 類中,方法的訪問標志:

我們知道在 java 中方法可以用的修飾符有:
public , protected , private , abstract , static , final , synchronized , synchronized 和 strictfp 。

但是我們再看 Class 文件 中方法的訪問標志:

欄位詳情 field_info 的格式如下:

方法詳情 method_info 的格式如下:

關於 Class 文件 中屬性相關信息,我們再後面章節介紹。

我們可以通過 javap 的命令來閱讀 Class 文件 中相關信息。

這個是最簡單的一個類,沒有任何欄位和方法,只繼承 Object 類,我們來看看它編譯後的位元組碼信息,通過 javap -p -v T.class 的命令:

我們重點關注常量池相關信息,會發現雖然 T.class 很乾凈,但是也有 15 個常量,來我們依次分析:

與之前的例子相比較,多了一個欄位和方法,那麼得到的位元組碼信息如下:

但是你會發現常量池中怎麼沒有這個欄位 name 的 CONSTANT_Fieldref_info 類型的常量呢?
那是因為我們沒有使用這個欄位。

多寫了一個方法 test1 來調用 name 欄位和 test 方法,那麼得到的位元組碼信息如下:

這里定義一個父類 TParent ,有一個公共欄位 name 和方法 say 。子類

Ⅵ java常量池是什麼

1. 首先String不屬於8種基本數據類型,String是一個對象。
因為對象的默認值是null,所以String的默認值也是null;但它又是一種特殊的對象,有其它對象沒有的一些特性。

2. new String()和new String(「」)都是申明一個新的空字元串,是空串不是null;

3. String str=」kvill」;
String str=new String (「kvill」);的區別:
在這里,我們不談堆,也不談棧,只先簡單引入常量池這個簡單的概念。
常量池(constant pool)指的是在編譯期被確定,並被保存在已編譯的.class文件中的一些數據。它包括了關於類、方法、介面等中的常量,也包括字元串常量。
看例1:

Java代碼
String s0=」kvill」;
String s1=」kvill」;
String s2=」kv」 + 「ill」;
System.out.println( s0==s1 );
System.out.println( s0==s2 );
String s0=」kvill」;
String s1=」kvill」;
String s2=」kv」 + 「ill」;
System.out.println( s0==s1 );
System.out.println( s0==s2 );
結果為:
true
true
首先,我們要知道Java會確保一個字元串常量只有一個拷貝。
因為例子中的s0和s1中的」kvill」都是字元串常量,它們在編譯期就被確定了,所以s0==s1為true;而」kv」和」ill」也都是字元串常量,當一個字元串由多個字元串常量連接而成時,它自己肯定也是字元串常量,所以s2也同樣在編譯期就被解析為一個字元串常量,所以s2也是常量池中」kvill」的一個引用。
所以我們得出s0==s1==s2;
用new String() 創建的字元串不是常量,不能在編譯期就確定,所以new String() 創建的字元串不放入常量池中,它們有自己的地址空間。
看例2:

Java代碼
String s0=」kvill」;
String s1=new String(」kvill」);
String s2=」kv」 + new String(「ill」);
System.out.println( s0==s1 );
System.out.println( s0==s2 );
System.out.println( s1==s2 );
String s0=」kvill」;
String s1=new String(」kvill」);
String s2=」kv」 + new String(「ill」);
System.out.println( s0==s1 );
System.out.println( s0==s2 );
System.out.println( s1==s2 ); 結果為:
false
false
false
例2中s0還是常量池中」kvill」的應用,s1因為無法在編譯期確定,所以是運行時創建的新對象」kvill」的引用,s2因為有後半部分new String(「ill」)所以也無法在編譯期確定,所以也是一個新創建對象」kvill」的應用;明白了這些也就知道為何得出此結果了。

4. String.intern():
再補充介紹一點:存在於.class文件中的常量池,在運行期被JVM裝載,並且可以擴充。String的intern()方法就是擴充常量池的一個方法;當一個String實例str調用intern()方法時,Java查找常量池中是否有相同Unicode的字元串常量,如果有,則返回其的引用,如果沒有,則在常量池中增加一個Unicode等於str的字元串並返回它的引用;看例3就清楚了
例3:

Java代碼
String s0= 「kvill」;
String s1=new String(」kvill」);
String s2=new String(「kvill」);
System.out.println( s0==s1 );
System.out.println( 「**********」 );
s1.intern();
s2=s2.intern(); //把常量池中「kvill」的引用賦給s2
System.out.println( s0==s1);
System.out.println( s0==s1.intern() );
System.out.println( s0==s2 );
String s0= 「kvill」;
String s1=new String(」kvill」);
String s2=new String(「kvill」);
System.out.println( s0==s1 );
System.out.println( 「**********」 );
s1.intern();
s2=s2.intern(); //把常量池中「kvill」的引用賦給s2
System.out.println( s0==s1);
System.out.println( s0==s1.intern() );
System.out.println( s0==s2 ); 結果為:
false
**********
false //雖然執行了s1.intern(),但它的返回值沒有賦給s1
true //說明s1.intern()返回的是常量池中」kvill」的引用
true
最後我再破除一個錯誤的理解:
有人說,「使用String.intern()方法則可以將一個String類的保存到一個全局String表中,如果具有相同值的Unicode字元串已經在這個表中,那麼該方法返回表中已有字元串的地址,如果在表中沒有相同值的字元串,則將自己的地址注冊到表中「如果我把他說的這個全局的String表理解為常量池的話,他的最後一句話,「如果在表中沒有相同值的字元串,則將自己的地址注冊到表中」是錯的:
看例4:

Java代碼
String s1=new String("kvill");
String s2=s1.intern();
System.out.println( s1==s1.intern() );
System.out.println( s1+" "+s2 );
System.out.println( s2==s1.intern() );
String s1=new String("kvill");
String s2=s1.intern();
System.out.println( s1==s1.intern() );
System.out.println( s1+" "+s2 );
System.out.println( s2==s1.intern() );

結果:
false
kvill kvill
true
在這個類中我們沒有聲名一個」kvill」常量,所以常量池中一開始是沒有」kvill」的,當我們調用s1.intern()後就在常量池中新添加了一個」kvill」常量,原來的不在常量池中的」kvill」仍然存在,也就不是「將自己的地址注冊到常量池中」了。
s1==s1.intern()為false說明原來的「kvill」仍然存在;
s2現在為常量池中「kvill」的地址,所以有s2==s1.intern()為true。

5. 關於equals()和==:
這個對於String簡單來說就是比較兩字元串的Unicode序列是否相當,如果相等返回true;而==是比較兩字元串的地址是否相同,也就是是否是同一個字元串的引用。

6. 關於String是不可變的
這一說又要說很多,大家只要知道String的實例一旦生成就不會再改變了,比如說:String str=」kv」+」ill」+」 「+」ans」;
就是有4個字元串常量,首先」kv」和」ill」生成了」kvill」存在內存中,然後」kvill」又和」 「 生成 」kvill 「存在內存中,最後又和生成了」kvill ans」;並把這個字元串的地址賦給了str,就是因為String的「不可變」產生了很多臨時變數,這也就是為什麼建議用StringBuffer的原因了,因為StringBuffer是可改變的

Ⅶ class文件詳解

能夠被JVM識別,載入並執行的文件格式

1.通過IDE自動幫我們build。
2.手動通過javac去生成class文件。

記錄一個類文件的所有信息。

1.一種8位位元組的二進制流文件
2.各個數據按順序緊密地排列,無間隙 (這樣做的好處可以減少class文件的體積,jvm載入我們class文件的時候更加快速)
3.每個類、介面和枚舉都單獨占據一個class文件(這樣做的好處是每個累介面等都可以獨自管理自己內部的內容而無需相互交叉)
整體文件格式:

格式詳解:
1.magic
無符號4位元組,用來表示class文件的開頭,加密段,給虛擬機用來判斷當前的 class文件是否被串改過。
2.minor_version
class文件最小可以被哪個版本的jdk所載入,也就是最小適配的jdk
3.major_version
表示我們當前class文件是由哪個版本的jdk生成的。
4.constant_pool_count
class文件中常量池的數量,通常只有一個常量池。
5.constant_pool
代表常量池,類型為cp_info(結構體類型)。
常量池中主要包含的內容:
首先列舉三個比較簡單的
CONSTANT_Integer_info:存儲class文件中的int類型。
CONSTANT_Long_info:存儲class文件中的long類型。
CONSTANT_String_info:存儲class文件中的string類型。
它們分別存儲位元組碼中的int、long、string類型,當然還有CONSTANT_Short_info、CONSTANT_Float_info等。
下面列舉幾個稍微復雜的
CONSTANT_Class_info:記錄類中相關的信息、不僅記錄了當前類的信息,還記錄了引用到的一些類的信息。
CONSTANT_Fieldref_info:記錄類中Field相關的信息。
CONSTANT_Methodref_info:記錄類中Method相關的信息。
這三個裡面存儲的並不是真正的內容,都是一些索引,這些索引指向的又是CONSTANT_String_info等。
6.access_flags
表示class文件的作用域標志,比如:public 、public final
取宴老爛值范圍:

7.this_class
this_class是指向constant pool的索引值,該值必須是CONSTANT_Class_info類型,指定當前位元組碼定義的類或介面。
8.super_class
super_class是指向constant pool的索引值,該值必須是CONSTANT_Class_info類型,指定當前位元組碼定義的類或介面的直接父類。只有Object類才沒有直接父晌漏類,此時該索引值為0。並且父類不能是final類型。介面的父類都是Object類。
9.interfaces_count
當前class文件直接實現的介面數量。
10.interfaces
當前class文件直接實現的介面,只記錄直接實現的,不會記錄間接實現的。
11.fields_count
class文件中成員變數的數量。
12.fields
class文件中所有的成員變數,field_info類型的結構體,該結構體中主要包含了每個成員變數的name、所屬的類以及類型含敗。
13.methods_count
記錄class文件中方法的數量。
14.methods
記錄class文件中所有的方法,類型為method_info結構體類型,主要包含了方法的名字、類型、access_flags等信息。
15.attribute_count
記錄了class文件屬性的數量。
16.attributes
記錄class文件的一些屬性,除去上面的一些信息剩下的都包含在attributes中,比如 說註解。

1.內存佔用大,不適合移動端。
2.堆棧的載入模式,載入速度慢。
3.文件IO操作多,類查找慢。

閱讀全文

與class文件常量池變數個數相關的資料

熱點內容
產品在網站優化多少錢 瀏覽:992
亂碼文件夾 瀏覽:480
mc編程後怎麼模擬加工 瀏覽:153
如何恢復刷機後的數據 瀏覽:243
重裝系統win81教程 瀏覽:317
nero10安裝教程 瀏覽:182
handJoy游戲大廳安卓版 瀏覽:663
wow的配置文件怎麼重置 瀏覽:921
css代碼在線編輯 瀏覽:383
哪個狼人殺app可以觀戰 瀏覽:797
你懂的免費qq空間 瀏覽:858
電影曲面是在哪裡拍的app 瀏覽:137
ipadwps怎麼改文件名 瀏覽:162
怎麼將結果顯示在jsp 瀏覽:819
word文檔解析度 瀏覽:108
如何在網站主頁中插入圖像 瀏覽:258
特斯拉數據需要多少伺服器 瀏覽:828
手機百度雲無法看種子文件 瀏覽:690
都有哪些街拍網站 瀏覽:482
賣家鄉特產要什麼網站賣呢 瀏覽:102

友情鏈接