ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java反射机制详解

2021-05-17 21:35:10  阅读:87  来源: 互联网

标签:反射 Java 构造方法 对象 Class 获取 详解 方法 public


一、Java反射机制

  在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

反射机制主要是用来破解类文件的:

正常的文件执行:在源代码已知的情况下导入类路径,通过new关键字实例化该类对象,通过对象名使用这个类。

反射文件执行:在源代码未知的情况下,对类文件进行破解,然后再去使用这个类。

  要想解剖一个类,必须先要获取到该类的字节码文件对象,而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。

二、 Class类

  想要破解类,就必须对类进行了解,类其实也是一种类型,对于一个类来讲,应该是由属性、普通方法、构造方法等等构成的。API中同样提供了一个Class类,用来对类本身进行操作,通过Class可以完整的得到一个类的结构,例如:方法、属性...

  Class没有公共构造方法,Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构成的。

获取Class对象:

  Class类没有公共构造方法,创建Class对象,获取Class对象的三种方式:

  1. 通过Object类中的getClass()方法:对象名.getClass();

  2. 通过类名.class获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种简单):类名.class;

  3. 通过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();

四、利用反射获得到的构造方法创建对象

  1. 获取到Class对象

  2. 获取指定的构造方法

  3. 通过构造方法类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语言访问检查。

获取私有构造方法并创建对象,步骤:

  1. 获取到Class对象。

  2. 获取指定的构造方法。

  3. 暴力访问,通过setAccessible(boolean flag)方法。

  4. 通过构造方法类Constructor中的方法创建对象。

六、通过反射获取属性(成员变量)并使用

  在反射机制中,把类中的成员变量使用类Field表示,可通过Class类中提供的方法获取成员变量:

// 返回一个成员变量(属性)
// 获取指定的 public修饰的变量
public Field getField(String name);
// 获取指定的任意变量
public Field getDeclaredField(String name);

// 返回多个成员变量(属性)
// 获取所有public 修饰的变量(属性)
public Field[] getFields();
// 获取所有的 变量 (包含私有)
public Field[] getDeclaredFields();

获取成员变量及其赋值与获取值的步骤:

  1. 获取Class对象

  2. 获取构造方法

  3. 通过构造方法创建对象

  4. 获取指定的成员变量(私有成员变量通过setAccessible(boolean flag)方法暴力访问)

  5. 通过方法,给指定对象的指定成员变量赋值或者获取值:

  • 在指定对象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();//获取方法的返回类型

获取成员方法步骤:

  1. 获取Class对象。

  2. 获取构造方法。

  3. 通过构造方法,创建对象。

  4. 获取指定的方法。

  5. 执行找到的方法: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);
    }
}

八、反射的应用场景

  1. 需要获取Class对象,使用其方法

  2. 开发代码生成工具时

  3. 在框架中(如:Hibernate、Spring、Struts2)

  4. 泛型的擦除:在程序编译后产生的.class文件中是没有泛型约束的,因为在编译时,就将类型替换完成了,没有填写类型的默认是Object,这种现象我们称之为泛型的擦除。

  5. 利用反射读取配置文件:通过反射配置文件,运行配置文件中指定类的对应方法。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,构造方法,对象,Class,获取,详解,方法,public
来源: https://www.cnblogs.com/zbh355376/p/14775843.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有