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?
}
}对于上面的问题,思考后还是不明白为什么,请高手指点?静态变量、方法在内存中到底是怎么理解?机制是怎么样的?谢谢。

解决方案 »

  1.   

    t.i等价于 Test.i
    编译器已经帮你换了,和实例无关
      

  2.   

    同意楼上
    java对语法限制的不够严格.
    或者说javac的功能强
    跟你对static的理解没关系.不管怎么说,如果你有个不对t.i进行转换的编译器,没准就如lz所愿了~
      

  3.   

    t.i
    ---------------------------------------------
    在Eclipse中,用类的实例来引用静态变量好像要报错吧
      

  4.   

    Test.i = test.i(效果) t.i
    ---------------------------------------------
    在Eclipse中,用类的实例来引用静态变量好像要报错吧=======
    不会的了,放心好了。
      

  5.   

    楼上的意思是编译器自动将t.i转换成Test.i , 这样做是为什么啊 , t的引用都为空了,如何还能指向一个静态的内存空间? 在这里面编译器到底做了哪些事情?为什么要这么做?
      

  6.   

    编译器会把你的t.i转换成Test.i,
    也就是说存在.class文件中的是Test.i,虚拟机根本就看不到t.i,他能看到的就是Test.i因为i是static的,也就是所谓的类成员变量.类变量应该通过类来调用.当你的类被装载的时候,这个变量就已经存在了,不论你是否去创建一个这个类的实例.既然i是类成员变量,那你为什么要通过t.i来调用而不用Test.i来调用呢?
      

  7.   

    Test t=new Test();
    --------------------
    此时,class Test 已经被加载,class 初次被加载的时候会初始化static成员,此时Test.i值是100,
      

  8.   

    public static int i=100;
    在程序启动的时候已经是加载的了,而且无论Test如何引用
    int i 都指向同一个内存地址,所以
      

  9.   

    可以把静态变量看作一个全局变量,所谓Test.i就把它的名称空间看作是Test,就好象包一样,
    在Eclipse中 t.i不会报错,只会警告。
      

  10.   

    t=null;这句话没意义吧
    类Test的默认构造函数不就是Test(){};吗,只是没写出来
    这句Test t=new Test();话调用的不就是默认构造函数?小弟初学,望大虾指点
      

  11.   


    public static int i=100;
    在程序启动的时候已经是加载的了,而且无论Test如何引用
    int i 都指向同一个内存地址,所以
    --------------
    好象是这样的.
      

  12.   

    是这样的,1)static变量是类的实例变量,无须生成对象来访问它,当然你也可以用对象名来访问。一般都是用类名来访问。所以,当你Test t;这步的时候,已经可以这样t.i访问变量i了.你可以这样:
        Test t;
        t.i; //无论你把t指向谁。t是Test类的变量。
        System.out.println(t.i);
    顺便说一下,一般我用都是用类名来访问静态变量。Test.i;
    就是这样。
      

  13.   

    public static int i=100;
    这句起了一定的实例作用.
      

  14.   

    atixujie(徐杰)
    说的很详细了~~
      

  15.   

    ...还是讲清楚点吧
    对你的程序稍微加了点东西.
    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在这里被初始化
      

  16.   

    一句话,楼主没有理解类与实例(或对象)的区别,没有理解static的含义。
      

  17.   

    谢谢大家zephyr_cc() ( ) 信誉:100 ,看来你对JVM有比较深的了解。谢谢你的答案,对本质做出了很详细的解释。