Java基础-反射
反射
- 反射的理解
- Class类的特点
- 通过反射创建Class类的对象
- 通过反射解析对应类的结构,获取信息
- 通过反射创建对应类的对象
- 通过反射调用类的成员(属性、方法、构造)
关键字:
- 反射机制
- 动态语言
一.Java反射机制概述
1.关于类的加载
1.类的加载过程:
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化:
- 1)类的装载:将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成
- 2)类的连接:将类的二进制数据合并到JRE中
- 3)类的初始化:JVM负责对类进行初始化
2.ClassLoader 类加载器:
-
类加载的作用: 将class文件字节码内容加载到内存中, 并将这些静态数据转换成方法区的运行时数据结构, 然后在堆中生成一个代表这个类的java.lang.Class对象, 作为方法区中类数据的访问入口。
-
类缓存: 标准的JavaSE类加载器可以按要求查找类, 但一旦某个类被加载到类加载器中, 它将维持加载(缓存) 一段时间。 不过JVM垃圾回收机制可以回收这些Class对象。
-
类加载器是用来把类(class)装载进内存的。
JVM 规范定义了两种类型的类加载器:启动类加载器(bootstrap)和用户自定义加载器(user-defined class loader)。
JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:
- 引导类加载器:用C++编写的,是JVM自带的类装载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取
- 扩展类加载器:负责jre/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库
- 系统类加载器:负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作 ,是最常用的加载器
3.类的加载时期:
- 编译期加载:又称为静态加载,编译时则加载所有用到的类,如果该类不存在,则直接报编译错误,依赖性太强
- 运行期加载:又称为动态加载,运行时加载用到的类,如果该类(字节码文件)不存在,则报运行错误,降低了依赖性,提高了维护性
4.类的加载时机:
- new对象
- 调用类的静态成员
- 加载子类
- 反射
5.类加载器的使用案例:
//1.获取一个系统类加载器 ClassLoader classloader = ClassLoader.getSystemClassLoader(); System.out.println(classloader); //2.获取系统类加载器的父类加载器,即扩展类加载器 classloader = classloader.getParent(); System.out.println(classloader); //3.获取扩展类加载器的父类加载器,即引导类加载器 classloader = classloader.getParent(); System.out.println(classloader); //4.测试当前类由哪个类加载器进行加载 classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader(); System.out.println(classloader); //5.测试JDK提供的Object类由哪个类加载器加载 classloader = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classloader); //*6.关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路径下的指定文件的输入流 InputStream in = null; in = this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties"); System.out.println(in);
2.什么是:Reflection
1.什么是反射:
反射,属于java实现动态语言的关键,可以获取运行期间的类的信息
Reflection(反射)是被视为动态语言的关键。
反射机制:允许程序在运行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
2.Java反射机制提供的功能:
- 在运行时,判断任意一个对象所属的类
- 在运行时,构造任意一个类的对象
- 在运行时,判断任意一个类所具有的成员变量和方法
- 在运行时,调用任意一个对象的成员变量和方法
- 生成动态代理
3.反射相关的主要API:
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造方法
3.反射的好处
java实现动态语言的关键,可以动态获取一个类的结构信息,并调用成员
1)Java中的软编码:维护性高
//配置文件信息 <class-name>com.atguigu.reflect.Student</class-name> //拿到类名 String clazzName = xxxx; //使用反射,创建类的对象、调用方法等等 ......
2)硬编码:维护性差
Person per = new Person();
二.Class类的理解
There is a class named Class.
- 1 Class本身也是一个类
- 2 Class 对象只能由系统建立对象,但我们可以通过一些方式获取该对象的引用
- 3 一个类在 JVM 中只会有一个Class实例 ,因为一个类只可能加载一次
- 4 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 5 每个类的对象,都能找到它所属的类型,也就是可以获取对应的Class类的对象
Class clazz = 对象.getClass(); - 6 通过Class类对象的引用,可以解剖类的结构,可以获取类的所有结构信息,并且调用方法、属性、构造等
Class 也是一个类:其对象是方法区中对应.class文件的加载
所谓反射,从程序的运行结果来看也很好理解,即:可以通过对象反射出类,以及对类的各种操作。
通过反射:可以解剖使用到的这个类
因为:通过Class可以完整地得到一个类中的完整结构
问题:如何获得Class对象(对应加载到JVM中的.class文件),以及通过Class对象,解剖对应的类?
三.反射的使用
1.通过反射,获取Class类的对象:
问题:如何获得一个Class对象?
获取Class对象的四种方法:
-
通过全类名字符串,获取Class类的对象:Class.forName()
-
通过某个具体类的对象获取Class类的对象:对象名.getClass()
-
通过类名获取Class类的对象,比较适合传参:String.class
-
通过类加载器获取Class类的对象,用的较少
//方式1:通过全类名字符串获取Class类的对象 @Test public void test1() throws ClassNotFoundException { Class<?> clazz = Class.forName("java.lang.String"); System.out.println(clazz.getSimpleName()); System.out.println(clazz.getPackage().getName()); } //方式2:通过某个具体类的对象获取Class类的对象 @Test public void test2() { String s = "令狐冲"; //可以调用 对象.getClass()方法,返回对应Class对象 Class clazz = s.getClass(); method("java"); } public <T> void method(T t){ System.out.println(t.getClass().getSimpleName()); //String } //方式3:通过类名获取Class类的对象:比较适合传参 @Test public void test3() { //每个类有对应的Class类型的class成员属性 Class clazz = String.class; } //方式4:通过类加载器获取Class类的对象,用的较少 @Test public void test4() throws ClassNotFoundException { //1)首先,使用当前对象的Class对象获取,获取一个类加载器 ClassLoader loader = this.getClass().getClassLoader(); //2)使用类加载器,通过全类名,加载获取对应的Class对象 Class clazz = loader.loadClass("java.lang.String"); }
2.通过反射,获取类的结构信息
问题:通过反射获得了Class对象,Class对象又能做什么?
1)通过Class对象,获取类的成员
包括类的全部属性、方法、构造器、父类、接口、包、泛型、注解
java.lang.Class 类中的方法:
1)获取类的属性
- getFields():获取本类以及从父类继承来的所有public修饰的属性,不限于直接父类
- getDeclaredFields():获取本类中定义的所有属性,不问修饰符
- getField(String name) : 返回一个 Field 对象
2)获取类的方法们
- getMethods():获取本类以及从父类继承来的所有public修饰的方法,不限于直接父类
- getDeclaredMethods():获取本类中定义的所有方法,不问修饰符
- getMethod(String name, Class<?>… parameterTypes) :返回对应方法名,参数列表的Method
3)获取类的构造器们
- getConstructors():获取本类以及从父类继承来的所有public修饰的构造器,不限于直接父类
- getDeclaredConstructors():获取本类中定义的所有构造器,不问修饰符
- getConstructor(Class<?>… parameterTypes)
4)获取类的父类
- getSuperClass():以Class类型返回父类对象
5)获取类的接口
- getInterfaces():以Class[]类型返回接口对象
6)获取类的注解
- getAnnotations():返回此元素上存在的所有注释。返回 Annotation[]
- getDeclaredAnnotations()
- getAnnotation(Class annotationClass) :如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。
**注意:**只能获取保留策略为RetentionPolicy.RUNTIME的注解
7)获取父类或接口的泛型
- getGenericSuperClass():获取父类的泛型(父类类型+泛型)
- getGenericInterfaces():获取接口的泛型(接口类型+泛型)
- 泛型类型:ParameterizedType
ParameterizeType pt = (ParameterizeType)type;
pt.getActualTypeArguments();
2.其次:返回值是对应类的数组,根据对应类的方法,获取类的结构信息
1)java.lang.reflect.Field类:
- getModifiers() 修饰符:public static
- getType()
- getName()
2)java.lang.reflect.Method:
- getModifiers()
- getReturnType()
- getName()
- getParameterTypes()
3)java.lang.reflect.Constructor:
- getModifiers()
- getName()
- getParameterTypes()
4)java.lang.reflect.Annotation:
- annotationType()
**注意:**并不是所有的注解都有返回的资格
- 注解的 @Retention 保留策略 --> 会使通过反射获取该类的注解时,获取不到
注解的作用机制:编译器通过反射判断你的注解是否正确;
3.通过反射,访问类的成员
问题:能不能通过Class对象,访问/设置类或对象对应单个的具体成员?
1.首先,通过Class获取具体的类成员,并返回该类型的对象
Class类的API:获取对应成员
- getField(属性名):获取本类以及从父类继承来的,公共的属性
- getDeclaredField(属性名):获取本类定义的属性,不问修饰符
- getMethod(方法名,形参列表):获取本类以及从父类继承来的,公共的方法
- getDeclaredMethod(方法名,形参列表):获取本类定义的方法,不问修饰符
2.通过具体的对象,实现类成员的访问和操作
Field类的API:
- set(对象,属性值):为指定对象的属性赋值,如果该属性为静态属性,则对象可以使用null
- get(对象):获取指定对象的该属性值,如果该属性为静态属性,则对象可以使用null
- setAccessible(true):暴破
Method类的API
- invoke(对象,实参列表):调用指定对象的方法,如果该方法为静态方法,则对象可以使用null
- setAccessible(true):暴破
1)访问属性
//步骤1:获取Class类对象 Class clazz = Class.forName("全类名"); //步骤2:获取name属性对象 Field field = clazz.getDeclaredField("属性名"); //步骤3:暴破(属性为私有时,需要爆破) field.setAccessible(true); //步骤4:访问 Object object = clazz.newInstance();//确保类中有public修饰的无参构造器 //设置object对象的field字段值为“属性值” field.set(object,"属性值"); //获取该对象的filed值 field.get(object);
2)访问方法
//步骤1:获取Class类对象 Class clazz = Class.forName("全类名"); //步骤2:获取方法对象 Method method = clazz.getDeclaredMethod(方法名,参数列表); //步骤3:暴破 method.setAccessible(true); //步骤4:访问 Object object = clazz.newInstance();//确保类中有public修饰的无参构造器 //调用object对象的method方法,并传入实参 Object value = method.invoke(object,实参列表);
3)获取接口的泛型/父类的泛型
@Test public void testGetGeneric() { //获取接口的类型Type[]:Class类实现了Type接口 Type[] types = clazz.getGenericInterfaces(); //Fly,Swim<String> 实现的 接口类型+泛型 for (Type type : types) { //判断接口类型 Type 是否为参数化类型:ParameterizedType(Type接口的子类),如 Collection<String> if (!(type instanceof ParameterizedType)) { continue; } //若是参数化类型(即该类型有参数,也就是有泛型),则向下转型 ParameterizedType pt = (ParameterizedType) type; //调用ParameterizedType接口的getActualTypeArguments()方法:返回表示此类型实际类型参数的 Type 对象的数组 Type[] actualTypeArguments = pt.getActualTypeArguments(); //向下转型为Class,获取其简单类名 System.out.println(((Class)actualTypeArguments[0]).getSimpleName()); } }
4)获取注解
@Test public void testAnnotation() { //获取Class对象的注解们 Annotation[] annotations = clazz.getAnnotations(); for (Annotation annotation : annotations) { //获取该注解的Class对象 Class<? extends Annotation> annotationType = annotation.annotationType(); System.out.println(annotationType.getSimpleName()); } }
4.通过反射,创建运行时类的对象
问题:有了Class对象,对应类也就加载到了内存,如何通过Class对象创建该类的对象呢?
1)使用无参构造器
调用Class对象的newInstance()方法
Object object = clazz.newInstance();
要求:
- 1)类必须有一个无参数的构造器(或默认有)
- 2)类的构造器的访问权限需要足够。
难道没有无参的构造器就不能创建对象了吗?
2)使用有参构造器
Object object = clazz.getDeclaredCosntructor(参数列表).newInstance(实参列表);
步骤如下:
- 1)通过Class类的getDeclaredConstructor(Class … parameterTypes):取得本类的指定形参类型的构造器
- 2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
//1.根据全类名获取对应的Class对象 String name = “com.mytest.Person"; Class clazz = Class.forName(name); //2.获取指定参数结构的构造器 Constructor 的实例 Constructor con = clazz.getConstructor(String.class,Integer.class); //3.通过Constructor的实例,创建对应类的对象,并初始化类属性 Person p2 = (Person)con.newInstance("Peter",20); System.out.println(p2);
泛型擦除
泛型擦除:
- 泛型只保留在编译期间,运行期间不存在泛型
面试题1:通过反射调用list2对象的add方法
List list1 = new ArrayList(); list1.add(""); list1.add(123); //使用泛型 List<String> list2 = new ArrayList<>(); list2.add(""); list2.add(123);//错误 //泛型擦除,测试: Class clazz = list2.getClass(); //Method method =clazz.getMethod("add",String.class);//找不到该方法 Method method =clazz.getMethod("add",Object.class);//√ method.invoke(list2,"john");
四、反射的应用:动态代理
1.什么是动态代理
1)代理设计模式的原理:
- 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。
- 任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原
始对象上。
2)动态代理是指:
- 客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
3)动态代理使用场合:
- 调试
- 远程方法调用
4)静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代
理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。 最好可以通过一个代理类完成全部的代理功能。
动态代理相比于静态代理的优点:
- 抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。
2.Java动态代理相关API
**1.java.lang.reflect.Proxy **:
- 专门完成代理的操作类,是所有动态代理类的父类;通过此类为一个或多个接口动态地生成实现类。
2.提供用于创建动态代理类和动态代理对象的静态方法
//创建一个动态代理类所对应的Class对象: static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) //直接创建一个动态代理对象 static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数含义:
- ClassLoader loader:类加载器
- Class<?>[] interfaces:得到被代理类实现的全部接口
- InvocationHandler h:得到InvocationHandler接口的实现类实例
3.动态代理与AOP(Aspect Orient Programming)
前面介绍的Proxy和InvocationHandler,很难看出这种动态代理的优势。
代码段1,2,3中都存在有相同的代码段,按照一般思路可以将相同的代码段封装成公共的方法,然后进行统一的调用。
改进后的说明:代码段1、代码段2、代码段3和深色代码段分离开了,但代码段1、 2、 3又和一个特定的方法A耦合了!
最理想的效果是:代码块1、 2、 3既可以执行方法A,又无须在程序中以硬编码的方式直接调用深色代码的方法
AOP(Aspect Orient Programming)面向切面编程:
使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理,这种动态代理在AOP中被称为AOP代理——AOP代理可代替目标对象, AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:
- AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理
/** * 动态代理与AOP的举例 * @author zxy */ //1.被代理类实现的接口 interface Human{ String getBelief(); void eat(String food); } //2.被代理类 class SuperMan implements Human{ @Override public String getBelief() { return "I believe I can fly!"; } @Override public void eat(String food) { System.out.println("我喜欢吃" + food); } } //AOP代理演示 class HumanUtil{ public void method1(){ System.out.println("====================通用方法一===================="); } public void method2(){ System.out.println("====================通用方法二===================="); } } /* 要想实现动态代理,需要解决的问题? 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。 */ //3.代理类 class ProxyFactory{ //调用此方法,传入一个被代理类的实例,返回一个代理类的对象:用于解决问题一 public static Object getProxyInstance(Object obj){//obj:被代理类的对象 //指派方法调用的调用处理程序 :用于解决问题二(详见4.) MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); //调用Proxy.newProxyInstance():返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。 return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); } } //4.java.lang.reflect.InvocationHandler 是代理实例的调用处理程序 实现的接口 class MyInvocationHandler implements InvocationHandler{ private Object obj;//需要使用被代理类的对象进行赋值 //用于绑定被代理类的实例 public void bind(Object obj){ this.obj = obj; } //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke() //将被代理类要执行的方法a的功能就声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //演示AOP代理增加的通用方法 HumanUtil util = new HumanUtil(); //通用方法1 util.method1(); //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法 //obj:被代理类的对象 Object returnValue = method.invoke(obj,args); //通用方法2 util.method2(); //上述方法的返回值就作为当前类中的invoke()的返回值。 return returnValue; } } //测试类 public class ProxyTest { public static void main(String[] args) { //创建一个被代理类的实例 SuperMan superMan = new SuperMan(); //proxyInstance:代理类的对象 Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法 String belief = proxyInstance.getBelief(); System.out.println(belief); proxyInstance.eat("四川麻辣烫"); System.out.println("*****************************"); NikeClothFactory nikeClothFactory = new NikeClothFactory(); ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory); proxyClothFactory.produceCloth(); } }
对比静态代理:代理类和被代理类在编译期间,就确定下来了
/* * 静态代理举例: * 特点:代理类和被代理类在编译期间,就确定下来了。 */ interface ClothFactory{ void produceCloth(); } //代理类 class ProxyClothFactory implements ClothFactory{ private ClothFactory factory;//用被代理类对象进行实例化 public ProxyClothFactory(ClothFactory factory){ this.factory = factory; } @Override public void produceCloth() { System.out.println("代理工厂做一些准备工作"); factory.produceCloth(); System.out.println("代理工厂做一些后续的收尾工作"); } } //被代理类 class NikeClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("Nike工厂生产一批运动服"); } } //测试类 public class StaticProxyTest { public static void main(String[] args) { //创建被代理类的对象 ClothFactory nike = new NikeClothFactory(); //创建代理类的对象 ClothFactory proxyClothFactory = new ProxyClothFactory(nike); proxyClothFactory.produceCloth(); } }
转自https://blog.csdn.net/select_alter_drop/article/details/98882949