Java-反射相关学习

反射体系是 Java 体系中一个非常具有代表性的体系。什么是反射体系呢?
通俗来讲,反射就是对象的反向处理操作。根据类的实例化对象来反向推出类的组成。一般来说,我们采用实例化的方式是className objectName = new constructor ( ) 这就是正向的一次操作,而反向操作是什么呢?

class Student { 
	Student() {}
} 
Student student = new Student(); // 正向操作
Class<?> cls = Student.class; // 反向操作

这里注意到,cls的类型是一个 Class 在这里有必要说一下 Class 与 class 的区别了。

class 是一个关键字,而 Class 则是一个类,而 Class 这个类描述的是类。也就说说 Class 是类的类(毕竟我是大写 C,比你 class 大一些)。

有三种方式可以拿到一个类的 Class 对象。

  1. Class cls = className.class;
  2. Class cls = Class.forName(String className);
  3. Class cls = 已经创建的对象名.getClass();

当我们拿到一个类的 Class 对象后,就可以根据这个对象来反推类的组成了。

newInstance ( )

这个方法就是在我们取得了 Class 对象之后,实例化类的一个方法。

 @CallerSensitive
    public T newInstance()
        throws InstantiationException, IllegalAccessException

此方法的返回值类型为 T ,一般情况下我们采用 Object 来接收。并且在某些情况下,我们可以把返回值类型强转成其它的类型。这个方法实例化的对象,默认调的是无参的构造函数,如果在 Class 对象中有其它构造方法,这个方法就会失效,我们就要获取该 Class 对象的构造方法然后才能够实例化出对象。

取得父类的信息

所有的类都是有父类的。当我们拿到 Class 类对象之后就可以获得其实现的接口以及继承的父类。

interface Brother {}
interface Consin {}
class Father {}

class SonClass extends Father implements Brother, Consin {} 

public class Test {
    public static void main(String[] args) {
		Class<?> cls = SonClass.class;
        System.out.println(cls.getSuperclass());
        Class<?>[] classes = cls.getInterfaces(); // 这里接口是可以有多个,所以用一个数组来存放
        for (Class<?> c : classes) {
            System.out.println(c);
        }
    }
}
反射调用类的构造

一个类中的构造方法可以重载很多,所以我们要获得类的构造方法,需要 Class 类中的一些方法。

getDeclaredConstructor ( ) : 取得类中的指定构造,无视权限
getDeclareConstructor(Class<?>... parameterTypes);

这个方法可以取得指定参数的,任何权限的构造方法。这个方法的参数都是 Class 对象,所以参数应该是:类型名.class。指的是该类型的对象。

getConstructor ( ) : 取得类中所有 public 构造
getConstructors();	

这个方法的返回值是一个 Constructor 的数组,它可以把一个类的所有权限为 public 的构造方法存放到一个数组内返回。无法返回父类的构造方法。

getDeclaredConstructors ( ) : 取得类中所有的构造方法,无视权限
getDeclaredConstructors();

这个方法与上个差不多,唯一的区别就是它能够把类中所有的构造方法全部返回,无视权限。同样无法返回父类的构造方法。

当我们拿到构造方法的时候,就能够创建实例化对象了,这个时候就可以利用 newInstance 进行实例化了。

import java.lang.reflect.Constructor;
class Person {
    public int name;
    public Person() {}
    public Person(int name) {
        this.name = name;
    }
}

Class Student extends Person {
    private int classNum;
    private String grade;
    public Student() {}
    private Student(int classNum, String grade) {
        this.classNum = classNum;
        this.grade = grade;
    }
}

public class Main {
    public static void main(String[] args) {
        Class cls = Student.class;
        Constructor con = cls.getDeclaredConstructor(int.class, String.class);
        Student stu = con.newInstance(100, "2");
        
        Constructor[] cons = cls.getConstructors();
        for (Constructor c : cons) {
            System.out.println(c);
        }
        
        cons = cls.getDeclaredConstructors();
        for (Constructor c : cons) {
            System.out.println(c);
        }
    }
}
反射调用类的普通方法

在获取普通方法时用的是 Method 类,用它来描述类中的普通方法。

getMethods() : 获得本类以及父类中所有权限为 public 的普通方法

该方法返回的就是一个 Method 数组,存放了本类以及父类中的所有 public 普通方法,该方法优先返回本类中的方法,也就是说数组前面的元素是本类的普通方法,后面的是父类中的普通方法。

getDelcaredMethods();取得本类中的所有普通方法,无视权限

该方法与 getMethods 差不多,但是这个方法仅仅只查询本类内部的普通方法,无视权限。

getMethod(String name, Class… parameterTypes)

该方法会根据方法名以及参数列表获取到指定的普通方法,权限为 public ,如果指定的普通方法在本类中没有,则会去父类中查找。

getDelcaredMethod(String name, Class… parameterTypes)

该方法只在本类中查找指定名称和参数的普通方法,不在父类中查找,无关权限。

拿到普通方法后,利用 invoke (Object object,Object…)来进行调用
invoke(Object object, Object...);
method.invoke(类对象,方法的参数列表);

调用获取到的普通方法的时候必须要有类的实例化对象,如果调用的普通方法没有参数,那么就不需要写参数列表了。

当我们获取到 private 的方法的时候,是否可以调用呢?

setAccessible()
void setAccessible(boolean flag);

我们获取到的 private 方法是不能够直接调用的,而 setAccessible 就是帮助我们调用。

这个方法的参数是一个 Boolean 类型的,当我们需要把普通方法或者属性破坏掉的时候,传入 true 即可。这个方法不会将 private 转化成 public ,权限并没有改变,只不过是对外可见了,同样也可以将 public 转化为 private ,只需要传入 false 即可。

import java.lang.reflect.Method;
class Person {
    public Person() {}
    public void PersonMethod1(String str) {
        System.out.println(str);
    }
    private void PersonMethod2() {}
}

class Student extends Person {
    public Student() {}
    public void StudentMethod1() {}
    private void StudentMethod2(String str) {
        System.out.pringln(str);
    }
}

public class Main {
    public static void main(String[] args) {
        Class<?> cls = Student.class;
        Method[] methods = cls.getMethods(); 
        for (Method method : methods) {
            System.out.println(method);
        }
        
        methods = cls.getDelcaredMethods();
        for (Method method : methods) {
            System.out.pringln(method);
        }
        
        Method method = cls.getMethod("PersonMethod1", String.class);
        Object object = cls.newInstance();
        System.out.println(method);
        method.invoke(object, "method.invoke");
        
        method = cls.getDelcaredMethod("StudentMethod2", String.class);
        method.setAccessible(true);
        System.out.println(method);
        method.invoke(object, "method.invoke");
    }
}
反射获取类的属性

在获取到 Class 对象后,也可以获得其中的属性,获取的方法与获取普通方法类似。描述类中属性的类是 Field 类。

getFields() : 取得所有权限为 public 的属性,先查找本类再查找父类。

此方法返回 Field 数组,该方法会拿到本类与父类的 public 属性,将其存放至数组中返回,查找顺序是先本类再父类。

getDelcaredFields( ) : 获取本类中的所有方法,无关权限。

该方法也返回的是 Field 数组,但是该方法不会查询父类的属性。

getField(String name) : 查找指定名称的属性,权限为 public

通过此方法查找属性,该方法的参数就是属性的名称,查找范围是先从本类开始查找,接着再查找父类。

getDelacredField(String name) : 查找本类中指定名称的属性,无关权限。

通过此方法查找本类中的指定属性,不查找父类。

field.set() 与 field.get() 方法

找到属性的后,可以对它进行设置。

set(Object object, Object....);

第一个参数 obj 是实例化的类对象,第二个参数是要设置的属性值。

get(Object object);

参数是 obj 实例化的类对象,返回值是属性值。

同样,属性也可以利用 setAccessible(Boolean flag) 方法来改变封装性即可。

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

class Person {
    public int age;
    public String name;
    Person() {}
    public void PersonPrint() {
        System.out.println("name=" + name);
    }
}

class Student extends Person {
    public String grade;
    private int classNum;
    Student() {}
    public void StudentPrint() {
        System.out.println("class: " + class);
    }
}

public class Main {
    public static void main(String[] args) {
        Class<?> cls = Student.class;
        Field[] fields = cls.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        
        fields = cls.getDelcaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        
        Field field = cls.getField("name");
        Object object = cls.newInstance();
        field.set(object, "chenxiliu");
        Method method = getMethod("PersonPrint");
        method.invoke(object);
        
        field = cls.getDelcaredField("classNum");
        field.setAccessible();
        field.set(object, 2);
        method = getMethod("StudentPrint");
        methid.invoke(object);
    }
}

你可能感兴趣的:(Java)