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 反射学到这里就告一段落了。等待发现更多知识点的你一起分享。
我的公众号
公众号内容描述
zone7 公众号,与您一起学习分享后端知识。本公众号涉及的知识点将会有:nodejs,python,docker,kubernetes,后端架构等
image