网站首页 > java教程 正文
使用类实例
在Java虚拟机中创建类的实例,是通过使用Java虚拟机的一个叫做"new"的指令来实现的。这里需要注意的是,在Java虚拟机这个层面上,我们通常所说的构造函数其实就是一个叫做<init>的方法,这个方法是由编译器提供的。并且,这个特别的<init>方法,被我们称为实例初始化方法。
对于每一个类来说,可能会有多个构造函数,所以对应的就会有多个实例初始化方法。当一个类的实例被创建后,包括这个类以及它的所有父类中的实例变量会被初始化为他们默认的值。接下来就会调用这个新创建的类实例的一个实例初始化方法。
Object create() {
return new Object();
}
编译成
Method Object create()
0 new #1 // 创建java.lang.Object类的实例
3 dup // 复制栈顶元素(即复制新创建的实例引用)
4 invokespecial #4 // 调用java.lang.Object类的<init>(即构造函数)
7 areturn // 返回创建的实例
类实例作为引用类型传递和返回,与数值类型非常相似,尽管引用类型有它自己的一套指令集。例如:
int i; // An instance variable
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 // 创建MyObj类的实例
3 dup // 复制栈顶数值
4 invokespecial #5 // 调用MyObj类的构造方法<init>()
7 astore_1 // 将引用存储到局部变量1
8 aload_0 // 将局部变量0(this指针)压入栈顶
9 aload_1 // 将局部变量1的引用压入栈顶
10 invokevirtual #4 // 调用实例方法Example.silly,并传入MyObj实例
13 areturn // 从当前方法返回对象引用
Method MyObj silly(MyObj)
0 aload_1 // 将方法参数MyObj的引用压入栈顶
1 ifnull 6 // 如果引用为空,则跳转到指令6
4 aload_1 // 如果引用不为空,将MyObj的引用再次压入栈顶
5 areturn // 返回MyObj的引用
6 aload_1 // 将MyObj的引用压入栈顶
7 areturn // 返回MyObj的引用
使用 getfield 和 putfield 指令访问类实例的字段(实例变量)。如果 i 是 int 类型的实例变量,则方法 setIt 和 getIt 定义为:
void setIt(int value) {
i = value;
}
int getIt() {
return i;
}
编译成
Method void setIt(int)
0 aload_0 // 将局部变量0(this指针)压入栈顶
1 iload_1 // 将方法的int类型参数压入栈顶
2 putfield #4 // 将栈顶的int值设定为Example类的字段i
5 return // 结束方法,不返回值
Method int getIt()
0 aload_0 // 将局部变量0(this指针)压入栈顶
1 getfield #4 // 获取Example类的字段i的值
4 ireturn // 返回int值
就像方法调用指令的操作数一样,putfield和getfield指令的操作数(运行时常量池索引#4)并不是类实例中字段的偏移量。编译器生成对实例字段的符号引用,这些引用被存储在运行时常量池中。这些运行时常量池项在运行时被解析,以确定引用对象内部字段的位置。
数组
Java 虚拟机数组也是对象。数组是使用一组不同的指令创建和操作的。 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 // 压入int常量100(bufsz为数组大小)
2 istore_2 // 将bufsz存储到局部变量2中
3 bipush 12 // 压入int常量12(value为值)
5 istore_3 // 将value存储到局部变量3中
6 iload_2 // 将bufsz压入栈顶...
7 newarray int // ...创建一个新的int数组,长度为bufsz
9 astore_1 // 将新数组存储到buffer中
10 aload_1 // 压入buffer
11 bipush 10 // 压入int常量10
13 iload_3 // 压入value
14 iastore // 将value存储到buffer[10]的位置
15 aload_1 // 压入buffer
16 bipush 11 // 压入int常量11
18 iaload // 将buffer[11]的值压入栈顶...
19 istore_3 // ...并将其存储到value中
20 return // 完成方法,不返回任何值
anewarray 指令用于创建对象引用的一维数组,例如:
void createThreadArray() {
Thread threads[];
int count = 10;
threads = new Thread[count];
threads[0] = new Thread();
}
编译成:
Method void createThreadArray()
0 bipush 10 // 压入int常量10
2 istore_2 // 使用该值初始化计数器
3 iload_2 // 将计数器压入栈顶,用于anewarray
4 anewarray class #1 // 创建类Thread的新数组
7 astore_1 // 将新数组存储在threads变量中
8 aload_1 // 将threads的值压入栈顶
9 iconst_0 // 压入int常量0
10 new #1 // 创建类Thread的实例
13 dup // 为Thread构造器方法复制引用...
14 invokespecial #5 // ...调用java.lang.Thread的构造函数<init>()V
17 aastore // 在数组的0位置存储新Thread
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 dim #2 // 创建一个三维整型数组的前两个维度,类为[[[I
7 astore_1 // 将新数组存储...
8 aload_1 // ...然后准备返回它
9 areturn // 返回引用的数组
对于多维数组的创建,JVM字节码中的 multianewarray 指令后面的操作数 dim 指的是需要初始化的维度数量,而不是数组的总维数。如果一个四维数组如 int[10][7][5][2],我们希望一次性初始化所有维度,那 multianewarray #index byte 4 就是正确的。
然而,如果我们只希望创建部分维度(例如,只创建前两个维度),就像你之前的示例代码 int create3DArray[][][] 中那样,则 dim 应该为2,因为只创建并初始化了前两个维度,剩下的一个维度被置为null。
multianewarray 指令的第一个操作数是运行时常量池指向要创建的数组类类型的索引。第二个是实际要创建的该数组类型的维度数量。如create3DArray的代码所示,multianewarray 指令可以用于创建类型的所有维度。请注意,多维数组只是一个对象,因此分别由aload_1和areturn指令加载和返回。
所有数组都有相关联的长度,可以通过 arraylength 指令访问。
猜你喜欢
- 2024-10-01 Java培训:不同编程语言中的JIT编译
- 2024-10-01 Java 代码编译的3种方式,其中JIT最重要!
- 2024-10-01 java考试题:选择题(1-10)共15题(java选择题100道)
- 2024-10-01 Java中文编译出现错误的问题(java中文编译出现错误的问题怎么办)
- 2024-10-01 程序的编译和解释,你还知道是什么吗?
- 2024-10-01 Java虚拟机编译原理二:Java的类加载过程
- 2024-10-01 这次我们来学习深入解析java虚拟机:C2编译器,编译流程吧
- 2024-10-01 Java带包结构命令行编译(java 包结构)
- 2024-10-01 Jenkins+Gitlab+Nginx+SonarQube+Maven编译Java项目自发布与回退
- 2024-10-01 JIT即时编译(基础概念)(即时编译器英文)
你 发表评论:
欢迎- 最近发表
-
- Java常量定义防暴指南:从"杀马特"到"高富帅"的华丽转身
- Java接口设计原则与实践:优雅编程的艺术
- java 包管理、访问修饰符、static/final关键字
- Java工程师的代码规范与最佳实践:优雅代码的艺术
- 编写一个java程序(编写一个Java程序计算并输出1到n的阶乘)
- Mycat的搭建以及配置与启动(mycat部署)
- Weblogic 安装 -“不是有效的 JDK Java 主目录”解决办法
- SpringBoot打包部署解析:jar包的生成和结构
- 《Servlet》第05节:创建第一个Servlet程序(HelloSevlet)
- 你认为最简单的单例模式,东西还挺多
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)