zoukankan      html  css  js  c++  java
  • 从垃圾回收机制解析为什么局部内部类只能访问final修饰的局部变量以及为什么加final能解决问题

    我们先稍微看一下代码:

    从这里的提示可以看到,必须要将a的修饰符变为final才行。

    现在笔者就这一结果做出自己的分析:

           首先来说,我们知道,方法被调用时会执行,当执行的时候,方法中的局部变量会加载到栈内存中,方法执

    行完毕后局部变量会从栈中被释放(会被垃圾回收器立即回收)。其次,当一个对象被new出来后,new出来的

    对象生存再堆(堆中的对象在用完后不会马上被回收),对象的引用存在于栈中,也会立即被收回。 结合以上的

    代码我们可以发现,当test方法执行完成后,int类型的a变量立即被回收了,A对象的引用a1也被立即回收了,但

    是new出来的A对象还是生存在堆上面,这个时候问题就出来了,一个活着对象持有一个被回收的变量。java作

    为一种强类型语言,这种情况是肯定不允许的。

            接下来我们分析,为什么加final后就可以了呢?

            我们先看下面两段话:


    final修饰局部变量

    final修饰的局部变量一样需要被显式地赋初始值,因为Java本来就要求局部变量必须被显式地赋初始值。与普通变量不同的是,final修饰的局部变量被赋初始值之后,将不能再被重新赋值。

    final修饰符的第一简单的功能就是一旦被赋初始值,将不可改变。
    final的另一个简单的功能就是在定义了该final类变量时指定了初始值,且该初始值可以在编译时就被确定下来,系统将不会在静态初始化块中对该类变量赋初始值,而将是在类定义中直接使用该初始化值代替该final变量。

    对于一个使用final修饰的变量而言,如果定义该final变量时就指定初始值,而且这个初始值可以在编译时就确定下来,那么这个final变量将不再是一个变量,系统会将其变成“宏变量”处理。所有出现该变量的地方,系统将直接把它当成对应的值处理。

    执行“宏替换”的变量

    final修饰符的一个重要用途就是定义“宏变量“,当定义final变量时就为该变量指定了初始值,而且该初始值可以在编译的时候就确定下来,那么这个final变量本质上就是一个”宏变量“,编译器会把程序中所用到该变量的地方直接替换成该变量的值。如果被赋的表达式只是基本的算术运算表达式或字符串连接运算,没有访问普通变量,调用方法,Java编译器同样会将这种final变量当成”宏变量“处理。

    对于实例变量而言,可以在定义该变量时赋初始值之外,还可以在非静态初始化块、构造器中对它赋初始值,在这三个地方指定初始值的效果基本一样。但对于final实例变量而言,只有在定义该变量时指定初始值才会有”宏变量“的效果,在非静态初始化块、构造器中为final实例变量指定初始值则不会有这种效果。对于普通类变量而言,在定义时指定初始值,在静态初始化块中赋初始值的效果基本一样。但对于final类变量而言,只有在定义final类变量时指定初始值,系统才会对该final类变量执行”宏替换“

    文字总是没有代码直观,接着我把上面代码的class文件反编译后得到如下代码:

    先将未编译的java代码贴上来以做比较:

    package com.itheima;
    // 这是测试类
     public class Test {
    	
    	public void test(){
    	
    		final int a = 10;
    		
    		A a1 = new A(){
    		
    			public void show(){
    			System.out.println(a);	
    			}
    		};
    	}
    }
    
    package com.itheima;
    // 就一个show方法
    public interface A {
    	
    	public void show();
    }
    

    反编译字节码文件后得到以下代码:

    package com.itheima;
    
    import java.io.PrintStream;
    
    public class Test
    {
      public void test()
      {
        int a = 10;
        
        A a1 = new A()
        {
          public void show()
          {
            System.out.println(10);   //在编译后,匿名内部类中的a直接被替换成了10
          }
        };
      }
    }
    
    

    通过以上分析结合之前的两段问题,我认为,用final修饰后,匿名内部类中所引用的局部变量将以字面值常量的

    形式存在,方法运行结束后,虽然方法中的局部变量被回收了,但是在匿名内部类并不受影响。所以加final能解

    决之前我们提出的问题。









  • 相关阅读:
    C#中String类的几个方法(IndexOf、LastIndexOf、Substring)
    typedef void (*Fun) (void) 的理解——函数指针——typedef函数指针
    Source Insight 常用设置和快捷键大全
    关闭SourceInsight的大括号自动缩进
    MDK中One ELF Section per Function选项功能探究【转载】
    Application.DoEvents()的作用
    C#中Invoke的用法
    C# 委托的应用1:将方法作为参数传递给另一个方法[转]
    C#之委托(函数参数传递)【转】
    sk-learn 选择正确的估算器
  • 原文地址:https://www.cnblogs.com/daimzh/p/12854510.html
Copyright © 2011-2022 走看看