1. 简介
本篇文章不探讨反射的实现机制或者说实现原理,仅仅从使用的角度去讲解我们常用的一些API接口,方便自己以后需要使用时信手拈来,同时也方便广大博友能够快速了解API的使用。
什么是反射?
反射是java语言的一个特性,它允许一个java的类获取他所有的成员变量和方法并且显示出来,这样说起来有些抽象,例如我们可以通过反射去实例化一个对象,并不非得使用new这个关键字来实例化,同时我们也可以通过反射知道Java类中有哪些变量、哪些方法等等,这些特性在C或者C++语言中是不存在的。
反射中,我们会常用到三个类,分别为Class、Method和Field,通过类名相信大家已经能够知道大概意思,分别代表类、方法和变量,本篇文章将会用大量的Demo来讲解这三个类中的部分常用重要API的作用,Demo中用到的一个实体类如下:
public class Person {
private String name;
public Person() {
System.out.println("Person()...");
}
public Person(String name) {
this.name = name;
System.out.println("Person(String name)...");
}
private void privateMethod(){
System.out.println("privateMethod-->name="+this.name);
}
protected void protectedMethod(){
System.out.println("protectedMethod-->name="+this.name);
}
void defaultMethod(){
System.out.println("defaultMethod-->name="+this.name);
}
public void setName(String name) {
this.name = name;
System.out.println("setName()...");
}
public String getName() {
System.out.println("getName()...");
return name;
}
@Override
public String toString() {
return "name: "+this.name;
}
}
2. Class 类
2.1 获取Class实例
// 方法一 forName函数
Class c= Class.forName("Person");
// 方法二 getClass()函数
Person person = new Person();
Class c = person.getClass();
// 方法三 使用类字面常量
Class c=Person.class;
2.2 通过反射来实例化对象
// 方法一
try {
Class c = Person.class;
Person person = (Person) c.newInstance();
System.out.println(person instanceof Person); // true
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 方法二
try {
Class c = Class.forName("reflect.demo.Person");
Person person = (Person) c.newInstance();
System.out.println(person instanceof Person); // true
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
以上方式都是调用的Person类无参构造,如果需要调用Person(String name),改如何呢?
// 首先获取到Person类中的有参构造,通过构造函数实例化对象
try {
Class c = Person.class;
Constructor constructor = c.getConstructor(new Class[]{String.class});
// 调用的是有参构造
Person person = (Person) constructor.newInstance("jack");
System.out.println(person.toString()); // name: jack
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
2.3 获取类中所有的方法对象
try {
Class c = Person.class;
// 方式一
Method [] methods = c.getMethods(); // 获取所有public方法,且包括父类的public方法
for (Method method:methods) {
System.out.print(method.getName()+", ");
}
System.out.println("");
// 方式二
methods = c.getDeclaredMethods(); // 获取所有方法,包括私有、共有等,但是只定义在该类中
for (Method method:methods) {
System.out.print(method.getName()+", ");
}
} catch (Exception e) {
e.printStackTrace();
}
输出:
toString, getName, setName, wait, wait, wait, equals, hashCode, getClass, notify, notifyAll,
toString, getName, setName, protectedMethod, privateMethod, defaultMethod,
// 其他
Method getMethod(String name, Class<?>... parameterTypes) // 获取某个方法对象,只能是public的
Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 获取获取某个方法对象,只能是当前类的,可以是private、protected、default、public等
2.4 获取类中字段对象
try {
Class c = Person.class;
Field[] fields = c.getFields(); // 同 getMethods
for (Field field : fields) {
System.out.print(field.getName() + ", ");
}
System.out.println("5***************");
fields = c.getDeclaredFields(); // 同 getDeclaredMethods
for (Field field : fields) {
System.out.print(field.getName() + ", ");
}
} catch (Exception e) {
e.printStackTrace();
}
2.5 其他方法
- getName : 返回类名,包括包名
- getSimpleName: 返回类名,不包含包名
- cast(Object obj):将obj类型对象转化为当前class类型对象
Class cc = Person.class;
// getName: reflect.demo.Person getSimpleName: Person
System.out.println("getName: "+cc.getName()+" getSimpleName: "+cc.getSimpleName());
3. Method类
- getName() 获取方法名
- invoke(Object obj, Object... args) 执行obj中该方法
- Parameter[] getParameters() 获取方法中的参数
- setAccessible(boolean flag) 设置访问权限
Person per = new Person();
Class c = Person.class;
Method method;
try {
method = c.getDeclaredMethod("setName", String.class);
System.out.println(method.getName()); // 输出: setName
method.invoke(per, "hello reflect"); // 调用了 setName方法
System.out.println(per); // 输出 name: hello reflect
Parameter[] parameters = method.getParameters();
for (Parameter parameter:parameters) {
System.out.print(parameter.getName()+", "); // 输出:arg0
}
} catch (Exception e) {
e.printStackTrace();
}
// 如何反射调用私有方法
try {
method = c.getDeclaredMethod("privateMethod");
method.setAccessible(true); // 私有方法必须设置访问权限为true
method.invoke(per); // 调用privateMethod方法
} catch (Exception e) {
e.printStackTrace();
}
4. Field API
- String getName() 获取变量名
- setAccessible(boolean flag) 设置访问权限
- Object get(Object obj) 获取obj对象中改变量值
- set(Object obj, Object value) 将obj对象中的改变量值设置为value
Class c = Person.class;
Person person = new Person("lvjie");
try {
// Field field = c.getField("name"); // 只能获取到共有变量,包括父类
Field field = c.getDeclaredField("name"); // 可以获取当前类中定义的任何变量,包括private、public等
System.out.println(field.getName()); // name
field.setAccessible(true); // 私有变量必须设置访问权限为true
System.out.println(field.get(person)); // 获取该对象中的变量值,私有变量需要添加访问权限
System.out.println(person); // name: lvjie
field.set(person, "jack"); // 对该对象的变量设置值, 私有变量需要添加访问权限
System.out.println(person); // name: jack
} catch (Exception e) {
e.printStackTrace();
}
以上相关API在使用反射过程中是经常遇见的,当然还有其他相关API,例如获取该类、方法和变量上使用到的注解等等,这些在注解的使用上会做讲解。反射配合注解,会产生巨大的功能,目前许多开源库也是基于这两点技术来实现,例如Android中的 EventBus、ButterKnife、DBFlow等等。
如果对Java注解感兴趣,请看下一篇Java注解全面总结