mysql使用变量填值为数组_面向JVM编译类实例、数组的创建和使用

码神手记-资深攻城狮的私房笔记,微信公众平台/知乎/头条同步。

动动小手,点个关注!

a77a6a5f08f932c80ba577abc46d3b58.gif

面向JVM编译-类实例、数组的创建和使用

在上一篇文章中提到过:在JVM级别,一个构造函数就是一个名称为(名称是由编译器提供的)的方法,这个特殊的方法就是类实例的初始化方法。如果一个类有多个构造函数,那么就会有多个对应的实例初始化方法。当一个类实例被创建,类变量已经被初始化了默认值,实例初始化方法就会被调用。

一个类实例被创建的过程中都发生了什么?数值数组、引用类型数组、一位数组、多维数组是怎样创建的?又是如何使用和操作的?这一章节详细描述类实例以及对象的创建和使用过程。

1

实例对象的创建和使用

类实例的创建和传递

JVM使用new指令创建类实例,直接上例子:

Object create(){    return new Object();}

编译后:

Method java.lang.Object create()0    new #1              //Class java.lang.Object 创建一个Object类的实例并将引用压入操作数栈。#1指向当前类运行时常量池中的一个Object类型引用3    dup                 //将栈顶的类实例引用复制为两个(后续指令要用到两次实例的引用)4    invokespecial #4    // Method java.lang.Object.()V。从操作数栈弹出一个引用作为this传入Object类的类实例初始化方法,并执行初始化7    areturn             //返回栈顶的类实例引用

类实例作为引用类型被传递和返回,这非常类似于数值类型。

int i;  // 一个实例变量MyObj example(){    MyObj o=new MyObj();    return silly(o);}MyObj silly(MyObj o){    if (o!=null){        return o;    }else{        return o;    }}

编译后:

Method MyObj example()0    new #2                    // Class MyObj 创建一个MyObj类的实例并将引用压入操作数栈。#1指向当前类运行时常量池中的一个MyObj类型引用3    dup                       //将栈顶的类实例引用复制为两个(后续指令要用到两次实例的引用)4    invokespecial #5          // Method MyObj.()V 从操作数栈弹出一个引用作为this传入MyObj类的类实例初始化方法,并执行初始化7    astore_1                  //弹出栈顶的类实例引用,存到局部变量表索引为1的位置,即变量o8    aload_0                   //将局部变量表索引0处的值(即example方法调用者类实例的引用)压入操作数栈9    aload_1                   //将局部变量表索引1出的值(即MyObj类实例的引用)压入操作数栈10   invokespecial #4          //调用silly方法,并将结果压入操作数栈。#4指向当前类运行时常量池中的方法引用,指向silly方法13   areturn                   //返回栈顶的对象引用Method MyObj silly(MyObj)0    aload_1                   //将局部变量表索引为1的值(即传入的MyObj类实例引用)压入操作数栈1    ifnull 6                  //如果栈顶对象是null,则控制流转到指令数组索引6处的指令,否则继续执行后续指令4    aload_1                   //将局部变量表索引为1的值(即传入的MyObj类实例引用)重新压入操作数栈5    areturn                   //从栈顶弹出MyObj类实例的引用,压入到调用者栈帧的操作数栈,程序结束。6    aload_1                   //将局部变量表索引为1的值(即传入的MyObj类实例引用)压入操作数栈7    areturn                   //从栈顶弹出MyObj类实例的引用,压入到调用者栈帧的操作数栈,程序结束(此时返回的引用是null)。

类实例字段的访问

JVM使用getfield和putfield指令访问类实例的字段(实例变量)。假设i是一个int类型的实例变量,那么setIt()和getIt()方法定义如下:

void setIt(int value){    i=value;}int getIt(){    return i;}

编译后:

Method void setIt(int)0    aload_0                            //将局部变量表索引为0的值(一个类实例引用)压入操作数栈【准备类实例引用】1    iload_1                            //将局部变量表索引为1的值(一个整型数值)压入操作数栈【准备字段值】2    putfield #4    //Field Example.i I //将字段值赋给类实例引用的指定字段,即i字段。#4是类实例字段的符号引用5    return                             //结束返回Method int getIt()0    aload_0                            //将局部变量表索引为0的值(一个类实例引用)压入操作数栈1    getfield #4    //Field Example.i I //获取类实例的指定字段值(i的值),压入操作数栈4    ireturn                            //从操作数栈弹出整型数值并返回

与方法调用指令的操作数一样,putfield和getfield指令的操作数(运行时常量池索引#4)不是类实例中字段的偏移量。编译器生成对实例字段的符号引用,这些字段存储在运行时常量池中。这些运行时常量池项在运行时被解析,以确定字段的实际位置。 

2

数组的创建和使用

JVM数组也是对象,有专门的指令集用于数组的创建和操作。

数值类型数组

newarray指令用于创建数值类型的数组。

void createBuffer() {       int buffer[];       int bufsz = 100;       int value = 12;       buffer = new int[bufsz];       buffer[10] = value;       value = buffer[11];}

编译后:

Method void createBuffer()0    bipush 100        //将整型常量100压入操作数栈2    istore_2          //从操作数栈弹出整型常量100,放入局部变量表索引2的位置3    bipush 12         //将整型常量12压入操作数栈4    istore_3          //从操作数栈弹出整型常量12,放入局部变量表索引3的位置6    iload_2           //将局部变量2(整型常量100)压入操作数栈7    newarray int      //从操作数栈中弹出100作为数组长度,创建一个int类型数组,并将引用压入操作数栈9    astore_1          //将数组引用弹出栈,放到局部变量表索引1的位置,即buffer变量的位置,buffer指向了新创建的数组对象10   aload_1           //将局部变量1中的数组引用压入操作数栈 【准备引用】11   bipush 10         //将整型常量10压入操作数栈 【准备索引】13   iload_3           //将局部变量3(整型值12)压入操作数栈 【准备数值】14   iastore           //在数组索引为10的位置,存储整型值1215   aload_1           //再将数组引用压入操作数栈【准备引用】16   bipush 11         //将整型常量11压入操作数栈 【准备索引】18   iaload            //将数组中索引为11的数值压入操作数栈,即buffer[11]19   istore_3          //从操作数栈弹出buffer[11]的值,作为整数存储到局部变量3,即value变量。

在本次对createBuffer方法的调用中,栈帧局部变量表中的变量依次是:this、buffer、bufsz、value,对应的索引是0、1、2、3。如果你已经理解了实例方法如何被调用,就不会对此处有疑惑,否则建议先回顾下面向JVM编译-编译器如何处理方法调用?。

引用类型数组

anewarray指令用于创建对象引用的一维数组,例如:

void createThreadArray() {       Thread threads[];       int count = 10;       threads = new Thread[count];       threads[0] = new Thread();}

编译后:

Method void createThreadArray()0    bipush 10            //将整型常量10压入操作数栈2    istore_2             //从操作数栈弹出整型常量10,放入局部变量表索引2的位置,即count变量3    iload_2              //将count变量值(10)压入操作数栈4    anewarray class #1   //创建一个Thread类型的数组,长度为10,将数组引用压入操作数栈7    astore_1             //从操作数栈弹出数组引用,放入局部变量表索引1的位置,即threads变量,threads指向了新创建的数组对象8    aload_1              //将thread变量中的数组引用压入操作数栈【准备数组引用】9    iconst_0             //将整型常量0压入操作数栈【准备数组索引】10   new #1               //创建一个Thread类实例,引用压入操作数栈【准备数组元素值】13   dup                  //复制栈顶Thread实例的引用,作为this参数提供给invokespecial指令使用。(此时操作数栈栈顶有两个相同的Thread实例引用,指向同一个对象)14   invokespecial #5     //调用Thread类实的构造函数,即类实例初始化方法,弹出栈顶的Thread实例引用,传入java.lang.Thread.V并执行函数。17   aastore              //将Thread实例的引用存入数组索引0的位置18   return

多维数组

anewarray指令还可以用于创建多维数组中的一个维度,multianewarray则可以用来一次性创建多个维度,比如,三维矩阵:

int[][][] create3DArray() {       int grid[][][];       grid = new int[10][5][];       return grid;}

编译后:

Method int create3DArray()[][][]0    bipush 10                    //将整型常量10压入操作数栈(第一个维度的长度)2    iconst_5                     //将整型常量5压入操作数栈(第二个维度的长度)3    multianewarray #1,2          //创建数组并压入操作数栈。#1指向了运行时常量池中的一个类型:[[[I。从操作数栈中获取前两个维度的数组长度,分别是10和5。7    astore_1                     //从操作数栈中弹出数组的引用,放入局部变量表索引1的位置,即变量grid8    aload_1                      //将局部变量1中的数组索引压入操作数栈9    areturn                      //从操作数栈中弹出数组索引,返回到调用者栈帧的操作数栈中

multianewarry指令的第一个操作数是一个运行时常量池的索引,指向还要创建的数组的类型,第二个参数代表要创建的数组的维度。因为代码中只指定了前两个维度的数组长度,第三个维度并没有指定长度,因此jvm实际只会创建前两个维度的数据,并从堆内存中分配空间。当我们把new int[10][5][]替换为new int[10][5][2],编译后代码就会变为multianewarry #1,3。

多维数组也是一个对象,分别由aload_1和areturn指令进行加载压栈和返回。所有数组都有自己的长度,可以通过arraylength指令访问。 

扫描二维码关注

f50fd9193c31fb81c449339d8d539511.png

码神手记

微信号 : iSuperCoder

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值