`
xm_king
  • 浏览: 392422 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
博客专栏
Group-logo
Spring技术内幕读书笔...
浏览量:15342
社区版块
存档分类
最新评论

Java的局部内部类以及final类型的参数和变量

    博客分类:
  • JAVA
阅读更多

      Thinking In Java里面的说法(唯一正确的说法): 如果定义一个匿名内部类,并且希望它使用一个在其外部定的对象,那么编译器会要求其参数引用是final 的。

 public class Tester {    
        public static void main(String[] args) {    
        A a = new A();    
        C c = new C();    
        c.shoutc(a.shout(5));    
     }    
 }    
////////////////////////////////////////////////////////    
 class A {    
     public void shouta() {    
        System.out.println("Hello A");    
     }    
    
     public A shout(final int arg) {    
         class B extends A {    
             public void shouta() {    
                System.out.println("Hello B" + arg);    
            }    
        }    
         return new B();    
     }    
 }    
 ////////////////////////////////////////////////////////    
 class C {    
     void shoutc(A a) {    
         a.shouta();    
     }    
 }   
 

      第5行c.shoutc(a.shout(5)),在a.shout(5)得到返回值后,a的shout()方法栈被清空了,即arg不存在了,而 c.shoutc()却又调用了a.shouta()去执行System.out.println("Hello B" + arg)。
       再来看Java虚拟机是怎么实现这个诡异的访问的:有人认为这种访问之所以能完成,是因为arg是final的,由于变量的生命周期,事实是这样的吗?方法栈都不存在了,变量即使存在,怎么可能还被访问到?试想下:一个方法能访问另一个方法的定义的final局部变量吗(不通过返回值)?
       研究一下这个诡异的访问执行的原理,用反射探测一下局部内部类 。编译器会探测局部内部类中是否有直接使用外部定义变量的情况,如果有访问就会定义一个同类型的变量,然后在构造方法中用外部变量给自己定义的变量赋值,而后局部内部类所使用的变量都是自己定义的变量,所以就可以访问了。见下:

 class   A$1$B  {  
     A$1$B(A,   int);  
     private   final   int   var$arg;  
     private   final   A   this$0;  
 }  

      A$1$B类型的对象会使用自定义的var$arg变量,而不是shout()方法中的final int arg变量,当然就可以访问了。
       那么为什么外部变量要是final的呢?即使外部变量不是final,编译器也可以如此处理:自己定义一个同类型的变量,然后在构造方法中赋值就行了。原因就是为了让我们能够挺合逻辑的直接使用外部变量,而且看起来是在始终使用 外部的arg变量(而不是赋值以后的自己的字段)。
       考虑出现这种情况:在局部内部类中使用外部变量arg,如果编译器允许arg不是final的,那么就可以对这个变量作变值操作(例如 arg++),根据前面的分析,变值操作改变的是var$arg,而外部的变量arg并没有变,仍然是5(var$arg才是6)。因此为了避免这样如此不合逻辑的事情发生:你用了外部变量,又改变了变量的值,但那个变量却没有变化,自然的arg就被强行规定必须是final所修饰的,以确保让两个值永远一样,或所指向的对象永远一样(后者可能更重要)。
       将函数的参数引用设为final,主要是考虑到局部变量的生命周期与局部内部类的对象的生命周期的不一致性!往深层次说,就是为了解决参数的不一致性问题。即
因为从编程人员的角度来看他们是同一个东西, 如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解 和接受,为了避免这种尴尬的问 题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。

       举个例子来说可能会更清楚一些,对于局部变量int i=3;方法中代码修改的是这个真正的变量i,而内部为对象修改的是i的复制品copy_i,这样这两个i就会发生值的不一致性,(这一点正是这个实现技术的缺陷),所以干脆就不允许这个int i=3;局部变量发生值的改变!由于不允许改int i的值,所以这两个int i的值就始终保持值的一致了,这才是final的这个规定的由来! 是一种不得不如此的无奈之举!

      简单的来说,因为生命周期的原因,内部类需要复制局部变量为内部类的一个属性变量,因为复制,所以要将修饰符设为final。

 

分享到:
评论

相关推荐

    Java中局部内部类可以访问它所在方法中定义的final修饰的局部变量的合理解释.doc

    Java中局部内部类可以访问它所在方法中定义的final修饰的局部变量的合理解释.doc

    final修饰成员变量和局部变量.md

    本文章是关于final部分知识所作的自我总结,内容为final对成员变量和局部变量修饰的简要解答,除了对自我java学习的一个小结,也希望能够帮助到在java路上对该内容疑惑的同行

    局部内部类和匿名内部类使用局部变量为什么要final1

    //从阅读角度,这里预期打印a=20,然而只会打印出a=10,会让人误解,因为在Inner的change()中修改的是Inner内部类对象的this.a,它是m

    java为什么匿名内部类的参数引用时final?final局部变量的生命周期

    在知乎上看到了一篇帖子 在这个问题下面大家已经吵得不可开交了,看了很多篇文章,被误导进了很多的坑,发现只有下面两篇文章是写的最好的,解释的很清楚,我把两篇文章简单的总结了一下: ...1.问题的引出 ...

    【Java语言基础】final关键字

    final修饰变量时,表示该变量一旦获得了初始值之后就不可被改变,final既可修饰成员变量(包括类变量和实例变量),也可以修饰局部变量,形参。 final修饰成员变量 类变量:当类初始化时,系统会为类变量分配内存,...

    Java并发--final关键字.docx

    在Java中变量可以分为成员变量和局部变量 成员变量 通常每个类中成员变量可以分为 类变量(static修饰的变量) 以及 实例变量 针对这两种类型的变量赋初始值的时机是不同的。 类变量可以再声明变量的时候直接赋...

    动力节点老杜Java基础入门视频教程——final修饰局部变量

    012-JavaSE进阶-final修饰局部变量

    计算机后端-Java-Java核心基础-第15章 面向对象07 26. final修饰局部变量.avi

    计算机后端-Java-Java核心基础-第15章 面向对象07 26. final修饰局部变量.avi

    Java基础知识点.html

    Date类 自动拆箱和自动装箱 Arrays 类和接口的关系 内部类 成员内部类 局部内部类 匿名内部类 抽象类 接口 多态 封装 类和对象 方法 StringBuilder类 String类 static for循环 final 权限修饰符 跳转控制语句 while...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    9.1.2 加餐:不可改变的final变量 211 9.1.3 多维数组的长度 212 9.1.4 一维数组的clone()方法 212 9.1.5 当数组类型不再是基本数据类型 214 9.1.6 多维数组的clone()方法 217 9.2 老朋友String类 220 9.2.1 ...

    Java岗面试核心MCA版

    一小块区域 成员变量:方法外部,类内部定义的变量 局部变量:类的方法中的变量。 成员变量和局部 变量的区别 作用域 成员变量:针对整个类有效。 局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    9.1.2 加餐:不可改变的final变量 211 9.1.3 多维数组的长度 212 9.1.4 一维数组的clone()方法 212 9.1.5 当数组类型不再是基本数据类型 214 9.1.6 多维数组的clone()方法 217 9.2 老朋友String类 220 9.2.1 ...

    JAVA 面向对象程序设计第4章 抽象类和接口.pptx

    4.2.2 final局部变量;4.2.2 final局部变量;4.2.3 final方法;4.2.4 final类;4.2.5 学生实践练习;4.2.5 学生实践练习;4.3 接口;4.3 接口;4.3.1 接口的定义;4.3.2 接口的实现;4.3.2 接口的实现;4.3.2 接口的实现;4.3.2 ...

    Java 基础核心总结 +经典算法大全.rar

    节点流和处理流 Java IO 的核心类 File Java IO 流对象 字节流对象InputStream OutputStream 字符流对象Reader Writer 字节流与字符流的转换新潮的 NIO 缓冲区(Buffer)通道(Channel) 示例:文件拷贝案例 BIO 和 NIO ...

    Java中final关键字详解

     在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。下面就从这三个方面来了解一下final关键字的基本用法。  1.修饰类  当用final修饰一个类时,表明这个类不能被继承。也就是说,...

    疯狂JAVA讲义

    6.4.1 final变量 177 6.4.2 final方法 181 6.4.3 final类 182 6.4.4 不可变类 182 6.4.5 缓存实例的不可变类 186 6.5 抽象类 188 6.5.1 抽象方法和抽象类 188 6.5.2 抽象类的作用 191 6.6 更彻底的抽象:...

    【05-面向对象(下)】

    •对一个final变量来说,不管它是类变量、实例变量,还是局部变量,只要该变量满足3个条件,这个final变量就 不再是一个变量,而是相当于一个直接量。  –使用final修饰符修饰;  –在定义该final变量时指定...

    21天学通Java-由浅入深

    240 12.2.1 创建局部内部类 240 12.2.2 在局部内部类中访问外部类成员变量 240 12.2.3 在局部内部类中访问外部类的局部变量 241 12.2.4 静态方法中的局部内部类 243 12.3 静态内部类 244 12.3.1 创建静态内部类 244 ...

    corejava培训文档

    7.6.1. final变量不能被改变; 7.6.2. final方法不能被改写; 7.6.3. final类不能被继承; 7.6.4. String 类 7.7. 抽象类 7.8. 接口 (模板方法模式) 7.9. Object 类 7.10. 封装类 7.11. 内部类 7.11.1. ...

    Java2实用教程.rar

    4 4 1实例变量和类变量的区别 4 4 2实例方法和类方法的区别 4 5this关键字 4 6包 4 6 1包语句 4 6 2import语句 4 6 3将类打包 4 7访问权限 4 7 1私有变量和私有方法 4 7 2共有变量和共有方法 4 7 3友好变量和友好...

Global site tag (gtag.js) - Google Analytics