最近回顾了下内部类的用法,有两个问题备忘下。
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类型的局部变量的生命周期比一般的局部变量周期要长,在方法调用完毕之后还存在。