基础知识:Java反射机制
反射机制的原理
一般来说,如果想生成一个类的对象,那么运行这个程序的JVM会去确认这个类的class对象是否已经加载。如果尚未加载,那么JVM会根据类名查找.class文件,并将其载入,一旦这个类的class对象被载入内存,它就可以被用来创建这个类的的所有对象
另外如果遇到一个未知类型的引用,(JVM?)通常会采用强制类型转换的形式来得到开发者想要的类型引用,如果执行了错误的类型转换,则会报一个ClassCastException异常
在以上两个过程中,Class类一直都在起作用,因为Class类实例包含的是一个类的全部信息,包括方法、属性、构造器等
如果不是在启动时去创建这个Class实例,而是在运行时获得这个Class类实例,那么我们就可以动态的去加载一个类,动态的调用类的方法,已经动态的去访问一个类的属性,反射机制就是为这种场景而产生的,反射机制的出发点就在于JVM会为每个类创建一个java.lang.Class类的实例,通过该对象可以获取该类的全部信息,然后通过java.lang.reflect包下的API以达到以上所述的动态需求
三种情况会导致一个Java类被加载到JVM中
Student stu=new Student();
使用该类创建对象
System.out.println(student.count)
访问该类的静态成员
Class.forName("com.Student");
使用Class类的静态方法forName方法,动态的加载一个指定类名的类
ps…JDBC导入驱动类就是一个很常用的例子
三种得到一个类Class对象的方法
Class类forName方法返回的就是一个类的对象的引用
通过类的Class属性
Class<Student> clazz=Student.class;
通过getClass方法,这个方法是从Object类继承下来的
Student stu=new Student();
Class<Student> clazz=stu.getClass();
ps…获得类的Class对象后,就可以用这个对象创建和调用和访问这个类中的各种东西了
ps…Class类对象的方法getName、getConstructor、getMethod、newInstance
Field的用法:操作类的成员变量
通过Class类的getDeclaredField方法可以获得一个Field类对象的引用,有了这个对象的引用之后,就可以调用该对象的getXXX方法,其中XXX包括int,double,byte等获得某个成员变量
通过Field对象的使用来按照某一规则比较两个对象的大小
private static FieldReflect compareReflect(FieldReflect obj1,FieldReflect obj2)
三种获得的方式,本质上是三种获得Class对象的方式
//1
Field field=obj1.getClass().getDeclaredField("age");
//2
Field field=FieldReflect.class.getDeclaredField("age");
//3
Class clazz = Class.forName("com.FieldReflect.FieldReflect");
得到Class对象之后,就可以调用getDeclaredField方法得到Field对象的引用了
Field field = clazz.getDeclaredField("age");
得到Field对象的引用之后,就可以调用getXXX方法获得某一成员变量了
Field的用法:操作类的私有成员
首先我们写一个类,类的成员变量为私有
class PrivateFieldReflectClass {
private String name;
private int age;
public PrivateFieldReflectClass(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
然后我们在主类中去通过反射机制得到类的Field类
public class PrivateFieldReflectTest {
public static void main(String[] args) {
PrivateFieldReflectClass fieldReflect1 = new PrivateFieldReflectClass(
"Tong", 23);
// 访问私有变量
Class<PrivateFieldReflectClass> clazz = PrivateFieldReflectClass.class;
Field field = clazz.getDeclaredField("age");
// field.setAccessible(true);
System.out.println(field.getInt(fieldReflect1));
}
}
当我们试图去访问私有变量时,会发生错误java.lang.IllegalAccessException
如果我们想去访问私有变量,需要把Field的实例对象设置Accessible属性为true即可
Field的用法:覆盖toString方法,使其能够动态更改类对象的toString展示
需要覆盖类中的toString方法,主要思想就是用类对象的getDeclaredFields方法得到Field数组,循环遍历,得到相应的Name和Value
class DataObject{
private String name;
private int age;
private String description;
private String other;
public DataObject(String name, int age, String description, String other) {
super();
this.name = name;
this.age = age;
this.description = description;
this.other = other;
}
@Override
public String toString() {
StringBuffer sb=new StringBuffer();
Field[] fields=DataObject.class.getDeclaredFields();
for (Field field : fields) {
sb.append(field.getName());
sb.append("=");
sb.append(field.get(this));
sb.append("\n");
}
return sb.toString();
}
}
Method的用法:操作类的方法
Method类,代表的是类的方法,包括静态和非静态的,与Field类似,通过反射机制,具体来说就是通过Class类对象的getMethod方法可以获得Method类的对象,然后通过该对象额invoke方法,将类名作为参数传入该方法,即可完成方法的调用
首先类中定义两个方法
public class MethodReflect {
public void m1() {
System.out.println("method 1");
}
public void m2() {
System.out.println("method 2");
}
}
然后在main函数就可以利用反射机制获取并调用想要的方法了
反射机制的过程仍旧是先得到类的对象的引用,然后通过类对象的getDeclaredMethod方法得到方法类对象的引用,然后创建一个类的对象,然后利用方法对象的invoke方法将刚创建的类对象作为参数传入,注意,这里如果方法有参数,需要将参数列表也作为参数传入invoke中
Class clazz=MethodReflect.class;
Method method=clazz.getDeclaredMethod("m1");
MethodReflect methodReflect=new MethodReflect();
method.invoke(methodReflect);
Constructor类的用法:利用反射机制实例化一个类
一般来说,我们可以通过new关键字实例化一个类,也就是创建一个类的对象,而如何通过反射机制实例话一个类呢,其实不管是通过new关键字还是通过反射,都是去调用类的构造函数,如果是去调用无参的构造函数,我们可以使用Class的newInstance方法。如果是要用到有参的构造函数的话,我们就需要用到反射包下的Constructor类
首先也是要得到类的对象的引用
Class<Student> clazz1=Student.class;
//无参:通过newInstance
Student student1=clazz1.newInstance();
//有参:通过构造器
Constructor<Student> constructor=clazz1.getConstructor(String.class,int.class);
student1=constructor.newInstance("Tong",23);