詳解JAVA 常量池
前言
對常量池的理解之前,需要熟悉的是一些術(shù)語:
字面量
在計(jì)算機(jī)科學(xué)中,字面量(literal)是用于表達(dá)源代碼中一個固定值的表示法(notation)。
幾乎所有計(jì)算機(jī)編程語言都具有對基本值的字面量表示,諸如:整數(shù)、浮點(diǎn)數(shù)以及字符串;而有很多也對布爾類型和字符類型的值也支持字面量表示;
還有一些甚至對枚舉類型的元素以及像數(shù)組、記錄和對象等復(fù)合類型的值也支持字面量表示法。C語言關(guān)于復(fù)合字面量的介紹可參考: [1] 。
百度也給了一個例子:
這個object-c 的例子,容易理解。
#include <stdio.h> int main(void) { int a = 10; // 10為int類型字面量 char a[] = {"Hello world!"} // Hello world 為字符串形式字面量 ............. // 以此類推,不再贅述 return 0; }
正文
JVM常量池主要分為Class文件常量池、運(yùn)行時常量池,全局字符串常量池,以及基本類型包裝類對象常量池。
我在網(wǎng)上找了一個例子:
private int value = 1; public String s = "abc"; public final static int f = 0x101; public static void main(String[] args) { } public void setValue(int v){ final int temp = 3; this.value = temp + v; } public int getValue(){ return value; }
編譯后:
下面只截取了一部分,常量池:
public class test.program minor version: 0 major version: 57 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #1 // test/program super_class: #3 // java/lang/Object interfaces: 0, fields: 3, methods: 4, attributes: 1 Constant pool: #1 = Class #2 // test/program #2 = Utf8 test/program #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Utf8 value #6 = Utf8 I #7 = Utf8 s #8 = Utf8 Ljava/lang/String; #9 = Utf8 f #10 = Utf8 ConstantValue #11 = Integer 257 #12 = Utf8 <init> #13 = Utf8 ()V #14 = Utf8 Code #15 = Methodref #3.#16 // java/lang/Object."<init>":()V #16 = NameAndType #12:#13 // "<init>":()V #17 = Fieldref #1.#18 // test/program.value:I #18 = NameAndType #5:#6 // value:I #19 = String #20 // abc #20 = Utf8 abc #21 = Fieldref #1.#22 // test/program.s:Ljava/lang/String; #22 = NameAndType #7:#8 // s:Ljava/lang/String; #23 = Utf8 LineNumberTable #24 = Utf8 LocalVariableTable #25 = Utf8 this #26 = Utf8 Ltest/program; #27 = Utf8 main #28 = Utf8 ([Ljava/lang/String;)V #29 = Utf8 args #30 = Utf8 [Ljava/lang/String; #31 = Utf8 setValue #32 = Utf8 (I)V #33 = Utf8 v #34 = Utf8 temp #35 = Utf8 getValue #36 = Utf8 ()I #37 = Utf8 SourceFile #38 = Utf8 program.java
好的下面介紹class 常量池;
class 常量池
主要包括:字面量和符號引用
首先字面量不是全部的字面量,如果不明白什么是字面值請看上面;
字符字面值:
#7 = Utf8 s
#20 = Utf8 abc
用final修飾的成員變量
#9 = Utf8 f
#11 = Integer 257
大概包含的就是這兩種。
符號引用
符號引用主要設(shè)涉及編譯原理方面的概念,包括下面三類常量:
類和接口的全限定名,也就是java/lang/String;這樣,將類名中原來的"."替換為"/"得到的,主要用于在運(yùn)行時解析得到類的直接引用,像上面
#5 = Class #33 // JavaBasicKnowledge/JavaBean
#33 = Utf8 JavaBasicKnowledge/JavaBean
字段的名稱和描述符,字段也就是類或者接口中聲明的變量,包括類級別變量和實(shí)例級的變量
#4 = Fieldref #5.#32 // JavaBasicKnowledge/JavaBean.value:I
#5 = Class #33 // JavaBasicKnowledge/JavaBean
#32 = NameAndType #7:#8 // value:I#7 = Utf8 value
#8 = Utf8 I//這兩個是局部變量,值保留字段名稱
#23 = Utf8 v
#24 = Utf8 temp
可以看到,對于方法中的局部變量名,class文件的常量池僅僅保存字段名。
方法中的名稱和描述符,也即參數(shù)類型+返回值
#21 = Utf8 setValue
#22 = Utf8 (I)V#25 = Utf8 getValue
#26 = Utf8 ()I
其實(shí)并不需要怎么關(guān)注符號引用。
那么這些class 常量池有什么好處呢?
運(yùn)行時常量池是方法區(qū)的一部分,所以也是全局貢獻(xiàn)的,我們知道,jvm在執(zhí)行某個類的時候,必須經(jīng)過加載、鏈接(驗(yàn)證、準(zhǔn)備、解析)、初始化,在第一步加載的時候需要完成:
通過一個類的全限定名來獲取此類的二進(jìn)制字節(jié)流
將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時數(shù)據(jù)結(jié)構(gòu)
在內(nèi)存中生成一個類對象,代表加載的這個類,這個對象是java.lang.Class,它作為方法區(qū)這個類的各種數(shù)據(jù)訪問的入口。
類對象和普通對象是不同的,類對象是在類加載的時候完成的,是jvm創(chuàng)建的并且是單例的,作為這個類和外界交互的入口, 而普通的對象一般是在調(diào)用new之后創(chuàng)建。
上面的第二條,將class字節(jié)流代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時數(shù)據(jù)結(jié)構(gòu),其中就包含了class文件常量池進(jìn)入運(yùn)行時常量池的過程,這里需要強(qiáng)調(diào)一下不同的類共用一個運(yùn)行時常量池,同時在進(jìn)入運(yùn)行時常量池的過程中,多個class文件中常量池相同的字符串,多個class文件中常量池中相同的字符串只會存在一份在運(yùn)行時常量池,這也是一種優(yōu)化。
運(yùn)行時常量池的作用是存儲java class文件常量池中的符號信息,運(yùn)行時常量池中保存著一些class文件中描述的符號引用,同時在類的解析階段還會將這些符號引用翻譯出直接引用(直接指向?qū)嵗龑ο蟮闹羔?,?nèi)存地址),翻譯出來的直接引用也是存儲在運(yùn)行時常量池中。
運(yùn)行時常量池相對于class常量池一大特征就是具有動態(tài)性,java規(guī)范并不要求常量只能在運(yùn)行時才產(chǎn)生,也就是說運(yùn)行時常量池的內(nèi)容并不全部來自class常量池,在運(yùn)行時可以通過代碼生成常量并將其放入運(yùn)行時常量池中,這種特性被用的最多的就是String.intern()。
那么就看下String.intern() 來理解:運(yùn)行時常量池。
首先看下:
string x="x" 和 String x=new String("x");
有什么區(qū)別?
可以肯定的是他們的值是一樣的。
但是他們運(yùn)行差別很大。string x="x" 會查找常量池,如果沒有x的話,那么會存入常量池,如果有的話,那么會存在于常量池并進(jìn)行引用。
而 String x=new String("x") 則只會生成在堆中,而不會和常量池產(chǎn)生聯(lián)系。
注:
常量字符串和變量拼接時(如:String str3=baseStr + “01”;)會調(diào)用stringBuilder.append()在堆上創(chuàng)建新的對象。
那么String.intern() 是什么意思呢?這個是會去查找變量詞中有沒有,如果有的話那么會返回引用,如果沒有的話,這個和版本有關(guān)。
題目
public static void main(String[] args) { // write your code here Integer i01=59; int i02=59; Integer i03=Integer.valueOf(59); Integer i04= new Integer(59); } public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
問題如下:
i01 是否和 i02 相等?
i03 是否和 i01 相等。
i04 是否和 i01相等。
總結(jié)
寫的比較倉促,后續(xù)會完善好。
以上就是詳解JAVA 常量池的詳細(xì)內(nèi)容,更多關(guān)于java 常量池的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
spring-boot實(shí)現(xiàn)增加自定義filter(新)
本篇文章主要介紹了spring-boot實(shí)現(xiàn)增加自定義filter(新),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05Java發(fā)送http請求的示例(get與post方法請求)
這篇文章主要介紹了Java發(fā)送http請求的示例(get與post方法請求),幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2021-01-01SpringBoot集成DJL實(shí)現(xiàn)圖片分類功能
DJL是一個使用Java?API簡化模型訓(xùn)練、測試、部署和使用深度學(xué)習(xí)模型進(jìn)行推理的開源庫深度學(xué)習(xí)工具包,開源的許可協(xié)議是Apache-2.0,本文給大家介紹了SpringBoot集成DJL實(shí)現(xiàn)圖片分類功能,需要的朋友可以參考下2024-10-10java?Date和SimpleDateFormat時間類詳解
這篇文章主要介紹了java?Date和SimpleDateFormat時間類詳解,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-08-08java實(shí)現(xiàn)基于SMTP發(fā)送郵件的方法
這篇文章主要介紹了java實(shí)現(xiàn)基于SMTP發(fā)送郵件的方法,實(shí)例分析了java基于SMTP服務(wù)發(fā)送郵件的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07Java cglib為實(shí)體類(javabean)動態(tài)添加屬性方式
這篇文章主要介紹了Java cglib為實(shí)體類(javabean)動態(tài)添加屬性方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02Java Socket實(shí)現(xiàn)多線程通信功能示例
這篇文章主要介紹了Java Socket實(shí)現(xiàn)多線程通信功能,結(jié)合具體實(shí)例形式較為詳細(xì)的分析了java多線程通信的原理及客戶端、服務(wù)器端相應(yīng)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-06-06Java實(shí)現(xiàn)規(guī)則幾何圖形的繪制與周長面積計(jì)算詳解
隨著計(jì)算機(jī)的發(fā)展,人們對圖形的計(jì)算要求會越來越高。在各行各業(yè)中的計(jì)算人員會對圖形的計(jì)算要有便利的要求,規(guī)則幾何圖形問題求解程序應(yīng)運(yùn)而生!本文將用Java編寫一個程序,可以實(shí)現(xiàn)規(guī)則幾何圖形的繪制與周長面積計(jì)算,感興趣的可以了解一下2022-07-07