Java 反射机制知识点

注:本文参考自:https://blog.youkuaiyun.com/stevenhu_223/article/details/9286121

我们知道,类和类的成员变量及方法都是要求有权限控制的(public、protected、private);而当类中的信息封装为私有时,外部对该类中私有的信息是没有访问权限的,也就是说当该类里的内容信息均受private权限控制时,外部想要获取和处理该类里的私有信息是几乎不可能的;但是,有时候这种需求是有的,而当我们非得需要去动用别的类里封装的私有信息时,java的反射机制就起到了非常关键的作用了;

java反射机制的实现主要由三个类来主导:它们分别是 Class、Field、Method;

1. Class

java在编译和运行时,会将需要被编译和运行的所有类加载进类加载器,每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到java虚拟机中的这个类,进而在运行时对这个被访问的类进行信息的获取和处理(当然,不管被访问的这个类里的信息是否私有的);通俗的讲,Class对象间接代表了它对应的类,通过这个Class对象,我们就可以大刀阔斧地去执行反射机制的实现;

获取Class对象主要有三种方式:

  • 1) 调用Class类的forName(String name)静态方法,参数name为Class对应的类的全名(包括包名);

    比如我们要创建Gesture这个类对应的Class对象:Class<Gesture> mClass = Class.forName("android.gesture.Gesture");

    android.gestureGesture的包名,Class<Gesture>中的Gesture表示得到的是Gesture类型对应的Class对象,<>中的Gesture也可为通配符?表示,如:Class<?>mClass = Class.forName("android.gesture.Gesture");

  • 2)调用类的class属性得到类对应的Class对象。如:Class<?>mClass = Gesture.class; (一般建议用这种方式得到Class对象)

  • 3)调用类的实例化对象的getClass()方法。特别说明的是,getClass()是java类的始祖Object类的方法,所以,所有java对象都可以调用该方法;如mGestureGesture类型的对象,Class<?> mClass = mGesture.getClass()得到的是Gesture类对应的Class对象

那么在得到对应类的Class对象对应后,我们就可以通过该Class对象得到它所对应的类的一些信息,比如该类的构造函数、成员(属性)、方法(函数);

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 修饰,只要是目标类中声明的构造函数均可
getField(String arg)Field得到目标类中指定的某个 public 属性对应的 Field 对象
getDeclaredField(String arg)Field同 getField,但不局限于 public 修饰,只要是目标类中声明的属性均可
getFields()Field[]得到目标类(包括父类)中所有的 public 属性对应的 Field 对象
getDeclaredFields()Field[]同 getFields(),但不局限于 public 修饰的属性,只能获取自身类中的(不包括父类)
getMethod(String arg0, Class<?>… arg1)Method得到目标类(包括父类)中指定的某个 public 方法对应的 Method 对象
getDeclaredMethod(String arg0, Class<?>… arg1)Method同 getMethod,但不局限于 public 修饰的方法,只能获取自身类中的(不包括父类)
getMethods()Method[]得到目标类(包括父类)中所有的 public 方法对应的 Method 对象
getDeclaredMethods()Method[]同 getMethods(),但不局限于 public 修饰的方法,只能获取自身类中的(不包括父类)
getEnclosingClass()Class得到目标类所在的外围类的 Class 对象
getGenericInterfaces()Type[]得到目标类实现的接口对应的 Type 对象
getGenericSuperclass()Type得到目标类继承的父类所对应的 Type 对象
getInterfaces()Class<?>[]得到目标类实现的接口所对应的 Class 对象
getSuperclass()Class得到目标类继承的父类所对应的 Class 对象
isMemberClass()boolean目标类是否为成员类
isAnonymousClass()boolean目标类是否为匿名类

2. Field

我们知道一般类里包含有属性(成员)和方法(函数),竟然Class是描述类的信息,那么类其它部分应该会对应有描述它们的东东,而Field类型的对象就是描述Class对象对应类的属性(包括public、protected、private属性);一个Field对象对应描述一个类的属性;

通过上文对Class的介绍,我们知道Class提供了四种接口函数可以得到对应属性的Field:

  • 1)getField(String name):返回类型为Fieldname为类中的属性名,得到的是描述类中的一个public属性对应的Field对象;如Field mField =mClass.getField("mGestureID") 得到的是Gesture类中的一个public属性mGestureID对应的Field对象;
  • 2) getFields():返回类型为Field类型数组,得到的是描述类中的所有public属性对应的所有Field对象;
  • 3) getDeclaredField(String name):同getField(String name),只不过得到的Field对象描述的不只是public属性,还包括protected、private属性,也是说只要是在类中声明的属性;
  • 4)getDeclaredFields():得到的是描述类中声明的所有属性(public、protected、private)对应的Field对象;

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

方法名返回类型接口功能实现
setAccessible(boolean flag)void参数为 true,只要是在类中声明的目标属性均可访问,为 false,只有 public 目标属性可访问
set(Object object, Object value)void给目标属性设置值(private、protected 属性均不能访问,但可以通过先调用 setAccessible(true) 实现访问),第一个参数为目标属性所在类的对象,第二个参数为传入的值
get(Object object)Object得到目标属性的值(private、protected 属性均不能访问,但可以通过调用 setAccessible(true) 实现访问),参数为目标属性所在类的对象
setBoolean(Object object, boolean value)void同 set(Object object, Object value),只不过操作的数据类型为 boolean
getBoolean(Object object)boolean同 get(Object object),只不过得到的数据类型为 boolean
setByte(Object object, boolean value)void同 set(Object object, Object value),只不过操作的数据类型为 byte
getByte(Object object)byte同 get(Object object),只不过得到的数据类型为 byte
setShort(Object object, boolean value)void同 set(Object object, Object value),只不过操作的数据类型为 short
getShort(Object object)short同 get(Object object),只不过得到的数据类型为 short
setInt(Object object, boolean value)void同 set(Object object, Object value),只不过操作的数据类型为 int
getInt(Object object)int同 get(Object object),只不过得到的数据类型为 int
setLong(Object object, boolean value)void同 set(Object object, Object value),只不过操作的数据类型为 long
getLong(Object object)long同 get(Object object),只不过得到的数据类型为 long
setFloat(Object object, boolean value)void同 set(Object object, Object value),只不过操作的数据类型为 float
getFloat(Object object)float同 get(Object object),只不过得到的数据类型为 float
setDouble(Object object, boolean value)void同 set(Object object, Object value),只不过操作的数据类型为 double
getDouble(Object object)double同 get(Object object),只不过得到的数据类型为 double
setChar(Object object, boolean value)void同 set(Object object, Object value),只不过操作的数据类型为 char
getChar(Object object)char同 get(Object object),只不过得到的数据类型为 char
getName()String得到目标属性的名字,不局限于 private 修饰符,只要是类中声明的属性
getGenericType()Type得到目标属性的类型,不局限于 private 修饰符
getType()Class<?>得到目标属性的类型对应的 Class 对象
getModifiers()int得到目标属性的修饰符值(private 为 2、protected 为 4、public 为 1、static 为 8、final 为 16)
getDeclaringClass()Class<?>得到目标属性所在类对应的 Class 对象

下面就以一个示例代码来验证Field表中的函数接口的实现,如下:

1) FieldBeReflected.java(被反射的类)

package com.stevenhu.field;

public class FieldBeReflected {
    private static String name;
    private static String name1;
    private boolean mBoolean = true;
    private final byte mByte = 111;
    private static final short mShort = 22;
    protected static int mInt;
    protected static long mLong;
    protected static float mFloat;
    protected static double mDouble;
    public static char mChar;
}

2)ReflectField.java(执行反射调用的类)

package com.stevenhu.reflection.test;

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

import com.stevenhu.field.FieldBeReflected;

public class ReflectField {

    public static void main(String[] args) {
        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");

            // 设置私有字段可访问
            fName.setAccessible(true);

            // 设置和获取字段的值
            fName.set(clazz, "reflection");
            String name = (String) fName.get(clazz);
            System.out.println(name);

            fBoolean.setAccessible(true);
            boolean mBoolean = fBoolean.getBoolean(clazz.newInstance());
            System.out.println(mBoolean);

            fByte.setAccessible(true);
            byte mByte = fByte.getByte(clazz.newInstance());
            System.out.println(mByte);

            fShort.setAccessible(true);
            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);
            fLong.setLong(clazz, 2222);
            Long mLong = fLong.getLong(clazz);
            System.out.println(mLong);

            fFloat.setAccessible(true);
            fFloat.setFloat(clazz, 22222);
            float mFloat = fFloat.getFloat(clazz);
            System.out.println(mFloat);

            fDouble.setAccessible(true);
            fDouble.setDouble(clazz, 222.222);
            double mDouble = fDouble.getDouble(clazz);
            System.out.println(mDouble);

            fChar.setAccessible(true);
            fChar.setChar(clazz, 'a');
            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<?> clazz1 = fName.getType();
            System.out.println(clazz1);

            Class<?> clazz2 = fName.getDeclaringClass();
            System.out.println(clazz2);

            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();
        }
    }
}

3. Method

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

Class对象也提供了四种接口函数得到对应方法的Method对象,如下:

  • 1)getMethod(String name, Class<?>... parameterTypes):返回类型为Method,第一个参数name为类中的方法名,第二个参数为可变参数,传入的是参数类型对应的Class对象(方法的参数可能为多个的情况);该函数得到的是描述类中的一个public方法对应的Method对象;
  • 2)getMethods():返回类型为Method类型数组,得到的是描述类中的所有public方法对应的Method对象;
  • 3)Method getDeclaredMethod(String name, Class<?>... parameterTypes):同getMethod(String name, Class<?>... parameterTypes),只不过得到的Method对象描述的不只是public方法, 还包括
    protected、private方法,也是说只要是在类中声明的方法;
    4)getDeclaredMethods():得到的是描述类中声明的所有方法(public、protected、private)对应的Method对象;

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

方法名返回类型接口功能实现
setAccessible(boolean flag)void参数为 true,只要是在类中声明的目标方法均可访问,为 false,只有 public 目标方法可访问
invoke(Object receiver, Object… args)Object动态执行调用目标方法,第一个参数为 Class 对象或者类的实例,第二个参数为可变实参的对象(多个实参)
getDeclaringClass()Class<?>得到目标方法所在类对应的 Class 对象
getExceptionTypes()Class<?>得到目标方法抛出的异常类型对应的 Class 对象
getGenericExceptionTypes()Type[]得到目标方法抛出的异常类型对应的 Type 对象
getReturnType()Class<?>得到目标方法返回类型对应的 Class 对象
getGenericReturnType()Type得到目标方法返回类型对应的 Type 对象
getParameterTypes()Class<?>[]得到目标方法各参数类型对应的 Class 对象
getGenericParameterTypes()Type[]得到目标方法各参数类型对应的 Type 对象
getModifiers()int得到目标方法修饰符的值
getName()String得到目标方法的名字

下面就以一个示例代码来验证Method表中的函数接口的实现,如下:

1)MethodBeReflected.java(被反射的类)

package com.stevenhu.method;

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() throws Exception, NoSuchMethodException, SecurityException {
        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;
    }
}

2)ReflectMethod.java(执行反射的类)

package com.stevenhu.reflection.test;

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

import com.stevenhu.method.MethodBeReflected;

public class ReflectMethod {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        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", null);
            nameMethod.setAccessible(true);
            // 调用 getmName 方法,得到 mName 的值
            String name1 = (String) nameMethod.invoke(clazz.newInstance(), null);
            System.out.println(name1);

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

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

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

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

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

            weightMethod = clazz.getDeclaredMethod("getmWeight", null);
            weightMethod.setAccessible(true);
            float weight2 = (Float) weightMethod.invoke(clazz.newInstance(), null);
            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 e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

补充:

  • class.getInterfaces() 返回当前类直接实现的接口类数组,没有泛型参数, Class<?>[]
  • class.getGenericInterfaces() 返回当前类直接实现的接口类数组,包含泛型参数,Type[]

可参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

川峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值