Java反射学习总结
一、概述
1、Java反射机制的概念
Java 反射机制就是
在程序运行过程中,对于任意一个类,都能够获取这个类中的所有属性和方法等信息,并且可以操作这些属性和方法
。
一般来说,要让 Java 程序能够运行,那么就得让 Java 类要被 Java 虚拟机(JVM)加载。Java类如果不被 Java 虚拟机加载,是不能正常运行的。现在我们运行的所有的程序都是在编译期的时候就已经知道了你所需要的那个类的已经被加载了。
Java 的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类,这就是反射
。
例如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码能否通过编译呢?这是不能通过编译的。利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。
2、Java反射机制能够做什么——编写框架
在平时的编程中,反射基本用不到,但是在
编写框架
的时候,反射用的就多了,比如你要使用某一个类进行操作,但是这个类是用户通过配置文件配置进来 的,你需要先读配置文件,然后拿到这个类的全类名,然后在利用反射 API 来完成相应的操作
。
(1)在运行时查看类的所有信息(属性和方法)。
(2)在运行时创建任意一个类的实例对象。
(3)在运行时操作任意一个对象的属性和方法。
(4)生成动态代理。
3、Java 反射机制的优缺点
优点
反射的优点当然是体现在它的动态性上面,能运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,降低类之间的藕合性。 一句话,反射机制的优点就是可以实现动态创建对象和编译。
缺点
(1)由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
二、Class 类
●
Java 在真正需要使用一个类的时候才会进行加载,而不是在程序启动时加载所有的类
,因为大多数人都只使用到应用程序的部分资源,在需要某些功能时在加载某些资源,这样可以让系统的资源运用更有效率。
● Java 程序在运行时,系统会一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是 Class 类。Class 类封装一个对象和接口运行时的状态,当装载类时,在 JVM 自动创建一个 java.lang.Class 的实例对象
。
● Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象
。
虚拟机为每种类型管理一个独一无二的 Class 对象。也就是说,每个类(型)都有一个 Class 对象,用于表示这个类的类型信息
。通过这个 Class 对象我们就能获得加载到虚拟机当中这个 Class 对象对应的方法、成员以及构造方法的声明和定义等信息。
●运行程序时,Java 虚拟机(JVM)首先检查是否所要加载的类对应的 Class 对象是否已经加载。如果没有加载,JVM 就会根据类名查找 .class 文件,并将其 Class 对象载入
。
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象
。
● 一般某个类的 Class 对象被载入内存,它就用来创建这个类的所有对象
。
三、Java反射机制的常用API
1、获取 Class 对象(三种方法)
(1)调用某个对象的 getClass() 方法直接获取某一个实例对象的 class对象;
student.getClass();
(2)使用某个类的 class 静态属性来获取该类的 Class 对象;
Class class2 = Student.class;
(3)使用 Class 类的 forName(String className) 静态方法来获取某一个类的 Class 对象(该方法需要传入表示某个类的全限定名(必须添加完整包名)的字符串参数)。
Class class1 = Class.forName("csdn.Student");
注:三种方式常用第二种。第一种都建立了对象还要反射干什么;第三种需要知道该类的全路径名,所以要导入类的包,不导包就抛编译错误;第二种代码更安全,程序在编译阶段就能够检查需要访问的 Class 对象是否存在,而且线程性能更好因为这种方式无须调用方法。
2、通过 Class 对象获取类的成员变量
(1)获取单个成员变量
Field getDeclaredField(String name);
获取 Class 对象对应类的指定名称的属性,与成员变量的访问权限无关。
Field getField(String name);
获取 Class 对象对应类的指定名称的 public 属性。
(2)获取多个成员变量的数组Field[] getDeclaredFields();
获取 Class 对象对应类的所有属性,与成员变量的访问权限无关。
Field[] getFields();
获取 Class 对象对应类的所有 public 属性。
3、通过 Class 对象获取类的构造方法
(1)获取单个构造方法
Constructor getDeclaredConstructor(Class<?>...parameterTypes);
返回此 Class 对象对应类的、带指定形参列表的构造函数,与构造函数的访问权限无关。
Constructor getConstructor();
返回此 Class 对象对应类的、不带参数的
默认构造函数
。Constructor getConstructor(Class<?>...parameterTypes);
返回此 Class 对象
对应类的、带指定形参列表的 public 构造函数
。(2)获取多个构造方法的数组
Constructor<?>[] getDeclaredConstructors();
获取 Class 对象对应类的所有声明构造函数,于构造函数的访问权限无关。
Constructor<?>[] getConstructors();
获取 Class 对象对应类的所有 public 构造函数。
4、通过 Class 对象获取类的成员方法
(1)获取单个成员方法
Method getDeclaredMethod(String name, Class<?>...parameterTypes);
返回此 Class 对象对应类的、带指定形参列表的方法,与方法的访问权限无关。
Method getMethod(String name, Class<?>...parameterTypes);
返回此 Class 对象
对应类的、带指定形参列表的 public 方法
。(2)获取多个成员方法的数组
Method[] getDeclaredMethods();
获取 Class 对象对应类的所有声明方法,于方法的访问权限无关。
Method[] getMethods();
获取 Class 对象对应类的所有 public 方法,包括父类的方法。
5、利用反射机制创建实例对象
(1)使用 Class 对象的 newInstance() 方法来创建该 Class 对象对应类的实例(Class 对象的对应类必须有默认构造器)。
Class cla = Student.class; Student stu = (Student)cla.newInstance();
(2)先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance() 方法来创建该 Class 对象对应类的实例(这种方式可以选择使用指定的构造器来创建实例)。
Class cla = Student.class; // 获取无参的默认构造函数 Constructor constructor = cla.getConstructor(); Student stu1 = (Student)constructor.newInstance(); // 获取指定参数的构造函数 Constructor constructor = cla.getConstructor(String.class, String.class, int.class); Student stu2 = (Student)constructor.newInstance("lhj", "男", 25);
注:实际上,只有当程序需要动态创建某个类的对象时才会考虑使用反射,通常在开发通用性比较广的框架、基础平台时可能会大量使用反射。
6、使用反射机制调用方法
当获得某个类对应的 Class 对象后,就可以通过该 Class 对象的 getMethods() 和 getMethod()
获取某个类的全部方法和某个类的指定方法。这两个方法的返回值分别是 Method 对象数组和 Method 对象。每个 Method 对象对应某个类的一个方法,该方法可以通过获Method 对象的Object invoke (Object obj, Object… args) 方法来调用。例:// 首先获得该类的 Class 对象 Class class1 = Student.class; // 首先需要获得与该方法对应的Method对象 Method method = class1.getMethod("setAge", int.class); // 调用指定的函数并传递参数 method.invoke(student, 28);
注:当通过 Method 的 invoke() 方法来调用对应的方法时,Java 会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的 private 方法,可以先调用以下方法:setAccessible(boolean flag) 。若值为true,指示该 Method 在使用时应该取消Java语言的访问权限检查;值为 false,则知识该 Method 在使用时要实施 Java 语言的访问权限检查。
7、使用反射机制修改属性
若要修改对象的属性值,首先要调用 getDeclaredField() 方法获得对象的属性值,再调用 Field 的
set(Object,value) 方法修改属性值。Class class2 = Student.class; Field field = class2.getDeclaredField("age"); //age默认值是18 field.set(student,22);