Java反射机制详解
2021/5/17 22:25:32
本文主要是介绍Java反射机制详解,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
一、Java反射机制
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
反射机制主要是用来破解类文件的:
正常的文件执行:在源代码已知的情况下导入类路径,通过new关键字实例化该类对象,通过对象名使用这个类。
反射文件执行:在源代码未知的情况下,对类文件进行破解,然后再去使用这个类。
要想解剖一个类,必须先要获取到该类的字节码文件对象,而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。
二、 Class类
想要破解类,就必须对类进行了解,类其实也是一种类型,对于一个类来讲,应该是由属性、普通方法、构造方法等等构成的。API中同样提供了一个Class类,用来对类本身进行操作,通过Class可以完整的得到一个类的结构,例如:方法、属性...
Class没有公共构造方法,Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构成的。
获取Class对象:
Class类没有公共构造方法,创建Class对象,获取Class对象的三种方式:
-
通过Object类中的getClass()方法:对象名.getClass();
-
通过类名.class获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种简单):类名.class;
-
通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可):Class.forName("完整包名.类名");
注:第三种方式与前两种的区别:前两种必须明确类的类型,第三种是指定这种类型的字符串就行,这种扩展性更强,灵活性较强,不需要知道你的类,只提供字符串,按照配置文件加载就可以了。
三、通过反射获取构造方法并使用
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示,其中构造方法使用类Constructor表示,可通过Class类中提供的方法获取构造方法:
// 获取一个构造方法: // 获取public修饰,指定参数类型所对应的构造方法: public Constructor<T> getConstructor(Class<?> parameterTypes); // 获取指定参数类型所对应的构造方法(包含私有的): public Consturctor<T> getDeclaredConstructor(Class<?> parameterTypes); // 返回多个构造方法: // 获取所有的public 修饰的构造方法: public Constructor<T>[] getConstructor(); // 获取所有的构造方法(包含私有的): public Consturctor<T>[] getDeclaredConstructor();
四、利用反射获得到的构造方法创建对象
-
获取到Class对象
-
获取指定的构造方法
-
通过构造方法类Constructor中的方法创建对象:public T newInstance(Object obj);
/** * copyright(c)2021 zbh.ALL rights Reserved * <p> * 描述:利用反射获得到的构造方法创建对象 * * @author zbh * @version 1.0 * @date 2021/5/17 */ public class ConstructorDemo { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InvocationTargetException { //1,获取到Class对象 Class c = Class.forName("Person");//包名.类名 //2,获取指定的构造方法 //public Person() //Constructor con = c.getConstructor(null); //public Person(int id, String name, String address) Constructor con = c.getConstructor(int.class, String.class, String.class); //3,通过构造方法类中Constructor的方法,创建对象 //Object obj = con.newInstance(null); Object obj = con.newInstance(22, "小明", "哈尔滨"); //显示 System.out.println(obj);// Person{id=22, name='小明', address='哈尔滨'} } }
五、利用反射获得的私有构造方法创建对象
AccessibleObject类是Field、Method和Constructor类的父类。它提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力。
对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用Field、Method或Constructor对象来设置或获取字段、调用方法或者创建和初始化类的实例的时候,会执行访问检查。
在Constructor的父类java.lang.reflect.AccessibleObject的常用方法:public void setAccessible(boolean flag) throws SecurityException 的参数为true时,则指示反射的对象在使用时应该取消Java语言访问检查,参数值为false时,则指示反射的对象应该实施Java语言访问检查。
获取私有构造方法并创建对象,步骤:
-
获取到Class对象。
-
获取指定的构造方法。
-
暴力访问,通过setAccessible(boolean flag)方法。
-
通过构造方法类Constructor中的方法创建对象。
六、通过反射获取属性(成员变量)并使用
在反射机制中,把类中的成员变量使用类Field表示,可通过Class类中提供的方法获取成员变量:
// 返回一个成员变量(属性) // 获取指定的 public修饰的变量 public Field getField(String name); // 获取指定的任意变量 public Field getDeclaredField(String name); // 返回多个成员变量(属性) // 获取所有public 修饰的变量(属性) public Field[] getFields(); // 获取所有的 变量 (包含私有) public Field[] getDeclaredFields();
获取成员变量及其赋值与获取值的步骤:
-
获取Class对象
-
获取构造方法
-
通过构造方法创建对象
-
获取指定的成员变量(私有成员变量通过setAccessible(boolean flag)方法暴力访问)
-
通过方法,给指定对象的指定成员变量赋值或者获取值:
- 在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值:public void set(Object obj, Object value)
- 返回指定对象obj中,此 Field 对象表示的成员变量的值:public Object get(Object obj)
/** * copyright(c)2021 zbh.ALL rights Reserved * <p> * 描述:通过反射获取属性(成员变量)并使用 * * @author zbh * @version 1.0 * @date 2021/5/17 */ public class FieldDemo { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException, InvocationTargetException { //1,获取Class对象 Class c = Class.forName("Person"); //2,获取构造方法 //public Person(String name) Constructor con = c.getConstructor(String.class); //3,通过构造方法,创建对象 Object obj = con.newInstance("小明"); //4,获取指定的成员变量 //public String name; Field nameField = c.getField("name"); //public int id; Field idField = c.getField("id"); //private String address; Field addressField = c.getDeclaredField("address"); addressField.setAccessible(true); //取消 Java 语言访问检查 //5,通过方法,给指定对象的指定成员变量赋值或者获取值 System.out.println("name = "+ nameField.get(obj));// name = 小明 System.out.println("id = "+ idField.get(obj));// id = 0 // 注:如果没有addressField.setAccessible(true);这行会报错 System.out.println("address = "+ addressField.get(obj));// address = null //赋值 idField.set(obj, 23); addressField.set(obj, "五一广场"); System.out.println("------------------------"); System.out.println("name = "+ nameField.get(obj));// name = 小明 System.out.println("id = "+ idField.get(obj));// id = 23 System.out.println("address = "+ addressField.get(obj));// address = 五一广场 } }
七、通过反射获取成员方法并调用
在反射机制中,把类中的成员方法使用Method表示,可通过Class类中提供的方法获取成员方法:
// 1、返回获取一个方法: // 获取public 修饰的方法 public Method getMethod(String name, Class<?> parameterTypes); // 获取任意的方法,包含私有的,默认的,protected修饰 public Method getDeclaredMethod(String name, Class<?>parameterTypes); // 参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型 // 2、返回获取多个方法: // 获取本类与父类中所有public 修饰的方法 public Method[] getMethods(); // 获取本类中所有的方法(包含私有的) public Method[] getDeclaredMethods(); // Method的常用方法: getName();//获取方法的名称 getReturnType();//获取方法的返回类型
获取成员方法步骤:
-
获取Class对象。
-
获取构造方法。
-
通过构造方法,创建对象。
-
获取指定的方法。
-
执行找到的方法:public Object invoke(Object obj, Object args); 执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
/** * copyright(c)2021 zbh.ALL rights Reserved * <p> * 描述:通过反射获取成员方法并调用 * * @author zbh * @version 1.0 * @date 2021/5/17 */ public class MethodDemo { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { //1, 获取Class对象 Class c = Class.forName("Person"); //2,获取构造方法 //public Person(String name, int age, String address){ Constructor con = c.getConstructor(int.class, String.class, String.class); //3,通过构造方法,创建对象 Object obj = con.newInstance(23, "小明", "哈尔滨"); //4,获取指定的方法 //public void method1() 没有返回值没有参数的方法 //Method m1 = c.getMethod("method1", null); //public String method4(String name) Method m4 = c.getMethod("method4", String.class); //5,执行找到的方法 //m1.invoke(obj, null); Object result = m4.invoke(obj, "zhangsan"); System.out.println("result = " + result);// zhangsan //4,获取指定的方法(访问private修饰符的方法) //private void method5(){ Method m5 = c.getDeclaredMethod("method5", null); //5,开启暴力访问 m5.setAccessible(true); //6,执行找到的方法 m5.invoke(obj, null); } }
八、反射的应用场景
-
需要获取Class对象,使用其方法
-
开发代码生成工具时
-
在框架中(如:Hibernate、Spring、Struts2)
-
泛型的擦除:在程序编译后产生的.class文件中是没有泛型约束的,因为在编译时,就将类型替换完成了,没有填写类型的默认是Object,这种现象我们称之为泛型的擦除。
-
利用反射读取配置文件:通过反射配置文件,运行配置文件中指定类的对应方法。Properties类在java.util包中读取Properties.txt文件中的数据,通过反射技术,来完成对象的创建。
九、动态代理
代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
Java中的动态代理:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理。
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象,JDK提供的代理只能针对接口做代理。而我们又更强大的代理cglib
Proxy类中的方法创建动态代理类对象:public static Object
newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);
Proxy类中创建动态代理对象的三个参数:
-
ClassLoader对象:定义了由哪个ClassLoader对象来对生成的代理对象进行加载
-
Interface对象的数组:表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了。
-
InvocationHandler对象:表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。
最终会调用InvocationHandler的方法:InvocationHandler Object invoke(Object proxy,Method method,Object[] args);
InvocationHandler接口中invoke方法的三个参数:
-
proxy:代表动态代理对象
-
method:代表正在执行的方法
-
args:代表调用目标方法时传入的实参
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来进行调用。
Proxy.newProxyInstance:创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
这篇关于Java反射机制详解的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-26JavaScript入门教程:从零开始学习JavaScript编程
- 2024-12-26JavaScript入门教程:从零开始学习JavaScript
- 2024-12-26JS编程入门指南:从零开始学习JavaScript
- 2024-12-25Java编程面试题详解与解答
- 2024-12-25TS基础知识详解:初学者必看教程
- 2024-12-252024面试题解析与攻略:从零开始的面试准备指南
- 2024-12-25数据结构与算法学习:新手入门教程
- 2024-12-25初学者必备:订单系统资料详解与实操教程
- 2024-12-24内网穿透资料入门教程
- 2024-12-24微服务资料入门指南