黑马程序员技术交流社区

标题: 讨论一个关于--定义在局部的内部类--的问题 [打印本页]

作者: Neverbelazy    时间: 2013-4-19 20:56
标题: 讨论一个关于--定义在局部的内部类--的问题
本帖最后由 Neverbelazy 于 2013-4-19 20:57 编辑

毕老师Java 基础视频Day10 中提到过 定义在局部的内部类如果调用局部变量,此局部变量需要被标记为final。
比如:
  1. class Outer{
  2. void method{
  3. final int x=0;
  4. class Inner{
  5. void function(){
  6. System.out.print(x);
  7. }
  8. }
  9. }
  10. }
复制代码
毕老师给出了结论,没有给出具体的说明,我自己思考了一些理由,写在这里,欢迎大家指正,谢谢!

我认为,原因由如下3点组成:
1. 局部变量在栈中,是在代码方法执行的时候被调用的。内部类的对象在堆中,是随着对象的生产而产生的;
2. 堆中的对象是可以根据程序运行时被对象的引用(栈中数据)调用,甚至改变的;但是栈中的数据一般不会也不应该在堆中被类的内部运算所调用甚至改变 (个人对这点有点不确定,希望给予指正)
3. 当内部类被定义在局部时,却造成了,局部的变量(栈中数据)可以被堆中数据使用的情况,为了避免这种堆中使用数据时改变了栈中数据的情况,所以强制的让这种调用,只能实用final修饰的局部变量

如果不用final会有什么情况? 如果没有final,那么,栈中的数据就会被内部类所改变,而这种状况是不可知的(因为类对代码的封装封装),所以会导致很多意外的情况发生。
作者: ~(@^_^@)~    时间: 2013-4-19 21:24

内部类,定义在了外部类的局部位置上,如果访问局部变量,这个变量必须加final
------------原因是生命周期
加final的原因:
    加上final的变量,变成了常量,常量存储在方法区的常量池
        这里边的内容生命最长,跟随类的加载而加载,跟随类的消失,而消失

        不加final,变量就是局部变量,跟随方法的存在而存在,跟随方法的消失而消失,
        生命周期相对很短,内部类需要对象调用,对象时存在于堆内存,随着虚拟机收取垃圾时才消失

        对象的生命周期,大于了局部变量的生命周期,所以内部类定义在局部位置上,访问局部的变量,
        这个局部变量的生命必须要大于对象的生命,因此加final,将这个变量变成常量
作者: 杜鹏飞    时间: 2013-4-19 22:34
  1.         A new frame is created each time a method is invoked. A frame is destroyed when its method invocation completes...

  2.         A frame is used to store data and partial results, as well as to perform dynamic linking, return values for methods, and dispatch exceptions.

  3.         Frames are allocated from the Java Virtual Machine stack of the thread creating the frame. Each frame has its own array of local variables , its own operand stack , and a reference to the run-time constant pool of the class of the current method.

  4.         In some of Oracle's implementations of the Java Virtual Machine, a reference to a class instance is a pointer to a handle that is itself a pair of pointers: one to a table containing the methods of the object and a pointer to the Class object that represents the type of the object, and the other to the memory allocated from the heap for the object data.
  5. ~The JVM doc
复制代码
你自己猜测的其实是不正确的.
局部变量是必须加final的,而数据成员则不必要.
至于为何必须加final,这与java语言自身有关系,java本身就是有某些缺陷的,它也在不断改进自己半吊子的窘境.

不过,您是有富有想象力的人!
作者: Neverbelazy    时间: 2013-4-19 23:30
~(@^_^@)~ 发表于 2013-4-19 21:24
内部类,定义在了外部类的局部位置上,如果访问局部变量,这个变量必须加final
------------原因是生命周 ...

谢谢你的意见,不过有一点我还是不太理解:

被final修饰的类如果是常量的话,生命周期最长的话,它是怎么被调用的呢? 比如说怎么实现下面代码?
  1. class Test{
  2. final int x=99;
  3. }

  4. class Get{
  5. public static void main{String[] args}{
  6. 怎样得到 Test类中x的值?
  7. }
复制代码

作者: Neverbelazy    时间: 2013-4-19 23:34
杜鹏飞 发表于 2013-4-19 22:34
你自己猜测的其实是不正确的.
局部变量是必须加final的,而数据成员则不必要.
至于为何必须加final,这与java ...

谢过回复。。。不过上面的引用 JVM doc好像也没解释我的问题啊???
作者: 杜鹏飞    时间: 2013-4-20 11:08
Neverbelazy 发表于 2013-4-19 23:30
谢谢你的意见,不过有一点我还是不太理解:

被final修饰的类如果是常量的话,生命周期最长的话,它是怎 ...
  1. class Test{
  2.         final int x = 1;
  3. }
  4. public class Get {
  5.         public static void main(String[] args)
  6.                 throws Exception{
  7.                 System.out.println(Test.class.getDeclaredField("x").getInt(new Test()));
  8.         }
  9. }
复制代码
被final修饰的量并不能称之为生命周期最长的,最长的应是被static修饰的量.
作者: 杜鹏飞    时间: 2013-4-20 11:09
Neverbelazy 发表于 2013-4-19 23:34
谢过回复。。。不过上面的引用 JVM doc好像也没解释我的问题啊???

试试过一两个月再看我摘录的这段话.
作者: Neverbelazy    时间: 2013-4-20 13:35
本帖最后由 Neverbelazy 于 2013-4-20 13:47 编辑
杜鹏飞 发表于 2013-4-20 11:08
被final修饰的量并不能称之为生命周期最长的,最长的应是被static修饰的量.

ok 这种情况确实是,虽然我没见过这段代码,不过可以看出在这段代码还是 new 了一个 Test()类才实现的调用x. (而且,这里面相当于是调用了 Test.class中的代码 得到的x) ,那么不就是说,这段代码的实现是在有了一个 Test()类对象的情况下才实现的吗?那不就是说其实final int x 的生命周期是小于Test()类对象的,也就是说上面的final修饰的代码没有延长x的生命周期。
那我们试想这样一个情况,也是我们讨论的情况,如果final被定义在局部的话,我们怎么得到这个x (这个x的生命周期是不是就是在所包含的{}局部内呢)?

请指正,谢谢:
  1. class Test{
  2. private void testForFun(){
  3. {
  4. final int x=99;
  5. }
  6. }

  7. private void testForQue(final int y){
  8. System.out.println(y);
  9. }
  10. }
  11. public class Get{
  12. public static void main(String[] args){
  13. 如何实用一段代码得到 x?
  14. 上面代码中的y是何时被生成的?
  15. }
  16. }
复制代码

作者: Neverbelazy    时间: 2013-4-20 13:38
杜鹏飞 发表于 2013-4-20 11:09
试试过一两个月再看我摘录的这段话.

ok 我先看完java基础课程,再有时间研习下 JVM的机制。
作者: 杜鹏飞    时间: 2013-4-20 17:59
本帖最后由 杜鹏飞 于 2013-4-20 18:21 编辑
Neverbelazy 发表于 2013-4-20 13:35
ok 这种情况确实是,虽然我没见过这段代码,不过可以看出在这段代码还是 new 了一个 Test()类才实现的调用 ...

如果变量被定义在局部,且被final修饰,那么我们只能获取这个变量的值.
至于y,只是一个引用,编译器不会为其创建实例.

比如下面这段代码
  1. void f(const int& i){
  2. }

  3. int main(){
  4.         int i = 1;
  5.         f(i);
  6. }
复制代码
  1. 0x00401503 <+14>:        movl   $0x1,0x1c(%esp)
  2. 0x0040150b <+22>:        lea    0x1c(%esp),%eax
  3. 0x0040150f <+26>:        mov    %eax,(%esp)
  4. 0x00401512 <+29>:        call   0x4014f0 <f(int const&)>
复制代码

作者: 杜鹏飞    时间: 2013-4-20 18:02
Neverbelazy 发表于 2013-4-20 13:38
ok 我先看完java基础课程,再有时间研习下 JVM的机制。

jvm的机制我也不太了解,我都是需要甚么再去查看文档.




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2