JAVA反射机制
我们知道JAVA中一个比较重要的特性就是反射机制,那么反射究竟是什么,我们又能用反射做些什么呢?今天我们就来一探究竟。
反射的概念
反射:将类的各个组成部分封装为其他对象,这就是反射机制。
反射的作用:
1、在程序运行时操作这些对象;
应用示例:IDEA中定义一个list对象,在使用“list.”时,会弹出对应的方法,这个就是利用反射,在IDEA运行时,就能知道对象list的成员方法;
2、可以解耦,提高程序的可扩展性;
讲到这里,我们可能还很迷惑,JAVA的反射机制究竟是什么?在进行下面的讲解前,我们先了解一下JAVA代码在计算机中的三个阶段,来帮助我们理解反射机制。
JAVA程序在计算机中的三个阶段
1、第一个阶段:源代码阶段也就是我们写的JAVA代码,通过编译器编译成字节码文件这个过程,我们写的类含有成员变量,构造方法,成员方法;
2、第二个阶段:类加载器将我们的字节码文件加载到内存,生成Class类对象(Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建)。反射定义中的”封装为其他对象“,这里的对象便是指,成员变量封装为Filed对象,构造方法封装为Constructor对象以及成员方法封装为Method对象;
3、第三个阶段:该阶段就可以创建对象和调用对象里的方法。通过new Student()创建对象。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。简单来说就是得到Class对象后反向获取Student对象的各种信息。
获取Class对象的方式:
对应java代码在程序中的三个阶段:
1、Class.forName("全类名"):将字节码文件加载进内存,返回Class对象;
使用场景:多用于配置文件,将全类名配置到配置文件中,通过读取配置文件加载类;
2、类名.class :通过类名的属性Class获取;
使用场景:多用于参数传递;
3、对象.getClass():通过类的getClass方法获取,getClass()方法在Object类中定义,所有的类都有该方法;
使用场景:已经得到类对象实例的情况;
示例代码如下:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void goToSchool(){
System.out.println("go to school");
}
public static void main(String[] args) throws Exception {
//1、Class.forName("全类名")
Class aClass = Class.forName("com.sean.Student");
System.out.println(aClass);
//2、类名.class
Class bClass = Student.class;
System.out.println(bClass);
//3、类对象.getClass()
Student student = new Student();
Class cClass = student.getClass();
System.out.println(cClass);
}
}
PS:同一个字节码文件(*.class),再一次程序运行中,只会被加载一次,三种方式获取的Class类对象是同一个,可以通过比较aClass,bClass和cClass来验证;
Class对象的使用
下面我们就简单列出几个常用的功能,其他功能使用可以参见JAVA的API文档中的Class类。
这里我们创建一个Student类,便于下面我们使用反射操作Student类对象。
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void goToSchool(){
System.out.println("go to school");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
1、获取成员变量对象(Filed),操作类的成员变量
方法 | 用途 |
---|---|
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
getDeclaredFields() | 获得所有属性对象 |
Filed类具体的用法可以查看JAVA的API文档,这里我们只演示了利用获取到的Filed对象获取以及设置成员变量的值。
public static void main(String[] args) throws Exception {
Class bClass = Student.class;
//获取Student对象的所有public属性成员变量Filed对象
Field[] fields = bClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
//获取Student对象中name属性成员变量,因为name为私有,这里会抛java.lang.NoSuchFieldException异常
Field name = bClass.getField("name");
System.out.println(name);
//获取Student对象中name属性成员变量Filed对象
Field name1 = bClass.getDeclaredField("name");
System.out.println(name1);
/**
* setAccessible(true)暴力反射
* 这里由于name属性是私有成员变量,不加这一句,
* 后面的获取以及设置成员变量会抛java.lang.IllegalAccessException异常
*/
name1.setAccessible(true);
//获取name成员变量的值
Student student = new Student();
Object o = name1.get(student);
System.out.println(o);
//设置name成员变量的值
name1.set(student,"zhangsan");
System.out.println(student.getName());
}
2、获取构造方法对象(Constructor),操作类的构造函数
方法 | 用途 |
---|---|
getConstructor(Class...> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class...> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
Constructor类具体的用法可以查看JAVA的API文档,这里我们只演示了利用获取到的Constructor对象创建Student对象。
public static void main(String[] args) throws Exception {
Class bClass = Student.class;
//获取Student对象的无参构造函数Constructor对象
Constructor constructor = bClass.getConstructor();
//利用获取到的Constructor对象创建Student对象
Object o = constructor.newInstance();
System.out.println(o);
}
3、获取成员方法对象(Method),操作类的成员方法
方法 | 用途 |
---|---|
getMethod(String name, Class...> parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethod(String name, Class...> parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
Method类具体的用法可以查看JAVA的API文档,这里我们只演示了利用获取到的Method对象执行执行Student类的成员方法。
public static void main(String[] args) throws Exception {
Class bClass = Student.class;
//获取Student对象的goToSchool成员方法Method对象
Method goToSchool = bClass.getMethod("goToSchool");
//利用获取到的Method对象执行Student类中的成员方法
Student student = new Student();
goToSchool.invoke(student);
}
4、获取类名
方法 | 用途 |
---|---|
getName() | 获得该类的类名 |
public static void main(String[] args) throws Exception {
Class bClass = Student.class;
//获取Student对象的类名
String name = bClass.getName();
System.out.println(name);
}
总结
JAVA中有很多开源的框架(框架是半成品的软件,可以在框架的基础上进行编码),使用这些框架能极大的简化我们的开发过程。而反射是框架的灵魂。当然不了解反射我们也可以使用这些框架进行开发,但了解反射的机制,能让我们知其然,知其所以然,在使用这些框架的时候更加得心应手。