Reflection(反射) 是Java语言被视为动态语言的一个关键,反射机制允许程序在执行期间借助于反射API 取得任何类的内部信息,并能直接操作任意的对象的内部属性及方法。
加载类之后,在堆内存的方法区中的Class区中就会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,可以通过Class对象看到类的结构。
正常的方式:引入类的全类名----通过 new实例化—通过对象调用类的成员
反射的方式:实例化对象----获取Class对象-----获取的完整的类的结构
java.lang Class Class
代表一个类
java.lang.reflect Class Constructor
代表类的构造器
java.lang.reflect Class Field
代表类的属性
java.lang.reflect Class Method
代表类的成员方法
返回值类型 | 方法 |
---|---|
Class>[] | getClasses() 返回包含一个数组 类表示所有的公共类和由此表示的类的成员接口的对象 类对象。 |
static Class> | forName(String className) 返回与给定字符串名称的类或接口相关联的 类对象。 |
@Test
public void test1() throws ClassNotFoundException {
//获取类的Class对象 通过对象来获取(不推荐)
Person p = new Person();
Class clazz = p.getClass();
System.out.println(clazz);
// 方式2 通过类的class属性类获取
Class clazz1 = Person.class;
System.out.println(clazz1);
//方式3 : 通过类的全类名
Class clazz2 = Class.forName("cn.lanqiao.demo.Person");
System.out.println(clazz2);
}
哪些类可以有Class对象:
@Test
public void test2(){
Class c1 = Object.class;//类
Class c2 = Character.class;
Class c3 = String[].class;//一维数组
Class c4 = int[][].class;//二维数组
Class c5 = ElementType.class;//注解
Class c6 = Comparable.class;//接口
Class c7 = int.class;// 基本类型
Class c8 = void.class;//空类型
Class c9 = Class.class;
}
返回值类型 | 方法 |
---|---|
T | newInstance() 创建由此 类对象表示的类的新实例。 |
@Test
public void test3() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class c = Class.forName("cn.lanqiao.demo.Person");
Person p = (Person) c.newInstance();
p.setName("张三");
System.out.println(p.getName());
}
获取类的构造方法:
返回值类型 | 方法 |
---|---|
Constructor |
getConstructor(Class >... parameterTypes) 返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。 |
@Test
public void test4() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, InvocationTargetException {
Class c = Class.forName("cn.lanqiao.demo.Person");
// 获取构造方法 获取的是无参构造
Constructor con = c.getConstructor();
Person p1 = (Person)con.newInstance();
System.out.println(p1);
//获取带参构造
Constructor con1 = c.getConstructor(String.class,int.class);
Person p2 = (Person)con1.newInstance("李四",22);
System.out.println(p2);
}
返回值类型 | 方法 |
---|---|
Field | getField(String name) 返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。 |
Field[] | getFields() 返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象。 |
Field | getDeclaredField(String name) 返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。 |
Field[] | getDeclaredFields() 返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。 |
@Test
public void fieldTest() throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c = Class.forName("cn.lanqiao.demo.Person");
// 获取类的指定的公共字段
Field field = c.getField("location");
// 创建对象
Constructor<?> con = c.getConstructor();
Person p = (Person)con.newInstance();
field.set(p,"太原");
System.out.println(p.location);
System.out.println("------------------------");
// 获取所有的公共字段
Field[] fields = c.getFields();
for(Field f : fields){
System.out.println(f +"--"+f.getName() +"---" + f.getType());
}
System.out.println("所有字段");
// 获取类中的所有的字段,包括公共的和私有的
Field[] fields1 = c.getDeclaredFields();
for(Field f : fields1){
System.out.println(f +"--"+f.getName() +"---" + f.getType());
}
// 获取特定的私有字段,想要为私有字段设置值或者修改值都需要做一个访问性的设置
Field nameField = c.getDeclaredField("name");
nameField.setAccessible(true);// 设置私有字段的可访问性为true
nameField.set(p,"张三");// 为成员变量赋值,成员变量属于某一个对象所有 所以必须指定赋值的对象是谁
System.out.println(p.getName());
}
返回值类型 | 方法 |
---|---|
Method | getDeclaredMethod(String name, 类>... parameterTypes) 返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。 |
Method[] | getDeclaredMethods() 返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。 |
Method | getMethod(String name, 类>... parameterTypes) 返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。 |
Method[] | getMethods() 返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。 |
调用方法Method:
返回值类型 | 方法 |
---|---|
Object | invoke(Object obj, Object... args) 在具有指定参数的方法对象上调用此方法对象表示的底层方法。 |
//获取成员方法 测试
@Test
public void methodTest1() throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class c = Class.forName("cn.lanqiao.demo.Person");
// 获取构造方法,获取的是无参构造
Constructor<?> con = c.getConstructor();
Person p1 = (Person)con.newInstance();
// 获取公共的成员方法
Method[] methods = c.getMethods();
for(Method m : methods){
System.out.println(m);
}
System.out.println("----------获取特定的带参方法-------------");
Method m1= c.getMethod("set",String.class,int.class);
m1.invoke(p1,"张三",22);
System.out.println("----------获取特定的无参方法-------------");
Method m = c.getMethod("show");
m.invoke(p1);
}
@Test
public void declaredMethodTest() throws Exception{
Class c = Class.forName("cn.lanqiao.demo.Person");
// 获取构造方法 获取的是无参构造
Constructor<?> con = c.getConstructor();
Person p1 = (Person)con.newInstance();
// 获取所有的方法
Method[] methods = c.getDeclaredMethods();
for(Method m : methods){
System.out.println(m);
}
//获取特定的私有方法
Method method = c.getDeclaredMethod("set",String.class,int.class);
method.setAccessible(true);
method.invoke(p1,"李四",25);
Method show = c.getDeclaredMethod("show");
show.setAccessible(true);// 设置私有方法的访问性
show.invoke(p1);//执行方法
}
静态方法的反射使用和非静态一样
@Test
public void genericTest() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<String> list = new ArrayList<>();
list.add("aaaa");
list.add("bbbb");
list.add("ccc");
System.out.println(list);
// 使用反射为list添加元素,此时虽然list有泛型,但是此时泛型已经丢失了,称为泛型擦除
//泛型只在编译器有效,运行时泛型就被丢弃了
Class<? extends ArrayList> c = list.getClass();
Method add = c.getMethod("add",Object.class);
add.invoke(list,12);
add.invoke(list,23.3);
add.invoke(list,true);
System.out.println(list);
}
@Test
public void propTest() throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 加载数据
Properties prop = new Properties();
prop.load(new FileReader("../day_20_Reflection\\dirs\\cls.properties"));
//获取配置文件中的类名及方法名
String className = prop.getProperty("classname");
String method1 = prop.getProperty("methodname1");
String method2 = prop.getProperty("methodname2");
System.out.println(className);
System.out.println(method1);
System.out.println(method2);
//通过反射来使用
Class c = Class.forName(className);
Constructor con = c.getConstructor();
Object obj = con.newInstance();
Method m1 = c.getMethod("set",String.class,int.class);
m1.invoke(obj,"王五",20);
Method m2= c.getMethod("show");
m2.invoke(obj);
}
代理模式:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用。
代理模式的核心:一个接口由两个实现类,一个实现类完成核心业务操作,另一个实现类完成与核心业务相关的辅助性操作。
代理的方式:静态、动态代理
静态代理是由程序员创建或特定工具自动生成源代码,在对其进行编译。在运行之前,代理类的.class文件就已经被创建.
动态代理:在程序运行是通过反射机制动态的创建。
最主要的原因:在不改变目标对象的方法的情况下对方法进行增强.
动态代理也是Spring框架中的 核心之一的AOP的核心
完成一个房屋买卖的场景。
//服务接口
public interface BuyHouse {
void buyHouse();
}
//实现服务接口
public class BuyHouseImpl implements BuyHouse{
@Override
public void buyHouse() {
System.out.println("买房");
}
}
//代理类
public class BuyHouseProxy implements BuyHouse{
private BuyHouse buyHouse;
public BuyHouseProxy(BuyHouse buyHouse){
this.buyHouse = buyHouse;
}
@Override
public void buyHouse() {
System.out.println("买房前的准备:了解房屋的基本信息...");
buyHouse.buyHouse();
System.out.println("买房后的工作:装修落户...");
}
}
public class BuyHouseTest {
public static void main(String[] args) {
BuyHouse proxy = new BuyHouseProxy(new BuyHouseImpl());
proxy.buyHouse();
}
}
静态代理的优缺点:
优点:可以做到在服务条件的情况下完成对功能的扩展
缺点:我们必须要为每一个服务类去单独的创建一个代理类,工作量太大,不易于管理、当接口发生改变时,代理类也需要做出相应的改变
InvocationHandler:
返回值类型 | 方法 |
---|---|
Object | invoke(Object proxy, 方法 method, Object[] args) 处理代理实例上的方法调用并返回结果。 |
Proxy:
返回值类型 | 方法 |
---|---|
static Object | newProxyInstance(ClassLoader loader, 类>[] interfaces, InvocationHandler h) 返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。 |
动态代理的实现:
//代理类,在运行期间通过反射生成
public class DynamicProxyHandier implements InvocationHandler {
//创建代理对象
private Object object;
public DynamicProxyHandier(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("买房前的准备:了解房屋的基本信息...");
//执行目标方法
Object result = method.invoke(object,args);
System.out.println("买房后的工作:装修落户...");
return result;
}
}
public class DynamicProxyTest {
public static void main(String[] args) {
//被代理对象
BuyHouse buyHouse = new BuyHouseImpl();
//代理对象 被代理类的类加载器 实现的接口 对于代理类的处理
BuyHouse proxyBuyHouse = (BuyHouse)Proxy.newProxyInstance(BuyHouse.class.getClassLoader(),new Class[]{BuyHouse.class},new DynamicProxyHandier(buyHouse));
proxyBuyHouse.buyHouse();
}
}
Jdk的动态代理本质:代理的接口,代理类和被代理类必须实现相同的接口才能实现动态代理。
Cglib是一个强大的高性能的代码生成包,它广泛被AOP框架使用。
引入cglib的依赖:
//代理类
public class CglibProxy implements MethodInterceptor {
//设置被代理对象
private Object target;
public Object getInstance(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
//设置回调对象
enhancer.setCallback(this);
//当被代理对象增强回调之后,产生一个新的对象
return enhancer.create();
}
//执行方法的拦截
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("买房前的准备...");
Object result = methodProxy.invoke(target,objects);
System.out.println("买房后的工作...");
return result;
}
}
public class CglibTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
CglibProxy cglibProxy = new CglibProxy();
BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(buyHouse);
buyHouseCglibProxy.buyHouse();
}
}
CGLib的总结:
CGLIB创建的动态代理对象比jdk创建的动态代理的对象的性能更高,但是CGLIB创建代理对象所花费的时间却比jdk多,所以对于单例的对象,因为无需频繁的创建对象,用CGLIB合适,反之用jdk方式更合适。
代理模式的区别:
静态代理:
通过在代码中显式的定义一个业务实现类的一个代理,在代理类中对同名的业务方法进行包装增强,用户通过代理类调用被包装过的业务方法。
Jdk的动态代理:
是通过接口中的方法名在动态生成的代理类中调用业务方法实现类的同名方法
CGlib动态代理:
是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。