JAVA反射

15. 反射

15.1 反射的简介

反射(Reflect),在Java中,可以在程序运⾏的过程中,动态的获取类、获取类中的成员(属性、⽅法、构造⽅法),并进⾏访问。这⾥的动态获取,可以通过⼀个字符串来进⾏获取。这样的机制,叫做反射。

15.2 Class的描述
15.2.1 Class类的简介

Class是Java中的⼀个类,⽤来描述Java中的类。对⼀个类编译之后的字节码⽂件进⾏的描述。这个类也是继承⾃Object类,主要来描述⼀个类中有什么成员,包括构造⽅法、属性、⽅法。

15.2.2 Class对象的获取

1、Object类中的getClass()⽅法。通过⼀个对象,获取⼀个⽤来描述这个对象对应的类的Class类的对象。

Dog dog = new Dog();
Class cls = dog.getClass();

2、使⽤类.class的⽅式获取。

Class cls = Dog.class;

3、Class.forName(String className)(最常用)

由于反射,会强调动态性,类的获取、属性的获取、⽅法的获取、构造⽅法的获取,都是要通过⼀个字符串(必须是全限定名,从对上层包开始)进⾏获取的,我们甚⾄可以将⼀个需要加载的类名写到⼀个配置⽂件中,在程序中读取这个配置⽂件中的数据,加载不同的类。这样⼀来,当需要进⾏不同的类加载的时候,直接改这个配置⽂件即可,其他程序不⽤修改。再例如,可以获取某些属性的名字,通过反射获取到属性,并进⾏赋值。因此,上述的两种Class对象的获取都不够动态,我们⽤来获取类,更多的使⽤的是这个⽅法。

异常描述:ClassNotFoundException: 字⾯意思,没有找到这个类,可能是类名写错了

try {
    Class cls = Class.forName("com.qf.aClass.Dog");
    System.out.println(cls);
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
15.3 实例化对象

这⾥所谓的“实例化对象”,是通过反射的形式来实例化的。并不是之前的通过new的⽅式实例化。这⾥,我们只需要知道⼀个类的名字字符串即可以完成对象的实例化了,做到了“动态”。

15.3.1 newInstance()

在Class类中,有⼀个⽅法 newInstance() ,这个⽅法的作⽤,就是去实例化⼀个类的对象。在这个

⽅法中,有⼏个异常:

  • IllegalAccessException 访问权限不够造成的异常。
  • InstantiationException 类中没有⽆参构造构成的异常。
try {
    // 1、获取 Class 对象
    Class cls = Class.forName("com.qf.bInstance.Dog");
    // 2、newInstance
    Object obj = cls.newInstance();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
}
15.3.2 使⽤指定的构造⽅法实例化

在上述⽅法中,只能通过⽆参构造进⾏对象的实例化,⽐较单⼀;并且,如果这个⽆参构造的权限不

⾜,或者没有这个构造⽅法,会导致异常的出现,⽆法实例化对象。

可也通过反射的⽅式,获取到这个构造⽅法,再使⽤这个构造⽅法实例化对象出来。

  • getConstructor(Class<?>… args)
    • 获取⼀个类中的指定参数的构造⽅法,这个只能获取到访问权限⾜够的构造⽅法
  • getDecarledConstructor(Class<?>… args)
    • 获取⼀个类中的指定参数的构造⽅法,这个能够获取每⼀种访问权限的构造⽅法
  • getConstructors()
    • 获取⼀个类中所有的构造⽅法(只能获取能看得到的构造⽅法,访问权限⾜够的构造⽅法)
  • getDecarledConstructors()
    • 获取⼀个类中所有的构造⽅法(包含所有权限的构造⽅法)

常⻅的异常:

  • NoSuchMethodException : 获取的构造⽅法,在类中不存在
  • IllegalArgumentException : 在实例化对象的时候的参数不对
  • IllegalAccessException : 要实例化对象使⽤的构造⽅法访问权限不够

构造方法案例:

/**
 * 使用反射进行对象的实例化
 * ClassNotFoundException       : 无法通过类名字符串找到指定的类(类名写错了)
 *
 * IllegalAccessException       : 常发生于访问权限不足
 * InstantiationException       : 常发生于没有无参构造的情况下,通过默认的无参构造进行实例化
 *
 * NoSuchMethodException        : 使用 getConstructor 方法获取指定的构造方法的时候,没有找到指定的构造方法(参数有误,或者访问权限不够)
 * IllegalArgumentException     : 形参和实参不匹配
 */
public class ConstructorDemo {
    public static void main(String[] args) {
        try {
            // 1. 获取用来描述这个类的 Class 对象
            Class<?> cls = Class.forName("day30.reflect.ConstructorDemo$Person");
            // 2. 实例化对象(只能通过类中的无参构造方法进行实例化)
            // Object o = cls.newInstance();

            // 3. 通过反射获取构造方法,使用构造方法进行对象的实例化
            // 3.1. 通过指定的参数, 获取指定的构造方法, 使用Class<?>...形式描述所有的参数
            // Constructor<?> constructor1 = cls.getConstructor(String.class, int.class);
            //      通过指定的构造方法,实例化对象。在实例化的时候,实参就是构造方法中需要的实参。
            // Object o = constructor1.newInstance("xiaoming", 2);

            // 3.2. 如果需要获取的构造方法,访问权限不足,需要使用getDeclaredConstructor
            // Constructor<?> constructor = cls.getDeclaredConstructor(String.class, int.class);
            //      设置一个属性、方法、构造方法,在进行访问的时候权限校验
            //      setAccessible(boolean flag)
            //      如果参数flag是true: 表示在进行成员访问的时候,跳过权限校验。即无论是什么权限的,都可以访问
            //      如果参数flag是false: 表示在进行成员访问的时候,需要进行权限校验。如果权限足够,可以访问;如果权限不够,无法访问
            // constructor.setAccessible(true);
            // Object xiaoming = constructor.newInstance("xiaoming", 2);

            // 3.3. 获取所有的构造方法(只能够获取访问权限足够的构造方法)
            // Constructor<?>[] constructors = cls.getConstructors();
            // for (Constructor<?> constructor : constructors) {
            //     System.out.println(constructor);
            // }

            // 3.4. 获取所有的构造方法(所有权限的所有构造方法)
            // Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
            // for (Constructor<?> declaredConstructor : declaredConstructors) {
            //     System.out.println(declaredConstructor);
            // }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static class Person {
        String name;
        int age;

        private Person(String name, int age) {
            System.out.println("一个Person对象被实例化了");
        }
    }
}
15.4 setAccessible

public void setAccessible(boolean flag);

设置是否可访问,但是,这个设置的意义和字⾯理解不⼀样!

参数:boolean类型的变量,代表在进⾏访问的时候,是否需要忽略权限校验。true: 代表访问的时候,不进⾏权限的校验,任何的权限都可以访问。false: 代表访问的时候进⾏权限的校验,如果访问权限⾜够,就可以访问;访问权限不够,不能访问。

15.5 属性的访问
15.5.1 属性的获取

在反射中,使⽤ Filed 描述⼀个属性。可以使⽤如下⽅法,对属性进⾏获取操作

  • getField(String fieldName) : 只能获取访问权限⾜够的属性(⽆论静态还是⾮静态)
  • getDeclaredField(String fieldName) : 获取任意权限的属性(⽆论静态还是⾮静态)
  • getFields() : 获取当前类中的所有的属性
  • getDeclaredFields() : 获取当前类中的所有权限的属性
15.5.2 属性的读写
try {
    Class cls = Class.forName("com.qf.cField.Person");
    // 1、实例化⼀个对象
    Object obj = cls.newInstance();
    Field age = cls.getField("age");
    // 给指定对象的这个属性进⾏赋值
    age.set(obj, 20);
    // 获取指定对象的这个属性值
    System.out.println(age.get(obj));
    Field name = cls.getDeclaredField("name");
    name.setAccessible(true);
    name.set(obj, "xiaobai");
    System.out.println(name.get(obj));
    Field count = cls.getDeclaredField("count");
    count.setAccessible(true);
    // 静态的属性,可以使⽤任意的对象进⾏设置和获取
    // ⼀般情况下,对于这个静态的属性,会使⽤null来设置、读取
    count.set(null, 10);
    count.get(null);
    System.out.println(obj);
    System.out.println(count.getName());
    System.out.println(count.getModifiers());
    System.out.println(count.getGenericType()); // 获取泛型
} catch (ClassNotFoundException | NoSuchFieldException |
         InstantiationException | IllegalAccessException e) {
    e.printStackTrace();
}
15.6 ⽅法的使⽤
15.6.1 ⽅法的获取
  • getMethod(String methodName, Class… paramaterTypes)
    • 获取⼀个类中的指定的⽅法
  • getDeclaredMethod(String methodName, Class… paramaterTypes)
    • 获取⼀个类中的所有的权限的指定的⽅法
  • getMethods()
    • 获取⼀个类中的可以访问到的⽅法
  • getDeclaredMethods()
    • 获取⼀个类中的所有的权限的⽅法

方法获取的案例:

/*
NoSuchFieldException            : 没有这个属性(有可能是属性名字写错了,也有可能是访问权限不够)
 */

public class FieldsDemo {
    public static class Person {
        public int publicField;
        private int privateField;
        public static int publicStaticField;
        private static int privateStaticField;
    }

    public static void main(String[] args) {
        try {
            // 1. 获取Class对象
            Class<?> cls = Class.forName("day30.reflect.FieldsDemo$Person");
            // 2. 实例化指定类的对象
            Object obj = cls.newInstance();
            // 3. 属性访问

            // 3.1. public权限的属性获取
            Field field1 = cls.getField("publicField");
            // 3.2. private权限的属性获取
            Field field2 = cls.getDeclaredField("privateField");
            // 3.3. public权限的静态属性获取
            Field field3 = cls.getField("publicStaticField");
            // 3.4. private权限的静态属性获取
            Field field4 = cls.getDeclaredField("privateStaticField");


            // 4. 获取到属性之后,进行属性的访问
            //    set(Object obj, Object value) : 将obj对象的这个属性,设置为指定的value
            //    get(Object obj)               : 获取obj对象这个属性的值

            // 4.1. public权限的非静态属性访问
            // field1.set(obj, 123);
            // Object value = field1.get(obj);
            // System.out.println(value);

            // 4.2. private权限的非静态属性访问
            // field2.setAccessible(true);
            // field2.set(obj, 123);
            // System.out.println(field2.get(obj));

            // 4.3. public权限的静态的属性访问
            //      对于静态的成员访问,其实可以使用任意的对象进行访问。但是习惯上,会使用null进行访问。
            // field3.set(null, 123);
            // System.out.println(field3.get(null));

            // 4.4. private权限的静态的属性访问
            // field4.setAccessible(true);
            // field4.set(null, 123);
            // System.out.println(field4.get(null));

        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}
15.6.2 ⽅法的调⽤
  • invoke

    try {
        // 1、获取Class对象
        Class cls = Class.forName("com.qf.dMethod.Dog");
        // 2、实例化描述的类的对象
        Object obj = cls.newInstance();
        // 3、获取⼀个⽅法
        Method method = cls.getDeclaredMethod("show", int.class, int.class);
        // 4、调⽤这个⽅法
        method.setAccessible(true);
        Object returnValue = method.invoke(obj, 10, 20);
        System.out.println(returnValue);
    } catch (ClassNotFoundException | InstantiationException |
             IllegalAccessException | NoSuchMethodException | InvocationTargetException
             e) {
        e.printStackTrace();
    }
    

方法调用的案例:

/**
 * 使用反射,调用方法
 */
public class MethodsDemo {
    public static class Person {
        public void publicMethod() {
            System.out.println("public Method");
        }
        private void privateMethod() {
            System.out.println("private Method");
        }
        public static void publicStaticMethod() {
            System.out.println("public Static Method");
        }
        private static void privateStaticMethod() {
            System.out.println("private Static Method");
        }


        public void show(int a) {
            System.out.println("show(" + a + ")");
        }

        public void show(int a, int b) {
            System.out.println("show(" + a + ", " + b + ")");
        }

        public int show(int a, int b, int c) {
            return a + b + c;
        }
    }

    public static void main(String[] args) {
        try {
            // 1. 获取Class对象
            Class<?> cls = Class.forName("day30.reflect.MethodsDemo$Person");
            // 2. 实例化对象
            Object obj = cls.newInstance();

            // 3. 方法的获取
            //    getMethod(String name, Class<?>... parameterTypes)
            //    获取指定的方法
            //    String name: 表示方法名字
            //    Class<?>... parameterTypes: 表示方法的参数

            // Method publicMethod = cls.getMethod("publicMethod");
            // Method privateMethod = cls.getDeclaredMethod("privateMethod");
            // Method publicStaticMethod = cls.getMethod("publicStaticMethod");
            // Method privateStaticMethod = cls.getDeclaredMethod("privateStaticMethod");

            Method show1 = cls.getMethod("show", int.class);
            Method show2 = cls.getMethod("show", int.class, int.class);
            Method show3 = cls.getMethod("show", int.class, int.class, int.class);

            // 调用方法  invoke(Object obj, Object... args)
            // Object obj: 调用方法的对象,如果是静态的方法,可以使用null
            // Object... args: 调用方法的参数,即方法的实参。需要和形参保持一致(数量、类型、顺序)
            // invoke方法的返回值,就是调用的方法的返回值
            show1.invoke(obj, 123);
            show2.invoke(obj, 123, 456);
            Object ret = show3.invoke(obj, 123, 456, 789);
            System.out.println(ret);


        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值