JDK提供的三种反射,通过反射构造类对象并调用其方法的实现与剖析

上一篇博客[java中反射与代理的关系,动态代理InvocationHandler和Proxy的实现与剖析]主要讲解代理模式,对于反射的使用只是简单说一下,本文主要对反射进行补充完善。

jdk中提供了三种方式来获取类Class,我们来逐一讲解和使用下:

1,Class.forName("com.lzm.controller.MainTestClassServiceImpl"); //类的全称

2, Class MTSI=MainTestClassServiceImpl.class                                      //类名

3,MainTestClassServiceImpl mtsi = new MainTestClassServiceImpl(); 

    Class MTSI2=mtsi.getClass();                                                                   //实例化对象

目录

1,通过类的名称,全限定名获取类Class信息

1.1 实例化对象并调用方法的方式 

1.2 反射并构建实例化对象的过程分析

2,通过类名获取类Class信息

3,通过实例化类对象获取类Class信息

总结:


1,通过类的名称,全限定名获取类Class信息

简单实现如下:

//MainTestClassService.java
package com.lzm.service;

public interface MainTestClassService {
    public void MainHelloTest();
}

//MainTestClassServiceImpl.java
package com.lzm.controller;
import com.lzm.service.MainTestClassService;

public class MainTestClassServiceImpl implements MainTestClassService{

    @Override
    public void MainHelloTest() {
        System.out.println("你好,我是小明");
    }
}

//ReflectionApplication.java
package com.lzm.reflection;

import com.lzm.controller.MainTestClassServiceImpl;
import com.lzm.service.MainTestClassService;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectionApplication {

    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("com.lzm.controller.MainTestClassServiceImpl");
            System.out.println("通过反射,实例化对象,获取方法");
            Object obj = clazz.newInstance();
            MainTestClassService mtcs = (MainTestClassService)obj;
            mtcs.MainHelloTest();
            System.out.println("通过Method获取方法");
            Method method = obj.getClass().getDeclaredMethod("MainHelloTest");
            method.invoke(obj,null);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

运行结果: 

1.1 实例化对象并调用方法的方式 

首先通过包名+类名获得全限定名称,使用Class.forName("名称"),获得MainTestClassServiceImpl类的类信息clazz,有两种反射对象获取相应方法的方法

(1) newInstance()构建实例化对象,并将其转为目标服务类,调用其方法。

(2) 使用Method方法获得方法MainHellTest方法,再使用invoke方法执行。Method方法的使用和介绍可以看[java中反射与代理的关系,动态代理InvocationHandler和Proxy的实现与剖析]里面对该方法做了说明。

obj.getClass()=clazz,可以替换,获取相应的方法。

其中 getDeclaredMethods()            获取所有的方法

         getDeclaredConstructors()     获取所有的构造方法

         getDeclaredField()                 获得所有属性值

         getMethod()                           获得public属性方法

1.2 反射并构建实例化对象的过程分析

既然要了解反射,那么获取相应的class对象信息根据构造方法并将其转化为实例化对象,根据实例化后的对象调用方法,有两种实现方式。

1,无参构造方法newInstance()生成实例化对象

内容只讲述重点内容,其中关于java安全管理器的一些方法,略过,这些方法主要是进行权限审查,防止未知代码的恶意访问和修改。源码主要部分如下:

(1.1) 获得无参构造方法

 如上,进行安全检查,发现是私有构造器,设置setAccessible(true)意思是这个不用进行权限检查了,然后通过调用getConstrucetor0()方法获取到无参构造器,我们可以看一下getConstrucetor0()方法的内容:

构造器获得类对象中所有为public的构造方法,privateGetDeclaredConstructors((which == Member.PUBLIC)),为获得public构造方法。对于Class对象.getDeclaredConstructors()可以获得所有构造方法,我们看一下源码

 其实getDeclaredConstructors()方法在下面也是调用privateGetDeclaredConstructors()方法获得所有构造方法集合。

privateGetDeclaredConstructors((which == Member.PUBLIC))获得了所有的构造方法之后,通过迭代,找到对应的无参构造方法,将其复制之后返回到cachedConstructor中。

 (1.2) 根据构造方法,返回实例化对象

我们经过上述部分,获取到相应的无参构造方法, 

getModifiers()获得构造器的修饰符,简单说几个返回值(PUBLIC 1,PRIVATE 2,PROTECTED 4,STATIC 8,FINAL 16等等),Reflection.quickCheckMemberAccess()进行快速检查,检查修饰符是否是public,如果不是的话,就调用getCallerClass()获取相应的Class对象(Reflection.getCallerClass的使用,可以得到调用者的类)。这个getCallerClass的使用可以做一个小例子:

 getCallerClass根据不同的输入可以得到调用者的类信息。获得调用者的类信息。

继续解读源码,如果newInstanceCallerCache缓存中没有该caller,ensureMemberAccess()进一步检查权限,更新newInstanceCallerCache。最后运行构造器,tmpConstructor.newInstance(),实际上Class.newInstance()最后调用的就是tmpConstructor.newInstance()方法。

经过一系列的权限检查和同步之后,调用ConstructorAccessor.newInstance(),创建实例化对象。 

2,构造函数函数对象获得实例化对象getConstructors()

继续按上述的代码,对主程序中的实例化类生成修改为通过getConstructors()构建

运行结果

修改为通过getConstructors()进行实例化构建,返回的是构造器集合,因为可能包含多个构造方法,例如,无参的,有参的,有参不同的等等。返回长度为1,证明我的类中只有一个无参的构造方法(默认的),其中getConstructors()获取所有PUBLIC的构造器内容,与getDeclaredConstructors()获取所有,不论什么修饰符。在上述部分已经提到过了,大家仔细观看。然后调用newInstance()方法构建实例对象,该方法与第一种方法最后的tmpConstructor.newInstance()调用的是同一个newInstance(),可以看上面的讲述。

总结:

所以这两种方法不同的是,Class.newInstance()和Constructor.newInstance(),一个必须使用无参构造器进行实例化构造,一个可以使用多种。

2,通过类名获取类Class信息

简单实现如下,接口和实现类延用上述内容:

//ReflectionApplication.java
package com.lzm.reflection;

import com.lzm.controller.MainTestClassServiceImpl;
import com.lzm.service.MainTestClassService;
import sun.misc.Unsafe;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;

import java.lang.reflect.*;

public class ReflectionApplication {

    public static void main(String[] args) {

        try {
            Class<?> clazz = MainTestClassServiceImpl.class;
            Object obj = clazz.newInstance();
            Method method = clazz.getDeclaredMethod("MainHelloTest");
//          Method method = obj.getClass().getDeclaredMethod("MainHelloTest");
            method.invoke(obj,null);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

运行结果: 

具体的反射构造对象并调用方法的内容,与第一部分相同。 

3,通过实例化类对象获取类Class信息

简单实现如下,接口和实现类延用上述内容:

//ReflectionApplication.java
package com.lzm.reflection;

import com.lzm.controller.MainTestClassServiceImpl;
import com.lzm.service.MainTestClassService;
import sun.misc.Unsafe;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;

import java.lang.reflect.*;

public class ReflectionApplication {

    public static void main(String[] args) {

        try {
            MainTestClassService mtcs = new MainTestClassServiceImpl();
            Class<?> clazz = mtcs.getClass();
            Method method = clazz.getDeclaredMethod("MainHelloTest");
            Object obj = clazz.newInstance();
            method.invoke(obj , null);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

运行结果:

具体的反射构造对象并调用方法的内容,与第一部分相同。

总结:

本文从JDK提供的三种反射方式,对通过反射实例化对象并调用其方法进行了实现和剖析.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值