Java 内部类的实现

在 Java 语言中,许多语言层面的细节是无法直接看到的。比如枚举的实现、泛型的擦除、自动拆箱装箱以及本次将说明的内部类的实现。主要原因在于编译器在编译我们的源代码时做了许多的改动,这时需要借助反编译的功能查看都做了哪些改动。在线反编译网站

内部类的种类

非静态

1. 非匿名类
public class InnerClassType {
	class InnerClass {}
}
2. 匿名类
public class InnerClassType {
	Object obj = new Object() {
	    @Override
	    public String toString() {
	        return "Anonymous inner class";
	    }
	};
}

静态

1. 非匿名类
public class InnerClassType {
	static class StaticInnerClass {}
}
2. 匿名类
public static Object anonymousStaticInnerClass = new Object() {
    @Override
    public String toString() {
        return "Anonymous static inner class";
    }
};

局部

1. 非匿名类
public void local() {
    class LocalInnerClass {}
}
2. 匿名类
public void local() {
	Object anonymousLocalInnerClass = new Object() {
        @Override
        public String toString() {
            return "Anonymous local inner class";
        }
    };
}

总结下来其实就是作用域与是否为匿名的组合。下面会以局部匿名类为例,其余的情况根据自己写的 Demo 代码通过上面的反编译网站反编译后就可以看到是如何实现的了。

局部匿名类案例

源代码:

public class Printer {

    private int count;

    public void print() {
    }

    public Printer create(final String msg) {
        return new Printer() {
            @Override
            public void print() {
                System.out.println(++count + ": " + msg);
            }
        };
    }
}

将 Class 文件反编译后:

public class Printer {

   private int count;

   public void print() {
   }

   public Printer create(final String msg) {
      return new Printer$1(this, msg);
   }

   // $FF: synthetic method
   static int access$004(Printer x0) {
      return ++x0.count;
   }
}

class Printer$1 extends Printer {
   // $FF: synthetic field
   final String val$msg;
   // $FF: synthetic field
   final Printer this$0;

   Printer$1(final Printer this$0, final String val$msg) {
      this.this$0 = this$0;
      this.val$msg = val$msg;
   }

   public void print() {
      System.out.println(Printer.access$004(this.this$0) + ": " + this.val$msg);
   }
}

会发现,编译器会帮我们生成一个继承自源类的局部匿名类的实现类 Preinter$1。来看一下它的构造方法:

Printer$1(final Printer this$0, final String val$msg) {
   this.this$0 = this$0;
   this.val$msg = val$msg;
}
  1. this$0 为外部类的引用
  2. val$msg 为 create 方法的形参 msg

实现很简单一目了然。

  1. 为什么内部类中可以直接访问外部类的数据?就是因为内部类的内部维护了一个外部类的引用。
  2. 为什么内部类可以使用外部类的非成员数据?比如方法的局部变量,也是因为内部类通过构造器将局部变量传递到了内部,维护成了一个成员常量。 并且外部类方法的局部变量也必须通过 final 修饰,避免外部方法对变量的修改影响了内部类中维护的相同变量。也就是,当使用了内部类,此时外部类的环境就不允许变更了,但成员数据除外。为什么下面会说到。
访问外部类的成员、局部数据间的差异

访问外部类的成员变量与访问外部类的局部变量是不一样的。可以观察一下生成在外部类中的方法 access$004 与 该方法的调用:

static int access$004(Printer x0) {
   return ++x0.count;
}
public void print() {
   System.out.println(Printer.access$004(this.this$0) + ": " + this.val$msg);
}

最终通过外部类的引用访问外部类的成员变量。思考一下,为什么不像处理局部变量那样处理成员变量呢?不能因为内部类的应用,而强迫使可能会长期驻留主存的、线程共享可读可写的成员数据都具有常量性质

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值