JAVA反射机制
一、Java反射机制的概念
•JAVA反射机制是在
运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种
动态获取信息以及
动态调用对象方法 的功能称为java语言的反射机制
Java反射机制是Java的特性之一,反射机制是构建框架基础的所在 我们为什么将这种机制命名为“反射”机制呢?
官方解答:要让Java程序能够运行,那么就得让Java类被Java虚拟机加载,Java类如果不被Java虚拟机加载,则不能正常运行,而现在我们运行的所有程序,都是在编译时就已经加载了我们所需要的那个类,
而Java的反射机制是在编译时并不确定是哪个类被加载了。而是在程序运行的时候才加载、探知、使用在编译时并不知道的类,这样的特点就是反射,类似于光学中的反射概念,所以我们把Java的这种机制称为反射机制
Java的反射机制能够知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。
例如:在MyEclipse中,当我们构建出一个对象,然后,去调用该对象的属性和方法的时候,在对象后面输入 " . ",那么编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来 供我们进行选择,这就是利用了Java反射的原理,是对我们创建对象的探知和自审
Java反射有三个动态性质:
1.运行时生成对象实例
2.运行期间掉用方法
3.运行时更改属性
通过这些动态性质,我们知道Java反射可以在程序运行时获取对象和类的真实信息
Java反射可以实现哪些功能?
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时判断任意一个类所具有的方法和属性
4.在运行时调用任意一个对象的方法
5.生成动态代理
Java反射的应用场合
Java程序中许多对象在运行时都会出现两种类型:编译时类型和运行时类型
编译时的类型有声明该是对象时使用的类型决定,运行时的类型实际赋给该对象的类型决定
例如:
Person p=new Student();
此时p的编译时类型为Person,运行时类型为Student;
因为对象p是引用类型,在编译时器类型由Person来确定,而在程序运行后发现p引用的内容实际是Student,因为只有在程序运行的时候才能知道p引用的内容改为了Student,所以称为运行时类型
除此之外,程序在运行时还可能接收到外部传入的一个对象,该对象的编译时类型是Object,但程序有需要调用该对象运行时类型的方法,为了解决这些问题,程序需要在运行时发现对象和类的真实信息。然而,如果编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,此时就必须使用反射
接下来我们先去学习一些常用的Java反射API,便于后来我们学习如何使用Java反射
Java反射API
Java反射API用来生成在当前Java虚拟机中的类、接口或者对象的信息。其中Class类是反射的核心类,反射所有的操作都是围绕该类来生成的。
通过Class类,我们可以获取类的属性、方法等内容信息。而Field、Method、Constructor等类都定义于Java.lang.reflect.
Field类表示类的属性,可以获取和设置类中属性的值
Method类表示类的方法,它可以用来获取类中方法的信息,或者执行方法
Constructor类表示类的构造方法
如何使用反射API在运行的时候得到指定类的方法和属性?
![]()
1 package cn.happy.reflet.classinfo;
2
3 public class Person {
4 private String name;
5 private String gender;
6 private int age;
7
8
9 private String getName() {
10 return name;
11 }
12 private void setName(String name) {
13 this.name = name;
14 }
15 public String getGender() {
16 return gender;
17 }
18 public void setGender(String gender) {
19 this.gender = gender;
20 }
21 public int getAge() {
22 return age;
23 }
24 public void setAge(int age) {
25 this.age = age;
26 }
27 public String toString() {
28 return "Person [name=" + name + ", gender=" + gender + ", age=" + age
29 + "]";
30 }
31
32 }
自定义的Person类
![]()
1 package cn.happy.reflet.classinfo;
2
3 import java.lang.reflect.Field;
4 import java.lang.reflect.Method;
5
6 import javax.swing.JOptionPane;
7
8 public class ReflectDemo {
9 /**
10 * 构造函数
11 * */
12 public ReflectDemo(){
13 //用户输入类的全路径()
14 String classpath=JOptionPane.showInputDialog(null,"");
15 try {
16 //利用class.forName()获得Class类的实例,寻找全路径 并将该类返回
17 //根据类的全路径进行类加载,返回该类的class对象
18 Class cla=Class.forName(classpath);
19 //利用class对象cla的自审,返回方法对象集合
20 Method[] method=cla.getDeclaredMethods();
21 //遍历method数组,并输出方法信息
22 System.out.println("*************获取方法信息************");
23 for (Method meth : method) {
24 System.out.println(meth.toString());
25 }
26 System.out.println("*************获取方法信息结束************");
27
28 //利用class对象cla的自审,返回成员属性对象集合
29 Field[] fileds=cla.getDeclaredFields();
30
31 System.out.println("*********获取属性的信息************");
32 for (Field field : fileds) {
33 System.out.println(field.toString());
34 }
35 System.out.println("*********获取属性的信息************");
36 } catch (ClassNotFoundException e) {
37 e.printStackTrace();
38 }
39 }
40
41 /**
42 *1.获取访问类的class对象
43 *2.调用class对象的方法返回访问类的方法和属性信息
44 * */
45 }
获取该类的属性和方法
![]()
1 package cn.happy.reflet.classinfo;
2
3 public class TestReflect {
4 //如何使用javaApi获取类的属性的步骤
5 public static void main(String[] args) {
6 ReflectDemo rd=new ReflectDemo();
7
8 }
9 }
在主程序中调用
每个类被加载后,系统就会为该类生成一个对应的Calss对象,通过该Class对象就可以访问到Java虚拟机中的这个类,Java程序中获得Class对象通常有如下三种方式:
1.调用某个对象的getClass()方法
PerSon p=new Person();
Class cla=p.getClass();
2.调用某个类的Class属性来获取该类对应的Class对象
Class cla=Person.Class;
注意:需要在编译时就知道该类的名字
3.使用Class类的forName()静态方法 该方法需要传入字符串参数,该字符串参数的值是某个类的全名(即类名前添加完整的包名)。
Class cla=Class.forName("类的全路径名");
后两种方式都是直接根据类来取得该类的Class对象,相比之下,调用某个类的Class属性来获取该类对应的Class对象这种方式有如下两种优势:
1.代码更安全。程序在编译阶段就可以检查需要访问的Class对象是否存在
2.程序性能更高,因为这种方式无须调用方法。所以程序的性能更好
获取Class对象的方式
在获得了某个类所对应的Class对象之后,程序就可以调用Class对象的方法来获得该类的真实信息了,Class类提供了大量实例方法来获取Class对象所对应的类的详细信息
如何从Class类中获取信息
1.访问Class对应的类所包含的构造方法
//方法1
Constructor getConstructor(Class[] params)
返回此Class对象所表示的类的指定的public构造方法,params参数是按声明顺序标识该方法参数类型的Class对象的一个数组
构造方法的参数类型与params所指定的参数类型所匹配
示例:
Constructor co=c.getConstructor(String.Class,List.Class); (c为某Class对象)
//方法2
Constructor[] getConstructors();
返回此Class对象所表示的类的所有public 构造方法
//方法3
Constructor getDeclaredConstructor(Class[] patams);
返回此Class对象所表示的类的指定构造方法,与构造方法的访问级别无关
//方法4
Constructor[] getDeclaredConstructors();
返回此Class对象所表示的类的全部方法,与方法的访问级别无关
2.访问Class对应的类所包含的构造方法
//方法1
Method getMethod(String name,Class[] params);
返回此Class对象所表示的类的指定的public方法,name参数用于指定方法名称,params参数是按声明顺序表示该方法参数类型的Class对象的一个数组
//方法2.
Method[] getMethods();
返回此Class对象所表示的类的所有public方法
//方法3
Method getDeclaredMethod(String name.Class[] params);
返回此Class对象所表示的类的指定的方法。与方法的访问级别无关
//方法4
Method[] getDeclaredMethods();
返回此Class对象所表示的类所有的方法。与方法的访问级别无关
访问Class对应的类所包含的属性
//方法1
Field getField(String name);
返回此Class对象所表示的类的指定的public属性,name参数指定属性名称
该方法将返回此Class对象所表示类的指定的public属性
//方法2
Field[] getFields();
返回此Class对象所表示的类的所有public属性
//方法3
Field getDeclaredField(String name);
返回此Class对象所表示的类的指定属性,与属性的访问级别无关
//方法4
Field[] getDeclaredFields();
返回此Class对象所表示的类的全部属性,与属性的访问级别无关
访问Class对应的类所包含的注释
<A extendsd Annotation> A getAnnotation(Class<A> annotationClass)
试图获取该Class对象所表示类上指定类型的注释,如果该类型的注释不存在则返回null,其中annotationClass参数对应注释类型的Class对象
Annotation[] getAnnotations();返回此元素上存在的所有注释
Annotation[] getDeclaredAnnotations(); 返回直接存在于此元素上的所有注释
如何访问Class对应的类所包含的内部类
Class[] getDeclaredClasses();
返回此Class对象所对应的类里包含的全部内部类,如果Class对象所对应的类是一个内部类,那我们如何得知Class所对应的外部类呢?
访问Class对应的类所在的外部类
Class getDeclaringClass();
返回此Class对象所在的外部类
注意:访问内部类与访问所在外部类的方法名称差不多,一定要注意他们的方法名称
如下的方法用于访问该Class对象所对应类所继承的父类,所实现的接口等
上面的getModifiers()返回的修饰由public protected private final static abstract等对应的常量 组成 返回的整数应使用Modifier工具类的方法来解码,才可以获取真实的修饰符
![]()
![]()
创建对象
![]()
上面三个对象都在java.lang.reflect包下,都实现了java.lang.reflect.Member接口
可以通过Constructor对象来调用相应的构造方法创建对象
通过Field对象直接访问并修改对象的属性值
如何通过反射创建对象
1.使用newInstance()创建对象
2.使用Constructor对象创建对象
![]()
![]()
实际上,只有当程序需要动态创建某个类的对象是才会考虑到使用反射,通常在开发通用性比较广的框架、基础平台时可能会大量使用反射,因为在很多Java EE框架中都需要根据配置文件信息来创建Java对象,从 配置文件读取的只是某个类的字符串类名,程序想根据字符串来创建对应的实例,就必须使用反射,如我们所知道的Spring框架就应用了这个技术
反射技术多用于框架即基础平台中
使用Constructor()创建对象
如果我们不想利用默认构造方法来创建java对象,而想利用指定的构造方法来创建java对象,则需要利用Constructor对象了,每个Constructor对应一个构造方法,利用指定构造方法来创建java对象需要三个步骤:
1.获取该类的Class对象
2.利用Class对象的getConstructor()方法来获取指定构造方法
3.调用Constructor的newInstance()方法来创建java对象![]()
![]()
如何使用反射调用方法:
![]()
![]()
实例:
![]()
![]()
在调用方法时如何传参数
![]()
如果我们把Person类的setName方法的访问权限设为私有,这时再运行程序,则会抛出NoSuchMethodexception异常,这时因为当通过Method的invoke()方法调用对应的方法时
java会要求程序必须有调用该方法的权限,如果程序确实需要调用某个对象的private方法,可以先调用setAccessible(Boolean flag)方法,将Method对象的accessible标志设置为指示的布尔值,值为true则表示该Method在使用时用该取消java语言的访问权限检查,值为false则表示该Method在使用时应该实施java语言访问权限检查
![]()
如何访问属性
![]()
![]()
![]()
![]()
File.setAccessible(true);
我们对获取到的私有属性调用方法setAccessible(true),便可以使用这两个方法随意的访问指定对象的所有属性,包括private访问控制属性
访问Person类中的属性
![]()
通过反射获取Person类中的属性![]()
事实上在实际开发中,我们没有必要使用反射,来访问已知类的方法和属性,只有当程序需要动态创建某个类的对象的时候我们才会考虑使用反射,例如,我们要从配置文件中读取以字符串形式表示的类时,我们就要使用反射来获取他的方法和属性了,
![]()
又比如,我们可以使用反射来写一个,在Action中接受参数的公共方法,这样不同的Action在接收参数时,只需要调用接收参数的公共方法就可以了,而不必每次都为没一个参数写一句request.getParameter语句了
![]()
动态创建和访问数组
在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组,程序可以通过使用Array来动态的创建数组,并且操作数组的元素
Array类提供了如下几类方法:
1.通过newInstance() 方法我们可以创建一个具有指定的元素类型、指定唯独的数组
![]()
2.getXxx()方法,将返回Array数组中的第index个元素,这与使用反射返回属性类似
![]()
3.
![]()
下面实例如何使用这些方法来动态的创建和访问数组![]()
只用三行代码就完成了动态创建数组及访问数组
![]()
使用Array类来创建多维数组
![]()
总结:
在反射机制概述中,我们了解到了java的一个动态特性:反射 知道了反射机制是一种动态获取信息以及动态调用对象方法的功能,通过反射我们可以在运行时判断任意一个对象所属的类,在运行时构造任意一个类的对象,判断任意一个类所具有的方法和属性,同时,也可以在运行时调用任意一个对象的方法以及生成动态代理,如果编译时我们根本无法预知该对象或类可能属于哪些类,程序只能依靠运行时信息来发现该对象和类的真实信息,那么此时我们就必须使用反射,事实上,在我们平时写代码的时候反射技术用的并不是很多,因为通过反射创建对象时性能要稍微低一些,只有当程序需要动态创建某个类的对象时才会使用反射Class类是反射的核心类,