Java通过反射来揭开外部类与内部类之间的关系(让你更加了解其中的原理)

在这之前先看看这个下面这个传统形式的奇怪语法!

先创建外部类A与内部类B

下面创建外部类A 与内部类B 内联代码片

package test;

public class A {    //外部类A
    public class B{ //内部类B
    	 
    }
}

创建内部类对象一般都这么写 内联代码片

  A aa=new A();   
  A.B bb=aa.new B();   //或者写成A.B bb=new A().new B();

上面第二行语句创建内部对象时候, 就感觉这种语法很奇怪… 虽然说这是规定 规定这样写就能与外部对象建立关联,但这种关联又是通过什么方式来建立的呢? 而且凭什么内部对象就能够随意访问外部对象的一切方法与属性呢?

废话不多说,下面我就通过反射带大家解开这个疑问 看清楚内部对象到底是怎么与外部对象建立关联的


通过反射揭晓答案

老规矩A是外部类 B内部类 内联代码片

package test;

public class A {    //外部类A
    public class B{ //内部类B
    	 
    }
}

下面先获取内部类的构造器看看 内联代码片

package test;
import java.lang.reflect.*;
public class Test {//测试类
     public static void main(String[] args) {
           Class<?> cs=A.class;//得到外部类A的class
           
           Class<?> inCs=cs.getClasses()[0];//得到内部类B的class
        
           Constructor[] cts=inCs.getConstructors();//获取内部类B的所有public构造器
           
           for(Constructor ct:cts) {//通过循环打印出内部类B构造器
        	   System.out.println(ct);
           }
    	 
     }
}

运行上面代码 输出结果如下:

public test.A$B(test.A)

可以看到明明再内部类B当中没有写任何构造器,但输出却发现有一个构造器存在 并且当中还有一个参数 test.A
出乎意料啊!
但你仔细观察会发现这个 test.A 不就是外部类 A 吗!?
到这里这个疑惑就解开了,原来在编译的时候编译器会在你的内部类当中添加一个构造器 这个构造器带了一个形参就是你的外部类,当通过外部类创建内部类对象时 也就是通过这个构造器将外部对象传入的。 就这样内部对象就拥有了外部对象的引用



既然搞清楚了这点那看看内部对象是怎么访问外部对象的属性和方法的:

在之前的基础上加了属性i和x方法f() 权限都是private内联代码片

package test;

public class A {    //外部类A

private int i;
private double x;
private void f(){}

    public class B{ //内部类B
    	 B (){
    	     x=3.5;//外部对象的x赋值
    	     i=20;//外部对象的i赋值
    	     f();//调用外部对象的f方法
         }
    }
}

可以看到内部类直接去访问了外部类的私有属性、方法!!
也许有些人会说 内部类不是定义在外部类里面啊!也属于外部类成员,既然是成员那就能够访问到啊! 可以这样理解,但今天要从更实际的一方面来探讨这个东西! 毕竟外部类A与内部类B经过编译后也会变成两个独立的类     A.class 与 A$B.class

好了,既然内部类通过构造器获得外部类的引用那我们就看看它拿这个引用做了些什么? 下面通过获取外部类A的class 来分析 外部类为内部类提供了什么东西

package test;
import java.lang.reflect.*;
public class Test {//测试类
     public static void main(String[] args) {
           Class<?> cs=A.class;//获取外部类A的class
           
           Method[] mds=cs.getDeclaredMethods();//获取外部A的所有方法
           
           for(Method md:mds) {//打印外部类A的所有方法
        	   System.out.println(md);
           }
     
     }
}

这是打印后的结果:

static void test.A.access$1(test.A,double)
static void test.A.access$2(test.A)
static void test.A.access$0(test.A,int)
private void test.A.f()

你会发现多出了三个方法(简化一下)
static void access$1(A,double)
static void access$2(A)
static void access$0(A,int)
这三个方法都是静态的 并且是默认权限(也就是说同一包下面的类可以访问到这三个方法)
注:你不能在写代码的时候去调用这些方法,因为这三个方法是由编译器在编译时期添加上去的

到这里疑惑就解开了
第一个方法access$1(A,double)是为外部类属性x赋值的
第二个方法access$2(A)是调用外部类的f()方法所用
第三个方法access$0(A,int)是为外部类的 i 赋值所用的
从这三个方法的形参就能看出。

所以内部类原本为这样的形式:

      {
    	   x=3.5;//外部对象的x赋值
    	   i=20;//外部对象的i赋值
    	   f();//调用外部对象的f方法
      }

经过编译后会被编译器替换为:

    {
        A.access$1(A.this, 3.5D);//外部对象的x赋值
        A.access$2(A.this, 20);//外部对象的i赋值
        A.access$0(A.this);//调用外部对象的f方法
    }

现在也就说明了这上面的一切都是编译器在后面悄悄的进行改变。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值