Java 反射机制

Java 反射机制也是进阶的知识点,在 Android 中有挺多地方用到,虽然我们自己写代码会比较少用到,但是这对阅读一些源码是挺有帮助的。就比如:RemoteView 中的一系列 set 方法,其最终就是通过反射机制去实现的。

什么是 Java 反射机制

Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
或许看完上面这段话之后还有点蒙蔽,我举个栗子:

User user = new User();

假如我已经有个 User 类,那么我可以利用反射机制,来知道 User 类里面有什么成员变量和方法,并且我能够调用这些变量和方法。

反射机制的简单运用

首先,大概浏览一下下面的代码。然后,思考一个问题:
我们写 Java 代码这么久了,我们新建的类(例如上面的:Reflect 类,User 类)都是谁的对象?
初次看到这个问题,我是蒙蔽的。搜寻答案之后,是 java.lang.Class 的对象,而我们的 Reflect 类和 User 类也有另外一种称呼,就是类的类型(class type)。而这就跟我们要说的反射机制跟这个 class type 有关系。下面这段代码就是反射的简单运用,有注释在旁边,应该也挺容易理解的。

package com.zone;
public class Reflect {
    public static void main(String[] args) {
//      类是 java.lang.Class 的对象
        User user = new User();
//      方法一
        Class userAlias1 = User.class;//获取类的类型
        User user1;
        try {
            user1 = (User)userAlias1.newInstance();//通过类的类型获取实例
            user1.printName("create by userAlias1");//通过实例调用方法
        } catch (InstantiationException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (IllegalAccessException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
//      方法二
        Class userAlias2 = user.getClass();//获取类的类型
        try {
            User user2 = (User)userAlias2.newInstance();//通过类的类型获取实例
            user2.printName("create by userAlias2");//通过实例调用方法
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
//      方法三
        Class userAlias3 = null;
        try {
            userAlias3 = Class.forName("com.zone.User");//获取类的类型
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            User user3 = (User)userAlias3.newInstance();//通过类的类型获取实例
            user3.printName("create by userAlias3");//通过实例调用方法
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
class User{
    public void printName(String message){
        System.out.println("My name is zone. "+ message);
    }
}

那么我们稍微解释一下方法一:

//      方法一
        Class userAlias1 = User.class;//获取类的类型
        User user1;
        try {
            user1 = (User)userAlias1.newInstance();//通过类的类型获取实例
            user1.printName("create by userAlias1");
        } catch (InstantiationException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (IllegalAccessException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

userAlias1 相当于 User 类的别名,userAlias1 还是指向 User 类的,因此我们可以通过它来获取实例。当然看成为别名是我个人的看法,只是为了更好理解。

user1 = (User)userAlias1.newInstance();//通过类的类型获取

获取方法的名称,参数类型,返回类型

    public static void printClassMessage(Object obj){
        Class c = obj.getClass();
        System.out.println("类的名称是:"+c.getName());
//      获取父类和本类的方法
        Method[] ms=c.getMethods();
//      获取本类的方法
//      c.getDeclaredMethods();
        for (int i = 0; i < ms.length; i++) {
//          获取方法的返回值的类的类型(class type)
            Class returnType = ms[i].getReturnType();
            System.out.print(returnType.getSimpleName()+" ");
//          获取方法的名字
            System.out.print(ms[i].getName()+"(");//这一句是为了打印出来更美观
//          获取参数的类的类型
            Class[] paramTypes = ms[i].getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++) {
                if (j==paramTypes.length-1) {
                    System.out.print(paramTypes[j].getName());
                }else {
                    System.out.print(paramTypes[j].getName()+",");
                }
            }
            System.out.println(");");//这一句是为了打印出来更美观

        }
    }

这里写了一个方法,通过传入一个实例,来获取类的名字,类的方法,方法的参数和返回值。来看看将 user 实例传进去,会有什么打印出来:

User user = new User();
printClassMessage(user);

以下为控制台打印出的数据:

类的名称是:com.zone.User
void printName(java.lang.String);
void wait();
void wait(long,int);
void wait(long);
boolean equals(java.lang.Object);
String toString();
int hashCode();
Class getClass();
void notify();
void notifyAll();

可以看到第一个方法是我们自己写的方法 void printName(java.lang.String);

获取成员变量的类型和名称

    private static void printFieldsMessage(Object obj) {
        Class c = obj.getClass();
        Field[] fs = c.getFields();//获取 public 变量
//      Field[] fs = c.getDeclaredFields();//获取所有成员变量
        for (Field field : fs) {
            Class fieldType = field.getType();
            String typeName  = fieldType.getName();//获取成员变量的类型属性的 class type
            String fieldName = field.getName();//获取成员变量的名称
            System.out.println(typeName+fieldName);
        }
    }

下面为 User 类添加如下代码:

public String name = "zone";

然后传入 user 实例运行,运行结果如下:

java.lang.String  name

获取构造函数信息

    private static void printConstructorMessage(Object obj){
        Class c  = obj.getClass();
//      Constructor[] cs = c.getConstructors();//获取 public 修饰的构造方法
        Constructor[] cs = c.getDeclaredConstructors();//获取所有的构造方法
        for (Constructor constructor : cs) {
            System.out.print(constructor.getName()+"(");//输出构造函数名
            Class[] paramTypes = constructor.getParameterTypes();//获取构造函数参数类型
            for (Class class1 : paramTypes) {
                System.out.print(class1.getName()+",");//输出参数名
            }
            System.out.println(")");
        }
    }

通过反射调用方法

接下来为 User 类添加如下方法:

    public void printStr(String str1,String str2){
        System.out.println(str1+" && "+str2);
    }

然后通过反射来调用方法:

        User user = new User();
        Class c1 = user.getClass();
        try {
            Method m = c1.getDeclaredMethod("printStr", String.class,String.class);//获取 public 方法
//          Method m2 = c1.getMethod("printStr", String.class,String.class);
//          Method m3 = c1.getDeclaredMethod("printStr", new Class[]{String.class,String.class});//获取自己声明的方法
            m.invoke(user, "first","second");//通过反射调用方法
        } catch (NoSuchMethodException e2) {
            e2.printStackTrace();
        } catch (SecurityException e2) {
            e2.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

浏览上面代码之后,先来说说 getDeclaredMethod() 的参数,第一个参数是要调用的方法的名字。从第二个参数开始,是要放反射方法的参数的类的类型,这有两种方式。第一种: new 一个类的类型(class type) 的数组,数组里面放反射的方法的参数的类的类型(class type)。第二种: 直接开始放反射方法的参数的类的类型(class type)。文字描述起来有点绕,拿上面的例子解说一下:

Method m3 = c1.getDeclaredMethod("printStr", new Class[]{String.class,String.class});

“printStr”是我要反射的方法。String.class,String.class,是 printStr(String str1,String str2) 方法的参数的类的类型。

java 反射学到这里就告一段落了。等待发现更多知识点的你一起分享。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值