上一篇博客[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信息
简单实现如下:
//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提供的三种反射方式,对通过反射实例化对象并调用其方法进行了实现和剖析.