最近回顾了下内部类的用法,有两个问题备忘下。
1 内部类标准用法
java中内部类用处很广,但是感觉标准用法如下:
内部类child使用private修饰符,实现一个公开的接口interface,然后通过父类parent的public方法初始化newInstance,在外部通过接口interface访问,而内部类child又可以访问父类parent的内部资源
其中最经典的例子当属于util中的Iterator方式了。父类AbstractList中有private内部类Itr,实现了java.util.Iterator接口,然后内部类中可以访问AbstractList的各种变量和方法
private class Itr implements Iterator<E>{
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
在外部通过接口访问
List list=new ArrayList();
Iterator it=list.iterator();//通过接口访问
it.next();
这样做能够实现外部接口,访问内部变量,且不暴露访问细节
因为内部类是private的,只能在父类内部访问,在外部不能访问,因此就和根本不存在一样
List list=new ArrayList();
List.Itr it=list.new Itr();//不能通过这种标准方式访问
2 匿名内部类和final
另外关于匿名内部类只能访问final类型的局部变量的原因,确定的是匿名内部类一般可能是线程之类的,生命周期远比一般的方法调用要长。
有一种说法是内部类里面将该变量copy一份,如果该变量还可以随意赋值的话,可能导致内部类中的那份copy和外部的相比不一样了,因此需要final修饰符,但是感觉这样不对,写了个例子测试了下,如下:
private static void test() throws Exception{
final List<String> list = new ArrayList<String>();
list.add("aa");
list.add("bb");
new Thread() {
public void run() {
int i = 0;
while (true) {
i++;
if (i == 10)
break;
System.out.println(list);
try { Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
list.add("cc");
Thread.sleep(2000);
list.add("dd");
}
代码的目的是匿名类里面不停访问list,匿名类外部修改list,结果如下:
[aa, bb, cc]
[aa, bb, cc]
[aa, bb, cc, dd]
[aa, bb, cc, dd]
[aa, bb, cc, dd]
[aa, bb, cc, dd]
[aa, bb, cc, dd]
[aa, bb, cc, dd]
[aa, bb, cc, dd]
从结果可以看到匿名类内部和外部访问的是同一个对象,而不是通过copy的方式。
因此怀疑final类型的局部变量的生命周期比一般的局部变量周期要长,在方法调用完毕之后还存在。