黑马程序员技术交流社区

标题: 程序里的i为什么要设成final类 [打印本页]

作者: 何艳梅    时间: 2014-8-7 23:18
标题: 程序里的i为什么要设成final类
public class Interf {
        static void function() {
                        int i =0;
                        class A {
                                void fun() {
                                        i ++;
                                }
                }
                       
                }
       
        public static void main() {
                function();
        }
}
作者: 何艳梅    时间: 2014-8-7 23:21
int 定义的时候。我这个是报错的代码。
作者: ┣┫流枫    时间: 2014-8-7 23:24
没看见final
作者: fantacyleo    时间: 2014-8-7 23:30
何艳梅 发表于 2014-8-7 23:21
int 定义的时候。我这个是报错的代码。

内部类只能访问外部类中的final局部变量
作者: 何艳梅    时间: 2014-8-7 23:35
fantacyleo 发表于 2014-8-7 23:30
内部类只能访问外部类中的final局部变量

我想知道为什么?
作者: fantacyleo    时间: 2014-8-7 23:43
何艳梅 发表于 2014-8-7 23:35
我想知道为什么?

因为你function函数执行完毕弹出栈时,局部变量被销毁了,内部类对象可能依然在堆中存活,但显然不可能去修改局部变量了,之前的修改也一并失效,所以干脆限定不能修改
作者: conglin1991    时间: 2014-8-7 23:45
来学习下
作者: 李林_    时间: 2014-8-7 23:51
局部内部类(类里定义了一个方法 方法里定义了一个类)只能访问方法里的用final修饰的变量  
原理比较复杂毕老师视频里面有提到   
但是这是个语法规定  个人觉得记住规定就行
作者: ysdolls    时间: 2014-8-7 23:51
这个记住就好了,如果想知道具体点,这里有篇
为什么匿名内部类和局部内部类只能访问final变量
转:http://feiyeguohai.iteye.com/blog/1500108

为什么匿名内部类参数必须为final类型
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从语法上约束了实际上两个不同变量的一致性(表现为同一变量).


转:http://blog.csdn.net/onisland/article/details/5807637

为什么匿名内部类和局部内部类只能访问final变量
是变量的作用域的问题,因为匿名内部类是出现在一个方法的内部的,如果它要访问这个方法的参数或者方法中定义的变量,则这些参数和变量必须被修饰为final。因为虽然匿名内部类在方法的内部,但实际编译的时候,内部类编译成Outer.Inner,这说明内部类所处的位置和外部类中的方法处在同一个等级上,外部类中的方法中的变量或参数只是方法的局部变量,这些变量或参数的作用域只在这个方法内部有效。因为编译的时候内部类和方法在同一级别上,所以方法中的变量或参数只有为final,内部类才可以引用。
二、为什么局部内部类只能访问final变量
简单的来说是作用域的问题。就好像方法外面做的事情并不能改变方法内才定义的变量,因为你并不知道方法里面这个时候已经存在了这个局部变量了没有。在这个内部类中方法里面的本地变量是失效的,也就是不在作用域内,所以是不能够访问的
但是为什么这里用final却又可以访问呢? 
因为Java采用了一种copy   local   variable的方式来实现,也就是说把定义为final的局部变量拷贝过来用,而引用的也可以拿过来用,只是不能重新赋值。从而造成了可以access   local   variable的假象,而这个时候由于不能重新赋值,所以一般不会造成不可预料的事情发生
三、如果定义一个局部内部类,并且局部内部类使用了一个在其外部定义的对象,为什么编译器会要求其参数引用是final呢?
注意:局部内部类,包括匿名内部类。
原因如下:
abstract class ABSClass{
public abstract void print();
}
public class Test2{
public static void test(final String s){//一旦参数在匿名类内部使用,则必须是final
ABSClass c=new ABSClass(){
public void print(){
System.out.println(s);
}
};
c.print();
}
public static void main(String[] args){
test("Hello World!");
}
}
JVM中每个进程都会有多个根,每个static变量,方法参数,局部变量,当然这都是指引用类型.基础类型是不能作为根的,根其实就是一个存储地址.垃圾回收器在工作时先从根开始遍历它引用的对象并标记它们,如此递归到最末梢,所有根都遍历后,没有被标记到的对象说明没有被引用,那么就是可以被回收的对象(有些对象有finalized方法,虽然没有引用,但JVM中有一个专门的队列引用它们直到finalized方法被执行后才从该队列中移除成为真正没有引用的对象,可以回收,这个与本主题讨论的无关,包括代的划分等以后再说明).这看起来很好.
但是在内部类的回调方法中,s既不可能是静态变量,也不是方法中的临时变量,也不是方法参数,它不可能作为根,在内部类中也没有变量引用它,它的根在内部类外部的那个方法中,如果这时外面变量s重指向其它对象,则回调方法中的这个对象s就失去了引用,可能被回收,而由于内部类回调方法大多数在其它线程中执行,可能还要在回收后还会继续访问它.这将是什么结果?
而使用final修饰符不仅会保持对象的引用不会改变,而且编译器还会持续维护这个对象在回调方法中的生命周期.所以这才是final变量和final参数的根本意义.
作者: 王健辉    时间: 2014-8-8 13:41
你这个程序是不是还没写全
我给你这个材料你看下,比较类似
package com.itheima;  
/**
* 方法中的内部类能不能访问方法中的局部变量,为什么?
*/  
  
  
/**
*说明下此程序在内存中的执行过程,就可以了解 为什么方法中的内部类不能访问方法中的局部变量。
*首先JVM找到主函数的入口, 在栈中就开辟了一个main方法的空间 同时 创建了一个 变量o,同时在堆内存中
*new Outer()分配了一块内存空间,然后将变量O的引用就指向了该内存空间。Outer o=new Outer();
*这就话就执行完毕,在调用function的方法时,就会将方法区中的funtion()方法进入栈中,同时将x=4
*加载进栈内存中。如果不将x设置为final ,执行完, o.function();就会将function方法弹栈,此时局部x也就不存在,
*内部了也是一个类,在创建一个改局部类的对象之后,只有没有其他变量引用它,它才会变成垃圾,并且在不定时消失
*。所以可能发生的情况是 :在局部变量消失之后,内部类的对象还存活。也就是说在执行 内部类add()方法的时候x已经
*不存在了。所以,方法中的内部类不能访问方法中的局部变量。
*
*解决办法 就是将x 前加final 变成该内部类inner对象中的一个数据成员。这样,即使栈中局部变量x已消失,
*但由于它是final,其值永不变,因而局部内部类对象在变量x死亡后,照样可以访问final型局部变量。

*/

class Outer{
       
        void function()
        {
                final int x=4;//方法的局部变量       
                //内部类
            class Inner{
                    //内部类的方法
                    void add()
                    {
                            System.out.print(x+4); //调用外部类方法中的局部变量
                    }
                   
            }
            new Inner().add();//创建个内部类的对象
           
        }
       
}
public class Test3{

        public static void main(String[] args) {
       
                Outer o=new Outer();//创建外部类对象并将 o的引用指向该变量的地址
                  o.function();//
        }
}





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