关于static的用法
public class Test {
public static int i=100;
public static void main(String[] args) {
Test t=new Test();
t=null;//已经将t指向空
System.out.println(t.i);//为什么这里i还可以打印100?
}
}
对于上面的问题,思考后还是不明白为什么,请高手指点?静态变量、方法在内存中到底是怎么理解?机制是怎么样的?谢谢。
[解决办法]
...还是讲清楚点吧
对你的程序稍微加了点东西.
public class Test {
public static int i=100;
public int j = 90;
public static void main(String[] args) {
Test t=new Test();
t=null;//已经将t指向空
System.out.println(Test.i);
System.out.println(t.i);//为什么这里i还可以打印100?
System.out.println(t.j);
}
}
以下是执行javap -verbose Test后的结果.
Compiled from "Test.java "
public class Test extends java.lang.Object
SourceFile: "Test.java "
minor version: 0
major version: 50
Constant pool:
const #1 = Method #8.#21; // java/lang/Object. " <init> ":()V
const #2 = Field #3.#22; // Test.j:I
const #3 = class #23; // Test
const #4 = Method #3.#21; // Test. " <init> ":()V
const #5 = Field #24.#25; // java/lang/System.out:Ljava/io/PrintS
tream;
const #6 = Field #3.#26; // Test.i:I
const #7 = Method #27.#28; // java/io/PrintStream.println:(I)V
const #8 = class #29; // java/lang/Object
const #9 = Asciz i;
const #10 = Asciz I;
const #11 = Asciz j;
const #12 = Asciz <init> ;
const #13 = Asciz ()V;
const #14 = Asciz Code;
const #15 = Asciz LineNumberTable;
const #16 = Asciz main;
const #17 = Asciz ([Ljava/lang/String;)V;
const #18 = Asciz <clinit> ;
const #19 = Asciz SourceFile;
const #20 = Asciz Test.java;
const #21 = NameAndType #12:#13;// " <init> ":()V
const #22 = NameAndType #11:#10;// j:I
const #23 = Asciz Test;
const #24 = class #30; // java/lang/System
const #25 = NameAndType #31:#32;// out:Ljava/io/PrintStream;
const #26 = NameAndType #9:#10;// i:I
const #27 = class #33; // java/io/PrintStream
const #28 = NameAndType #34:#35;// println:(I)V
const #29 = Asciz java/lang/Object;
const #30 = Asciz java/lang/System;
const #31 = Asciz out;
const #32 = Asciz Ljava/io/PrintStream;;
const #33 = Asciz java/io/PrintStream;
const #34 = Asciz println;
const #35 = Asciz (I)V;
{
public static int i;
public int j;
public Test();
Code:
Stack=2, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object. " <init> ":()V
4: aload_0
5: bipush 90
7: putfield #2; //Field j:I
10: return
LineNumberTable:
line 1: 0
line 3: 4
public static void main(java.lang.String[]);
Code:
Stack=2, Locals=2, Args_size=1
0: new #3; //class Test
3: dup
4: invokespecial #4; //Method " <init> ":()V
7: astore_1
8: aconst_null
9: astore_1
10: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
13: getstatic #6; //Field i:I
16: invokevirtual #7; //Method java/io/PrintStream.println:(I)V
19: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
22: aload_1
23: pop
24: getstatic #6; //Field i:I
27: invokevirtual #7; //Method java/io/PrintStream.println:(I)V
30: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: getfield #2; //Field j:I
37: invokevirtual #7; //Method java/io/PrintStream.println:(I)V
40: return
LineNumberTable:
line 5: 0
line 6: 8
line 7: 10
line 8: 19
line 9: 30
line 10: 40
static {};
Code:
Stack=1, Locals=0, Args_size=0
0: bipush 100
2: putstatic #6; //Field i:I
5: return
LineNumberTable:
line 2: 0
}
主要看一下下面这几行:
0: new #3; //class Test
3: dup
4: invokespecial #4; //Method " <init> ":()V
7: astore_1
以上这几行表示:创建了一个Test类的对象,调用其实例初始化方法,将该对象的引用存入局部变量区 "1 "的位置.
8: aconst_null
9: astore_1
表示用null覆盖了这个 "1 "的位置.对应的就是: t = null;这句
13: getstatic #6; //Field i:I
这行对应的是: Test.i
#6为常量池的符号引用,对应寻找上面的Constant pool,可以知道#6代表的是Test.i:I
getstatic这个操作指令表示读取静态字段(类成员变量),它不需要对象的引用.
22: aload_1 //把刚才那个局部变量区的 "1 "位置的内容压入栈,实际上就是null
23: pop //把这个null弹出栈,也就是扔掉了
24: getstatic #6; //Field i:I 这行就和上面的Test.i一样了
这几行对应的是: t.i
从这里可以看到,如果使用t.i要比Test.i多除了两个无用的操作.但结果是一样的.而且由于这两个无用的指令,对i的调用还要受到限制.比如不初始化t,是无法通过编译的.
33: aload_1
34: getfield #2; //Field j:I
这几行对应的是: t.j
从这里可以看出,对于实例成员变量和类成员变量完全对应的是不同的操作指令.
getfield有两个操作数,其中一个就是前一句通过aload_1入栈的Test对象的引用.
getfield要用过这个引用来找到这个对象,从而获取其实例成员变量j.
虚拟机是完全针对.class文件进行操作的,所以虚拟机能看到到也就是写在.class文件里面的东西,并不是写在.java里面的东西.所以虚拟机会严格按照上面的操作指令执行.
这些在编译时候就已经决定了,并不是虚拟机对t.i和Test.i做了什么不同的处理.
最后值得一提的是,前面的代码中有3个方法:
public Test(); //这个就是自动添加的默认构造方法, j在这里被初始化
public static void main(java.lang.String[]);
static {}; //这个方法叫做静态初始化方法,它是类装载,初始化时被调用的,i在这里被初始化