JavaSE-反射体系

        反射体系是Java中最有代表性的体系之一,反射是所有Java框架的基础,没有反射体系的话就没有任何JavaEE的框架。

        什么是反射?通俗的来讲就是对象的反向处理操作,就是根据已有的类的实例化对象来反推类的组成。一般情况下我们采用的实例化方式是:类名 对象名 = new 构造方法();这是正向操作。

        我们现在来观察一下正反两种操作的例子

import java.util.Date;

public class Main{
    public static void main(String[] args)throws Exception{
        Date date = new Date();//正向操作
        System.out.println(date);
        Class<?> cls = Class.forName("java.util.Date");//反向操作
        System.out.println(cls.newInstance());
    }
}

        这里有一个Class<?>,是返向操作(反射)的核心。反向操作是根据对象来取得对象的来源信息,对象的来源信息,主要集中在Class类里,现在我们来介绍一下Class类。

Class类:

        首先我们要分清Class和class的区别:class是关键字,Class是一个类,这个类是描述类的类,有点绕口。

        可以举一个通俗的例子:对象是由类来创建的,我们可以把对象想象成产品,类是生产产品的图纸,那么,Class类就是画图纸和管理图纸的地方。Class类产生的对象,可以理解成产生产品的图纸。Class类的对象不是由用户自己,而是由JVM来创建的。类类装载的时候,JVM会产生唯一的Class类对象,就是生产出唯一的产品图纸。举个例子,假设用户的程序用到了4个类,那么在程序运行的时候,Class类对象就只有4个,Class类对象的个数与类对象的个数没有关系,就像有很多产品但却只有一份图纸。

        我们有三种方法可以拿到某个类的Class对象。

        1:Class<?> cls = 已创建对象名.getclass();

  public final native Class<?> getClass();

        这是getclass()方法的源码,它是Object下的方法,意味着所有的类都可以通过此方式拿到其Class对象。此方法返回一个Class<?>对象,这个Class对象描述的就是调用此方法的对象的模板类。这个方法可以更好的体现反操作的本质:通过对象取得其来源的类。不过使用此方法的前提是必须要有一个已经创建好的对象。

        2:Class<?> cls = Class.forName(“完整的类名”);

 @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

        以上是此方法的源码,同样的,这个方法返回一个Class对象,这种方式的好处是,不需要类的实例化对象就可以拿到Class对象,此方法在我们以后的开发中是最常见到的,因为此方法可以从任何地方拿到Class对象,总之我们现在必须对这个方法有所掌握。

        3:Class<?> cls = 类名称.class

        这个好理解一点,相当于我们知道了类名程之后直接拿到Class对象,相当于直接赋值,在平时的编程中,这种方法也是最常用的一种。

        现在我们拿到了Class对象,相当于我们已经拿到了产品的设计图纸,接下来,我们就要生产对象了。

        当然,反射的重点根本就不是对象,而是对象的组成,我们可以通过反射拿到对象中的方法,属性,甚至我们可以对其成员无视权限的进行访问......


newInstance()方法

        这个方法就是在我们取得Class对象后,实例化类的一个方法。我们先看一下源码。

 @CallerSensitive
    public T newInstance()
        throws InstantiationException, IllegalAccessException

        此方法返回类型为T,我们一般用Object来接收,在有一些情况下,我们会把返回类型强转为其他的类型。

        要注意的是,这个方法实例化对象的时候调用的是默认的无参构造。如果Class对象中有其他的构造方法,这个方法就会失灵,我们就要取得该Class对象的构造方法然后才能实例化对象。所以为了方便起见,一般我们的POJO中都会有一个由用户自己提供的一个无参构造方法。


反射体系与类

        我们通过反射创建了类的对象,我们就可以通过反射来做出一个对象所能做出所有行为。

取得父类的信息

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

interface Brother{ }
interface Consin{ }

class Father{ }

class SonClass extends Father implements Brother,Consin{ }

public class Main{
    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类中一些方法。

        Constructor类:描述类中构造方法的类

getDeclareConstructor():取得类中指定构造,无视权限

 @CallerSensitive
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return getConstructor0(parameterTypes, Member.DECLARED);
    }

        这个方法可以取得指定参数的,任何权限的构造方法,我们可以发现此方法的参数都是Class对象,所以我们传参时的参数应该是:类型名.class。这里和包装类没什么关系,比如“int.class”指的其实是描述基本类型int的Class类对象。

getConstructors()取得类中所有public权限构造

 @CallerSensitive
    public Constructor<?>[] getConstructors() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(true));
    }

        这个方法返回的时Constructor<?>[]数组,它可以把一个类中的所有构造方法都返回存到一个数组里,这个方法只能把一个类中权限为public的构造方法返回,至于父类的构造方法,是无法获得的。

getDeclaredConstructors()取得类中所有构造,无视权限

 @CallerSensitive
    public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(false));
    }

        这个方法和getConstructors()差不多,唯一的区别就是它可以把类中的所有构造方法(无权限限制)全部返回。同样无法获得父类的构造方法。

        我们拿到构造方法的时候,就要创建实例化对象了,这时候就要用到newInstance方法了,接下来看测试反射获取构造方法的代码的示范

import java.lang.reflect.Constructor;

class Person{
    int ss;
    public Person(){}
    public Person(int ss){
        this.ss = ss;
    }

}

class Student extends Person{
    private String name;
    private int age;
    public String grade;
    public Student(){}
    public Student(String name){
        this.name = name;
    }
    protected Student(String name, int age){
        this.name = name;
        this.age = age;
    }
    private Student(String name, int age, String grade){
        this.name = name;
        this.age = age;
        this.grade = grade;
    }

    @Override
    public String toString() {
        return this.name+this.age+this.grade;
    }
}

public class Main {
    public static void main(String[] args)throws Exception {
        Class<?> cls4 = Student.class;
        Constructor constructor = cls4.getDeclaredConstructor(String.class,int.class);//取得某种构造方法,无权限限制
        Student student = (Student) constructor.newInstance("liu",20);//实例化对象
//        System.out.println(student);
        Constructor[] constructors = cls4.getConstructors();//拿到类中所有属性为public的构造,拿不到父类的
        for(Constructor c : constructors){
            System.out.println(c);
        }
        Constructor[] constructors1 = cls4.getDeclaredConstructors();//拿到类中所有构造,无权限限制,拿不到父类的
        for(Constructor c1 : constructors1){
            System.out.println(c1);
        }
    }
}

        反射调用类的普通方法

        既然我们可以通过反射调用构造方法,那么同样的,我们也可以调用普通方法,不过与调用构造方法不同的时,使用的类也不同了。我们在获取普通方法时用的是Method类,它用来描述类中的普通方法。

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

 @CallerSensitive
    public Method[] getMethods() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyMethods(privateGetPublicMethods());
    }

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

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

  @CallerSensitive
    public Method[] getDeclaredMethods() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyMethods(privateGetDeclaredMethods(false));
    }

        该方法和getMethods()差不多,只不过只会返回本类中所有普通方法,无视权限,相对于上面的方法,这个方法还是比较常用的。

getMethod(String name,class<?>...)

    @CallerSensitive
    public Method getMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Method method = getMethod0(name, parameterTypes, true);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }

        该方法会根据方法名以及参数列表取得指定的普通方法,权限为public,如果指定名称的方法在本类中没有,会继续查找父类。如果找到的话,就返回找到的方法。

getDclaredMethod(String name,class<?>...)

 @CallerSensitive
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }

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

调用拿到的普通方法(invoke(Object obj,Object...))

        参数Obj:具体类的对象

        method.invoke(类对象,方法的参数列表...);

        调用获取到的普通方法时,必须要有类的对象,因为方法是通过对象调用的。如果调用的方法没有参数,那么就不用写参数列表那一部分了。

        其实,还存在一个问题:如果拿到了private的方法,不就调用不了了么?我们通过反射可以强制破坏封装性,使private方法可见。

setAccessible()方法

    public void setAccessible(boolean flag) throws SecurityException {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
        setAccessible0(this, flag);
    }

        这里有一点要强调的是:此方法属于Object类的方法,就是说类的构造方法,普通方法,属性,都可以通过这个方法来动态设置封装或者破坏封装。此方法的参数是Boolean类型的,如果要把构造方法,普通方法或者属性的破坏性破坏掉,只需要传入true就可以了,这样我们就可以调用private方法了。不过,并不是说private被改成public了,权限并没有改变,只是对外可见了。同理,如果我们想把public的成员变为不可见,就传入false。然而,这种特性只能维持在当前的JVM的进程中。这是反射很强大的一点了。

        以下是我演示反射调用普通方法的代码

import java.lang.reflect.Method;

class Person{
    public Person(){}
    public void PersonMethod_A(){
        System.out.println("This is PersonMethod_A");
    }
    private void PersonMethod_B(){
        System.out.println("This is PersonMethod_B");
    }
    protected void PersonMethod_C(){
        System.out.println("This is PersonMethod_C");
    }
}

class Student extends Person{
    private String name;
    public int age;
    public Student(){}//一般来说POJO需要自己设置一个无参构造
    //因为newinstance()默认使用无参构造
    public void Method_A(){
        System.out.println("This is Studebt's Method_A");
    }
    public void Method_B(){
        System.out.println("This is Studebt's Method_B");
    }
    private void Method_C(){
        System.out.println("This is Studebt's Method_C");
    }
    protected void Method_D(){
        System.out.println("This is Studebt's Method_D");
    }
    public void Method_Print(String name){
        this.name = name;
        System.out.println(this.name);
    }

    @Override
    public String toString() {
        return this.name+this.age;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Student.class;
        Method[] methods = cls.getMethods();//取得本类以及父类中所有权限为public的方法,优先搜索本类
        for (Method m : methods) {
            System.out.println(m);
        }
        Method[] methods1 = cls.getDeclaredMethods();//拿到本类中所有普通方法,无关权限
        for (Method m1 : methods1) {
            System.out.println(m1);
        }
        Object object = cls.newInstance();//方法的调用必须要有类对象
        Method method = cls.getMethod("Method_Print", String.class);//取得本类或者父类的public普通方法,可能会找不到,要抛异常
        method.invoke(object, "调用方法设置的名字");//invoke方法的第一个参数是类的实例化对象,接下来的参数列表是方法要参数
        Method method1 = cls.getDeclaredMethod("Method_B");///取得本类的普通方法,无关权限
        method1.invoke(object);//因为取得的方法无参数,所以直接是对象调用
        Method method2 = cls.getDeclaredMethod("Method_C");//使用此方法拿到了一个privata方法
        method2.setAccessible(true);//在一次JVM进程中暂时破环封装,是Object的方法,意味着构造,普通方法,属性的封装性都可以被破坏
        System.out.println(method2);//查看此方法,仍然是private
        method2.invoke(object);//调用,发现调用没问题
    }
}


反射获取类的属性

        我们获取Class对象后,也可以获取其中的属性,获取的方法,和获取普通方法差不多。描述类中属性的类是Field类。获取属性的方法主要是以下四种

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

@CallerSensitive
    public Field[] getFields() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyFields(privateGetPublicFields(null));
    }

        此方法返回Field[]数组,该方法会拿到本类和父类中所有权限为public的属性,并把它存放在数组中返回,查找顺序是先本类再父类。

getDeclaredFields();取得本类中所有属性,无关权限

    @CallerSensitive
    public Field[] getDeclaredFields() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyFields(privateGetDeclaredFields(false));
    }

        该方法同样返回Field数组,不过该方法不会返回父类的属性,但是会返回本类的所有权限的属性。

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

    @CallerSensitive
    public Field getField(String name)
        throws NoSuchFieldException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Field field = getField0(name);
        if (field == null) {
            throw new NoSuchFieldException(name);
        }
        return field;
    }

        我们可以通过此方法来查找属性,方法的参数就是属性的名称,查找范围是本类以及父类所有public权限的属性,先查找本类,再查找父类。

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

    @CallerSensitive
    public Field getDeclaredField(String name)
        throws NoSuchFieldException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Field field = searchFields(privateGetDeclaredFields(false), name);
        if (field == null) {
            throw new NoSuchFieldException(name);
        }
        return field;
    }

        按照属性名称来查找,查找范围是本类的所有权限的属性。不查找父类的属性。

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

        在我们找到属性后,我们可以对它进行设置。设置使用set(Object obj,Object...)方法,第一个参数obj是实例化的类对象,后面的是要设置的属性值。get(Object obj)返回obj对象的属性。两个方法都通过 取得的属性.get()/set()来调用。

        如果我们想改变属性的封装性,可以直接采用属性.setAccessible(Boolean Flag)方法来改变封装性即可。

        有关反射调用属性的演示代码如下

import java.lang.reflect.Field;

class Person{
    public String Person_field1;
    protected String Person_field2;
    private String Person_field3;
}

class Student extends Person{
    public String field1;
    protected String field2;
    private String field3;

    public void Method_A(){
        System.out.println(this.field1+this.field2+this.field3);
    }
}

public class Main{
    public static void main(String[] args)throws Exception{
        Class<?> cls = Student.class;
        Object object = cls.newInstance();
        Field[] fields = cls.getFields();//取得本类以及父类的public属性
//        for(Field f : fields){
//            System.out.println(f);
//        }
        Field[] fields1 = cls.getDeclaredFields();//取得本类中所有属性,不限权限
//        for(Field f1 : fields1){
//            System.out.println(f1);
//        }
        Field field = cls.getField("Person_field1");//按名字取得本类或者父类的public属性
//        System.out.println(field);
        Field field1 = cls.getDeclaredField("field3");//按名字取得本类中的属性,不限权限
//        System.out.println(field1);
        field1.setAccessible(true);//因为setAccessible()方法是Object的类方法,所以在此JVM进程中破坏field的封装性
        System.out.println(field1);//发现field1还是privata的属性
        field1.set(object,"被改变的private属性");//属性设置,第一个参数是实例化的对象,第二个是要设置的值
        System.out.println(field1.get(object));//属性的值获取,参数是实例化的对象
    }
}



有关反射的知识点就先总结到这里,如果有错误还请大家指出。







        

        

猜你喜欢

转载自blog.csdn.net/qq_38449518/article/details/80261871