欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java核心学习笔记--反射

程序员文章站 2022-04-29 15:05:18
...

反射

反射库(reflection library)提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵Java代码的程序。 使用反射,能在设计或者在添加新类的时候,能够快速应用开发工具动态的查询新添加类的能力。

能够分析类能力的程序称为 反射 反射功能极其强大可以用来

  • 在运行时分析类的能力
  • 在运行时查看对象
  • 实现通用的数组来操作代码
  • 利用Method对象,此对象很类似C++中的函数指针

1,前提介绍: Class 类

在程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时的类型标识,这个信息跟踪着每个对象专属的类。
Class类就是用来专门保存这些类的信息,通过Object类中的getClass()方法得到每个类的Class类实例

Person p;
Class cl = p.getClass();

一个Class类可以用来保存一个类的属性,如:

p.setName("ZhangSan");
System.out.print(cl.getName()+" "+p.getName());

此时程序会输出

Person ZhangSan

Class的getName()方法可以得到这个类的类名。

获得类的Class实例的第2种方法:Class.forName(类名)

String className = "com.henu.Person";
Class cl = Class.forName(className);

第三种方法,直接 类名.class

Class class1 = Person.class;
Class class2 = Random.class;

通过获得的Class实例再创建类的实例

Class cl = p.getClass();
Perons p = cl.newInstance();

以上代码在使用时要放在try catch代码块里,来处理捕获的异常


2,利用反射分析类的能力,检查类的结构

java.lang.reflect包中有三个类

  • Field 描述类的域 getType()方法可以返回描述域所属类型的Class对象
  • Method 描述类的方法
  • Constructor 描述类的构造器

这三个类都有一个getModifiers的方法,描述各自的访问修饰符,public private final...等,
再通过Modifier类中的isPublic,isPrivate,isXxx方法对其进行判断.

  • getFields

  • getMethods

  • getConstructors 这三个方法分别返回类提供的public域,方法,构造器数组,其中包括超类的共有成员.

  • getDeclareFields

  • getDeclareMethods

  • getDeclaredConstructors
    这三个方法将返回类中声明的全部域,方法和构造器,包括private,protect,但不包括超类的共有成员

在运行时使用反射分析对象

接下来进一步查看数据域的实际内容给

Person p = new Person("张三",20);
Class cl = p.getClass();
Field name = cl.getDeclaredField("name");//获得Person 类的name域;

Object value = name.get(p); // 此时value = "张三"


值的注意的一点是,若name域在Person类中是私有的,代码会抛出异常,我们需要对获得的数据域进行设置


Person p = new Person("张三",20);
Class cl = p.getClass();
Field name = cl.getDeclaredField("name");//获得Person 类的name域;

name.setAccessible(true);

Object value = name.get(p); // 此时value = "张三"

setAccessible方法是AccessibleObject类中的一个方法,它是Field,Method,Constructor类的共有超类,是为了调试,持久存储和相似调试提供的.

若数据域是基本数据类型,可以使用域.getDouble(类的实例),getInt(类的实例),也可以继续使用get( )方法,反射机制会自动的将这个域值装箱,变成Double,Integer。

.set(obj,value)方法可以将obj对象的指定域进行赋值

name.set(p,"李四")//此时p对象的name为李四

3,使用反射编写泛型数组代码

java.lang.reflect包中的Array类允许动态的创建数组
以前我们学过Arrays类中有个copyOf()方法,可以用来拓展已经满了的数组.

Person[ ] team = new Person[10];

team = Arrays.copyOf(team,2*team.length);  

我们可以用反射写一个通用的拓展数组长度的方法,能拓展任意类型的数组。 首先我们写一个错误的例子

public static Object[] badCopyOf(Object[] a,int newLength) {
    Object[] newArray = new Object[newLength];
    System.arrayCopy(a,0,newArray,0,Math.min(a.length,newLength));//第四个参数是要从原数组复制元素的个数
    return newArray;
}

为什么这个代码是错的呢,因为这个函数返回的数组类型是对象数组类型(Object [])
一个对象数组不能转化成特定的类的数组(Object[]->person[]),这样做会产生异常,其实,当我们把Person[]转化成 Object[] 再转化成Person[]是可以的,但是一个一开始就是Object[]的数组是不能转的,我们可以通过下面这个方法

Object newArray = Array.newInstance(componentType,int newLength);

java.lang.reflect.Array中的静态方法newInstance方法能够构造新的数组,第一个参数是元素的类型,第二个是数组长度。所以现在我们需要获得新数组的元素类型和数组长度。

  • 获得数组的长度可以通过.getLength()或者Array.getLength(数组)获得
  • 获得数组的元素类型可以通过Class类的getComponentType()获得

所以我们现在要实现一个通用的拓展任意类型的方法要经过3步

  1. 首先获得数组的Class对象
  2. 确认它是一个数组类型(通过Class类的isArray()方法)
  3. 使用Class类的getComponentType()方法确定数组的类型
public static Object goodCopyOf(Object a,int newLength) {
    Class cl = p.getClass();
    if (cl.isArray()) return null;//如果不是数组类型就结束
    
    Class componentType = cl.getComponentType();//获得数组的元素类型
    int length = a.getLength();
    Object newArray = Array.newInstance(componentType,length);
    System.arrayCopy(a,0,newArray,0,Math.min(length,newLength));
    return newArray;
}

这样这个方法就可以用了来拓展任意类型的数组了

int[] a = {1,2,3,4,5};
a = (int [])goodCopyOf(a,10);

通过Method的invoke方法调用类的任意方法

两个方法
Method method = Person.getClass().getMethod(方法名,方法的参数类型.class)
method.invoke(方法隶属的类的实例,方法的参数值)

//动态构造InvokeTest类的实例
Class<?> classType = InvokeTest.class;
Object invokeTest = classType.newInstance();

//动态构造InvokeTest类的add(int num1, int num2)方法,标记为addMethod的Method对象
Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});

//动态构造的Method对象invoke委托动态构造的InvokeTest对象,执行对应形参的add方法
Object result = addMethod.invoke(invokeTest, new Object[]{1, 2});