一、什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
二、Class类
知道一个类后,我们怎么知道这个类的所有属性和方法呢?这就需要用到Class类了。
Class 类十分特殊。它和一般类一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及关键词void。
当我们编写完一个 Java 项目之后,所有的 Java 文件都会被编译成一个.class 文件,这些 Class 对象承载了这个类型的父类、接口、构造函数、方法、属性等原始信息,这些 class 文件在程序运行时会被 ClassLoader 加载到虚拟机中。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象。我们通过 new 的形式创建对象实际上就是通过这些 Class 来创建,只是这个过程对于我们是不透明的而已。
- Class类是Java反射的起源。
- 运行中的类或接口在JVM中都会有一个对应的Class对象存在,它保存了对应类或接口的类型信息。
- JVM为每种类型管理着一个独一无二的Class对象。
Class的API很多,具体大家可以查看文档,或者这个在线中文文档。
三、反射的具体使用
获得Class对象
有三种方式:
1.直接调用类的class
Class clazz = Dog.class;
2.调用对象的getClass()方法
Dog dog = new Dog("小黄");
Class clazz2 = dog.getClass();
3.使用Class类的forName(String className)方法,这里注意className要填写类的全部路径,包括包名和类名。
try {
Class clazz3 = Class.forName("com.wangjj.android_training.reflection.Dog");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
获取构造方法并调用
返回所有:
getConstructors(): 获取所有"公有的"构造方法,返回一个包含某些 Constructor对象的数组。
getDeclaredConstructors(): 获取所有的构造方法(包括私有、受保护、默认、公有)
返回单个:
getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法,参数是要获取的构造方法的参数Class
getDeclaredConstructor(Class... parameterTypes):获取单个的构造方法(包括私有、受保护、默认、公有),参数是要获取的构造方法的参数Class
调用构造方法:
Constructor-->newInstance(Object... args)
下面看一个例子:
public class Animal {
protected String name;
protected String color;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
狗类继承自动物类,有三个构造方法,分别是私有有参,公有无参,公有有参的。
public class Dog extends Animal {
private int loyalty;
private int weight;
private Dog(int loyalty){
this.loyalty = loyalty;
}
public Dog(){
}
public Dog(String name){
this.name = name;
}
public int getLoyalty() {
return loyalty;
}
public void setLoyalty(int loyalty) {
this.loyalty = loyalty;
}
public int getWeight() {
return weight;
}
private void setWeight(int weight) {
this.weight = weight;
}
}
Class clazz = Dog.class;
System.out.println("**********************所有公有构造方法*********************************");
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("*****************获取公有、无参的构造方法*******************************");
try {
Constructor con = clazz.getConstructor(null);
System.out.println(con);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
System.out.println("******************获取私有构造方法,并调用*******************************");
try {
Constructor con = clazz.getDeclaredConstructor(int.class);
//调用构造方法
con.setAccessible(true);// 忽略掉访问修饰符
Dog dog1 = (Dog) con.newInstance(100);
System.out.println(con);
System.out.println("忠诚度:"+ dog1.getLoyalty());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
打印结果如下:
**********************所有公有构造方法*********************************
public com.wangjj.android_training.reflection.Dog(java.lang.String)
public com.wangjj.android_training.reflection.Dog()
************所有的构造方法(包括:私有、受保护、默认、公有)***************
public com.wangjj.android_training.reflection.Dog(java.lang.String)
public com.wangjj.android_training.reflection.Dog()
private com.wangjj.android_training.reflection.Dog(int)
*****************获取公有、无参的构造方法*******************************
public com.wangjj.android_training.reflection.Dog()
******************获取私有构造方法,并调用*******************************
private com.wangjj.android_training.reflection.Dog(int)
忠诚度:100
获取成员变量并调用
返回所有:
Field[] getFields():获取所有的"公有字段",包括继承的公有字段
Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有,不包括继承的字段
返回单个:
public Field getField(String fieldName):获取某个"公有的"字段;
public Field getDeclaredField(String fieldName):获取某个字段
设置字段的值:
Field --> public void set(Object obj,Object value):
参数说明:
1.obj:要设置的字段所在的对象;
2.value:要为字段设置的值;
例如:
Field field = clazz.getDeclaredField("loyalty");
field.setAccessible(true);
field.set(dog1, 60);
System.out.println("忠诚度:"+ dog1.getLoyalty()); // 忠诚度:60
获取成员方法并调用
返回所有:
public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
public Method[] getDeclaredMethods():获取所有的成员方法,包括:私有、受保护、默认、公有(不包括继承的)
返回单个:
public Method getMethod(String name,Class<?>... parameterTypes)
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
参数:
name : 方法名;
Class ... : 形参的Class类型对象
调用方法:
Method --> public Object invoke(Object obj,Object... args):
参数说明:
obj: 要调用方法的对象;
args:调用方式时所传递的实参;
如下:
Method method = clazz.getDeclaredMethod("setWeight", int.class);
method.setAccessible(true);
method.invoke(mydog, 2);
System.out.println(mydog.getWeight()); // 2
四、反射的用途
反射最重要的用途就是开发各种通用框架。很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。
当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。
反射还可以用来越过泛型检查。