Java 之反射

反射

概念:通过Classs实例获取class信息的方法称为反射

一、通过反射获取 class 的四种方法

    /**
     * 通过反射获取class实例的方法有四种:
     */
    public static void f() {

        // 方法一:直接通过一个class的静态变量class获取:
        // 1. 知道具体类的情况下可以使用:
        Class cls = String.class;

        // 方法二:如果我们有一个实例变量,可以通过该实例变量提供getClass()方法获取:
        String s = "hello";
        Class clss = s.getClass();

        // 方法三: 如果知道一个class 的完整类型,可以通过静态方法class.forName()获取
        try {
            Class cls1 = Class.forName("java.lang.String");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
         // 方式四:通过类加载器直接获取
        Object tmp = ClassLoader.getSystemClassLoader().loadClass("com.liaoxuefeng.eReflection.ReflectionDemo");
        System.out.println("方式四: " + tmp);

    }

/**
     * class 中常用的方法有:
     *
     * @param cls
     */
    public static void printClassInfo(Class cls) {
        System.out.println("cls.getName() = " + cls.getName());
        System.out.println("cls.getSimpleName() = " + cls.getSimpleName());

        if (cls.getPackage() != null) {
            System.out.println("cls.getPackage().getName() = " + cls.getPackage().getName());
        }

        System.out.println("cls.isInterface() = " + cls.isInterface());
        System.out.println("cls.isEnum() = " + cls.isEnum());
        System.out.println("cls.isArray() = " + cls.isArray());
        System.out.println("cls.isPrimitive() = " + cls.isPrimitive());

    }

 public static void main(String[] args) {

        printClassInfo("".getClass());
        printClassInfo(Runnable.class);
        printClassInfo(java.time.Month.class);
        printClassInfo(String[].class);
        printClassInfo(int.class);

   



    }

二、反射中访问字段

    /**
     * CLass 类提供了以下几种方法来获取字段:
     * 1、Field getField(name):根据字段名获取某个public的field(包括父类)
     * 2、Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
     * 3、Field getFields(): 获取所有public的field(包括父类)
     * 4、Field[] getDeclaredFields():获取当签类的所有field
     */
    public static void getFields() throws NoSuchFieldException {

        Class stdClass = Student.class;

        // 获取public 字段 "score"
        System.out.println("stdClass.getField(\"score\") = " + stdClass.getField("score"));

        // 获取student 中的"grade"
        System.out.println("stdClass.getDeclaredField(\"grade\") = " + stdClass.getDeclaredField("grade")); // class [B 表示byte[]类型

        System.out.println("stdClass.getFields() = " + Arrays.toString(stdClass.getFields()));

        System.out.println("stdClass.getDeclaredFields() = " + Arrays.toString(stdClass.getDeclaredFields()));
    }

    /**
     * 一个Field对象包含一个字段的所有信息:
     * 1、getName():返回字段名称,例如,"name"
     * 2、getType():返回字段类型,也是一个Class实例,例如:String.class;
     * 3、getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义
     */
    public static void fieldClass() throws NoSuchFieldException {

        Field field = String.class.getDeclaredField("value");
        System.out.println("field.getName() = " + field.getName());
        System.out.println("field.getType() " + field.getType());

        int m = field.getModifiers();
        System.out.println("m = " + m);
        System.out.println("Modifier.isFinal(m) = " + Modifier.isFinal(m));
        System.out.println("Modifier.isPublic(m) = " + Modifier.isPublic(m));
        System.out.println("Modifier.isProtected(m) = " + Modifier.isProtected(m));
        System.out.println("Modifier.isPrivate(m) = " + Modifier.isPrivate(m));
        System.out.println("Modifier.isStatic(m) = " + Modifier.isStatic(m));
    }

    /**
     * 获取具体值
     * 通过Field对象的get(Object)方法可以获取字段的当前值:
     */
    public static void getFieldValue() throws NoSuchFieldException, IllegalAccessException {

        Person1 p = new Person1("xiaoming");
        Class c = p.getClass();
        Field field = c.getDeclaredField("name");
        System.out.println("field = " + field);
        field.setAccessible(true); // Field.setAccessible(true)的意思是,别管这个字段是不是public,一律允许访问。

        Object value = field.get(p); // 根据实例获取字段值
        System.out.println("value = " + value);
    }

    /**
     * 设置字段值 <br/>
     * 通过Field实例既然可以获取到指定实例的字段值,自然也可以设置字段的值。<br/>
     * 设置字段值是通过Field.set(Object, Object)实现的,其中第一个Object参数是指定的实例,第二个Object参数是待修改的值。示例代码如下
     *
     * @param args
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    public static void setFieldValue() throws NoSuchFieldException, IllegalAccessException {

        Person3 person3 = new Person3("xiao ming");
        System.out.println("person3.getName() = " + person3.getName());
        Class c = person3.getClass();
        Field field = c.getDeclaredField("name");
        field.setAccessible(true);
        field.set(person3, "xiao  bai");
        System.out.println("Person3 = " + person3.getName());


    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {

        getFields();
        fieldClass();
        getFieldValue();
        setFieldValue();
    }

三、获取反射的方法

 /**
     * 获取Method的几种方法:
     * Class类提供以下几种方法获取Method:
     * 1、Method  getMethod(name,Class ,,,): 获取某个public的method(包括父类)
     * 2、Method  getMethods() : 获取所有public 的Menthod(包括父类)
     * 2、Method getDeclareMethods() : 获取当前类的所有Menthod,包含括private方法 (不包括父类)
     * 3、Method getDeclaredMethod(name.Class ...):获取当前类的某个method,包含括private方法(不包括父类)
     **/
    public static void getMethodWays() throws NoSuchMethodException {

        Class stdClass = Student4.class;
        // 获取private方法getScore,参数为String
        System.out.println("stdClass.getMethod(\"getScore\", String.class) = " + stdClass.getMethod("getScore", String.class));
        // 获取继承的public getName 方法,无参数
        System.out.println("stdClass.getMethod(\"getName\") = " + stdClass.getMethod("getName"));
        // 获取private方法getGrade,参数为int:
        System.out.println("stdClass.getDeclaredMethod(\"getGrade\", int.class) = " + stdClass.getDeclaredMethod("getGrade", int.class));
        // 获取所有public 的Menthod(包括父类)
        System.out.println("stdClass.getMethods() = " + Arrays.toString(stdClass.getMethods()));
        // 获取当前类中所有方法,包括private方法
        System.out.println("stdClass.getDeclaredMethods() = " + Arrays.toString(stdClass.getDeclaredMethods()));
    }

    /**
     * 反射 Methond 里面常用的一些方法
     * * 一个Menthod 对象可以包含一个方法的所有信息:
     * * 1、getName() 返回方法,例如:"getScore"
     * * 2、getReturnType(): 返回方法返回值类型,也是一个Class类型,例如:String.class
     * * 3、getParameterType():返回方法的参数类型,是一个Class数据,例如:{String.class,int,class}
     * * 4、getModifiers():返回方法的修饰符,它是一个int,不同的bit有不同的含义
     *
     * @throws NoSuchMethodException
     */
    public static void methodClass() throws NoSuchMethodException {
        // String对象:
        String s = "Hello world";
        // 获取String substring(int)方法,参数为int:
        Method m = String.class.getMethod("substring", int.class);

        System.out.println("m.getName() = " + m.getName());
        System.out.println("m.getReturnType() = " + m.getReturnType());
        System.out.println("m.getParameterTypes() = " + Arrays.toString(m.getParameterTypes()));
        System.out.println("m.getModifiers() = " + m.getModifiers());
    }

    /**
     * 使用反射调用方法的实际应用
     * 核心就是invoke() 方法调用
     * invoke 需要传入一个实例对象,对Method实例调用invoke就相当于调用该方法,invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。
     */
    public static void methodsScenarios() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        // 原来实现逻辑
        String s = "Hello world";
        String r = s.substring(6); // "world"
        System.out.println("r = " + r);

        // 使用反射实现逻辑 
        //  方法一:反射传一个参数
        Method m = String.class.getMethod("substring", int.class);
        // 在s对象上调用该方法并获取结果:
        // invoke 需要传入一个实例对象
        // 对Method实例调用invoke就相当于调用该方法,invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。
        String r1 = (String) m.invoke(s, 6);
        System.out.println("反射调用一个参数:" + r1);

        // 方法二:反射传多个参数
        // 获取String substring(int,int)方法,参数为int,int:
        Object o1 = String.class.getMethod("substring", int.class, int.class).invoke(s, 0, 6);
        System.out.println("反射调用多个参数:" + o1);

        // 调用public方法
        // 如果获取的方法是静态方法,则invoke方法传入的第一个参数是null,后面的可变参数要与方法参数一致
        Method method = Integer.class.getMethod("parseInt", String.class);
        Object o = method.invoke(null, "123");
        System.out.println("o = " + o);

        // 调用非public方法
        // 先使用 Class.getDeclaredMethod()获取该方法实例,然后,用Method.setAccessible(true)允许其调用
        Student4 s4 = new Student4();
        Method m1 = s4.getClass().getDeclaredMethod("getGrade", int.class);
        m1.setAccessible(true);
        m1.invoke(s4, 1);
    }


    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        getMethodWays();
        methodClass();
        methodsScenarios();


    }

四、反射调用构造方法

public class TestConstructionMethod {

    public static void f1() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        // 正常创建实例
        Person p = new Person();

        // 反射创建实例
        Person person = Person.class.newInstance();

    }

    /**
     * 使用Constuctor对象,调用,返回实例
     * <p>
     * 通过Class实例获取Constructor的方法如下:
     * 1、getConstructor(Class...):获取某个public的Constructor;
     * 2、getDeclaredConstructor(Class...):获取某个Constructor;
     * 3、getConstructors():获取所有public的Constructor;
     * 4、getDeclaredConstructors():获取所有Constructor。
     * 注意Constructor总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。
     * <p>
     * <p>
     * Constructor对象封装了构造方法的所有信息;
     * <p>
     * 通过Class实例的方法可以获取Constructor实例:getConstructor(),getConstructors(),getDeclaredConstructor(),getDeclaredConstructors();
     * <p>
     * 通过Constructor实例可以创建一个实例对象:newInstance(Object... parameters); 通过设置setAccessible(true)来访问非public构造方法。
     * <p>
     * 调用非public的Constructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败
     */
    public static void f2() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 获取构造方法 Interger(int)
        Constructor<Integer> constructor = Integer.class.getConstructor(int.class);
        // 调用构造方法
        Integer i = constructor.newInstance(123);

        System.out.println("i = " + i);
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        f1();
        f2();


    }

}

五、反射获取继承关系

public class TestExtends {

    /**
     * 使用反射获取实class的三种方式
     */
    public static void f1() throws ClassNotFoundException {
        // 方式一: 使用.class
        Class<String> stringClass = String.class;

        // 方式二: 使用getCLass()
        String s = "Hello";
        Class cls = s.getClass();

        // 方式三:class.forName(""),传入完整的Class 类名
        Class<?> aClass = Class.forName("java.lang.String");

    }

    /**
     * 获取父类类型
     * 1、使用getSuperclass()方法
     * 2、getInterfaces()只返回当前类直接实现的接口类型,并不包括其父类实现的接口类型
     * 过Class对象可以获取继承关系:
     * <p>
     * Class getSuperclass():获取父类类型;
     * Class[] getInterfaces():获取当前类实现的所有接口。
     * 通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现。
     */
    // 获取父类
    public static void f2() {

        Class<Integer> i = Integer.class;
        Class<? super Integer> s = i.getSuperclass();
        System.out.println("s = " + s);
        Class<? super Integer> s1 = s.getSuperclass();
        System.out.println("s1 = " + s1);
        System.out.println("s1.getSuperclass() = " + s1.getSuperclass());

    }

    // 获取Interface
    // getInterfaces()只返回当前类直接实现的接口类型,并不包括其父类实现的接口类型:
    // Class[] getInterfaces():获取当前类实现的所有接口。
    public static void f3() {
        Class<Integer> s = Integer.class;
        Class<?>[] interfaces = s.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println("anInterface = " + anInterface);
        }
        System.out.println(java.io.DataInputStream.class.getSuperclass());// java.io.FilterInputStream,因为DataInputStream继承自FilterInputStream
        System.out.println(java.io.Closeable.class.getSuperclass());  // null,对接口调用getSuperclass()总是返回null,获取接口的父接口要用getInterfaces()
        System.out.println(java.io.Closeable.class.getInterfaces()); // 返回继承的接口

    }

    // 继承关系
    // 通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现。
    public static void f4() {
        Class n = Integer.class;
/*      boolean isDouble = n instanceof Double; // false
        boolean isInteger = n instanceof Integer; // true
        boolean isNumber = n instanceof Number; // true
        boolean isSerializable = n instanceof java.io.Serializable; // true*/

        // 如果是两个Class实例,要判断一个向上转型是否成立,可以调用isAssignableFrom():
        // Integer i = ?
        Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer
// Number n = ?
        Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number
// Object o = ?
        Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object
// Integer i = ?
        Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer
    }

    public static void main(String[] args) throws ClassNotFoundException {
        f1();
        f2();
        f3();
        f4();


    }

}
posted @ 2024-08-22 15:24  快乐小王子帅气哥哥  阅读(7)  评论(0编辑  收藏  举报

Loading