`

为什么匿名内部类参数必须为final类型

    博客分类:
  • java
 
阅读更多

1)  从程序设计语言的理论上:局部内部类(即:定义在方法中的内部类),由于本身就是在方法内部(可出现在形式参数定义处或者方法体处),因而访问方法中的局部变量(形式参数或局部变量)是天经地义的.是很自然的


2)  为什么JAVA中要加上一条限制:只能访问final型的局部变量?


3)  JAVA语言的编译程序的设计者当然全实现:局部内部类能访问方法中的所有的局部变量(因为:从理论上这是很自然的要求),但是:编译技术是无法实现的或代价极高.


4)  困难在何处?到底难在哪儿?
     局部变量的生命周期与局部内部类的对象的生命周期的不一致性!


5)  设方法f被调用,从而在它的调用栈中生成了变量i,此时产生了一个局部内部类对象inner_object,它访问了该局部变量i .当方法f()运行结束后,局部变量i就已死亡了,不存在了.但:局部内部类对象inner_object还可能   一直存在(只能没有人再引用该对象时,它才会死亡),它不会随着方法f()运行结束死亡.这时:出现了一个"荒唐"结果:局部内部类对象 inner_object要访问一个已不存在的局部变量i!


6)  如何才能实现?当变量是final时,通过将final局部变量"复制"一份,复制品直接作为局部内部中的数据成员.这样:当局部内部类访问局部变量 时,其实真正访问的是这个局部变量的"复制品"(即:这个复制品就代表了那个局部变量).因此:当运行栈中的真正的局部变量死亡时,局部内部类对象仍可以 访问局部变量(其实访问的是"复制品"),给人的感觉:好像是局部变量的"生命期"延长了.


那么:核心的问题是:怎么才能使得:访问"复制品"与访问真正的原始的局部变量,其语义效果是一样的呢?
当变量是final时,若是基本数据类型,由于其值不变,因而:其复制品与原始的量是一样.语义效果相同.(若:不是final,就无法保证:复制品与原始变量保持一致了,因为:在方法中改的是原始变量,而局部内部类中改的是复制品)

当 变量是final时,若是引用类型,由于其引用值不变(即:永远指向同一个对象),因而:其复制品与原始的引用变量一样,永远指向同一个对象(由于是 final,从而保证:只能指向这个对象,再不能指向其它对象),达到:局部内部类中访问的复制品与方法代码中访问的原始对象,永远都是同一个即:语义效 果是一样的.否则:当方法中改原始变量,而局部内部类中改复制品时,就无法保证:复制品与原始变量保持一致了(因此:它们原本就应该是同一个变量.)

一句话:这个规定是一种无可奈何.也说明:程序设计语言的设计是受到实现技术的限制的.这就是一例. 因为:我就看到不少人都持这种观点:设计与想法是最重要的,实现的技术是无关紧要的,只要你作出设计与规定,都能实现.



 

现在我们来看,如果我要实现一个在一个方法中匿名调用ABSClass的例子:
 public static void test(final String s){
     //或final String s = "axman";
  ABSClass c = new ABSClass(){
   public void m(){
      int x = s.hashCode();

 

      System.out.println(x);

 

   }
  };
  //其它代码.
 }

 

 从代码上看,在一个方法内部定义的内部类的方 法访问外部方法内局部变量或方法参数,是非常自然的事,但内部类编译的时候如何获取这个变量,因为内部类除了它的生命周期是在方法内部,其它的方面它就是 一个普通类。那么它外面的那个局部变量或方法参数怎么被内部类访问?编译器在实现时实际上是这样的:


  public static void test(final String s){
     //或final String s = "axman";


  class OuterClass$1 extends ABSClass{

 

   private final String s;
   public OuterClass$1(String s){
      this.s = s;   
   }
   public void m(){
      int x = s.hashCode();

      System.out.println(x);

   }
  };

  ABSClass c = new OuterClass$1(s);
  //其它代码.
 }


即外部类的变量被作为构造方法的参数传给了内部类的私有成员.
假如没有final,那么:
 public static void test(String s){
     //或String s = "axman";
  ABSClass c = new ABSClass(){
   public void m(){
     s = "other";
   }
  };
  System.out.println(s);
 }
 就会编译成:
  public static void test(String s){
     //或String s = "axman";

  class OuterClass$1 extends ABSClass{

 

   private String s;
   public OuterClass$1(String s){
      this.s = s;   
   }
   public void m(){
     s = "other";

 

   }
  };

   ABSClass c = new OuterClass$1 (s);

  }

 

 

 

 内部类的s重新指向"other"并不影响test的参数或外部定义的那个s.同理如果外部的s重新赋值内部类的s也不会跟着改变。
 而你看到的
  public static void test(String s){
     //或String s = "axman";
  ABSClass c = new ABSClass(){
   public void m(){
     s = "other";
   }
  };
  System.out.println(s);
 }

 

 在语法上是一个s,在内部类中被改变了,但结果打印的出来的你认为是同一的s却还是原来的"axman",
 你能接收这样的结果吗?
 所以final从语法上约束了实际上两个不同变量的一致性(表现为同一变量).

分享到:
评论
3 楼 Master-Gao 2017-05-26  
稍微明白了点,,有点萌萌哒
2 楼 waw0931 2016-10-26  

终于明白了,谢谢!
1 楼 阿郎丶 2016-08-10  

相关推荐

    Java基础【final、权限、内部类、引用类型】

    day11【final、权限、内部类、引用类型】第一章 final关键字1.1 概述1.2 使用方式修饰类修饰方法修饰变量第二章 权限修饰符2.1 概述2.2 不同权限的访问能力第三章 内部类3.1 概述什么是内部类成员内部类访问特点3.2 ...

    【05-面向对象(下)】

    •匿名内部类适合创建那种只需要一次使用的类,定义匿名内部类的语法格式如下: •new 父类构造器(实例列表) |实现接口) •{ • //匿名内部类的 类体部分 •} •匿名内部类不能是抽象类,匿名内部类不能定义...

    Java基础知识点.html

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

    day022-jdk8新特性和lambda表达式 代码和笔记.rar

    概念:就是对函数式接口匿名内部类的简写 2. 作用:简化代码 3. 使用: 语法: 函数式接口 变量名 = (参数1,参数2...) -> { //方法体 } (参数1,参数2…)表示参数列表; ->表示连接符;...

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

    为什么要重载? 168 7.5.3 给汽车加个重载的方法 169 7.5.4 测试一下 169 7.5.5 重载容易引发误解的两个地方——返回类型和形参名 170 7.5.6 重载中的最难点——参数匹配原则 171 7.6 使用类的实例作为方法参数...

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

    为什么要重载? 168 7.5.3 给汽车加个重载的方法 169 7.5.4 测试一下 169 7.5.5 重载容易引发误解的两个地方——返回类型和形参名 170 7.5.6 重载中的最难点——参数匹配原则 171 7.6 使用类的实例作为方法参数...

    corejava培训文档

    匿名内部类 7.12. 集合 7.12.1. 集合接口类层次 7.12.2. 集合类层次 7.12.3. 五个最常用的集合类之间的区别和联系 7.12.4. 比较 7.13. 反射 8. 七 异常 8.1. 异常的基本概念 8.2. 捕获异常 8.3. ...

    Java学习题答案

    Anonymous Inner Class (匿名内部类) 可以extends(继承)其它类 13 final类是为防止他人从你的类上派生新类,此类是不可继承的。 14 super()和this()方法只能在构造函数里调用. 15 方法的参数变量不能是...

    21天学通Java-由浅入深

    245 12.3.3 在外部类外访问静态内部类 246 12.4 匿名内部类 247 12.4.1 创建匿名内部类 247 12.4.2 匿名内部类的初始化 249 12.5 综合练习 250 12.6 小结 250 12.7 习题 250 第13章 多线程(精彩视频:55分钟) 252 ...

    JAVA面试题最全集

    被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载 finally?再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,...

    thinkinjava源码-ThinkingInJava:用Java源代码思考

    为了避免出现问题,所以规定匿名内部类使用的外部参数一定要是final类型的。 3、文件工具类 Directory.java 该文件工具类可根据正则表达式返回文件夹或者文件树目录下的所有符合条件的文件集合 4、文件输入输出流 ...

    JAVA面向对象详细资料

    Java面向对象 1 1 学习方法与要求 1 2 面向对象语言与面向过程语言的区别 7 3 面向对象?...41.2 匿名内部类 70 41.3 成员内部类 71 41.4 静态内部类 72 41.5 局部内部类 73 42 作业 :商超案例,以OOP重构 73

    疯狂JAVA讲义

    6.7.5 匿名内部类 212 6.7.6 闭包(Closure)和回调 215 6.8 枚举类 217 6.8.1 手动实现枚举类 217 6.8.2 枚举类入门 219 6.8.3 枚举类的属性、方法和构造器 220 6.8.4 实现接口的枚举类 223 6.8.5 包含抽象...

    2013年最全的JAVA面试题集,内部资料,绝对能帮助到你,不要后悔哦。

    18.2.Anonymous Inner Class (匿名内部类)是否可以extends(继承)其它类?是否可以 implements(实现)interface(接口)?(等级1级) 19.写出长度为10的队列,使该队列可以支持多线程数据存取(先入先出,队列为空时不能出...

    java 面试题 总结

    java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类 3、int 和 Integer 有什么区别 Java 提供两种不同的类型:引用类型和原始类型(或内置...

    java基础案例与开发详解案例源码全

    8.1.4 匿名内部类211 8.2 对象包装器213 8.3 装箱和拆箱216 8.4 本章习题218 第9章 9.1 String类220 9.1.1 字符串常量221 9.1.2 字符串对象操作224 9.1.3 字符串对象修改228 9.1.4 类型转换230 9.2 StringBuffer类的...

    java学习笔记 初学者必读

    7.11.5. 匿名内部类 7-31 7.12. 集合 7-31 7.12.1. 集合接口类层次 7-32 7.12.2. 集合类层次 7-33 7.12.3. 五个最常用的集合类之间的区别和联系 7-33 7.12.4. 比较 7-35 7.13. 反射 7-37 8. 七•异常 8-37 8.1. 异常...

    千方百计笔试题大全

    44、Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类?是否可以implements(实现)interface(接口)? 12 45、内部类可以引用他包含类的成员吗?有没有什么限制? 12 46、java 中实现多态的机制是什么...

Global site tag (gtag.js) - Google Analytics