前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java Class文件之常量池

Java Class文件之常量池

作者头像
心平气和
发布2021-04-22 17:05:18
6520
发布2021-04-22 17:05:18
举报

上一篇Java Class文件格式详解 讲解了整个Class文件的组成部分,因为篇幅的原因没做太多过细的分析,今天我们重点分析下常量池。

一、示例代码介绍

为了加深认识,这篇文章会以一个实际的例子来讲解常量池中具体结构,代码如下:

代码语言:javascript
复制
package com.stock.test;

@RunWith(SpringJUnit4ClassRunner.class)
public class StringTest extends SpringTest {
    @Test
    public void testEncoding(){
        writeToDb("中国");
        return;
    }

    /**
     * 写入到数据库
     * @param str
     */
    private void writeToDb(String str){
        try {
            String connStr;

            connStr = "jdbc:mysql://127.0.0.1:3306/stock?useUnicode=true&characterEncoding=UTF-8";
            Connection conn = DriverManager.getConnection(connStr, "root", "");
            conn.setAutoCommit(false);
            PreparedStatement pst = conn.prepareStatement("");

            String sql = "INSERT INTO test(str) VALUES ('" + str + " ')";
            pst.addBatch(sql);
            pst.executeBatch();
            conn.commit();

            pst.close();
            conn.close();

        }catch (Exception ex){

        }
    }

    /**
     * 将内容保存到文件之中
     * @param str
     * @param path
     */
    private void saveToFile(String str, String path){

        try {
            File file = new File(path);
            if (!file.exists()) {
                file.createNewFile();
            }


            //保存文件
            FileOutputStream fileOutputStream = new FileOutputStream(file);
           byte[] bytes = str.getBytes();
           fileOutputStream.write(bytes);
           fileOutputStream.flush();
           fileOutputStream.close();
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
}

二、常量池中常量类型

上一篇文章讲了常量池总共17(上一篇写的是18,修正一下,因为2没有使用)种类型,我们来分析其中最常用的类型。

1、CONSTANT_Utf8

这种类型存放具体的字符串,这个是基础又基础的类型,好多其它类型都会间隔指向它。

具体格式:

名称

长度(单位为字节)

说明

tag

1

类型,固定为1

length

2

接下来的字符串的长度

bytes

length

存放具体的字符串

如要存放saveToFile这个UTF8字符串,则大概的组成是这样的:

1000AsaveToFile

第1个字节是1,接下来2个字节是000A,表示长度为10,最后是存放具体的字符串内容

2、CONSTANT_Integer

名称

长度(单位为字节)

说明

tag

1

类型,固定为3

bytes

4

存放整型变量具体的数值

3、CONSTANT_Float、CONSTANT_Long、CONSTANT_Double

这几个和上面CONSTANT_Integer,区别在于存放具体的内容不一样,其中Double的数值是用8字节,其余是4字节

4、CONSTANT_Class

名称

长度(单位为字节)

说明

tag

1

类型,固定为7

index

2

指向类名常量项的索引,即指向CONSTANT_Utf8类型的索引

5、CONSTANT_String

一些字符常量就是通过这个类型表示的,像上面的writeToDb的参数:中国

名称

长度(单位为字节)

说明

tag

1

类型,固定为8

index

2

指向字符串值的索引,即指向CONSTANT_Utf8类型的索引

6、CONSTANT_Fieldref

描述类的成员,或者说字段

名称

长度(单位为字节)

说明

tag

1

类型,固定为9

class_index

2

指向类名的索引,即指向CONSTANT_Utf8类型的索引

desc_index

2

指向成员描述符的索引项,即指向后面要讲的类型为CONSTANT_NameAndType的常量

7、CONSTANT_Methodref

描述方法

名称

长度(单位为字节)

说明

tag

1

类型,固定为10

class_index

2

指向类名的索引,即指向CONSTANT_Utf8类型的索引

desc_index

2

指向方法描述符的索引项,如入参和返回值等信息

8、CONSTANT_InterfaceMethodref

同上面CONSTANT_Methodref,只不过指向接口的方法

9、CONSTANT_NameAndType

名称

长度(单位为字节)

说明

tag

1

类型,固定为12

name_index

2

指向方法名或字段名常量的索引

desc_index

2

指向描述符常量索引索引

其它几种类型用的比较少,就不在这里一一介绍了。

三、实例分析

接下来我们分析上面代码中字节码

上面是整个字节码的部分截图,我们来逐一分析下。

前面4字节是CA FE BA BE,这是固定的魔幻数;

接下来4字节是版本号:00 00 00 34 ,换成10进制是52,这是JDK8的版本号;

接下来是00 8C ,这个表示常量池常量项的个数,一共有140-1=139项。

接下来是具体的常量项了,看第1个字节,这个表示类型,这里值为0A,即10,对应的类型是CONSTANT_Methodref,按照上面的分析这种格式接下来是2字节的类名索引和2字节的类型索引,接下来2字节是0022,再接下来2字节是004C,表示类名索引是34,描述索引是76,那整个常量池的第34和76项是什么呢,可以用Javap看下结果,

代码语言:javascript
复制
#1 = Methodref          #34.#76       // com/stock/test/SpringTest."<init>":()V
#34 = Class              #110          // com/stock/test/SpringTest
#76 = NameAndType        #35:#36       // "<init>":()V

可以看到javap出来的结果第一行就是索引为1的常量项,它有类型是Methodref,后面的#34和#76表示由这2项组成,具体的值就是:

代码语言:javascript
复制
com/stock/test/SpringTest."<init>":()V

前面是类名好理解;

后面方法描述符"<init>":()V,这里双引号之间是方法名,后面的()表示参数,这里为空表示没有参数,最后一个V表示返回值,完整的类型表示有:

再往下分析,接下来是08,表示类型为CONSTANT_String,这种类型接下来2字节表示字符串值的索引,这里为004D,即77,即它指向索引77的常量项,77项,77项的位置在哪呢,在偏移量为0x328的位置:

这里第1个字节是1,表示类型为CONSTANT_Utf8,接下是2字节是0006,表示这个字符串的字节长度是6,再接来是是6字节的具体内容,即E4B8ADE59BBD,这个就是字符串“中国”的UTF8表示了。

再看其它几种类型的,索引为16的,对应的偏移量为0X47

类型为0B,即11,对应的是CONSTANT_InterfaceMethodref;

接下来2字节是005C和005D,表示对应的接口名的索引为92,接口方法描述索引为93,因为我们有这么一段代码:

代码语言:javascript
复制
pst.addBatch(sql);

92和93索引联合的内容为:java/sql/PreparedStatement.addBatch:(Ljava/lang/String;)V

其中()之间为Ljava/lang/String;,这里以;结束便于多个类型串联起来阅读,前面说过L开头的表示对象,这里表示参数的类型为String,返回值为void,对应addBatch方法原型为:

代码语言:javascript
复制
 void addBatch( String sql ) throws SQLException;
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员升级之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档