Java 反射基础(上)

时间:2022-04-27
本文章向大家介绍Java 反射基础(上),主要内容包括我理解的 Java 反射机制、实战演练、Step2. 使用反射获取类的信息、结尾、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

写在前面:

投稿作者是一位非常爱刨根问底的人,爱钻研技术,和他多次交流过,喜欢他那股不达目的誓不罢休的那股劲。今天他投稿的两篇文章讲的是java反射的基础内容,如果你的技术非常好,也懂,这次可以不用看了,主要推荐给基础的初学者和基础不牢的人看的。

在之前的文章中,有读者反馈我博客的内容有点长,如果要说长,可能是我习惯于思考,写博客的过程中会带着问题去写,解释我为什么这么想,我是怎么解决的,而不是上来直接说解决方案。可是,这样做无疑会增加篇幅,以后,我会在表达清楚的前提下尽可能减少文字描述(但不会省略代码),也算是对自己精简表达能力的锻炼。下面,直接进入正题。

本博文主要记录我学习 Java 反射(reflect)的一点心得,在了解反射之前,您应该先了解 Java 中的 Class 类,如果您不是很了解,可以查看我的另一篇博客《浅谈 Java 的 Class 类》。

我理解的 Java 反射机制

参考了许多博文,总结了以下个人观点,如您有更好的看法还望指导:

Java 反射机制在程序运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称为 java 的反射机制。

反射机制很重要的一点就是“运行时”,其使得我们可以在程序运行时加载、探索以及使用编译期间完全未知的 .class 文件。换句话说,Java 程序可以加载一个运行时才得知名称的 .class 文件,然后获悉其完整构造,并生成其对象实体、或对其 fields(变量)设值、或调用其 methods(方法)。

不知道上面的理论您能否明白,反正刚接触反射时看的我一脸懵比,后来写了几个例子之后:哦~~原来是这个意思!如果您不明白理论没关系,建议您先往下看例子,之后再回来看相信您就能明白了。

实战演练

Step1. 编写测试类

为使得测试结果更加明显,我首先定义了一个 ParentClass 类(默认继承自 Object 类),然后定义一个继承自 ParentClass 类的 SonClass 类,如下所示。可以看到测试类中变量以及方法的访问权限不是很规范,是为了更明显得查看测试结果而故意设置的,实际项目中不提倡这么写。

FatherClass.java

public class FatherClass {
    public String mFatherName;
    public int mFatherAge;

    public void printFatherMsg(){}
}

SonClass.java

public class SonClass extends FatherClass{

    private String mSonName;
    protected int mSonAge;
    public String mSonBirthday;

    public void printSonMsg(){
        System.out.println("Son Msg - name : "
                + mSonName + "; age : " + mSonAge);
    }

    private void setSonName(String name){
        mSonName = name;
    }

    private void setSonAge(int age){
        mSonAge = age;
    }

    private int getSonAge(){
        return mSonAge;
    }

    private String getSonName(){
        return mSonName;
    }
}

Step2. 使用反射获取类的信息

获取类的所有变量

/**
 * 通过反射获取类的所有变量
 */
private static void printFields(){
    //1.获取并输出类的名称
    Class mClass = SonClass.class;
    System.out.println("类的名称:" + mClass.getName());

    //2.1 获取所有 public 访问权限的变量
    // 包括本类声明的和从父类继承的
    Field[] fields = mClass.getFields();

    //2.2 获取所有本类声明的变量(不问访问权限)
    //Field[] fields = mClass.getDeclaredFields();

    //3. 遍历变量并输出变量信息
    for (Field field :
            fields) {
        //获取访问权限并输出
        int modifiers = field.getModifiers();
        System.out.print(Modifier.toString(modifiers) + " ");
        //输出变量的类型及变量名
        System.out.println(field.getType().getName()
                 + " " + field.getName());
    }
}

以上代码注释很详细,就不再解释了。需要注意的是 2.1的 getFields() 与 2.2的 getDeclaredFields() 之间的区别(见注释),下面分别看一下两种情况下的输出。看之前强调一下 SonClass extends FatherClass extends Object

  • 调用 getFields() 方法,输出 SonClass 类以及其所继承的父类( 包括 FatherClassObject ) 的 public 方法。注:Object 类中没有成员变量,所以没有输出。 类的名称:obj.SonClass public java.lang.String mSonBirthday public java.lang.String mFatherName public int mFatherAge
  • 调用 getDeclaredFields() , 输出 SonClass 类的所有成员变量,不问访问权限。 类的名称:obj.SonClass private java.lang.String mSonName protected int mSonAge public java.lang.String mSonBirthday

获取类的所有方法信息

/**
 * 通过反射获取类的所有方法
 */
private static void printMethods(){
    //1.获取并输出类的名称
    Class mClass = SonClass.class;
    System.out.println("类的名称:" + mClass.getName());

    //2.1 获取所有 public 访问权限的方法
    //包括自己声明和从父类继承的
    Method[] mMethods = mClass.getMethods();

    //2.2 获取所有本类的的方法(不问访问权限)
    //Method[] mMethods = mClass.getDeclaredMethods();

    //3.遍历所有方法
    for (Method method :
            mMethods) {
        //获取并输出方法的访问权限(Modifiers:修饰符)
        int modifiers = method.getModifiers();
        System.out.print(Modifier.toString(modifiers) + " ");
        //获取并输出方法的返回值类型
        Class returnType = method.getReturnType();
        System.out.print(returnType.getName() + " "
                + method.getName() + "( ");
        //获取并输出方法的所有参数
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter:
             parameters) {
            System.out.print(parameter.getType().getName()
                    + " " + parameter.getName() + ",");
        }
        //获取并输出方法抛出的异常
        Class[] exceptionTypes = method.getExceptionTypes();
        if (exceptionTypes.length == 0){
            System.out.println(" )");
        }
        else {
            for (Class c : exceptionTypes) {
                System.out.println(" ) throws "
                        + c.getName());
            }
        }
    }
}

同获取变量信息一样,需要注意2.1与2.2的区别,下面看一下打印输出:

  • 调用 getMethods() 方法 获取 SonClass 类所有 public 访问权限的方法,包括从父类继承的。打印信息中,printSonMsg() 方法来自 SonClass 类, printFatherMsg() 来自 FatherClass 类,其余方法来自 Object 类。 类的名称:obj.SonClass public void printSonMsg( ) public void printFatherMsg( ) public final void wait( ) throws java.lang.InterruptedException public final void wait( long arg0,int arg1, ) throws java.lang.InterruptedException public final native void wait( long arg0, ) throws java.lang.InterruptedException public boolean equals( java.lang.Object arg0, ) public java.lang.String toString( ) public native int hashCode( ) public final native java.lang.Class getClass( ) public final native void notify( ) public final native void notifyAll( )
  • 调用 getDeclaredMethods() 方法 打印信息中,输出的都是 SonClass 类的方法,不问访问权限。 类的名称:obj.SonClass private int getSonAge( ) private void setSonAge( int arg0, ) public void printSonMsg( ) private void setSonName( java.lang.String arg0, ) private java.lang.String getSonName( )

结尾

这次的内容主要是代码,文字部分在能表达清楚意思的前提下已经压缩到最少了,希望您看的开心哈!如果文中有任何不妥或错误之处,还请不吝赐教,十分感谢!

本博文演示了如何借助反射获取类的所有变量方法,当然还有其他许多获取类信息的API(比如得到类的包名、构造函数、接口等),我就不一一列举了,使用方法都差不多。下次将向大家分享如何利用反射在运行时刻修改类成员变量和方法的访问权限,一起期待吧 :)

特别声明:本文经原作者授权转载,未经原作者允许,不得转载其他平台。