十二、反射
12.1 反射的概述
-
反射的应用场合
在编译时根本无法知道该对象或者类可能属于哪些类,程序只依靠运行时的信息来发现该对象和类的真实信息。 -
反射的作用
通过反射可以使程序代码访问装载到JVM中的类的内部信息:- 获取已装载类的属性信息;
- 获取已装载类的方法;
- 获取已装载累类的构造方法的信息。
-
反射的机制
在JDK中,主要由这些类来实现java反射机制,这些类都位于java.lang.reflect包中:- Class类:代表一个类;
- Field类:代表类的成员变量(属性);
- Method类:代表类的成员方法;
- Constructor类:代表类的构造方法;
- Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
-
Class类
- Class类是java反射机制的起源和入口
- 用于获取与类相关的各种信息;
- 提供了获取类信息的相关方法;
- Class类继承自Object类。
- Class类是所有类的共同的图纸
- 每个类都有自己的对象,好比图纸和实物的关系;
- 每个类也可以看做是一个对象,有共同的图纸Class,存放类的结构信息,能够通过相应方法取出相应信息(类的名字、属性、方法、构造方法、父类和接口)。
12.2 反射的优缺点
- 优点
- 反射提高了java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程创建和控制人格类的对象,无需提前硬编码目标类;
- 反射是其他一些常用语言。如C、C++、Fortran或者Pascal等都不具备的;
- java反射技术应用领域很广,如软件测试、EJB、JavaBean等;
- 许多流行的开源框架例如Struts、Hibernate、Spring在实现过程中都采用了该技术。
- 缺点
- 性能问题
使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。 - 使用反射会模糊程序内部逻辑
程序人员希望在源代码中看到程序的逻辑,反射的等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
12.3 反射的方法和使用
Class类的常用方法:
方法名 | 功能说明 |
---|---|
getFields() | 获得类的public类型的属性 |
getDeclaredFieldds() | 获得类的所有属性 |
getField(Strign name) | 获得类的指定属性 |
getMethods() | 获得类的public类型的方法 |
getMethod(String name,Class[] args) | 获得类的指定方法 |
getConstrutors() | 获得类的public类型的构造方法 |
getConstrutor(Class[] args) | 获得类的特定构造方法 |
newInstance() | 通过类的无参构造方法创建该类的一个对象 |
getName() | 获得类的完整名字 |
getPackage() | 获取此类所属的包 |
getSuperclass() | 获得此类的父类对应的Class对象 |
Administrator类:
package com.wbs.entity;
import java.sql.Date;
public class Administrator extends Customer {
public String names;
public String sex;
/**
* 管理员编号
*/
private String adminNumber;
/**
* 管理员密码
*/
private String adminPwd;
/**
* 管理员名字
*/
private String adminName;
public Administrator() {
System.out.println("我是无参构造函数!!!");
}
public Administrator(String adminNumber, String adminPwd, String adminName) {
this.adminNumber = adminNumber;
this.adminPwd = adminPwd;
this.adminName = adminName;
System.out.println("我是带参构造函数!!!");
}
public String getAdminNumber() {
return adminNumber;
}
public void setAdminNumber(String adminNumber) {
this.adminNumber = adminNumber;
}
public String getAdminPwd() {
return adminPwd;
}
public void setAdminPwd(String adminPwd) {
this.adminPwd = adminPwd;
}
public String getAdminName() {
return adminName;
}
public void setAdminName(String adminName) {
this.adminName = adminName;
}
public int hello(){
System.out.println("我是无参hello函数");
return 0;
}
public int hello1(String name,int age){
System.out.println("我是" + name + ",今年" + age + "岁了!");
return 1;
}
}
Reflect测试类:
package com.wbs.reflect;
import java.io.File;
import java.lang.reflect.*;
/**
* 反射机制的实现和功能
*/
public class TestRflect {
public static void main(String[] args) {
TestRflect t = new TestRflect();
t.t7();
}
/**
* 通过反射得到类实例化对象
*/
public void t1(){
try{
//1.加载驱动类(添加到JVM中)
Class clz = Class.forName("com.wbs.entity.Administrator");
//2.实例化对象
//2.1使用原始的方法(此方法已淘汰)
//Object objInstance = clz.newInstance();
//2.2使用构造函数实例化对象(正规写法)
Constructor ct = clz.getDeclaredConstructor(new Class[]{});
Object objInstance = ct.newInstance(new Class[]{});//此处空参数new Class[]{}或者null都可以
//3.输出类名
System.out.println(objInstance);//带类路径
System.out.println(clz.getSimpleName());//简化只输出类名Administrator
//3.获取类属性
//Field f = clz.getField("names");//只能获取到public属性(并且要指定字段名)
//Field[] fs = clz.getFields();//只能获取到public属性
//Field f = clz.getDeclaredField("names");//只能获取到public属性(并且要指定字段名)
Field[] fs = clz.getDeclaredFields();//获取所有的类属性
//System.out.println(f.getName());
//System.out.println(fs.getName());
//4.输出所有的public属性
/*for(Field f:fs){
System.out.println(f.getName());
}*/
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* 通过反射获取类属性(字段)和访问修饰符
*/
public void t2(){
try {
//1.加载类(添加到JVM中)
Class clz = Class.forName("com.wbs.entity.Administrator");
//2.实例化对象
Constructor ct = clz.getConstructor(new Class[]{});//此处空参数new Class[]{}或者null都可以
Object objInstance = ct.newInstance(new Class[]{});
//3.获取类属性
/**
* 以下方法只能获取到类的公共属性(public修饰符)
* getFields()获取类所有公共属性(public修饰符),返回数组
* getField("字段名")指定字段名获取属性
* getDeclaredField("字段名")同getField("字段名")方法
*/
Field f1 = clz.getField("names");
Field f2 = clz.getDeclaredField("names");
System.out.println(f1.getName());
System.out.println(f2.getName());
Field[] fs = clz.getFields();
for(Field f:fs){
System.out.println(f.getName());
}
/**
* getDeclaredFields()此方法可以获取类的所有属性
*/
Field[] fds = clz.getDeclaredFields();
for(Field f:fds){
System.out.println(f.getName());
}
/**
* getModifiers()此方法获得是访问修饰符的序号,需要做转化
* Modifier.toString(f.getModifiers())
* getSimpleName()此方法是数据类型名的简化方法
*/
System.out.println("-----------访问修饰符-------------");
for(Field f:fds){
System.out.print("属性名:" + f.getName());
System.out.print("\t数据类型:" + f.getType().getSimpleName());
System.out.println("\t访问修饰符:" + Modifier.toString(f.getModifiers()));
}
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* 通过反射获取类中的属性并赋值
*/
public void t3(){
try {
//1.加载类(添加到JVM中)
Class clz = Class.forName("com.wbs.entity.Administrator");
//2.获取类的属性
Field f1 = clz.getDeclaredField("names");
Field f2 = clz.getDeclaredField("adminName");//私有属性不可直接获取
//2.1可以通过设置访问权限
f2.setAccessible(true);//此时private属性就获取到了
//3.实例化对象
Constructor ct = clz.getConstructor(new Class[]{});//此处空参数new Class[]{}或者null都可以
Object objInstance = ct.newInstance(new Class[]{});
//4.给属赋值
f1.set(objInstance,"张三是public修饰的");
f2.set(objInstance,"李四是private修饰的");
//5.输出属性值
System.out.println(f1.getName() + "=" + f1.get(objInstance));
System.out.println(f2.getName() + "=" + f2.get(objInstance));
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* 通过反射访问中的方法
*/
public void t4(){
try {
//1.加载类(添加到JVM中)
Class clz = Class.forName("com.wbs.entity.Administrator");
/**
* getMethods()此方法获取指定类和其父类中所有的公共方法(public修饰符),返回数组
*/
Method[] ms1 = clz.getMethods();
for(Method m:ms1){
System.out.println(m.getName());
}
System.out.println("-------------------------------");
/**
* getDeclaredMethods()此方法只获取指定类中的公共方法(public修饰符),返回数组
*/
Method[] ms2 = clz.getDeclaredMethods();
for(Method m:ms2){
System.out.println(m.getName());
}
/**
* Modifier.toString(m.getModifiers())获取方法的访问修饰符
* m.getReturnType().getSimpleName()获取方法的返回值类型
* m.getName()获取方法的名字
*/
for(Method m:ms1){
System.out.println(Modifier.toString(m.getModifiers()) + "\t" + m.getReturnType().getSimpleName()+ "\t" +m.getName());
}
System.out.println("--------------------------------------------");
for(Method m:ms2){
System.out.println(Modifier.toString(m.getModifiers()) + "\t" + m.getReturnType().getSimpleName()+ "\t" +m.getName());
}
//2.实例化对象
Constructor ct = clz.getConstructor(new Class[]{});//此处空参数new Class[]{}或者null都可以
Object objInstance = ct.newInstance(new Class[]{});
//
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* 通过反射调用类中的方法,如果函数有返回值就获取
*/
public void t5(){
try {
//1.加载类(添加到JVM中)
Class clz = Class.forName("com.wbs.entity.Administrator");
//2.获取方法
//2.1获取无参函数
Method m1 = clz.getDeclaredMethod("hello");
//2.2获取带参函数,传递参数也可以写成new Class[]{String.class,int.class}
Method m2 = clz.getDeclaredMethod("hello1", String.class, int.class);
//3.实例化对象
Constructor ct = clz.getConstructor(new Class[]{});//此处空参数new Class[]{}或者null都可以
Object objInstance = ct.newInstance(new Class[]{});
//4.invoke(Object obj,Object... args)此方法调用函数
int r1 = (int)m1.invoke(objInstance);//我是无参hello函数
int r2 = (int) m2.invoke(objInstance,new Object[]{"张三",21});//我是张三,今年21岁了!
//5.接收函数的返回值,并输出
System.out.println("hello函数的返回值:" + r1);
System.out.println("hello1函数的返回值:" + r2);
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* 通过反射获取类中的所有构造函数,
* 通过传入参数获取指定构造函数,并调用
*/
public void t6(){
try {
//1.加载类(添加到JVM中)
Class clz = Class.forName("com.wbs.entity.Administrator");
//2.获取类中所有构造函数
Constructor[] cs = clz.getDeclaredConstructors();
//3.输出构造函数的个数和名字
System.out.println("构造函数个数:" + cs.length);
for(Constructor c:cs){
System.out.println(c.getName());
}
System.out.println("----------------------------");
/**
* 4.通过getDeclaredConstructor("无参或者带参")方法获取指定的构造函数并调用
*/
//4.1获取无参构造函数
Constructor c1 = clz.getDeclaredConstructor(new Class[]{});//默认调用无参构造函数
Object objInstance1 = c1.newInstance(new Object[]{});//实例化对象时即调用构造函数
//4.2获取带参构造函数
Constructor c2 = clz.getDeclaredConstructor(new Class[]{String.class,String.class,String.class});
Object objInstance2 = c2.newInstance(new Object[]{"1001","123456","李四"});
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* 通过反射获取系统中的封装类型(一维数组)
*/
public void t7(){
try {
//1.加载类(添加到JVM中)
Class clazz = Class.forName("java.lang.Integer");
//2.定义Integer类型数组
Object arr = Array.newInstance(clazz,10);//同Integer[] arr = new Integer[10];
//3.给数组赋值
Array.set(arr,0,1);//同arr[0] = 1
Array.set(arr,3,3);//同arr[3] = 3
//4.输出数组值
System.out.println(Array.get(arr,0));
System.out.println(Array.get(arr,3));
}catch (Exception ex){
ex.printStackTrace();
}
}
/**
* 通过反射动态创建数组并存取元素(多维)
*/
public void t8(){
// 创建一个含有10*15*18个元素的整型数组
int dims[] = { 10, 15, 18 };
Object arr = Array.newInstance(int.class, dims);
// 给arr[5][8][10]赋值
Object arr5 = Array.get(arr, 5);
Object arr58 = Array.get(arr5, 8);
Array.set(arr58, 10, 30);
// 取出arr[5][8][10]值并输出
Object elem = Array.get(arr58, 10);
System.out.println(elem);
/**
* 相当于执行语句:
* int arr[ ][ ][ ]=new int[10][15][18];
* arr[5][8][10]=20; System.out.println(arr[5][8][10]);
*/
}
/**
* 通过反射访问类及其父类
*/
public void t9(){
try {
//1.加载类(加载到jvm中)
Class cls = Class.forName("com.qhj.fanshe.Administrator");
//2.类的完整名字
System.out.println(cls.getName());
//3.类所在的包
System.out.println(cls.getPackage().getName());
//4.此类的父类
System.out.println(cls.getSuperclass());
} catch (Exception e) {
e.printStackTrace();
}
}
}