字节码分析-可变参数
源码
package java.bytecode;
/**
* 可变参数示例 - 演示可变参数方法
*/
public class VarargsExample {
// 可变参数示例
public void varargsExample(String... args) {
for (String arg : args) {
System.out.println(arg);
}
}
// 可变参数与其他参数组合
public void varargsWithOtherParams(String prefix, int... numbers) {
System.out.println("Prefix: " + prefix);
for (int num : numbers) {
System.out.println(num);
}
}
// 可变参数 - 数组形式
public void varargsArrayExample(int[] numbers) {
for (int num : numbers) {
System.out.println(num);
}
}
}
结构化反解析
Classfile /D:/zcp/github/yggdrasil/java/bytecode/VarargsExample.class
...
{
public void varargsExample(java.lang.String...);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_VARARGS
Code:
stack=2, locals=6, args_size=2
0: aload_1
1: astore_2
2: aload_2
3: arraylength
4: istore_3
5: iconst_0
6: istore 4
8: iload 4
10: iload_3
11: if_icmpge 34
14: aload_2
15: iload 4
17: aaload
18: astore 5
20: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
23: aload 5
25: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
28: iinc 4, 1
31: goto 8
34: return
public void varargsWithOtherParams(java.lang.String, int...);
descriptor: (Ljava/lang/String;[I)V
flags: ACC_PUBLIC, ACC_VARARGS
Code:
...
}
详细说明
ACC_VARARGS标志
可变参数方法使用ACC_VARARGS标志:
public void varargsExample(String...);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_VARARGS
数组参数
可变参数在字节码中被转换为数组参数:
String... args→[Ljava/lang/String;int... numbers→[I
方法描述符
可变参数方法的描述符使用数组类型:
varargsExample(String...)
descriptor: ([Ljava/lang/String;)V
varargsWithOtherParams(String, int...)
descriptor: (Ljava/lang/String;[I)V
调用方式
可变参数方法可以:
1. 传入多个参数:varargsExample("a", "b", "c")
2. 传入数组:varargsExample(new String[]{"a", "b"})
3. 不传参数:varargsExample()(传入空数组)
增强for循环
可变参数在方法内部被当作数组处理,增强for循环转换为传统索引循环:
for (String arg : args) {
System.out.println(arg);
}
字节码实现:
0: aload_1 // 加载数组
1: astore_2 // 存储到局部变量2
2: aload_2 // 加载数组
3: arraylength // 获取长度
4: istore_3 // 存储长度
5: iconst_0 // 初始化索引
6: istore 4 // 存储索引
8: iload 4 // 加载索引
10: iload_3 // 加载长度
11: if_icmpge 34 // 如果索引>=长度,跳转
14: aload_2 // 加载数组
15: iload 4 // 加载索引
17: aaload // 获取array[index]
18: astore 5 // 存储到arg
20: ... // 循环体
28: iinc 4, 1 // 索引++
31: goto 8 // 跳转到条件判断
与其他参数组合
可变参数必须放在参数列表的最后:
public void varargsWithOtherParams(String prefix, int... numbers)
descriptor: (Ljava/lang/String;[I)V
性能考虑
- 可变参数会创建数组,可能产生额外的内存分配
- 对于频繁调用的方法,考虑使用重载方法避免数组创建
注意事项
- 一个方法只能有一个可变参数
- 可变参数必须是最后一个参数
- 可变参数在方法内部被当作数组处理