反射知识复习总结

为什么要用反射机制?

既然java的对象可以通过new关键字创建,那么为什么还要引入反射呢?new关键字的使用前提是你必须知道这个类的名字,如果不知道此时的类名,那么就无法通过硬编码实现类的创建。此时反射就起到很大的作用。反射的目的就是为了扩展未知的应用

简单使用回顾

反射机制的实现主要是由三个类来主导:他们分别是:ClassFieldMethod

Class

java在编译和运行时候,会将需要被编译和运行的所有类加载到类加载器中,每个类被加载之后,系统就会为该类生成对应的Class对象,通过该Class对象就可以访问到JVM中的这个类,之后再运行的时候就可以通过这个类进行信息的获取和处理。

反射得到对象的三种方式

首先准备一个java类:IUser.java

package reflex;

import lombok.ToString;

/**
 * @author zhangshuai
 */
@ToString
public class IUser {
    private Long uId;
    private String uName;
    private String password;
}

复制代码
  • 1、调用Class类的forName(String name)静态方法
package reflex;

/**
 * @author zhangshuai
 */
public class Main {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("reflex.IUser");
        System.out.println("该类的包路径:" + aClass.getPackage());
        System.out.println("该类的toString方法:" + aClass.getMethod("toString"));
    }
}

---------------------------
该类的包路径:package reflex
该类的toString方法:public java.lang.String reflex.IUser.toString()
复制代码

这种方式通过寻找包路径下的IUser类进行反射,参数name必须包含name的全路径(包括包名)。其中reflex为包名;Class<?>表示通过泛型接收IUser类型对应的Class对象。 场景: JDBC的注册驱动的过程:Class.forName("com.mysql.jdbc.Driver");

源码分析:

/**
     * Returns the {@code Class} object associated with the class or
     * interface with the given string name.  Invoking this method is
     * equivalent to:
     *
     * <blockquote>
     *  {@code Class.forName(className, true, currentLoader)}
     * </blockquote>
     *
     * where {@code currentLoader} denotes the defining class loader of
     * the current class.
     *
     * <p> For example, the following code fragment returns the
     * runtime {@code Class} descriptor for the class named
     * {@code java.lang.Thread}:
     *
     * <blockquote>
     *   {@code Class t = Class.forName("java.lang.Thread")}
     * </blockquote>
     * <p>
     * A call to {@code forName("X")} causes the class named
     * {@code X} to be initialized.
     *
     * @param      className   the fully qualified name of the desired class.
     * @return     the {@code Class} object for the class with the
     *             specified name.
     * @exception LinkageError if the linkage fails
     * @exception ExceptionInInitializerError if the initialization provoked
     *            by this method fails
     * @exception ClassNotFoundException if the class cannot be located
     */
    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        // jdk提供的一个native方法,通过该方法可以得到调用者的链路
        Class<?> caller = Reflection.getCallerClass();
        // 在对系统加载程序访问检查进行安全检查后调用。
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
复制代码

方法说明:返回与具有给定字符串名称的类或接口关联的Class对象。 调用这个方法相当于:Class.forName(className, true, currentLoader)其中currentLoader表示当前类的定义类加载器。例如,以下代码片段返回名为java.lang.Thread的类的运行时Class描述符:Class t = Class.forName("java.lang.Thread")forName("X")调用导致名为X的类被初始化

  • 2、调用类的class属性得到类对应的Class对象

JVM将使用类装载器,将类装入内存(前提是:类还没有装入内存),不做类的初始化工作,返回Class的对象。也就是说,通过这种方式得到的对象由于没有初始化,在这个过程中不会调用对象中的任何代码或者代码块。

Class<?> mClass = IUser.class;
复制代码
  • 3、调用类的实例化对象的getClass()方法。

getClass()java类的始祖Object类的方法,所以,所有java对象都可以调用该方法;如Class<?> mClass = new IUser().getClass()得到的是IUser类对应的Class对象

Class<?> aClass3 = new IUser().getClass();
复制代码

考虑到获取类的扩展性,推荐使用第一种方式

Class类提供的相关接口介绍:(注:在表中,Class对象对应的类,姑且称为目标类)

接口 返回类型 接口功能实现
getPackage() Package 得到目标类的包名对应的Package对象
getCanonicalName() String 得到目标类的全名(包名+类名)
getName() String 同getCanonicalName()
getClassLoader() ClassLoader 得到加载目标类的ClassLoader对象
getClasses() Class<?>[] 得到目标类中的所有的public内部类以及public内部接口所对应的Class对象
getDeclaredClasses() Class<?>[] 同getClasses(),但不局限于public修饰,只要是目标类中声明的内部类和接口均可
getConstructors() Constructor<?>[] 得到目标类的所有public构造函数对应的Constructor对象
getDeclaredConstructors() Constructor<?>[] 同getConstructors(),但不局限于public修饰,只要是目标类中声明的构造函数均可
getEnclosingClass() Class 得到目标类所在的外围类的Class对象
getInterfaces() Class<?>[] 得到目标类实现的接口所对应的Class对象
getSuperclass() Class 得到目标类继承的父类所对应的Class对象

Field

一个类包括属性方法Class描述的是类的信息,而Field描述的Class类对应类的出现包括public、protected、private属性。

接口方法

准备基础类:FieldBeReflected.java

package reflex;

/**
 * @author zhangshuai
 */
public class FieldBeReflected {
    private static String name;
    protected static double mDouble;
    public static char mChar;
    int mInt;
}

复制代码
  • getField(String name):返回类型为Field,name是类中的属性名,得到的描述类中一个public属性对应的Field对象;
package reflex;

import java.lang.reflect.Field;

/**
 * @author zhangshuai
 */
public class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        // 拿到类
        Class<?> mClass = Class.forName("reflex.FieldBeReflected");
        // 拿属性
//        Field name = mClass.getField("name"); // NoSuchFieldException
//        Field mDouble = mClass.getField("mDouble"); // NoSuchFieldException
//        Field mInt = mClass.getField("mInt");  // NoSuchFieldException
        Field mChar = mClass.getField("mChar");
        System.out.println(mChar); //public static char reflex.FieldBeReflected.mChar
    }
}

复制代码

由此可见,getField(String name)方法只能得到public修饰的属性

源码分析:


    /**
     * Returns a {@code Field} object that reflects the specified public member
     * field of the class or interface represented by this {@code Class}
     * object. The {@code name} parameter is a {@code String} specifying the
     * simple name of the desired field.
     *
     * <p> The field to be reflected is determined by the algorithm that
     * follows.  Let C be the class or interface represented by this object:
     *
     * <OL>
     * <LI> If C declares a public field with the name specified, that is the
     *      field to be reflected.</LI>
     * <LI> If no field was found in step 1 above, this algorithm is applied
     *      recursively to each direct superinterface of C. The direct
     *      superinterfaces are searched in the order they were declared.</LI>
     * <LI> If no field was found in steps 1 and 2 above, and C has a
     *      superclass S, then this algorithm is invoked recursively upon S.
     *      If C has no superclass, then a {@code NoSuchFieldException}
     *      is thrown.</LI>
     * </OL>
     *
     * <p> If this {@code Class} object represents an array type, then this
     * method does not find the {@code length} field of the array type.
     *
     * @param name the field name
     * @return the {@code Field} object of this class specified by
     *         {@code name}
     * @throws NoSuchFieldException if a field with the specified name is
     *         not found.
     * @throws NullPointerException if {@code name} is {@code null}
     * @throws SecurityException
     *         If a security manager, <i>s</i>, is present and
     *         the caller's class loader is not the same as or an
     *         ancestor of the class loader for the current class and
     *         invocation of {@link SecurityManager#checkPackageAccess
     *         s.checkPackageAccess()} denies access to the package
     *         of this class.
     *
     * @since JDK1.1
     * @jls 8.2 Class Members
     * @jls 8.3 Field Declarations
     */
     
    @CallerSensitive
    public Field getField(String name)
        throws NoSuchFieldException, SecurityException {
        // 进行安全校验,检查该操作是否有权限,如果没有则抛 SecurityException
        // 默认策略:允许所有客户端使用普通的java访问控制进行访问
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        // 在所有Field的public属性中进行搜索
        Field field = getField0(name);
        if (field == null) {
            throw new NoSuchFieldException(name);
        }
        return field;
    }

复制代码

返回一个Field对象,该对象反映了此Class对象表示的类或接口的指定公共成员字段。 name参数是一个String指定所需字段的简单名称。要反射的场由以下算法确定。 令 C 为该对象所代表的类或接口:如果 C 声明了一个具有指定名称的公共字段,那就是要反映的字段。如果在上面的步骤 1 中没有找到任何字段则该算法将递归应用于 C 的每个直接超接口。直接超接口按照它们声明的顺序进行搜索。如果在上面的第 1 步和第 2 步中没有找到字段,并且 C 有一个超类 S,那么这个算法将在 S 上递归调用。如果 C 没有超类,则抛出NoSuchFieldException 。如果此Class对象表示数组类型,则此方法找不到数组类型的length字段。抛出:NoSuchFieldException – 如果未找到具有指定名称的字段。NullPointerException – 如果name为null;SecurityException – 如果存在安全管理器s并且调用者的类加载器与当前类的类加载器不同或不是该类加载器的祖先,并且调用s.checkPackageAccess()拒绝访问此类的包

  • getFields():返回类型为Field类型数组,得到的是描述类中的所有public属性对应的所有Field对象
package reflex;

import java.lang.reflect.Field;

/**
 * @author zhangshuai
 */
public class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        // 拿到类
        Class<?> mClass = Class.forName("reflex.FieldBeReflected");
        // 拿属性
        Field[] fields = mClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }
}

-------------------
public static char reflex.FieldBeReflected.mChar
复制代码

getField(String name)方法一样,getFields()方法只能拿到一个public修饰的Field类型的数组

源码分析:

 /**
     * Returns an array containing {@code Field} objects reflecting all
     * the accessible public fields of the class or interface represented by
     * this {@code Class} object.
     *
     * <p> If this {@code Class} object represents a class or interface with no
     * no accessible public fields, then this method returns an array of length
     * 0.
     *
     * <p> If this {@code Class} object represents a class, then this method
     * returns the public fields of the class and of all its superclasses.
     *
     * <p> If this {@code Class} object represents an interface, then this
     * method returns the fields of the interface and of all its
     * superinterfaces.
     *
     * <p> If this {@code Class} object represents an array type, a primitive
     * type, or void, then this method returns an array of length 0.
     *
     * <p> The elements in the returned array are not sorted and are not in any
     * particular order.
     *
     * @return the array of {@code Field} objects representing the
     *         public fields
     * @throws SecurityException
     *         If a security manager, <i>s</i>, is present and
     *         the caller's class loader is not the same as or an
     *         ancestor of the class loader for the current class and
     *         invocation of {@link SecurityManager#checkPackageAccess
     *         s.checkPackageAccess()} denies access to the package
     *         of this class.
     *
     * @since JDK1.1
     * @jls 8.2 Class Members
     * @jls 8.3 Field Declarations
     */
    @CallerSensitive
    public Field[] getFields() throws SecurityException {
        // 进行安全校验,检查该操作是否有权限,如果没有则抛 SecurityException
        // 默认策略:允许所有客户端使用普通的java访问控制进行访问
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        // 将这个class类中可复制的Field属性通过ReflectionFactory的copyField方法进行复制
        return copyFields(privateGetPublicFields(null));
    }
复制代码

首先这个方法是从jdk1.1版本开始的,意思就是说,这个方法会返回一个Field对象的一个数组,这些对象反映了此Class对象表示的接口的类或者接口所有可访问的公共字段。如果这个Class对象没有可访问公共字段的类或接口,则返回长度为0的数组;如果Class对象表示一个类,则返回该类及其所有超类的公共字段;如果Class对象表示一个接口,则返回该接口及其所有超接口的公共字段;如果Class对象是一个数组类型、基本类型或void,则返回长度为0的数组;返回数组类型中没有特定的排序,并且该方法会抛出SecurityException

  • getDeclaredField(String name):同getField(String name),只不过得到的Field对象描述的不只是public属性,还包括protected、private属性,也是说只要是在类中声明的属性;
package reflex;

import java.lang.reflect.Field;

/**
 * @author zhangshuai
 */
public class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        // 拿到类
        Class<?> mClass = Class.forName("reflex.FieldBeReflected");
        // 拿属性
        Field name = mClass.getDeclaredField("name");
        Field mDouble = mClass.getDeclaredField("mDouble");
        Field mInt = mClass.getDeclaredField("mInt");
        Field mChar = mClass.getDeclaredField("mChar");
        System.out.println(name);
        System.out.println(mDouble);
        System.out.println(mInt);
        System.out.println(mChar); 
    }
}

------------
private static java.lang.String reflex.FieldBeReflected.name
protected static double reflex.FieldBeReflected.mDouble
int reflex.FieldBeReflected.mInt
public static char reflex.FieldBeReflected.mChar
复制代码

该方法能够拿到该Class类的所有属性

源码分析:


    /**
     * Returns a {@code Field} object that reflects the specified declared
     * field of the class or interface represented by this {@code Class}
     * object. The {@code name} parameter is a {@code String} that specifies
     * the simple name of the desired field.
     *
     * <p> If this {@code Class} object represents an array type, then this
     * method does not find the {@code length} field of the array type.
     *
     * @param name the name of the field
     * @return  the {@code Field} object for the specified field in this
     *          class
     * @throws  NoSuchFieldException if a field with the specified name is
     *          not found.
     * @throws  NullPointerException if {@code name} is {@code null}
     * @throws  SecurityException
     *          If a security manager, <i>s</i>, is present and any of the
     *          following conditions is met:
     *
     *          <ul>
     *
     *          <li> the caller's class loader is not the same as the
     *          class loader of this class and invocation of
     *          {@link SecurityManager#checkPermission
     *          s.checkPermission} method with
     *          {@code RuntimePermission("accessDeclaredMembers")}
     *          denies access to the declared field
     *
     *          <li> the caller's class loader is not the same as or an
     *          ancestor of the class loader for the current class and
     *          invocation of {@link SecurityManager#checkPackageAccess
     *          s.checkPackageAccess()} denies access to the package
     *          of this class
     *
     *          </ul>
     *
     * @since JDK1.1
     * @jls 8.2 Class Members
     * @jls 8.3 Field Declarations
     */
    @CallerSensitive
    public Field getDeclaredField(String name)
        throws NoSuchFieldException, SecurityException {
        // 进行安全校验,与getField(String name)方法的不同在于,该方法不能拿到父类或者父接口的属性
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Field field = searchFields(privateGetDeclaredFields(false), name);
        if (field == null) {
            throw new NoSuchFieldException(name);
        }
        return field;
    }
复制代码

返回一个Field对象,该对象反映了此Class对象表示的类或接口的指定声明字段。 name参数是一个String ,用于指定所需字段的简单名称。如果此Class对象表示数组类型,则此方法找不到数组类型的length字段。该方法同样会抛出NoSuchFieldException、NullPointerException、SecurityException

  • getDeclaredFields():getFields(),得到的是描述类中声明的所有属性(public、protected、private)对应的Field对象;
package reflex;

import java.lang.reflect.Field;

/**
 * @author zhangshuai
 */
public class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        // 拿到类
        Class<?> mClass = Class.forName("reflex.FieldBeReflected");
        // 拿属性
        Field[] fields = mClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }
}

------
private static java.lang.String reflex.FieldBeReflected.name
protected static double reflex.FieldBeReflected.mDouble
public static char reflex.FieldBeReflected.mChar
int reflex.FieldBeReflected.mInt
复制代码

返回所有本类的字段,不包括超类

源码分析:



    /**
     * Returns an array of {@code Field} objects reflecting all the fields
     * declared by the class or interface represented by this
     * {@code Class} object. This includes public, protected, default
     * (package) access, and private fields, but excludes inherited fields.
     *
     * <p> If this {@code Class} object represents a class or interface with no
     * declared fields, then this method returns an array of length 0.
     *
     * <p> If this {@code Class} object represents an array type, a primitive
     * type, or void, then this method returns an array of length 0.
     *
     * <p> The elements in the returned array are not sorted and are not in any
     * particular order.
     *
     * @return  the array of {@code Field} objects representing all the
     *          declared fields of this class
     * @throws  SecurityException
     *          If a security manager, <i>s</i>, is present and any of the
     *          following conditions is met:
     *
     *          <ul>
     *
     *          <li> the caller's class loader is not the same as the
     *          class loader of this class and invocation of
     *          {@link SecurityManager#checkPermission
     *          s.checkPermission} method with
     *          {@code RuntimePermission("accessDeclaredMembers")}
     *          denies access to the declared fields within this class
     *
     *          <li> the caller's class loader is not the same as or an
     *          ancestor of the class loader for the current class and
     *          invocation of {@link SecurityManager#checkPackageAccess
     *          s.checkPackageAccess()} denies access to the package
     *          of this class
     *
     *          </ul>
     *
     * @since JDK1.1
     * @jls 8.2 Class Members
     * @jls 8.3 Field Declarations
     */
    @CallerSensitive
    public Field[] getDeclaredFields() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyFields(privateGetDeclaredFields(false));
    }
复制代码

与getFields()方法类似,唯一的区别在于该方法不能接收父类或者超类的字段:返回一个Field对象数组,这些对象反映了由此Class对象表示的类或接口声明的所有字段。 这包括公共、受保护、默认(包)访问和私有字段,但不包括继承的字段。如果此Class对象表示没有声明字段的类或接口,则此方法返回长度为 0 的数组。如果此Class对象表示数组类型、基本类型或 void,则此方法返回长度为 0 的数组。返回的数组中的元素没有排序,也没有任何特定的顺序

Feild使用

package reflex;

import java.lang.reflect.Field;
import java.lang.reflect.Type;


public class ReflectField {

    public static void main(String[] args) {
        /*  1.Class<?> clazz = Class.forName("com.stevenhu.field.FieldBeReflected");
         *  2.Class<?> clazz = new FieldBeReflected().getClass();
         */
        Class<?> clazz = FieldBeReflected.class;

        try {
            Field fName = clazz.getDeclaredField("name");
            Field fBoolean = clazz.getDeclaredField("mBoolean");
            Field fByte = clazz.getDeclaredField("mByte");
            Field fShort = clazz.getDeclaredField("mShort");
            Field fInt = clazz.getDeclaredField("mInt");
            Field fLong = clazz.getDeclaredField("mLong");
            Field fFloat = clazz.getDeclaredField("mFloat");
            Field fDouble = clazz.getDeclaredField("mDouble");
            Field fChar = clazz.getDeclaredField("mChar");

            /*
             * 参数为true,只要是在类中声明的目标属性均可访问,
             * 为false,则被反射类和反射类在同一个包中时,private目标属性不可访问,
             * 不在同一个包中时,private、protected目标属性均不可访问
             */
            fName.setAccessible(true);

            /*给目标属性设置值(private属性不能访问,但可以通过先调用setAccessible(true)实现访问),
             * 由于ReflectField类中的name属性是静态的(static),所以方法的第一个实参传入的是
             * 目标属性所在类对应的Class对象clazz,也可以是类的实例clazz.newInstance();
             */
            fName.set(clazz, "reflection");
            //得到目标属性的值(private属性不能访问,但可以通过调用setAccessible(true)实现访问)
            String name = (String) fName.get(clazz);
            System.out.println(name);

            fBoolean.setAccessible(true);
            /*得到目标属性的布尔值,由于ReflectField类中的mBoolean属性是非静态的,
             * 所以此处的传入实参为目标属性所在类的实例clazz.newInstance()
             */
            boolean mBoolean = fBoolean.getBoolean(clazz.newInstance());
            System.out.println(mBoolean);

            fByte.setAccessible(true);
            //得到目标属性的Byte类型值
            byte mByte = fByte.getByte(clazz.newInstance());
            System.out.println(mByte);

            fShort.setAccessible(true);
            //得到目标属性的short整型值
            short mShort = fShort.getShort(clazz);
            System.out.println(mShort);

            fInt.setAccessible(true);
            //给目标属性设置整型值
            fInt.setInt(clazz, 222);
            //得到目标属性的整型值
            int mInt = fInt.getInt(clazz);
            System.out.println(mInt);

            fLong.setAccessible(true);
            //给目标属性设置Long整型值
            fLong.setLong(clazz, 2222);
            //得到目标属性的Long整型值
            Long mLong = fLong.getLong(clazz);
            System.out.println(mLong);

            fFloat.setAccessible(true);
            //给目标属性设置float类型值
            fFloat.setFloat(clazz, 22222);
            //得到目标属性的float类型值
            float mFloat = fFloat.getFloat(clazz);
            System.out.println(mFloat);

            fDouble.setAccessible(true);
            //给目标属性设置double类型值
            fDouble.setDouble(clazz, 222.222);
            //得到目标属性的double类型值
            double mDouble = fDouble.getDouble(clazz);
            System.out.println(mDouble);

            //给目标属性设置字符值(private、protected属性不能访问)
            fChar.setChar(clazz, 'a');
            //得到目标属性的字符值(private、protected属性不能访问)
            char mChar = fChar.getChar(clazz);
            System.out.println(mChar);

            //目标属性的名字,不局限于修饰符,只要是类中声明的属性
            String name1 = fName.getName();
            System.out.println(name1);
            //目标属性的类型,不局限于修饰符
            Type type = fName.getGenericType();
            System.out.println(type);
            //目标属性的类型对应的Class对象
            Class<?> clazz1 = fName.getType();
            System.out.println(clazz1);
            //目标属性所在类对应的Class对象
            Class<?> clazz2 = fName.getDeclaringClass();
            System.out.println(clazz2);
            //目标属性的权限修饰值(private为2、protected为4、public为1)
            int modifier = fName.getModifiers();
            int modifier1 = fByte.getModifiers();
            int modifier2 = fShort.getModifiers();
            System.out.println(modifier);
            System.out.println(modifier1);
            System.out.println(modifier2);

            System.out.println(fName.isAccessible());
            System.out.println(fChar.isAccessible());

        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }

}
复制代码

Method

同Field一样,一个Method对象描述一个类的方法

接口方法

  • getMethod(String name, Class<?>... parameterTypes):返回类型为Method,第一个参数name为类中的方法名,第二个参数为可变参数,传入的是参数类型对应的Class对象(方法的参数可能为多个的情况);该函数得到的是描述类中的一个public方法对应的Method对象;

  • getMethods():返回类型为Method类型数组,得到的是描述类中的所有public方法对应的Method对象;

  • Method getDeclaredMethod(String name, Class<?>... parameterTypes):同getMethod(String name, Class<?>… parameterTypes),只不过得到的Method对象描述的不只是public方法, 还包括protected、private方法,也是说只要是在类中声明的方法;

  • getDeclaredMethods():getMethods(),得到的是描述类中声明的所有方法(public、protected、private)对应的FMethod对象;

Method使用

package reflex;

public class MethodBeReflected {

    private static String mName;
    private static int mAge;
    private static float mWeight;

    private String getmName() {
        return mName;
    }

    protected void setmName(String mName) {
        this.mName = mName;
    }

    protected static int getmAge() {
        return mAge;
    }

    private static void setmAge(int age) {
        mAge = age;
    }

    private float getmWeight() {
        return mWeight;
    }

    protected void setmWeight(float mWeight) {
        this.mWeight = mWeight;
    }

    private void setAllValues(String name, int age, float weight) {
        this.mName = name;
        this.mAge = age;
        this.mWeight = weight;
    }
}
复制代码
package reflex;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;


public class ReflectMethod {
    public static void main(String[] args) {

        Class<?> clazz = MethodBeReflected.class;

        try {
            //第一个实参为方法名,第二个实参为方法参数类型对应的class对象
            Method nameMethod = clazz.getDeclaredMethod("setmName", String.class);
            Method ageMethod = clazz.getDeclaredMethod("setmAge", int.class);
            Method weightMethod = clazz.getDeclaredMethod("setmWeight", float.class);
            Method allValuesMethod = clazz.getDeclaredMethod("setAllValues", String.class, int.class, float.class);

            nameMethod.setAccessible(true);
            //调用setmName方法,给ReflectMethod类中的属性mName赋值为"stevenhu"
            nameMethod.invoke(clazz.newInstance(), "lisa");
            nameMethod = clazz.getDeclaredMethod("getmName");
            nameMethod.setAccessible(true);
            //调用getmName方法,得到mName的值
            String name1 = (String) nameMethod.invoke(clazz.newInstance());
            System.out.println(name1);

            ageMethod.setAccessible(true);
            /*调用setmAge方法设置年龄,由于该方法是静态方法,所以第一个实参可为类的Class对象clazz,
             * 也可以是类的对象clazz.newInstance();
             */
            ageMethod.invoke(clazz, 21);
            ageMethod = clazz.getDeclaredMethod("getmAge");
            ageMethod.setAccessible(true);
            //调用getmAge方法,得到之前设置的年龄
            int age1 = (Integer) ageMethod.invoke(clazz);
            System.out.println(age1);

            weightMethod.setAccessible(true);
            //调用setmWeight方法,设置体重
            weightMethod.invoke(clazz.newInstance(), 50.5F);
            weightMethod = clazz.getDeclaredMethod("getmWeight");
            weightMethod.setAccessible(true);
            //调用getmWeight方法,得到之前设置的体龄
            float weight1 = (Float) weightMethod.invoke(clazz.newInstance());
            System.out.println(weight1);

            allValuesMethod.setAccessible(true);
            /*调用ReflectMethod的setAllValues方法赋值
             * 注:此处不能直接传入实参63.5;浮点型必须创建Float对象
             * 整型和字符串可创建Integer、String对象,也可以不创建
             */
            allValuesMethod.invoke(clazz.newInstance(), "stevenhu", 23, 63.5F);

            nameMethod = clazz.getDeclaredMethod("getmName");
            nameMethod.setAccessible(true);
            String name2 = (String) nameMethod.invoke(clazz.newInstance());
            System.out.println(name2);

            ageMethod = clazz.getDeclaredMethod("getmAge");
            ageMethod.setAccessible(true);
            int age2 = (Integer) ageMethod.invoke(clazz.newInstance());
            System.out.println(age2);

            weightMethod = clazz.getDeclaredMethod("getmWeight");
            weightMethod.setAccessible(true);
            float weight2 = (Float) weightMethod.invoke(clazz.newInstance());
            System.out.println(weight2);

            //得到目标方法所在类对应的Class对象
            Class<?> clazz1 = weightMethod.getDeclaringClass();

            //得到目标方法抛出的异常类型对应的Class对象
            Class<?>[] clazzs1 = weightMethod.getExceptionTypes();
            for (Class<?> cl : clazzs1) {
                System.out.println(cl);
            }
            //得到目标方法抛出的异常类型对应的Type对象
            Type[] types1 = weightMethod.getGenericExceptionTypes();
            //得到目标方法返回类型对应的Class对象
            Class<?> clazz2 = nameMethod.getReturnType();
            //得到目标方法返回类型对应的Type对象
            Type type = nameMethod.getGenericReturnType();
            //得到目标方法各参数类型对应的Class对象
            Class<?>[] clazzs2 = allValuesMethod.getParameterTypes();
            //得到目标方法各参数类型对应的Type对象
            Type[] types2 = allValuesMethod.getGenericParameterTypes();
            //得到目标方法修饰符的值
            int modifier = ageMethod.getModifiers();
            System.out.println(modifier);
            //得到目标方法的名字
            String methodName = nameMethod.getName();
            System.out.println(nameMethod.isVarArgs());
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
            e.printStackTrace();
        }
    }

}
复制代码

常见面试题

1、什么是反射

概念:在运行状态中,对于任意一个类都能知道这个类的所有的属性和方法;并且对于任意一个对象都能调用它的任意一个方法,这种动态获取信息以及动态调用对象的方法的功能称为java的反射机制;理解:根据字节码文件获得类、字段、方法的信息,并且可以创建实例,调用方法的技术。

2、反射有哪些场景

JDBC原始代码注册驱动,Spring的Bean的加载过程,Spring的AOP,Tomcat加载Servlet的过程;

3、反射和类的关系

在程序运行状态中,对任意一个类(指的是.class文件),都能够知道这个类的所有的属性和方法。

4、反射和类对象的关系

反射对于某个类的一个对象,都能够调用它的任意一个方法和属性。

5、反射的优缺点

反射极大地提高了应用程序的扩展性;反射之前是通过多态提高程序的扩展性,将子类对象传递给父类引用。比如:Animal dog = new Dog();这种方式有一个很大的缺点就是必须通过new关键字来建立子类对象,子类对象必须写死在代码中,而通过反射技术省略调new子类对象的一步直接将子类对象的类名以字符串的形式传递给反射技术的框架,并交由反射技术来创建这个字符串代表的累的实例。

6、创建对象的两种方法

  • Class 对象的 newInstance()使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求该 Class 对象对应的类有默认的空构造器。

  • 调用 Constructor 对象的 newInstance()先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()方法来创建 Class 对象对应类的实例,通过这种方法可以选定构造方法创建实例。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享