在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。
想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
获取字节码文件对象的三种方式:
// 通过Class类中的静态方法forName获取类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。
1、Class clazz1 = Class.forName("全限定类名");
// 当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己,该类处于字节码阶段。
2、Class clazz2 = Person.class;
// 通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段
Person p = new Person();
3、Class clazz3 = p.getClass();
反射API:
获取构造方法:
// 返回所有public构造方法
Constructor[] constructors = stuClass.getConstructors();
// 所有的构造方法(包括:私有、受保护、默认、公有)
Constructor[] constructors2 = stuClass.getDeclaredConstructors();
// 返回指定类型的public构造器(空参或随意参数)
Constructor constructor = stuClass.getConstructor(String.class, int.class);
// 返回指定类型的构造器(空参或随意参数)
Constructor constructor = stuClass.getDeclaredConstructor(int.class);
获取属性:
// 获取类中所有public类型的属性变量
Field[] getFields()
// 获得类中所有属性变量
Field[] getDeclaredFields()
// 根据变量名获取对应public类型的属性变量
Field getField(String name)
// 根据变量名获得对应的变量
Field getDeclaredField(String name)
获取方法:
// 获取“名称是name,参数是parameterTypes”的public的函数(包括从基类继承的、从接口实现的所有public函数)
public Method getMethod(String name, Class[] parameterTypes)
// 获取“名称是name,参数是parameterTypes”,并且是类自身声明的函数,包含public、protected和private方法。
public Method getDeclaredMethod(String name, Class[] parameterTypes)
// 获取全部的public的函数(包括从基类继承的、从接口实现的所有public函数)
public Method[] getMethods()
// 获取全部的类自身声明的函数,包含public、protected和private方法。
public Method[] getDeclaredMethods()
// 如果这个类是“其它类中某个方法的内部类”,调用getEnclosingMethod()就是这个类所在的方法;若不存在,返回null。
public Method getEnclosingMethod()
// invoke()方法eg:
List list = new ArrayList();
Method m = getMethod("add", String.calass)
m.invoke(list, "aaa");
1、Object转Map方法:
public static Map objectToMap(Object obj, boolean keepNullVal) {
if (obj == null) {
return null;
}
Map<String, Object> map = new HashMap<String, Object>();
try {
Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
if (keepNullVal == true) {
map.put(field.getName(), field.get(obj));
} else {
if (field.get(obj) != null && !"".equals(field.get(obj).toString())) {
map.put(field.getName(), field.get(obj));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
2、通过反射越过泛型检查
public class Demo {
public static void main(String[] args) throws Exception{
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// strList.add(100);
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
//获取add()方法
Method m = listClass.getMethod("add", Object.class);
//调用add()方法
m.invoke(strList, 100);
//遍历集合
for(Object obj : strList){
System.out.println(obj);
System.out.println(obj.getClass());
}
}
}
3、动态代理的实现
作用:将你要使用的类,重新生成一个子类或本类,在该类的方法前后加一些代码。解耦合,不修改源码。
具体应用的话,比如可以添加调用日志,做事务控制等。动态代理是设计模式当中代理模式的一种。
主要实现原理:
InvocationHandler接口:
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,
当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的
invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
// proxy: 指代我们所代理的那个真实对象
// method: 指代的是我们所要调用真实对象的某个方法的Method对象
// args: 指代的是调用真实对象某个方法时接受的参数
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
Proxy类:
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是
newProxyInstance 这个方法:
// loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
// interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
// h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
demo:
/**
* 真实对象的接口
*/
public interface Subject
{
public void rent();
public void hello(String str);
}
/**
* 真实对象
*/
public class RealSubject implements Subject
{
@Override
public void rent()
{
System.out.println("I want to rent my house");
}
@Override
public void hello(String str)
{
System.out.println("hello: " + str);
}
}
/**
* 动态代理类
* 实现InvocationHandler接口
*/
public class DynamicProxy implements InvocationHandler
{
// 这个就是我们要代理的真实对象
private Object subject;
// 构造方法,给我们要代理的真实对象赋初值
public DynamicProxy(Object subject)
{
this.subject = subject;
}
@Override
public Object invoke(Object object, Method method, Object[] args)
throws Throwable
{
// 在代理真实对象前我们可以添加一些自己的操作
System.out.println("before invoke");
System.out.println("Method:" + method);
// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
method.invoke(subject, args);
// 在代理真实对象后我们也可以添加一些自己的操作
System.out.println("after invoke");
return null;
}
}
/**
* 客户端类
*/
public class Client
{
public static void main(String[] args)
{
// 我们要代理的真实对象
Subject realSubject = new RealSubject();
// 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
InvocationHandler handler = new DynamicProxy(realSubject);
/*
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
.getClass().getInterfaces(), handler);
System.out.println(subject.getClass().getName());
subject.rent();
subject.hello("world");
}
}