A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© zzmxhm 中级黑马   /  2014-4-3 12:16  /  1179 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 zzmxhm 于 2014-4-3 12:17 编辑

Java 允许将一个类定义在另一个类之中,这样的类称为嵌套类(nested class),包含嵌套类的类称为外部类(outer class)。其中,嵌套类又可进一步分为静态内部类(static nested class)、内部类(inner class)和局部类(local class)。嵌套类同泛型一样,只是一种编译期的机制,JVM 并不区分嵌套类和普通的类,因此为了进一步了解嵌套类,需要知道编译器都做了哪些事。

内部类被编译为常规类文件
  1. class OuterClass {
  2.     class InnerClass {}
  3. }
复制代码
这个程序编译后会产生两个类文件,分别是 OuterClass.class 和 OuterClass$InnerClass.class,对虚拟机而言,这两个类均为普通类文件。但和普通类不同的是,编译器会自动在内部类中添加一个外部类对象的引用,可以通过 `javap -p ClassName` 查看编译后的类文件,这时会看到下面的结果:

  1. class OuterClass$InnerClass {
  2.     final OuterClass this$0;
  3.     OuterClass$InnerClass(OuterClass);
  4. }
复制代码
为了能在内部类中访问外部类,编译器添加了一个实例域 this$0 以及包含一个参数的构造器。在内部类中,可以通过  OuterClass.this  获得外部类对象的引用。

内部类访问外部类的实例域

我们已经知道内部类包含了外部类对象的引用,因此可以访问外部类的公有成员,但内部类是如何访问外部类的私有成员的呢?先看一下如下代码及其反编译的结果:
  1. class OuterClass {
  2.     private int foo;
  3.    
  4.     class InnerClass {
  5.         public void fun() {
  6.             System.out.println(foo);
  7.         }
  8.     }
  9. }
复制代码
  1. // javap -p OuterClass
  2. class OuterClass {
  3.   private int foo;
  4.   OuterClass();
  5.   static int access$000(OuterClass);
  6. }
复制代码
可以发现编译器为外部类添加了一个静态方法 access$000,通过该方法和外部类对象的引用便可以在内部类中访问外部类的私有成员。

局部类访问局部变量

局部类中可以访问声明为 final 的局部变量,这看起来似乎是很自然的,但看看下面的代码就会发现问题没有那么简单:
  1. class OuterClass {
  2.         public void fun(final int foo) {
  3.                 class LocalClass implements Runnable {
  4.                         public void run() {
  5.                                 while (true) {
  6.                                         try {
  7.                                                 Thread.sleep(1000);        
  8.                                         } catch (InterruptedException e) {
  9.                                                 e.printStackTrace();
  10.                                         }
  11.                                        
  12.                                         System.out.println(foo);
  13.                                 }
  14.                         }
  15.                 }

  16.                 new Thread(new LocalClass()).start();
  17.         }

  18.         public static void main(String[] args) {
  19.                 new OuterClass().fun(42);
  20.         }
  21. }
复制代码
查看一下这个程序的执行过程可以发现,当调用 fun 方法时,创建了一个新的线程,该线程中的 LocalClass 不停地打印出局部变量 foo,但是开启了新线程之后,fun 方法结束,局部变量 foo 也就不存在了,那 LocalClass 是如何打印出该局部变量的呢?看一下反编译的结果:
  1. // javap -p OuterClass$1LocalClass
  2. class OuterClass$1LocalClass implements java.lang.Runnable {
  3.   final int val$foo;
  4.   final OuterClass this$0;
  5.   OuterClass$1LocalClass();
  6.   public void run();
  7. }
复制代码
可以发现局部类中保存了局部变量的一个副本,通过泛型查看局部类的构造器可以得到如下结果:
  1. import java.lang.reflect.*;

  2. public class ClassChecker {
  3.     public static void main(String[] args) throws Exception {
  4.         String className = "OuterClass$1LocalClass";
  5.         Class<?> cl = Class.forName(className);
  6.         for (Constructor cons : cl.getDeclaredConstructors()) {
  7.             System.out.println(cons);
  8.         }
  9.     }
  10. }
复制代码
  1. // Output from above program
  2. OuterClass$1LocalClass(OuterClass,int)
复制代码
可以发现编译器为局部类生成的构造器中包含了一个 int 类型的参数,通过这个参数可以在局部类中保存一个局部变量的副本。




0 个回复

您需要登录后才可以回帖 登录 | 加入黑马