class文件结构[3] 常量池

【参考链接】

Java虚拟机原理图解》 1.2class文件中的常量池http://blog.csdn.net/luanlouis/article/details/40148053

Java虚拟机原理图解》 1.2.2Class文件中的常量池详解(上)http://blog.csdn.net/luanlouis/article/details/39960815

Java虚拟机原理图解》 1.2.3Class文件中的常量池详解(下)http://blog.csdn.net/luanlouis/article/details/40301985

 

常量池中的内容大致上分为3

1

当前类中出现的各种基本数据类型的数值。

CONSTANT_Utf8_info

这个又可以分为5

1

字符串字面量

注意charchar[]类型不会生成字符串字面量,JVM会使用后面的CONSTANT_Integer_info整数数值来表示。

以如下代码为例

 JavaCode 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

 

package com.shadowfaxghh.test.b;

public class Test {
    
    
private String m1="str1";
    
private String m2="str1";
    
private String m3="str2";
    
    
private void method1(){
        
String v1="123";
        System.out.println(
"12345");
    }
    
}

 

字符串字面量有

 

2

当前类/接口的全限定名

当前类的直接父类/父接口的全限定名

当前类所实现的所有接口的全限定名

其中全限定名是指

Object类, .java源文件中的全限定名是java.lang.Object 而在.class文件中的全限定名是将点号替换成“/”, java/lang/Object

 

当前类所定义的成员变量的名称和描述符

其中 成员变量的描述符是指

1)void和基本数据类型的成员变量的描述符为

void和基本数据类型

描述符

void

V

byte

B

char

C

double

D

float

F

int

I

long

J

short

S

boolean

Z

基本上都是以类型的首字母变成大写来对应的其中longboolean是特例 long类型的描述符是J boolean类型的描述符是Z

2) 引用类型的成员变量的描述符为

“L” + 类型的全限定名 + “;”

 Object类型的成员变量的描述符是:Ljava/lang/Object ArrayList类型的成员变量的描述符是: Ljava/lang/ArrayList 

3) 数组类型的成员变量的描述符为

若干个“[”  +  数组中元素类型的对应字符串

int[]类型的对应字符串是: [I  。 int[][]类型的对应字符串是:[[I 。 Object[]类型的对应字符串是: [Ljava/lang/Object; 。 Object[][][]类型的对应字符串是:[[[Ljava/lang/Object;

 

当前类中所定义的成员方法的名称和描述符

其中 成员方法的描述符是指

(参数1类型 参数2类型 参数3类型 ...)返回值类型 

举例说明如下

成员方法

描述符

int getSize()

()I

String toString()

()Ljava/lang/String;

void main(String[] args)

([Ljava/lang/String;)V

void wait()

()V

void wait(long timeout, int nanos)

(JI)V

boolean regionMatches(boolean ignoreCase, int toOffset, String other, int ooffset, int len)

(ZILjava/lang/String;II)Z

int read(byte[] b, int off, int len )

([BII)I

Object[][] getObjectArray()

()[[Ljava/lang/Object;

 

以如下代码为例

 Java Code 

1
2
3
4
5
6
7
8
9
10
11
12
13

 

package com.test.b;

import java.util.Date;

public class Test {
    
    
private Date m1=new Date();
    
    
private void method1(){
        System.out.println(
"12345");
    }
    
}

 

其中

/接口的全限定名有



成员变量和成员方法的名称和描述符有


 

其中成员变量和成员方法的名称和描述符,实际是用于常量池后面的FieldsMethods的引用




 

3

当前类所引用的其他类/接口的全限定名

当前类所引用的其他类的成员变量的名称和描述符

当前类所引用的其他类的成员方法的名称和描述符

所谓引用,是指创建了其他类的对象,或者调用了其他类的成员变量或成员方法

依然以上面的代码为例

 Java Code 

1
2
3
4
5
6
7
8
9
10
11
12
13

 

package com.test.b;

import java.util.Date;

public class Test {
    
    
private Date m1=new Date();
    
    
private void method1(){
        System.out.println(
"12345");
    }
    
}

 

常量池中的相关项有


这些常量池项,其实是用于下面的ClassFieldref MethodrefInterfaceMethodRef的引用,最终是用于常量池后面的Methods中的相关的指令字节码,如newgetputinvoke

 




所以,如果Methods的指令字节码中并没有创建其他类的对象,或者调用其他类的成员变量或成员方法,则不会生成相关的常量池项

在上面代码的基础上,更改m1不进行初始化(会有默认值null),即

 Java Code 

1
2
3
4
5
6
7
8
9
10
11
12
13
14

 

package com.test.c;

import java.util.Date;

public class Test {
    
    
private Date m1;
    
//或者private Date m1=null;
    
    
private void method1(){
        System.out.println(
"12345");
    }
    
}

 

则指令字节码中就不会有new Date,则不会有DateCONSTANT_Class_info(下面讲到),也不会有DateCONSTANT_Utf8_info

 




4

形式参数和局部变量的名称和描述符

这里需要注意两点

1、  非静态成员方法,会在局部变量表中默认添加一个局部变量this

2、  如之前所讲,如果局部变量只定义,未进行初始化,后续也未赋值使用,则会被删除掉。

依然以上面的代码为例

 Java Code 

1
2
3
4
5
6
7
8
9
10
11
12
13

 

package com.test.b;

import java.util.Date;

public class Test {
    
    
private Date m1=new Date();
    
    
private void method1(){
        System.out.println(
"12345");
    }
    
}

常量池中的相关项有


这些常量池项,其实是用于Methods中的局部变量表的引用



5

属性相关的字符串

 

CONSTANT_Integer_info

CONSTANT_Long_info

CONSTANT_Float_info

CONSTANT_Double_info

对于longfloatdouble,只要是出现的常量值,都会有对应的常量池项

以如下代码为例

 Java Code 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

 

package com.test.d;

import java.util.Date;

public class Test {
    
    
private long m1=100l;
    
private long m2=200l;
    
    
private void method1(){
        m1+=300l;
        System.out.println(400l);
    }
    
}



其使用时的字节码指令如下


 

其中integer的有些特殊

1、  只有final类型的integer才会生成常量池项

2、  会直接将数值整合到字节码指令中去(搞不懂为什么final类型的不引用常量池,那final类型的还生成常量池项干什么?)

3、  虽然.java源代码/java语言中支持byteshort char boolean类型, 但是.class文件/JVM中却不支持这几种类型, 这几种类型都当做int来对待。

表现在.class文件中就是, 这几种类型的数值在常量池中都为CONSTANT_Integer_info类型。

注意只是数值是CONSTANT_Integer_info类型,描述符还是各自有各自的,前文已有介绍。

 

以如下代码为例

 Java Code 

1
2
3
4
5
6
7
8
9
10

 

package com.test.e;

public class Test {
    
    
private int m1=100;
    
private final int m2=200;
    
    
private char m3='a';
    
private final char m4='b';    
}

常量池中的项为,其中小写字母b对应的整数值是98



字节码指令为


2

CONSTANT_String_info

对于字符串字面量,除了会生成一个上面的CONSTANT_Utf8_info,还是生成一个CONSANT_String_info,引用CONSTANT_Utf8_info

那为什么还要再多存储这么一项呢?这么做其实跟JVM对字符串的管理有关,有何作用详见后续文章

以上面最开始的代码为例

 JavaCode 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

 

package com.shadowfaxghh.test.b;

public class Test {
    
    
private String m1="str1";
    
private String m2="str1";
    
private String m3="str2";
    
    
private void method1(){
        
String v1="123";
        System.out.println(
"12345");
    }
    
}

其对应的常量池项有

 

 

3

CONSTANT_Class_info

CONSTANT_Fieldref_info

CONSTANT_Methodref_info

CONSTANT_InterfaceMethodref_info

这些其实在讲CONSTANT_Utf8_info的时候就已经讲过了,就是对CONSTANT_Utf8_info的引用或多次引用

在执行字节码指令newputgetinvoke时会引用到这些

 


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值