目录
【以TestBean类为例:源码地址 下的 demo-reflect分支】
1、反射介绍
1.1 概念
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象,即反射模板,而解剖使用的就是 Class 类中的方法。所以先要获取到每一个字节码文件对应的 Class 类型的对象。
【注:字节码文件是经过编译器预处理过的一种文件,是Java的执行文件存在形式,它本身是二进制文件,但是不可以被系统直接执行,而是需要虚拟机解释执行,由于被预处理过,所以比一般的解释代码要快,但是仍然会比系统直接执行的慢】
1.2 作用
① 反编译:.class --> .java
② 通过反射机制访问 java 对象的属性、方法和构造方法(即构造器)等
1.3 sun提供的反射机制中的类
java.lang.Class;
java.lang.reflect.Constructor;
java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier; 【Modifier 修饰器】
注:反射的api在java.lang.reflect包提供
2、具体功能实现
2.1 获取/加载反射模板
在运行的时候,通过对象得到它的字节码,用于操作对象
- 方式一
/*
① 语法:类.class
即:使用"类名.class"的方式,Object的getClass()方法,返回与该类对应的Class对象 (java中每个类型都有class属性)
*/
Class clazz1 = TestBean.class;
优点:这个方法可以直接获得与指定类关联的Class对象,而并不需要有该类的对象存在。
- 方式二
/*
② 语法:对象.getClass()
即:Object的 getClass() 方法,返回所封装的类的名称 (java语言中任何一个java对象都有getClass方法)
*/
TestBean testBean = new TestBean(); // 调用了TestBean类得到空构造器
Class clazz2 = testBean.getClass();
- 方式三
/* ③ 语法:Class.forName()
即:著名的static方法
*/
Class<?> clazz3 = Class.forName("com.entity.TestBean");
注:1) 该方法会抛出异常,声明抛出ClassNotFoundException异常;
2) forName( )方法需要重点掌握,因为它可以在类不确定的情况下实例化Class,更具灵活性。
小结:
(1) Object -> Class -> 类 -> 对象 ,所有的对象都是 Objec t的子类
(2) Class表示字节码、对象、也表示当前的类,所有的字节码都是Class的对象
(3) 打印出来的形式均是:Class + 类的信息
(4) 也可以使用泛型,如 Class<?> clazz = TestBean.class;
(5) 其他方法:
// getSimpleName() 返回源代码中给出的底层类的简称,如果底层类是匿名,返回一个空字符串;
// getPackage() 得到这个类的包,此方法返回类的包名,或者null(如果没有包的对象是由这个类的类加载器创建的,则返回null )
// getName() 返回Class对象所表示的实体(类,接口,数组类,基本类型或void)的名字,作为一个字符串
// getType() 返回当前实例的运行时类型
2.2 通过反射模板创建对象
语法:反射模板对象.newInstance()
返回值类型:Object
解析:创建此Class对象所表示的类的一个新实例,即获取(或调用)无参构造器;
在面向对象的编程中,通常把用类创建对象的过程称为实例化。多数语言中,实例化一个对象 就是为对象开辟内存空间,或者是不用声明,直接使用
TestBean testBean = (TestBean) clazz.newInstance();
/* 上句代码等效于下面:
* Object obj=clazz.newInstance();
* TestBean testBean=(TestBean) obj;
*/
注:newInstance和new对比:
①newInstance:弱类型,低效率,只能调用无参构造方法
②new:强类型,相对高效,能调用任何public构造方法
2.3 获取属性
2.3.1 获取单一属性
-
获取公共的属性:反射对象名.getField(String);
注:属性对象名.set(类对象名,值); //给哪个对象设置这个属性的值
属性对象名.get(类对象名); //从哪个对象取这个属性的值 -
获取所有的(公共和非公共)属性:反射对象名.getDeclaredField(String);
补充:访问非公共的属性需要涉及暴力访问
A. 访问非public的属性需要设置暴力访问,即setAccessible(true);
B. 访问完成以后再关闭,即setAccessible(false);
C. 在访问之前应先获取,即boolean bl=对象.isAccessible();
【isAccessible()值为 true,则指示反射的对象在使用时应该取消 Java 语言访问检查;值为 false 则指示反射的对象应该实施 Java 语言访问检查。实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问。】 -
获取公共静态的属性:使用时不用传对象,传null;如果传入对象,会被忽略为null
2.3.2 获取多个属性
- 获取模板上所有公共的属性:clazz.getFields()
- 获取模板上所有的(公共和非公共)属性:clazz.getDeclaredFields()
2.4 获取方法
2.4.1 获取单一方法
-
获取public方法:反射对象名.getMethod(方法名,[该方法的参数类型(可以多个)]);
-
获取非公共的方法:反射对象名.getDeclaredMethod(方法名)
-
获取public static(公共的静态的)方法:同1
2.4.2 获取所有的方法
-
获取该模板的【public方法】和【父类继承的公共的方法】:clazz.getMethods()
-
获取该模板所有的,但【不】包括父类继承的方法:clazz.getDeclaredMethods()
注:方法名.getReturnType() // 获取返回类型
2.5 获取构造器
【见下表】
| 方法关键字 | 描述 |
| getDeclaredField() | 获取所有的方法 |
| getReturnType() | 获取方法的返回类型 |
| getParametr4Types() | 获取方法的传入参数类型 |
| getDeclaredMethod(“方法名”, 参数类型.class, …) | 获取特定的方法 |
| 构造方法关键字 | 描述 |
| getDeclaredConstructors() | 获取所有的构造方法 |
| getDeclaredConstructor(参数类型.class, …) | 获取特定的构造方法 |
| 父类和父接口 | 描述 |
| getSuperclass() | 获取某类的父类 |
| getInterface() | 获取某类实现的接口 |
3、应用场景
JDBC加载驱动
Spring IOC