字节码分析-可变参数

源码

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

数组参数

可变参数在字节码中被转换为数组参数:

方法描述符

可变参数方法的描述符使用数组类型:

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

性能考虑

注意事项