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

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 修饰的局部变量的合理解释 在 Java 中,局部内部类可以访问它所在方法中定义的 final 修饰的局部变量,这是一个非常重要的知识点。这是因为 JVM 在编译时会将 ...

    局部变量用final的讨论

    这是因为内部类可以访问外部类的final或effectively final变量,而这些变量被视为常量。 3. **代码优化**:JVM可能会对`final`局部变量进行优化,如逃逸分析,将它们存储在栈上而不是堆上,提高运行效率。 4. **...

    Java基础权限控制与final,内部类

    在Java编程中,权限控制是一个重要的话题,它涉及到变量、方法和类的访问权限,以确保代码的封装性和安全性。Java使用不同的权限修饰符来控制成员的可访问性。另一个重要的概念是final关键字,它用于声明类、方法和...

    java 匿名内部类的使用规范

    匿名内部类没有名字,它不能被其他类直接引用,但可以作为局部变量、成员变量或者方法参数。它可以直接继承一个类或实现一个接口,并且可以在声明的同时初始化。 1. **创建匿名内部类** - **作为局部变量**:在...

    JAVA内部类总结

    1. **访问权限**:局部内部类可以访问方法内的局部变量,但这些变量必须声明为final。 2. **实例化**:局部内部类只能在其定义的方法或构造函数内部实例化。 3. **示例**: ```java public void method() { final...

    java代码笔记2010-06-01:Java内部类 静态内部类 局部内部类 明明内部类;StringBuffer reverse的使用;

    内部类可以分为四种类型:静态内部类、成员内部类(非静态内部类)、局部内部类和匿名内部类。 1. **静态内部类**: 静态内部类与普通的成员内部类不同,它不持有对外部类的引用。因此,可以像其他静态成员一样,...

    java内部类详解

    由于其局部性,局部内部类可以访问方法内的所有局部变量和参数,但这些变量必须是 final 或 effectively final。局部内部类不能声明为 static,也不能被外部类访问,除非通过方法返回。 4. 匿名内部类(Anonymous ...

    java 内部类使用(内部匿名类)

    内部类分为几种类型,包括成员内部类、局部内部类、匿名内部类以及方法参数内部类。 1. **成员内部类**:成员内部类就像是外部类的一个普通成员,可以是静态或非静态的。非静态内部类拥有对外部类的引用,可以直接...

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

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

    Java内部类总结

    Java内部类主要包括以下几种类型:成员内部类(非静态内部类)、静态内部类(也称为静态嵌套类)、局部内部类和匿名内部类。 - **成员内部类**:这种类型的内部类是定义在外部类的一个成员位置上,它可以访问外部类...

    Java成员变量类变量局部变量的区别共5页.pdf.zip

    Java编程语言中有三种主要的变量类型:成员变量(也称为实例变量)、类变量(也称为静态变量)和局部变量。理解这些变量之间的区别是掌握Java基础的重要部分,这对于编写高效、可维护的代码至关重要。 成员变量是...

    Java内部类.pdf

    在Java中,内部类主要有四种类型:成员内部类、局部内部类、匿名内部类和静态内部类。 - 成员内部类:定义在外部类的成员位置,可以直接访问外部类的所有成员。 - 局部内部类:定义在方法或者作用域块中,不能有...

    java内部类的讲解

    其中`Employee`类包含了一个`Job`类型的数组`jobs`,以及用于迭代这个数组的`hasMoreJobs()`和`nextJob()`方法。这体现了成员内部类的使用场景,即当需要一个类能够直接访问另一个类的私有成员时。 具体而言: - `...

    Java 匿名内部类

    在Java编程中,匿名内部类主要用于事件处理、回调机制、简化代码以及实现特定功能的短暂对象。 1. **接口实现**: Java匿名内部类可以用来直接实现一个接口,无需为这个接口创建单独的实现类。这在处理事件监听时...

    final类,方法,变量

    本篇文章将深入探讨`final`关键字在类、方法和变量中的应用,以及它们对程序设计的影响。 首先,让我们了解`final`关键字在类中的使用。当一个类被声明为`final`时,这意味着它不能被继承。这意味着这个类是封闭的...

    Java4Android 35_内部类和匿名内部类

    在Java编程语言中,内部类和匿名内部类是两种特殊类型的类,它们为代码的组织和功能实现提供了独特的灵活性。本教程"Java4Android 35_内部类和匿名内部类"深入探讨了这两个概念,旨在帮助开发者更好地理解和运用它们...

    day11【final、权限、内部类】.pdf

    通过文件内容的描述,我们可以看出`final`关键字的使用、权限修饰符的定义和作用、内部类的概念及其优势、以及引用类型在Java编程中的重要性和应用场景。这些知识点对于理解Java编程的基本概念和提高编程技能至关...

    Java 干货之深入理解Java内部类(学习资料)

    内部类根据其定义的位置和用途,可以分为四种类型:成员内部类、局部内部类、匿名内部类和静态内部类,每种都有其特定的使用场景和规则。 1. 成员内部类 成员内部类就像是类的成员变量一样,可以被权限修饰符修饰,...

Global site tag (gtag.js) - Google Analytics