反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。
反射是为了解决在运行期,对某个实例一无所知的情况下,如何调用其方法。
在 java 中,只要给定类的名字,那么就可以通过反射机制来获得类的所有属性和方法。
Java 的 Class 类是 java 反射机制的基础,通过 Class 类我们可以获得关于一个类的相关信息。
Java.lang.Class 是一个比较特殊的类,它用于封装被装入到 JVM 中的类(包括类和接口)的信息。当一个类或接口被装入的 JVM 时便会产生一个与之关联的 java.lang. Class 对象,可以通过这个Class 对象对被装入类的详细信息进行访问。虚拟机为每种类型管理一个独一无二的 Class 对象。也就是说,每个类(型)都有一个 Class 对象。运行程序时,Java 虚拟机(JVM)首先检查是否所要加载的类对应的Class 对象是否已经加载。如果没有加载,JVM 就会根据类名查找.class 文件,并将其Class 对象载入。
JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载
以 String 类为例,当JVM加载 String 类时,它首先读取String.class 文件到内存,然后,为 String 类创建一个 Class 实例并关联起来; 这个 Class 实例是JVM内部创建的, Class 类的构造方法 是 private ,只有JVM能创建 Class 实例,我们自己的Java程序是无法创建 Class 实例的;
这种通过 Class 实例获取 class 信息的方法称为反射
获取一个 class 的 Class 实例有三个方法:
Class 类提供了以下几个方法来获取字段:
Field 对象包含了一个字段的所有信息:
package com.demo;
import java.lang.reflect.Field;
public class ReflectionDemo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException {
Box box = new Box();
Class<Box> boxClass = Box.class;
Class box_class = box.getClass();
box_class = Class.forName("com.demo.Box");
Field[] fields = box_class.getDeclaredFields();
for (Field f : fields) {
System.out.println(f.getName());
System.out.println(f.getType());
System.out.println(f.getModifiers());
f.setAccessible(true); // 调用 Field.setAccessible(true) 的意思是,别管这个字段是不是 public ,一律允许访问
System.out.println(f.get(box)); //,用 Field.get(Object) 获取指定实例的指定字段的值
f.set(box,111); // 设置字段值
}
}
}
Class 类提供了以下几个方法来获取 Method :
一个 Method 对象包含一个方法的所有信息:
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Box box = new Box();
Class<Box> boxClass = Box.class;
Class box_class = box.getClass();
box_class = Class.forName("com.demo.Box");
Method[] methods = boxClass.getDeclaredMethods();
for (Method method: methods) {
System.out.println(method.getName());
System.out.println(method.getReturnType());
System.out.println(method.getParameterTypes());
}
Method m = boxClass.getMethod("getWidth",Integer.class);
m.setAccessible(true); // 调用非public方法,我们通过 Method.setAccessible(true) 允许其调用
int re = (int)m.invoke(box_class,3); //调用非静态方法;
Method m_static = boxClass.getMethod("getWidth",Integer.class,Integer.class);
// 调用静态方法时,由于无需指定实例对象,所以 invoke 方法传入的第一个参数永远为 null 。
int result = (int) m.invoke(null,1,2);
}
如果通过反射来创建新的实例,可以调用Class提供的newInstance()方法;
调用Class.newInstance()的局限是,它只能调用该类的public无参数构造方法。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstance()来调用。为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。
通过Class实例获取Constructor的方法如下:
Constructor 总是当前类定义的构造方法,和父类无关
通过 Class 对象可以获取继承关系:
JDK提供的动态创建接口对象的方式,就叫动态代理。
Java标准库提供了一种动态代理(Dynamic Proxy)的机制:可以在运行期动态创
建某个 interface 的实例。
在运行期动态创建一个 interface 实例的方法如下:
反射是动态代理的一种实现方式
Java 中,实现动态代理有两种方式:
区别:
JDK 的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用 CGLIB实现。
Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 Java 类与实现 Java
接口。它广泛的被许多 AOP 的框架使用,例如 Spring AOP 和 dynaop,为他们提供方
法的 interception(拦截)
Java 实现动态代理主要涉及哪几个类
Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理。JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK 动态代理的核心是 InvocationHandler 接口和 Proxy 类。如果目标类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。