JAVA语言——反射

Posted by 冷眼樵夫 on 12-29,2018

在JAVA运行环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。
这种动态获取类的信息以及动态调用类的对象的方法的功能来自于Java语言的反射(Reflection)机制。
反射是框架设计的灵魂。
使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)。

1. 概述

JAVA反射机制是在运行状态中,
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

说白了,反射就是把java类中的各种成分映射成一个个的Java对象。
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
file
只是需要留意,其中这个Class对象比较特殊。

2.反射API

2.1.Class类

反射的核心类,可以获取类的属性,方法等信息。
file

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。
也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

涉及到具体方法就不一一细说了,反正很多,用到那个再看哪个就好了。
file

2.2 Field类

表示类的成员变量,可以用来获取和设置类之中的属性值。
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
反射的字段可能是一个类(静态)字段或实例字段。
file

类中的变量分为两种类型,基本类型和引用类型:
file

java.lang.reflect.Field 提供了两个方法获去变量的类型:
file

Class<?> getType()
返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。
Type getGenericType()
返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。

举个栗子:
测试类:
file
主方法:
file
执行结果:
file
可以看到这个两个方法都能正确的返回当前字段的类型。这两个方法的区别还是在返回类型上;
getType()方法返回的是Class<?>类型的,而getGenericType()返回的是Type类型的。

2.3 Method类

表示类的方法,它可以用来获取类中的方法信息或者执行方法。
file
具体方法使用就不细说了,直接看API文档:
file

3.反射使用

在Java程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。
编译时的类型由声明对象时实用的类型来决定,运行时的类型由实际赋值给对象的类型决定。
譬如:
Person p = new Student();
其中编译时类型为Person,运行时类型为Student。
除此之外,程序在运行时还可能接收到外部传入的对象,该对象的编译时类型为Object,但是程序有需要调用该对象的运行时类型的方法。
为了解决这些问题,程序需要在运行时发现对象和类的真实信息。然而,如果编译时根本无法预知该对象和类属于哪些类,程序只能依靠运行时信息来发现该对象和类的真实信息,此时就必须使用到反射了。

3.1.使用步骤

  • 获取想要操作的类的Class对象;
  • 调用Class类中的方法;
  • 使用反射API来操作这些信息。

3.2.获取Class对象的方法

  • 调用某个对象的getClass()方法;

    Class clazz=p.getClass();

  • 调用某个类的class属性来获取该类对应的Class对象

    Class clazz=Person.class;

  • 使用Class类中的forName()静态方法; (最安全/性能最好)

    Class clazz=Class.forName("类的全路径"); (最常用)

3.3.获取方法和属性信息

当我们获得了想要操作的类的Class对象后,可以通过Class类中的方法获取并查看该类中的方法和属性。
示例代码:
file
输出结果:

方法信息: 
public java.lang.String reflection.Person.toString() 
private java.lang.String reflection.Person.getName() 
private void reflection.Person.setName(java.lang.String) 
public void reflection.Person.setAge(int) 
public int reflection.Person.getAge() 
public java.lang.String reflection.Person.getGender() 
public void reflection.Person.setGender(java.lang.String) 
属性信息: 
private java.lang.String reflection.Person.name 
private java.lang.String reflection.Person.gender 
private int reflection.Person.age 
构造方法信息 
private reflection.Person() 
public reflection.Person(java.lang.String,java.lang.String,int)

3.4.创建对象

当我们获取到所需类的Class对象后,可以用它来创建对象,创建对象的方法有两种:

  • 使用Class对象的newInstance()方法来创建该Class对象对应类的实例,但是这种方法要求该Class对象对应的类有默认的空构造器。
  • 先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建 Class对象对应类的实例,通过这种方法可以选定构造方法创建实例。
    示例代码:
    file
    输出结果:
    姓名:张三 性别:男 年龄: 16
    姓名:李四 性别:男 年龄: 20

0评论