当前位置:   article > 正文

Java 内部类的实现_类的实现 java

类的实现 java

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

内部类的种类

非静态

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

静态

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

局部

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

总结下来其实就是作用域与是否为匿名的组合。下面会以局部匿名类为例,其余的情况根据自己写的 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);
            }
        };
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

将 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);
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

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

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

实现很简单一目了然。

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

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

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/43761
推荐阅读
相关标签
  

闽ICP备14008679号