关于Java反射

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

关于Java反射_第1张图片

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

Class类

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

/**
 * 获取一个class类文件对象的三种方式
 * 1、对象获取
 * 2、类名获取
 * 3、Class类的静态方法获取
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        //1、对象获取,通过父类方法getClass获取
        Person p = new Person();
        Class pC01 = p.getClass();

        //2、类名获取
        //每个类型,包括基本和引用,都会赋予这个类型一个静态属性class
        Class pC02 = Person.class;

        //3、Class类的静态方法获取
        Class pC03 = Class.forName("com.if010.classloader.Person");

        //疑问:pC01 == pC02 == pC03 ?
        System.out.println(pC01 == pC02); //True
        System.out.println(pC01 == pC03); //True
        System.out.println(pC02 == pC03); //True
    }
}

第三种和前两种的区别,前两种你必须明确Person类型,后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了。

获取公共构造方法,创建对象

获取构造方法,步骤如下:

  1. 获取到Class对象
  2. 获取指定的构造方法
  3. 通过构造方法类Constructor中的方法,创建对象
import java.lang.reflect.Constructor;

/**
 * 通过反射获取Class文件中的构造方法,并运行构造方法
 * 运行构造方法创建对象
 *  1. 获取到Class对象
 *  2. 获取指定的构造方法
 *  3. 通过构造方法类Constructor中的方法,创建对象
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class pClass = Class.forName("com.if010.classloader.Person");

        //使用class文件对象,获取类中的构造方法,有两种方式拿取
        //1、getConstructors() 获取class文件中的所有的公共的构造方法
        Constructor[] constructors = pClass.getConstructors();
        for (Constructor c : constructors){
            System.out.println(c);
        }
        //2、getConstructor() 获取class文件中的一个空参构造方法
        Constructor constructor = pClass.getConstructor();
        //运行空参构造器的方法,Constructor类newInstance方法
        Object obj = constructor.newInstance();

        //getConstructor(Class...parameterTypes) 获取class文件中的一个有参构造方法
        Constructor constructor1 = pClass.getConstructor(String.class,int.class);
        //运行有参构造器的方法,Constructor类newInstance方法
        Object obj1 = constructor1.newInstance("zhangsan",19);

        //快捷方法(条件:被反射的类有空参构造方法,且该方法是public)
        Object obj2 = Class.forName("com.if010.classloader.Person").newInstance();
        System.out.println(obj2);
    }
}

获取私有构造方法,创建对象

AccessibleObject 类是 Field、Method 和 Constructor 对象的父类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。

对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。常用方法如下:

  • public void setAccessible(boolean flag) throws SecurityException

参数值为true则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值为false则指示反射的对象应该实施 Java 语言访问检查。

获取私有构造方法,步骤如下:

  1. 获取到Class对象
  2. 获取指定的构造方法
  3. 暴力访问, 通过setAccessible(boolean flag)方法
  4. 通过构造方法类Constructor中的方法,创建对象public T newInstance(Object… initargs)
import java.lang.reflect.Constructor;

/**
 * 反射获取私有的构造方法运行
 * 不推荐,破坏了程序的封装性、安全性
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class pClass = Class.forName("com.if010.classloader.Person");

        //getDeclaredConstructors()获取所有的构造方法(包括私有的)
        Constructor[] constructors = pClass.getDeclaredConstructors();
        for (Constructor c : constructors){
            System.out.println(c);
        }

        //getDeclaredConstructor(Class...c)获取指定参数列表的构造方法
        Constructor constructor = pClass.getDeclaredConstructor(int.class,String.class);
        //Constructor类的父类AccessibleObject类setAccessible(boolean flag)方法可以取消访问权限,简称暴力反射
        //如果不取消会报错"IllegalAccessException"
        constructor.setAccessible(true);
        Object obj = constructor.newInstance(19,"zhangsan");
        System.out.println(obj);
    }
}

获取成员变量并使用

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

  • 返回一个成员变量
    • public Field getField(String name) 获取指定的 public修饰的变量
    • public Field getDeclaredField(String name) 获取指定的任意变量
  • 返回多个成员变量
    • public Field[] getFields() 获取所有public 修饰的变量
    • public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)
import java.lang.reflect.Field;

/**
 * 反射获取成员变量,并修改值
 * Person类中的成员变量,String name
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class pClass = Class.forName("com.if010.classloader.Person");
        //获取所有的公共成员变量,Class类中的方法getFields()
        Field[] fieldsPub = pClass.getFields();
        for (Field field : fieldsPub){
            System.out.println(field);
        }

        //获取所有的成员变量(包括私有的),Class类中的方法getFields()
        Field[] fieldsAll = pClass.getDeclaredFields();
        for (Field field : fieldsAll){
            System.out.println(field);
        }

        //获取指定的公共成员变量
        //Class类的方法 getField(String name) 传递字符串类型的变量名
        Field fieldPub = pClass.getField("name");
        //修改成员变量的值,void get(Object obj, Object value)
        //Object obj 必须有对象的支持, Object value修改后的值
        Object o = pClass.newInstance();
        fieldPub.set(o, "zhangsan");

        //获取指定的私有成员变量(不推荐,会破坏封装性和安全性)
        //Class类的方法 getDeclaredField(String name) 传递字符串类型的变量名
        Field fieldAll = pClass.getDeclaredField("age");
        //暴力反射,取消访问权限检查,不取消会报错"IllegalAccessException"
        fieldAll.setAccessible(true);
        fieldAll.set(o, 19);

        System.out.println(o);
    }
}

获取成员方法并使用

获取成员方法,步骤如下:

  1. 获取Class对象
  2. 获取构造方法
  3. 通过构造方法,创建对象
  4. 获取指定的方法
  5. 执行找到的方法public Object invoke(Object obj, Object… args),执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
import java.lang.reflect.Method;

/**
 * 反射获取成员方法并运行
 *
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class pClass = Class.forName("com.if010.classloader.Person");
        //获取class文件中的公共成员方法(包括继承的)
        Method[] methods = pClass.getMethods();
        for (Method m : methods){
            System.out.println(m);
        }

        //获取class文件中指定的空参公共成员方法
        //Method getMethod(String methodName, Class...c)
        //String methodName 方法名,Class...c 参数列表
        Method method = pClass.getMethod("eat");
        //使用Method类方法Object invoke(Object obj, Object...o) 运行class文件中的方法
        method.invoke(pClass.newInstance());

        //获取class文件中指定的有参公共成员方法
        Method method1 = pClass.getMethod("speak", String.class);
        Object speak = method1.invoke(pClass.newInstance(), "Hello呀!");
        System.out.println(speak);

        //获取class文件中指定的空参私有成员方法(不推荐,会破坏封装性和安全性)
        Method method2 = pClass.getDeclaredMethod("work");
        //暴力反射,取消访问权限检查,不取消会报错"IllegalAccessException"
        method2.setAccessible(true);
        method2.invoke(pClass.newInstance());
    }
}

泛型擦除

将已存在的ArrayList集合中添加一个字符串数据,如何实现呢?

其实程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素。

import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 * 定义一个集合类,固定类型String
 * 要求向集合中加入int类型
 *
 * 反射方式,获取出集合ArrayList类的class文件对象
 * 通过class文件对象,调用add方法
 */
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        ArrayList<String> arrayList = new ArrayList<String>();
        arrayList.add("hello");
        arrayList.add("world");

        //获取出集合ArrayList类的class文件对象
        Class c = arrayList.getClass();
        //通过ArrayList类的class文件对象获取add方法
        Method method = c.getMethod("add", Object.class);
        //使用invoke运行add方法
        method.invoke(arrayList, 100);
    }
}

反射配置文件

通过反射配置文件,运行配置文件中指定类的对应方法,读取Peoperties.txt文件中的数据,通过反射技术,来完成Person对象的创建。

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 读取class.properties文件中的数据,通过反射技术,来完成Person对象的创建
 *
 * class.properties文件内容:
 * ClassName=com.if010.classloader.Person
 * MethodName=eat
 */
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //通过IO流读取配置文件
        FileReader fileReader = new FileReader("./Test/class.properties");
        Properties classPropertie = new Properties();
        classPropertie.load(fileReader);
        fileReader.close();
        String className = classPropertie.getProperty("ClassName");
        String methodName = classPropertie.getProperty("MethodName");


        //开始反射
        //1、获取Person.class 字节码文件对象
        Class c = Class.forName(className);
        //2、获取构造方法
        Method method = c.getMethod(methodName);
        //3、创建对象
        Object object = c.newInstance();
        //4、运行方法
        method.invoke(object);
    }
}

你可能感兴趣的:(java,python,开发语言)