访问静态字段与静态方法
访问静态字段与静态方法
与非静态方法的调用和非静态字段的访问不同,获取静态字段、修改静态字段、调用静态方法不需要一个该类型的对象引用作为隐式参数,且静态方法的局部变量表不会存储this引用。
静态字段的初始赋值由编译器编译后在类初始化方法<clinit>中生成赋值的字节码指令,而被声明为final的静态字段初始赋值则在类加载的准备阶段赋值。
读写静态字段的字节码指令是getstatic与putstatic,这两条指令都要求一个操作数,操作数的值为常量池中某个CONSTANT_Fieldref_info常量的索引。getstatic指令的操作码为0xB2,putstatic指令的操作码为0xB3。读写静态字段的例子如代码清单3-25所示。
代码清单3-25 读写静态字段
public class StaticFieldMain { static String name; public static void main(String[] args) { name = "wujiuye"; System.out.println(name); } }
使用javap命令输出这段代码的字节码如下。
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V Code: 0: ldc #2 // String wujiuye 2: putstatic #3 // Field name:Ljava/lang/String; 5: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 8: getstatic #3 // Field name:Ljava/lang/String; 11: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 14: return
偏移量为0和2的字节码指令完成为静态字段name赋值,先使用ldc字节码指令将putstatic指令所需要的参数放入操作数栈顶,putstatic指令将栈顶的元素赋值给类的静态字段。
调用静态方法的例子如代码清单3-27所示。
代码清单3-27 调用静态方法
public class StaticMethodMain { static void show(String msg){ System.out.println(msg); } public static void main(String[] args) { StaticMethodMain.show("hello word!"); } }
例子中,在main方法调用show静态方法,调用show方法需要传递一个参数,在show方法中打印main方法传递的参数。对应的字节码如下。
static void show(java.lang.String); descriptor: (Ljava/lang/String;)V Code: 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: invokevirtual #3 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 7: return public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V Code: 0: ldc #4 // String hello word! 2: invokestatic #5 // Method show:(Ljava/lang/String;)V 5: return
main方法中,偏移量为0和2的字节码指令完成调用show方法,ldc指令将调用show方法所需的参数放入操作数栈的栈顶。方法需要多少个参数就将多少个参数放入操作数栈顶,如果传null则使用aconst_null指令,aconst_null指令的操作码为0x01。调用静态方法的指令是invokestatic,指令的操作码为0xB8,该指令需要一个操作数,操作数的值必须是常量池中某个CONSTANT_Methodref_info常量的索引。在show方法中,偏移量为3的aload_0指令获取到的局部变量不再是this,而是方法的第一个参数。