8.1 反射

51 阅读4分钟

1. 反射

反射库( reflection library) 是一个工具集, 用于动态的操作java代码的程序.

能够分析类能力的程序被称为反射(reflection)

  1. 在运行时分析类的能力
  2. 在运行时查看对象,
  3. 实现通用的数组操作代码
  4. 用Method对象

1.1 Class类

在程序运行期间,Java 运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。 虚拟机利用运行时类型信息选择相应的方法执行。然而, 可以通过专门的 Java 类访问这些信息,保存这些信息的类被称为 Class。

一个 Class 对象将表示一个特定类的属性, 获取的方式有三种

Student s=new Student("大肥羊","(*^▽^*)"); 
//getClass获取 
Class class1=s.getClass(); 
//通过forName 类名获取 
Class class2=Class.forName(s.getClass().getName()); 
//根据 类.class获取 
Class class3=Student.class;

Class表示的是一个类型 并代表这个类型必须是类 int.Class 也是一个Class类型的对象

通过Class创建对象

通过newInstance()可以调用 某类的无参构造器 来创建一个实例

Class class1=s.getClass(); 
class1.newInstance();

2. 使用反射

使用反射检查类的结构, 在 java.lang.reflect 包中有三个类 Field、 Method 和 Constructor 分别用于描述类的域、 方法和构造器。

package bai.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ReflectionTest {
    public static void main(String[] args) {
        Class c1= Student.class;
        //返回父类
        Class supercl=c1.getSuperclass();
        //类的修饰符
        String modifiers= Modifier.toString(c1.getModifiers());
        p("类的修饰符: "+modifiers);
        //构造器
        printConstructors(c1);
        //方法
        printMethods(c1);
        //字段
        printFields(c1);


    }
    private static void p(String str){
        System.out.println(str);
    }

    private static void printFields(Class c1) {
        //获取所有的属性
        Field[] fields=c1.getDeclaredFields();
        for (Field field:fields){
            p("-----------");
            p("字段名称:"+field.getName());
            Class type=field.getType();
            String name= type.getName();
            p("字段类型:"+name);
            String modifiers=Modifier.toString(field.getModifiers());
            p("修饰符:"+modifiers);

        }
    }

    private static void printMethods(Class c1) {
        //获取所有方法
        Method[] methods = c1.getDeclaredMethods();
        for (Method m : methods) {
            p("-----------");
            //获取返回类型
            Class returnType = m.getReturnType();
            String name = m.getName();
            p("方法名:"+name);
            String modifiers = Modifier.toString(m.getModifiers());
            p("方法值修饰符:"+modifiers);
            Class[] paramTypes=m.getParameterTypes();
            for (Class paramType : paramTypes) {
                p("参数:"+paramType.getName());
            }
            p("返回值:"+returnType.getName());
        }

    }

    private static void printConstructors(Class c1) {

        Constructor[] constructors=c1.getDeclaredConstructors();
        for (Constructor c : constructors) {
            p("-----------");
            //获取构造函数声明类的二进制名称
            String name=c.getName();
            p("构造函数的名称: "+name);
            //获取修饰符
            String modifiers = Modifier.toString(c.getModifiers());
            p("构造函数的修饰符: "+modifiers);
            //获取所有的参数类型
            Class[] paramTypes = c.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++)
            {
                p("参数"+j+" : "+paramTypes[j].getName());
            }
        }
    }
}

2.1 Class常用方法 获取 字段 方法 构造函数

image.png

2.2 field Method Constructor

image.png image.png

2.3 Modifier 修饰符

image.png

2.4 使用反射查看对象内容

获取属性的当前值,需要使用 java.lang.reflect.Field 类 该类可以获取 属性对象(Field) ,Field提供了关于属性的多个方法.

image.png

package bai.reflect;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class RefleCtionTest2 {
    public static void main(String[] args) throws Exception {
        Class c = Student.class;

        //根据参数...获取构造器 如果需要获取private的 加上getDeclaredConstructor(XXX)
        Constructor constructor = c.getConstructor(String.class, String.class);

        //根据方法名和参数...获取方法对象
        Method method = c.getMethod("toString");
        //创建对象
        Student o = (Student) constructor.newInstance("哈哈", "1123");
        //通过 Method 的 invoke 调用 o 的toString 方法
        System.out.println("创建对象:"+method.invoke(o));

        //获取private字段
        Field field = c.getDeclaredField("name");

        //System.out.println(field.get(o));//get时候异常
        // java.lang.IllegalAccessException:Class bai.reflect.RefleCtionTest2
        // can not access a member of class bai.reflect.Student with modifiers "private"

        //修改访问权限
        field.setAccessible(true);
        field.set(o,"哦噢耶");
        System.out.println(field.get(o));
        System.out.println("修改后:"+method.invoke(o));

    }

    public static Object toArray(Object[] a,int newLength){
        //获取对象的类型
        Class c1=a.getClass();
        if (!c1.isArray()) return null;
        //获取数组的类型
        Class componentType=c1.getComponentType();
        int length= Array.getLength(a);
        //创建一个新的数组 object[] 数组不能转成其他类型的数组 但是obj对象可以
        Object newArray=Array.newInstance(componentType,newLength);
        //拷贝数组
        System.arraycopy(a,0,newArray,0,Math.min(length,newLength));
        return newArray;
    }
}

Class的getDeclaredFields( )可以获取Field[ ] 即所有的字段, 包括private,调用field.get()方法时,收到java访问控制的约束,会抛出异常

Field继承了AccessibleObject类(可访问对象) 通过类的方法 可以设置属性的访问权限, 就可以获取和修改私有属性.

2.5 使用 反射编写泛型数组

reflect 中操作数组的类 java.lang.reflect.Array, Object[]数组不能转换为Test[] 数组 会转换异常 但是Object对象可以转换成Test[]

image.png

public static Object toArray(Object[] a,int newLength){
    //获取对象的类型
    Class c1=a.getClass();
    if (!c1.isArray()) return null;
    //获取数组的类型
    Class componentType=c1.getComponentType();
    int length= Array.getLength(a);
    //创建一个新的数组 object[] 数组不能转成其他类型的数组 但是obj对象可以
    Object newArray=Array.newInstance(componentType,newLength);
    //拷贝数组
    System.arraycopy(a,0,newArray,0,Math.min(length,newLength));
    return newArray;
}

2.6 任意调用方法

image.png

Class c = Student.class; 
//根据参数...获取构造器 如果需要获取private的 加上getDeclaredConstructor(XXX) Constructor constructor = c.getConstructor(String.class, String.class); 
//根据方法名和参数...获取方法对象 
Method method = c.getMethod("toString");
//创建对象 
Student o = (Student) constructor.newInstance("哈哈", "1123"); 
//通过 Method 的 invoke 调用 o 的toString 方法 
System.out.println("创建对象:"+method.invoke(o));