解释为何内部类访问外部对象时要设参数为final

final变量的定义:变量一经初始化就不能指向别的对象了。

final用于定义基本类型时,数值将保持不变;final用于定义对象引用时,final使引用保持不变,一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象了,然而,对象其自身却是可以被修改的,例如:

final StringBuffer sb= new StringBuffer("abc"),则:

sb = new StringBuffer("def"); //错误操作,因为不能再将sb指向另一个对象

                sb.append("def"); //正确操作,可以修改对象本身

当final类型作为参数传递时,其原理与平常定义基本无异,即参数为基本类型时,数据只可读不可改;为对象引用时,可改对象本身,但不能指向其他对象,这一特性主要用来向匿名内部类传递数据,其目的是为了保证调用的一致性,即保证变量在内部类和其外部的同步改变。下面对此作出解释:

假设方法test匿名调用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);

   }
  };
  //其它代码.
}

从代码上看,内部类ABSClass访问了外部参数s,它是怎么获得s的呢,编译器是这样实现的:

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呢,此时s就可以指向其他对象了,即:

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";  //s指向另一个对象new String("other")

   }
  };
   ABSClass c = new OuterClass$1 (s);

  }

 内部类的s重新指向"other",但并不影响test的参数或外部定义的那个s.同理如果外部的s重新赋值内部类的s也不会跟着改变。

在语法上是一个s,在内部类中被改变了,但结果打印的出来的你认为是同一的s却还是原来的"axman",

总而言之,实际上是用final来阻止将变量指向另一个对象的操作,从而阻止变量在内外部不一致变化的现象。

上述内容参考:书籍《Thinking in Java》

       博文 http://blog.csdn.net/axman/article/details/1460544#comments

为了进一步理解上面例子,我做了一个测试例子:

public class MyTest {
private final StringBuffer sb ;
//private StringBuffer sb;
public MyTest(StringBuffer sb) {
this.sb = sb;
}
public void m() {
sb.append("***");
//sb = new StringBuffer("def");
}
public StringBuffer getSb() {
return this.sb;
}
public static void main(String[] args) { 
final StringBuffer sb = new StringBuffer("abc");

//StringBuffer sb = new StringBuffer("abc");
MyTest mt = new MyTest(sb);
mt.m();
System.out.println(sb);
System.out.println(mt.getSb());
}
}

若将变量sb都定义为final,输出结果都为abc****,类比前面例子,保证了前后的一致;若都去掉final修饰符,则在方法m中,sb可以指向另一个对象,使得输出结果为abc, def,即不再同步改变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值