在C/C++中,每一个函数中自定义的变量,其生命周期长度 == 函数执行的长度,即“}”时销毁变量!
比如 void funtion(){int a;},在调用function()后,执行到"}"时销毁a!我在看java的内部类,却发现了一个我实在是想不通的地方,望高手们指点 !
这是我在一个CSDN博客上看到的代码:public interface Contents {  
    int value();  
}  
public interface Destination {  
    String readLabel();  
}
public class Goods1 {
public Destination dest(String s) {
class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
return new GDestination(s);
}
public static void main(String[] args) {
Goods1 g = new Goods1();
Destination d = g.dest("Beijing");
                System.out.println(d);
}
}在以上的代码中,Goods1类里面有一个dest(String s)方法,在dest方法里面,定义了一个内部类GDestination来实现接口Destination ,之后返回一个new GDestination(s);问题:
   我的理解:
    dest方法中,GDestination是一个内部类,依照我的理解,这个new GDestination(s)的生命期长度只是等同于dest{}执行期长度,dangdest执行完毕后,new GDestination(s)被销毁!
    那么,main函数中Destination d = g.dest("Beijing");  中,先执行右边g.dest("Beijing"); ,执行时return 返回一个new GDestination(s)的地址,执行遇到"}"后new GDestination(s)被销毁,此时原本的new GDestination(s)变成了 null ,接着才执行 " = ",把null赋值给d,则d为null !
   
   但实际上,d不是null,用Eclipse运行发现,存在结果,结果为:Goods1$1GDestination@1fc4bec ,这显然是一个存在的空间!我的价值观瞬间被颠覆了,我很是想不通,求解析 ... 

解决方案 »

  1.   

    跟类定义在什么地方无关,关键在于return,它将Destination对象的引用返回给调用者了,因此不会为null的。
    可以试试这样的调用:    class GDestination//此类在Goods1内部
            implements Destination
        {        private String label;        public GDestination(String whereTo)
            {
                label = whereTo;
            }        public String readLabel()
            {
                return label;
            }
        }    public void dest2(Destination s)
        {
            Goods1 g = new Goods1();
            //为null,则初始化
            if (s == null)
            {
                s = g.new GDestination("aaaa");
            }
        }    public static void main(String[] args)
        {
            /*Goods1 g = new Goods1();
            Destination d = g.dest("Beijing");
            System.out.println(d);*/        Goods1 g = new Goods1();
            Destination d = null;
            g.dest2(d);
            System.out.println(d);//为null,作用域体现出来了
        }
      

  2.   


    C++程序员转java的通病!
    多了解下java的垃圾收集器的相关知识!
      

  3.   

    函数是在方法区内,Jvm会为每一个函数分配一片内存空间。包含,函数的参数,本地变量。
    当一个函数执行完后,起相应的内存空间的值就会被销毁(回收),只是函数的变量(包含参数)如果是一个对象,那么在函数对应的内存空间内,只是存放的一个对象的引用(不是对象本身全部,java对象是放在堆栈区的)。jvm虚拟机为一个函数的内部存放的一个变量值最多分配的空间为64位。当函数执行完成过后,如果内部是一个对象,只是将那个对象的引用值得空间回收,而不会回收对象。对象由JVM进行回收。好像一个函数的返回值的空间是分配在调用函数对应得空内,不是在他本身的函数的空间中。这儿记得不太清楚了。楼主可以看下JVM方面的书籍。就会明白了
      

  4.   

    另外,Goods1$1GDestination也不是无意义的,你可以去工程classes文件夹下找对应的编译后的类,这个方法内部的类还是被编译了的。
      

  5.   


    毛哥,在你这里,让我想起了一个“拷贝”的问题,也就是所谓传值还是传引用的问题;在我看的书里面,是这样说的:一般,基本类型或者String等数据 作为参数时,就是“值传递”,
    而一般对象来作为参数,则是作为“引用传递”。
    你的那条代码里,传入的是一个对象Destination  d! 
    此时,把 g.new GDestination("aaaa");的地址传给d ,之后函数销毁,g.new GDestination("aaaa")在函数dest2(Destination d)中的引用也被销毁,但是真实开辟的g.new GDestination("aaaa")并没有被销毁,那么按理说 d 依然能指向 g.new GDestination("aaaa") 的真实地址啊 !!!为什么确实 d == null ?我猜测是无论值传递还是引用传递都是在传递参数时生成一个副本给函数,比如d的一个副本temp_d,但是这样的话,为什么一些new出来的对象作为参数,却可以改变对象内部的值 ?我承认我有点混乱了 ... 
      

  6.   


    楼主,我觉得你讲的有道理:但是,你看看2楼给我写的代码。在2楼的代码里面,有一个g,是一个真实开辟好的地址的引用,
     public void dest2(Destination s)
        {
            Goods1 g = new Goods1();
            //为null,则初始化
            if (s == null)
            {
                s = g.new GDestination("aaaa");
            }
        }
    此时,就说函数结束了,参入的参数dest2(Destination d)也应该获取了g.new GDestination("aaaa");的真实地址,也就是说,应该已经OK了,获取相当于new了,但是为什么还是 d == null 呢 ?感觉和你之前说的有一些悖论!望指教!
      

  7.   


    毛哥,在你这里,让我想起了一个“拷贝”的问题,也就是所谓传值还是传引用的问题;在我看的书里面,是这样说的:一般,基本类型或者String等数据 作为参数时,就是“值传递”,
    而一般对象来作为参数,则是作为“引用传递”。
    你的那条代码里,传入的是一个对象Destination  d! 
    此时,把 g.new GDestination("aaaa");的地址传给d ,之后函数销毁,g.new GDestination("aaaa")在函数dest2(Destination d)中的引用也被销毁,但是真实开辟的g.new GDestination("aaaa")并没有被销毁,那么按理说 d 依然能指向 g.new GDestination("aaaa") 的真实地址啊 !!!为什么确实 d == null ?我猜测是无论值传递还是引用传递都是在传递参数时生成一个副本给函数,比如d的一个副本temp_d,但是这样的话,为什么一些new出来的对象作为参数,却可以改变对象内部的值 ?我承认我有点混乱了 ... 
    毛哥,在你这里,让我想起了一个“拷贝”的问题,也就是所谓传值还是传引用的问题;在我看的书里面,是这样说的:一般,基本类型或者String等数据 作为参数时,就是“值传递”,
    而一般对象来作为参数,则是作为“引用传递”。
    你的那条代码里,传入的是一个对象Destination  d! 
    此时,把 g.new GDestination("aaaa");的地址传给d ,之后函数销毁,g.new GDestination("aaaa")在函数dest2(Destination d)中的引用也被销毁,但是真实开辟的g.new GDestination("aaaa")并没有被销毁,那么按理说 d 依然能指向 g.new GDestination("aaaa") 的真实地址啊 !!!为什么确实 d == null ?我猜测是无论值传递还是引用传递都是在传递参数时生成一个副本给函数,比如d的一个副本temp_d,但是这样的话,为什么一些new出来的对象作为参数,却可以改变对象内部的值 ?我承认我有点混乱了 ... 
    因为d传的时候就是null呀,根本就没有给它分配空间的,所以在方法内部分配的对象,也是无效的。
    同样的例子,你可以试试d=new ...分配个对象,然后再传到方法里,对d这个对象里的属性进行修改,是可以改掉的。
      

  8.   

    根本就没有问题,返回的是用new创建出来的实实在在的内存区块...GDestination作用范围限于dest函数内部,也就说离开了作用域,不能使用GDestination不能用来定义对象等
      

  9.   

    垃圾收集器干吗用的?你能对他进行操作吗?
    点这里什么情况下用finalize( )方法?
      

  10.   

    垃圾收集器干吗用的?你能对他进行操作吗?
    点这里什么情况下用finalize( )方法?
    不太明白C++的机制,在Java中,类的信息,方法字节码等是存储在方法区的,对象是存储在堆内存的,而方法的执行是在栈内存进行的。
    上面的问题应该是这样的:
     Destination d = g.dest("Beijing");调用dest将分配一块栈内存,即栈巾帧,存放参数,方法字节码等信息,class GDestination作为一个内部类,其在new之前是加载到内存的永代区或方法区的,而new 关键字在堆内存中开辟了一个对象区域,并将此对象区域的引用(首地址)返回,方法执行完成后,此栈帧将弹出,即销毁。且JVM会将返回的引用交给变量d持有(发生在main方法的栈帧中)。
    此方法中创建的对象由于还存在引用(作为方法的返回,也是一个引用),是不会被销毁的--JavaGC机制。
      

  11.   

    垃圾收集器干吗用的?你能对他进行操作吗?
    点这里什么情况下用finalize( )方法?
    不太明白C++的机制,在Java中,类的信息,方法字节码等是存储在方法区的,对象是存储在堆内存的,而方法的执行是在栈内存进行的。
    上面的问题应该是这样的:
     Destination d = g.dest("Beijing");调用dest将分配一块栈内存,即栈巾帧,存放参数,方法字节码等信息,class GDestination作为一个内部类,其在new之前是加载到内存的永代区或方法区的,而new 关键字在堆内存中开辟了一个对象区域,并将此对象区域的引用(首地址)返回,方法执行完成后,此栈帧将弹出,即销毁。且JVM会将返回的引用交给变量d持有(发生在main方法的栈帧中)。
    此方法中创建的对象由于还存在引用(作为方法的返回,也是一个引用),是不会被销毁的--JavaGC机制。

    如果将Destination d = g.dest("Beijing");直接写作g.dest("Beijing")即不将对象的引用返回,那么在方法执行完毕后会不会马上销毁这个对象呢?
      

  12.   

    垃圾收集器干吗用的?你能对他进行操作吗?
    点这里
    什么情况下用finalize( )方法?
    忘了它吧
      

  13.   

    我的理解是new GDestination(s);是在heap上创建了一个对象,但是这个对象的引用,也就是指向这个对象的指针是Destination类型的
    你所说的更类似与把class GDestination的定义问题变成了变量访问范围问题,这个角度的话,也不是不正确,比如你在dest方法外事绝对不能直接访问到GDestination类的,所以你不能在dest方法外创建或者操作GDestination,但是已存在的GDestination的实例却可以由其他向上兼容的指针来引用并存在
    综上,也就是说java中内部类的访问范围是与变量访问范围一致的,但是其已存在的实例的存在范围却是不限制的
    至于为何在访问范围外却可以打印出Goods1$1GDestination@1fc4bec,包含了GDestination的类名,我想是因为jvm中凡是定义的类都会存储在一个类域中,而每一个实例都会指向类域中的某一个地址,我们在代码级别是不能访问这个类域的,但是jvm是可以找到并访问的,这也是为什么你能够调用这个实例所实现的方法的原因个人见解,求大神指正!
      

  14.   

    垃圾收集器干吗用的?你能对他进行操作吗?
    点这里什么情况下用finalize( )方法?
    不太明白C++的机制,在Java中,类的信息,方法字节码等是存储在方法区的,对象是存储在堆内存的,而方法的执行是在栈内存进行的。
    上面的问题应该是这样的:
     Destination d = g.dest("Beijing");调用dest将分配一块栈内存,即栈巾帧,存放参数,方法字节码等信息,class GDestination作为一个内部类,其在new之前是加载到内存的永代区或方法区的,而new 关键字在堆内存中开辟了一个对象区域,并将此对象区域的引用(首地址)返回,方法执行完成后,此栈帧将弹出,即销毁。且JVM会将返回的引用交给变量d持有(发生在main方法的栈帧中)。
    此方法中创建的对象由于还存在引用(作为方法的返回,也是一个引用),是不会被销毁的--JavaGC机制。

    如果将Destination d = g.dest("Beijing");直接写作g.dest("Beijing")即不将对象的引用返回,那么在方法执行完毕后会不会马上销毁这个对象呢?
    此对象如果不存在引用,并不一定会马上销毁,这取决于JVM的GC策略,是否被触发GC操作,和这段代码就没有关系了。更详细的情况可以查阅JVM的GC策略,JVM的HEAP区分为new,tenured,perm,new区又分为伊甸区,幸存0,和幸存1区,对象在伊甸区创建,当伊甸区满后,即会触发GC,没有被GC掉的对象,会被移动到幸存0区,依此类推。根据GC基本条件,此对象在下一次伊甸区满后会被GC掉。
      

  15.   


    楼主,我觉得你讲的有道理:但是,你看看2楼给我写的代码。在2楼的代码里面,有一个g,是一个真实开辟好的地址的引用,
     public void dest2(Destination s)
        {
            Goods1 g = new Goods1();
            //为null,则初始化
            if (s == null)
            {
                s = g.new GDestination("aaaa");
            }
        }
    此时,就说函数结束了,参入的参数dest2(Destination d)也应该获取了g.new GDestination("aaaa");的真实地址,也就是说,应该已经OK了,获取相当于new了,但是为什么还是 d == null 呢 ?感觉和你之前说的有一些悖论!望指教!//你说的是这段代码吧        Goods1 g = new Goods1();
            Destination d = null;
            g.dest2(d);
            System.out.println(d);//为null,作用域体现出来了
    这儿是因为 :Destination d = null;// d的作用域是在main  函数中。
                 g.dest2(d); // 这儿是的d是作为一个参数传入到dest2()中的,那么他会在dest2()中存在d的            一个副本 ,也就是说我们在dest2()修改d的值,实际是修改是的d副本的值,而不是他本身。 楼主可以思考一下 这样设计的好处。还有如果想要修改d的值,只能通过返回值,因为返回值的作用域与调用的函数是同一个作用域。  
      

  16.   

    毛哥,求指教:
     http://bbs.csdn.net/topics/390540677 
    麻烦你下了,
    我在 安卓 的 服务端没有用 Hibernate ,直接用的是JDBC连接DB,在操作DB时碰到了问题,项目后天实训部就要评审了,比较急,求指教
      

  17.   


    楼主,我觉得你讲的有道理:但是,你看看2楼给我写的代码。在2楼的代码里面,有一个g,是一个真实开辟好的地址的引用,
     public void dest2(Destination s)
        {
            Goods1 g = new Goods1();
            //为null,则初始化
            if (s == null)
            {
                s = g.new GDestination("aaaa");
            }
        }
    此时,就说函数结束了,参入的参数dest2(Destination d)也应该获取了g.new GDestination("aaaa");的真实地址,也就是说,应该已经OK了,获取相当于new了,但是为什么还是 d == null 呢 ?感觉和你之前说的有一些悖论!望指教!//你说的是这段代码吧        Goods1 g = new Goods1();
            Destination d = null;
            g.dest2(d);
            System.out.println(d);//为null,作用域体现出来了
    这儿是因为 :Destination d = null;// d的作用域是在main  函数中。
                 g.dest2(d); // 这儿是的d是作为一个参数传入到dest2()中的,那么他会在dest2()中存在d的            一个副本 ,也就是说我们在dest2()修改d的值,实际是修改是的d副本的值,而不是他本身。 楼主可以思考一下 这样设计的好处。还有如果想要修改d的值,只能通过返回值,因为返回值的作用域与调用的函数是同一个作用域。  
    就是这个意思,其实每个方法都会分配一个独立的栈帧,保存其参数,局部变量,出口,字节码等信息,这些参数变量是独的,如: g.dest2(d);中,引用d在main的栈帧中为null,在dest的帧中,保存的参数引用d是一个副本,执行完一引用d的值为新对象堆地址。
    Java里面的引用有一个特点,当改变的是引用本身时,在不同作用域中是不相影响的,因为不同的作用域代表着不同的内存区域。
    可以参考一下我的另一个问题解答:http://bbs.csdn.net/topics/390535783
      

  18.   

    你可以参考下javascript的“闭包”,语言不一样,道理是一样的
    java里面除非你主动销毁或者被GC回收,否则的话是没有离开作用域就销毁的概念的。
    之所以会有貌似“离开作用域就销毁”的假象,那是因为它的引用计数清零了,被GC回收了
    =================
    比如,你进入某函数fun,执行A a=new A(),这时候因为等于号赋值,引用计数变成了1
    当你执行A b=a时候,引用计数变成了2
    当你离开函数时候a和b均变为不可达,于是引用计数减2,变成0,被GC回收。然而如果你函数返回时写了return a,而调用处写了A c=fun(),则
    因为a和b变为不可达,引用计数减2,但是你赋值给了c,引用计数加1,故引用计数=1,不被GC回收但是如果你写的return a,而调用处写的fun(),那么引用计数还是0,被GC回收
    =================
    即使在C语言中,也不会“自动销毁”啊,如果你在函数里面new了一个对象,没delete就退出函数了,会发生的是内存泄露,而不是销毁或者回收
    =================
    至于你说的作用域问题,你好好想想,在这个作用域里Destination接口不是可见的么?你在返回的时候,此变量由此类的实例已经隐含的转型成了接口的实例,所以,如果你在GDestination里面定义了新方法c(),出来后确实是无法调用的了。如果你想问“既然转型成了接口,怎么还能调用类的方法”,那么,如果转型成接口就不能调用了还要接口干嘛?
    =================
    你给出的代码,在工厂模式下面十分常见,即函数里面new一个对象,然后return出来给你用