文章目录
一、什么是反射?
Java的反射是指程序在运行期间可以拿到一个对象的所有信息。
1、反射的原理
了解反射之前,我们需要先大致了解一下java程序的编译运行过程。 程序员写下代码之后,这些源代码由javac (java编译器)编译成字节码(class)文件,字节码文件再由jvm(java虚拟机)加载进内存,然后运行程序。
在jvm第一次读取到一种class类型文件并将其加载进堆内存时,就会为其创建一个 Class类型的对象,并关联起来。
(注意:这里的Class和class(类)不同,Class是一个名为Class的class(类),就像我们自己创建的类:Person类、Student类…只不过Class类只能由jvm才能创建,我们无法手动创建)。
比如加载一个String类:
Class cls=new Class(String);
而这个Class对象中保存了class的所有信息,包括类名、包名、父类、实现的接口、所有的方法、字段等。因此,如果获取了Class对象,就可以通过这个Class对象获取到该对象对应的class的所有信息。这种通过Class实例获取class信息的方法称为反射。
2、反射的好处
(1)可以在程序运行过程中,操作对象
(2)可以解耦,提高程序的灵活性和可扩展性
3、反射的缺点
(1)性能问题:使用反射,基本上是一种解释性操作,用于字段和方法的接入时要远慢于直接代码。因此反射主要用于对灵活性和扩展性的要求较高的系统框架上,普通程序不建议使用。
(2)使用反射会模糊程序的逻辑:程序员希望在源代码中看到程序的逻辑,而反射绕过了源代码,因而带来了维护问题,反射比相应的直接代码更复杂。
二、获取Class对象的三种方式
1、Class.forName(“全类名”)
Class cls1 = Class.forName("cn._16_reflect.Person");
2、类名.Class
Class cls2 = Person.class;
3、对象.getClass( )
Class cls3 = p.getClass();
注意:同一个字节码文件在一次程序运行过程中,字节码文件只会被加载一次,不论用那种获取方式得到的字节码文件都是同一个。
论证如下:
public static void main(String[] args) throws ClassNotFoundException {
//1.Class.forName("全类名")
Class cls1 = Class.forName("cn._16_reflect.Person");
System.out.println(cls1);
//2.类名.Class
Class cls2 = Person.class;
System.out.println(cls2);
//3.对象.getClass()
Person p=new Person();
Class cls3 = p.getClass();
System.out.println(cls3);
System.out.println(cls1==cls2);//true
System.out.println(cls2==cls3);//true
}
三、Class对象的功能
1、获取成员字段
//1.Field[] getField(String name);获取成员变量指定Field
Field a = cls.getField("a");
//2.Field[] getFields();获取所有public修饰的成员变量
Field[] fields = cls.getFields();
//3.Field getDeclaeredField(String name);获取成员变量指定Field,不考虑修饰符
Field a = cls.getDeclaredField("a");
//4.Field[] getDeclaredFields();获取所有成员变量,不考虑修饰符
Field[] fields = cls.getDeclareFields();
注意:使用不考虑修饰符的方法时,访问私有成员时需要忽略访问修饰符的安全检查,具体怎么做呢?如下:
Class对象 .setAccessible(boolean flag);//称为暴力反射
cls.setAccessible(true);
2、获取构造方法
//1.Constructor<T> getConstructors(类<?>... parameterTypes);获取指定的构造方法
Constructor<Person> constructor = cls.getConstructor(String.class, int.class);//括号里是不同类型的参数的class对象
//2.Constructor<?>[] getConstructors();获取public修饰的所有构造方法
Constructor[] c=cls.getConstructors();
//3.Constructor<T> getDeclaredConstructors(类<?>... parameterTypes);获取指定的构造方法,并且不考虑修饰符
//4.Constructor<?>[] getDeclaredConstructors();获取所有构造方法,并且不考虑修饰符
//注意:使用空参构造有一个创建对象的方法,class对象的newInstance方法
Object person1 = constructor1.newInstance();
3、获取成员方法
//1.Method getMethod(String name);获取指定名称的空参方法
Method eat1 = cls.getMethod("eat");
//2.Method getMethod(String name,类<?>... parameterTypes);获取指定名称的带参方法
Method eat2 = cls.getMethod("eat", String.class);
//3.Method[] getMethods();获取所有方法
Method[] methods = cls.getMethods();
/*会连同父类的public修饰的方法都打印出来,Person类直接继承自Object,所以会打印Object的方法*/
//4.Method getDeclaredMethod(String name);
//如果想获取私有方法,就用method.setAccessible(true)暴力反射
//5.Method getDeclaredMethod(String name,类<?>... parameterTypes);
//6.Method[] getDeclaredMethods();
/*获取成员方法之后可以创建类的对象,用获取到的方法对象调用invoke()方法,
并传入类对象和参数,可以实现获取到的成员方法
Object invoke(Object obj,Object...args)*/
Person p=new Person();//创建对象
//执行方法
eat1.invoke(p);
eat2.invoke(p,"rice");//传进对象p、参数rice
4、获取全类名
//获取全类名String getName();用Class对象调用
String name = cls.getName();
System.out.println(name);//cn._16_reflect.Person
5、获取方法名
//获取方法名String getName();用方法对象调用
String name = method.getName();
System.out.println(name);