java中的反射详解

主要介绍以下几方面内容

  • 理解 Class 类
  • 理解 Java 的类加载机制
  • 学会使用 ClassLoader 进行类加载
  • 理解反射的机制
  • 掌握 Constructor、Method、Field 类的用法
  • 理解并掌握动态代理

1.理解Class类

  –对象照镜子后可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
  –Class 对象只能由系统建立对象
  –一个类在 JVM 中只会有一个Class实例
  –每个类的实例都会记得自己是由哪个 Class 实例所生成
      1: Class是什么?

      Class是一个类:

2:Class这个类封装了什么信息?

  Class是一个类,封装了当前对象所对应的类的信息
一个类中有属性,方法,构造器等,比如说有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是Class,它应该有类名,属性,方法,构造器等。Class是用来描述类的类

Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性,方法,构造器,实现了哪些接口等等

3.对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
4.Class 对象只能由系统建立对象,一个类(而不是一个对象)在 JVM 中只会有一个Class实例

通过Class类获取类对象

在断点处就可以看到Class对像包含的信息

同样,这些属性值是可以获取的

查看fields的内容

对象为什么需要照镜子呢?

1. 有可能这个对象是别人传过来的

2. 有可能没有对象,只有一个全类名

通过反射,可以得到这个类里面的信息

获取Class对象的三种方式

  1.通过类名获取      类名.class    

  2.通过对象获取      对象名.getClass()

  3.通过全类名获取    Class.forName(全类名)

Class类的常用方法

方法名 功能说明
static Class forName(String name) 返回指定类名 name 的 Class 对象
Object newInstance() 调用缺省构造函数,返回该Class对象的一个实例
Object newInstance(Object []args) 调用当前格式构造函数,返回该Class对象的一个实例
getName() 返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称
Class getSuperClass() 返回当前Class对象的父类的Class对象
Class [] getInterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Class getSuperclass() 返回表示此Class所表示的实体的超类的Class

Class类的newInstance()方法

可以看出确实是创建了一个Person实例
但是Person类有两个构造方法,到底是调用的哪一个构造方法呢

实际调用的是类的无参数的构造器。所以在我们在定义一个类的时候,定义一个有参数的构造器,作用是对属性进行初始化,还要写一个无参数的构造器,作用就是反射时候用。

一般地、一个类若声明一个带参的构造器,同时要声明一个无参数的构造器

2.ClassLoader

类装载器是用来把类(class)装载进 JVM 的。JVM 规范定义了两种类型的类装载器:启动类装载器(bootstrap)和用户自定义装载器(user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:

使用类加载器获取当前类目录下的文件

首先,系统类加载器可以加载当前项目src目录下面的所有类,如果文件也放在src下面,也可以用类加载器来加载

调用 getResourceAsStream 获取类路径下的文件对应的输入流.

3.反射

反射概述

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。

Java反射机制主要提供了以下功能:

  • 在运行时构造任意一个类的对象
  • 在运行时获取任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法(属性)
  • 生成动态代理
  Class 是一个类; 一个描述类的类.
  封装了描述方法的 Method,
              描述字段的 Filed,
              描述构造器的 Constructor 等属性.

 3.1如何描述方法-Method

主要用到的两个方法

自定义工具方法

自定义一个方法

把类对象和类方法名作为参数,执行方法

             把全类名和方法名作为参数,执行方法

比如Person里有一个方法

那么我们自定义一个方法
1. 把类对象和类方法名作为参数,执行方法

调用:

这样就通过对象名,方法名,方法参数执行了该方法

2.把全类名和方法名作为参数,执行方法

调用

使用系统方法(前提是此类有一个无参的构造器(查看API))

  这种反射实现的主要功能是可配置和低耦合。只需要类名和方法名,而不需要一个类对象就可以执行一个方法。如果我们把全类名和方法名放在一个配置文件中,就可以根据调用配置文件来执行方法

 如何获取父类定义的(私有)方法

前面说一般使用getDeclaredMethod获取方法(因为此方法可以获取类的私有方法,但是不能获取父类方法)

如何获取父类方法呢,上一个例子format方法其实就是父类的方法(获取的时候用到的是getMethod)

首先我们要知道,如何获取类的父亲:

比如有一个类,继承自Person

使用

此时如果Student中有一个方法是私有方法method1(int age); Person中有一个私有方法method2();
怎么调用

定义一个方法,不但能访问当前类的私有方法,还要能父类的私有方法

3.2 如何描述字段-Field

但是如果需要访问父类中的(私有)字段:

3.3如何描述构造器-Constructor

3.4 如何描述注解 — Annotation

定义一个Annotation

此注解只能用在方法上

那么我们在给Person类对象的age赋值时,是感觉不到注解的存在的

必须通过反射的方式为属性赋值,才能获取到注解

  如果在程序中要获取注解,然后获取注解的值进而判断我们赋值是否合法,那么类对象的创建和方法的创建必须是通过反射而来的

4.反射与泛型

定义一个泛型类

再定义一个子类,继承这个泛型类:

父类中的泛型T,就相当于一个参数,当子类继承这个类时,就要给这个参数赋值,这里是把Person类型传给了父类

或者还有一种做法

然后进行测试

问题出来了。这里的get方法是父类的get方法,对于父类而言,方法返回值是一个T类型,当T的值为Person时,本该返回一个Person类型,但是必须用反射来创建这个对象(泛型方法返回一个对象),方法无非就是clazz.newInstance(); 所以关键点就是根据T得到其对于的Class对象。

那么首先,在父类中定义一个字段,表示T所对应的Class,然后想办法得到这个clazz的值

如何获得这个clazz呢?

所以就定义一个方法,获得 Class 定义中声明的父类的泛型参数类型 

反射小结

1. Class: 是一个类; 一个描述类的类.

封装了描述方法的 Method,

描述字段的 Filed,

描述构造器的 Constructor 等属性.

2. 如何得到 Class 对象:
2.1 Person.class
2.2 person.getClass()
2.3 Class.forName(“com.atguigu.javase.Person”)

3. 关于 Method:
3.1 如何获取 Method:
1). getDeclaredMethods: 得到 Method 的数组.
2). getDeclaredMethod(String methondName, Class … parameterTypes)

3.2 如何调用 Method
1). 如果方法时 private 修饰的, 需要先调用 Method 的 setAccessible(true), 使其变为可访问
2). method.invoke(obj, Object … args);

4. 关于 Field:
4.1 如何获取 Field: getField(String fieldName)
4.2 如何获取 Field 的值:
1). setAccessible(true)
2). field.get(Object obj)
4.3 如何设置 Field 的值:
field.set(Obejct obj, Object val)

5. 了解 Constructor 和 Annotation

6. 反射和泛型.
6.1 getGenericSuperClass: 获取带泛型参数的父类, 返回值为: BaseDao<Employee, String>
6.2 Type 的子接口: ParameterizedType
6.3 可以调用 ParameterizedType 的 Type[] getActualTypeArguments() 获取泛型参数的数组.

转自:https://www.cnblogs.com/tech-bird/p/3525336.html

发表回复