我爱编程Java 核心技术学习笔记

JAVA核心技术学习笔记--反射

2018-08-09  本文已影响0人  Limmerence

反射

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

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

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包中有三个类

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

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

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

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方法能够构造新的数组,第一个参数是元素的类型,第二个是数组长度。所以现在我们需要获得新数组的元素类型和数组长度。

所以我们现在要实现一个通用的拓展任意类型的方法要经过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});
上一篇下一篇

猜你喜欢

热点阅读