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

反射

反射库(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});
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 第一章:Java程序设计概述 Java和C++最大的不同在于Java采用的指针模型可以消除重写内存和损坏数据的可能...
    loneyzhou阅读 5,039评论 1 7
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,954评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,448评论 19 139
  • 一 国庆节回即墨老家,老家还有一亩七分土地,按照往年,家里一般会种一亩花生、七分玉米。今年春天因为天气干燥,老天迟...
    堂小诃德阅读 4,751评论 6 10
  • 今天是什么日子 起床:7:45 就寝:23:00 天气:微风不燥,阳光正好。 心情:平和简单 纪念日:无 任务清单...
    楠澧阅读 1,534评论 2 3