写下这个题目的时候,我压力比较大,怕的是费力不讨好。因为反射这一块,对于大多数人员而言太熟悉了,稍微不注意就容易把方向写偏,把知识点写漏。但是,我已经写了注解和动态代理这两个知识点的博客,阅读量还可以,这两个知识点是属于反射机制中的,现在对于注解和动态代理息息相关的反射知识基础我倒是退缩了,所以说看起来很普通的东西,其实真的要一五一十地把它的门道说才方显功力。我们经常说一个人半吊子二把刀,说起来头头是道,做起来却不是那么一回事。 
王阳明说知行合一,很多人只让自己停留在知的阶段,没有行,或者说行的能力薄弱,因为没有行来“事上练”,所以就没有办法不停检测自己的“知”是否正确,也就无法“致良知”,这就是王阳明心学,有兴趣的同学可以自行去阅读相关的书籍。听不懂的也没有关系,大体意思就是实践出真理,理论和实践相结合。对于 Java 反射这类基础知识,很多同学看了一遍就觉得懂了,其实很多时候还是没有懂,只是跟着书本被动阅读,你会产生一种错觉,这种错觉就是你以为你懂了,其实,你没有。如何检测呢?很简单,你在阅读某本书,某个章节之后,你合上书本,闭上眼睛,你试着回想一下,你刚才看过的内容,你能记住多少?别不信,你现在就可以找一本书试一试。 
讲了这么多,我的观点其实很简单,就是认真对待你的一技之长,尽可能把每个知识点真正弄懂,带着自己的思考去学习新的概念,然后适时做一些练习来检测和巩固。

下面,让我们一起认真对待之前可能没有多在意的基础知识之一—— Java 反射。

注意,这篇文章因为内容太多,所以篇幅非常长。中途受不了的同学可以回到目录跳转到感兴趣的小节进行学习。

  • 向一个门外汉介绍反射
  • 反射入口
    • Class
    • Class 的获取
      • 通过 ObjectgetClass
      • 通过 class 标识
      • 通过 ClassforName 方法
  • Class 内容清单
    • Class 的名字

      • 当 Class 代表一个引用时
      • 当 Class 代表一个基本数据类型比如 intclass 的时候
      • 当 Class 代表的是基础数据类型的数组时 比如 int 这样的 3 维数组时
      • simplename 的不同
    • Class 获取修饰符
    • 获取 Class 的成员
      • 获取 Filed
      • 获取 Method
      • 获取 Constructor
    • Field 的操控
      • Field 类型的获取
      • Field 修饰符的获取
      • Field 内容的读取与赋值
    • Method 的操控
      • Method 获取方法名
      • Method 获取方法参数
      • Method 获取返回值类型
      • Method 获取修饰符
      • Method 获取异常类型
      • Method 方法的执行
    • Constructor 的操控
    • 反射中的数组
      • 反射中动态创建数组
      • Array 的读取与赋值
    • 反射中的枚举 Enum
      • 枚举的获取与设定
  • 反射与自动驾驶
  • 总结

向一个门外汉介绍反射

反射是什么?

官方文档上有这么一段介绍:

Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.

我来翻译一下:反射技术通常被用来检测和改变应用程序在 Java 虚拟机中的行为表现。它是一个相对而言比较高级的技术,通常它应用的前提是开发者本身对于 Java 语言特性有很强的理解的基础上。值得说明的是,反射是一种强有力的技术特性,因此可以使得应用程序突破一些藩篱,执行一些常规手段无法企及的目的。

我再通俗概括一下:反射是个很牛逼的功能,能够在程序运行时修改程序的行为。但反射是非常规手段,反射有风险,应用需谨慎。

相信,大部分同学会有稍微清晰一点的概念了。但这还不是我的目的所在。

我的目的是想,我如何向一个刚有一点点 Java 基础的初学者,或者是说毫无 Java 基础的门外汉解释清楚反射这样一种东西?

直接翻译官方文档,显然是不太行。因为那仍然是抽象的,所以,最好的方法仍然是通过类比或者是拟人,用生活场景中具体的事物与抽象的概念建立相关性。

把程序代码比作一辆车,因为 Java 是面向对象的语言,所以这样很容易理解,正常流程中,车子有自己的颜色、车型号、品牌这些属性,也有正常行驶、倒车、停泊这些功能操作。

正常情况下,我们需要为车子配备一个司机,然后按照行为准则规范行驶。

那么反射是什么呢?反射是非常规手段,正常行驶的时候,车子需要司机的驾驶,但是,反射却不需要,因为它就是车子的——自动驾驶。

因为,反射牛逼,又因为反射非常规,所以,它风险未知,需要开发者极强的把控力。而汽车中的自动驾驶技术现在是热门,但是特斯拉都出过故障,所以同样在汽车领域,自动驾驶技术也需要车厂家有极牛逼的风险把控能力,这个基础就是要遵从汽车本身的结构与交通规则,不能因为运用了自动驾驶技术的汽车就不叫做汽车了,应用了反射技术的代码就不叫做代码了。

自动驾驶需要遵守基础规则,同样反射也需要,下面的文章就是介绍反射技术应该遵守的规格与限制。

反射入口

我们试想一下,如果自动驾驶要运用到一辆汽车之上,研发人员首先要拿到的是什么?

肯定是汽车的规格说明书。

同样,反射如果要作用于一段 Java 代码上,那么它也需要拿到一本规格说明书,那么对于反射而言,这本规格说明书是什么呢?

Class

因为 Java 是面向对象的语言,基本上是以类为基础构造了整个程序系统,反射中要求提供的规格说明书其实就是一个类的规格说明书,它就是 Class。

注意的是 Class 是首字母大写,不同于 class 小写,class 是定义类的关键字,而 Class 的本质也是一个类,因为在 Java 中一切都是对象。

public final class Class<T> implements java.io.Serializable,GenericDeclaration,Type,AnnotatedElement {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

Class 就是一个对象,它用来代表运行在 Java 虚拟机中的类和接口。

把 Java 虚拟机类似于高速公路,那么 Class 就是用来描述公路上飞驰的汽车,也就是我前面提到的规格说明书。

Class 的获取

反射的入口是 Class,但是反射中 Class 是没有公开的构造方法的,所以就没有办法像创建一个类一样通过 new 关键字来获取一个 Class 对象。

不过,不用担心,Java 反射中 Class 的获取可以通过下面 3 种方式。

1. 通过 Object.getClass()

对于一个对象而言,如果这个对象可以访问,那么调用 getClass() 方法就可以获取到了它的相应的 Class 对象。

public class Car {}public class Test {public static void main(String[] args) {Car car = new Car();Class clazz = car.getClass();}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

值得注意的是,这种方法不适合基本类型如 int、float 等等。

2. 通过 .class 标识

上面的例子中,Car 是一个类,car 是它的对象,通过 car.getClass() 就获取到了 Car 这个类的 Class 对象,也就是说通过一个类的实例的 getClass() 方法就能获取到它的 Class。如果不想创建这个类的实例的话,就需要通过 `.class 这个标识。

public class Test {public static void main(String[] args) {Class clazz = Car.class;Class cls1 = int.class;Class cls2 = String.class;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3. 通过 Class.forName() 方法

有时候,我们没有办法创建一个类的实例,甚至没有办法用 Car.class 这样的方式去获取一个类的 Class 对象。

这在 Android 开发领域很常见,因为某种目的,Android 工程师把一些类加上了 @hide 注解,所示这些类就没有出现在 SDK 当中,那么,我们要获取这个并不存在于当前开发环境中的类的 Class 对象时就没有辙了吗?答案是否定的,Java 给我们提供了 Class.forName() 这个方法。

只要给这个方法中传入一个类的全限定名称就好了,那么它就会到 Java 虚拟机中去寻找这个类有没有被加载。

try {Class clz = Class.forName("com.frank.test.Car");
} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

“com.frank.test.Car” 就是 Car 这个类的全限定名称,它包括包名+类名。

如果找不到时,它会抛出 ClassNotFoundException 这个异常,这个很好理解,因为如果查找的类没有在 JVM 中加载的话,自然要告诉开发者。

所以,上面 3 节讲述了如何拿到一个类的 Class 对象。

Class 内容清单

仅仅拿到 Class 对象还不够,我们感兴趣的是它的内容。

在正常的代码编写中,我们如果要编写一个类,一般会定义它的属性和方法,如:

public class Car {private String mBand;private Color mColor;public enum Color {RED,WHITE,BLACK,BLUE,YELLOR}public Car() {super();// TODO Auto-generated constructor stub}public Car(String mBand) {this.mBand = mBand;}public void drive() {System.out.println("di di di,开车了!");}@Overridepublic String toString() {return "Car [mBand=" + mBand + ", mColor=" + mColor + "]";}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

现在我们来一一分解它。

Class 的名字

Class 对象也有名字,涉及到的 API 有:

Class.getName();Class.getSimpleName();Class.getCanonicalName();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

现在,说说它们的区别。

因为 Class 是一个入口,它代表引用、基本数据类型甚至是数组对象,所以获取它们的方式又有一点不同。

先从 getName() 说起。

当 Class 代表一个引用时

getName() 方法返回的是一个二进制形式的字符串,比如“com.frank.test.Car”。

当 Class 代表一个基本数据类型,比如 int.class 的时候

getName() 方法返回的是它们的关键字,比如 int.class 的名字是 int。

当 Class 代表的是基础数据类型的数组时 比如 int[][][] 这样的 3 维数组时

getName() 返回 [[[I 这样的字符串。

为什么会这样呢?这是因为,Java 本身对于这一块制定了相应规则,在元素的类型前面添加相应数量的 [ 符号,用 [ 的个数来提示数组的维度,并且值得注意的是,对于基本类型或者是类,都有相应的编码,所谓的编码大多数是用一个大写字母来指示某种类型,规则如下:

需要注意的是类或者是接口的类型编码是 L类名; 的形式,后面有一个分号。

比如 String[].getClass().getName() 结果是 [Ljava.lang.String;。

我们来测试一下代码:

public class Test {public static void main(String[] args) {try {Class clz = Class.forName("com.frank.test.Car");Class clz1 = float.class;Class clz2 = Void.class;Class clz3 = new int[]{}.getClass();Class clz4 = new Car[]{}.getClass();System.out.println(clz.getName());System.out.println(clz1.getName());System.out.println(clz2.getName());System.out.println(clz3.getName());System.out.println(clz4.getName());} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

上面代码的打印结果如下:

com.frank.test.Car
float
java.lang.Void
[I
[Lcom.frank.test.Car;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

刚刚介绍的都是 getName() 的情况,那么 getSimpleName() 和 getCaninolName() 呢?

getSimpleName() 自然是要去获取 simplename 的,那么对于一个 Class 而言什么是 SimpleName 呢?我们先要从嵌套类说起

public class Outter {static class Inner {}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

Outter 这个类中有一个静态的内部类。

Class clz = Outter.Inner.class;System.out.println(" Inner Class name:"+clz.getName());
System.out.println(" Inner Class simple name:"+clz.getSimpleName());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

我们分别打印 Inner 这个类的 Class 对象的 name 和 simplename。

 Inner Class name:com.frank.test.Outter$InnerInner Class simple name:Inner
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

可以看到,因为是内部类,所以通过 getName() 方法获取到的是二进制形式的全限定类名,并且类名前面还有个 $ 符号。 
getSimpleName() 则直接返回了 Inner,去掉了包名限定。

打个比方,我的全名叫做 Frank Zhao,而我的 simplename 就叫做 frank,simplename 之于 name 也是如此。

simplename 的不同

需要注意的是,当获取一个数组的 Class 中的 simplename 时,不同于 getName() 方法,simplename 不是在前面加 [,而是在后面添加对应数量的 [] 。

Class clz = new Outter.Inner[][][]{}.getClass();System.out.println(" Inner Class name:"+clz.getName());
System.out.println(" Inner Class simple name:"+clz.getSimpleName());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

上面代码打印结果是:

 Inner Class name:[[[Lcom.frank.test.Outter$Inner;Inner Class simple name:Inner[][][]
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

还需要注意的是,对于匿名内部类,getSimpleName() 返回的是一个空的字符串。

Runnable run = new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stub}
};System.out.println(" Inner Class name:"+run.getClass().getName());
System.out.println(" Inner Class simple name:"+run.getClass().getSimpleName());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

打印结果是:

 anonymous Class name:com.frank.test.Test$1anonymous Class simple name:
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

最后再来看 getCanonicalName()。

Canonical 是官方、标准的意思,那么 getCanonicalName() 自然就是返回一个 Class 对象的官方名字,这个官方名字 canonicalName 是 Java 语言规范制定的,如果 Class 对象没有 canonicalName 的话就返回 null。

getCanonicalName() 是 getName() 和 getSimpleName() 的结合。

  • getCanonicalName() 返回的也是全限定类名,但是对于内部类,不用 $ 开头,而用 .。
  • getCanonicalName() 对于数组类型的 Class,同 simplename 一样直接在后面添加 [] 。
  • getCanonicalName() 不同于 simplename 的地方是,不存在 canonicalName 的时候返回 null 而不是空字符串。
  • 局部类和匿名内部类不存在 canonicalName。
Class clz = new Outter.Inner[][][]{}.getClass();System.out.println(" Inner Class name:"+clz.getName());
System.out.println(" Inner Class simple name:"+clz.getSimpleName());
System.out.println(" Inner Class canonical name:"+clz.getCanonicalName());//run 是匿名类
Runnable run = new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stub}
};System.out.println(" anonymous Class name:"+run.getClass().getName());
System.out.println(" anonymous Class simple name:"+run.getClass().getSimpleName());
System.out.println(" anonymous Class canonical name:"+run.getClass().getCanonicalName());// local 是局部类
class local{};System.out.println("Local a name:"+local.class.getName());
System.out.println("Local a simplename:"+local.class.getSimpleName());
System.out.println("Local a canonicalname:"+local.class.getCanonicalName());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

打印结果如下:

Inner Class name:[[[Lcom.frank.test.Outter$Inner;
Inner Class simple name:Inner[][][]
Inner Class canonical name:com.frank.test.Outter.Inner[][][]anonymous Class name:com.frank.test.Test$1
anonymous Class simple name:
anonymous Class canonical name:nullLocal a name:com.frank.test.Test$1local
Local a simplename:local
Local a canonicalname:null
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Class 去获取相应名字的知识内容就讲完了,仔细想一下,小小的一个细节,其实蛮有学问的。

好了,我们继续往下。

Class 获取修饰符

通常,Java 开发中定义一个类,往往是要通过许多修饰符来配合使用的。它们大致分为 4 类。

  • 用来限制作用域,如 public、protected、priviate。
  • 用来提示子类复写,abstract。
  • 用来标记为静态类 static。
  • 注解。

Java 反射提供了 API 去获取这些修饰符。

package com.frank.test;public abstract class TestModifier {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们定义了一个类,名字为 TestModifier,被 public 和 abstract 修饰,现在我们要提取这些修饰符。我们只需要调用 Class.getModifiers() 方法就是了,它返回的是一个 int 数值。

System.out.println("modifiers value:"+TestModifier.class.getModifiers());
System.out.println("modifiers :"+Modifier.toString(TestModifier.class.getModifiers()));
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

打印结果是:

modifiers value:1025
modifiers :public abstract
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

大家肯定会有疑问,为什么会返回一个整型数值呢?

这是因为一个类定义的时候可能会被多个修饰符修饰,为了一并获取,所以 Java 工程师考虑到了位运算,用一个 int 数值来记录所有的修饰符,然后不同的位对应不同的修饰符,这些修饰符对应的位都定义在 Modifier 这个类当中。

public class Modifier {public static final int PUBLIC           = 0x00000001;public static final int PRIVATE          = 0x00000002;public static final int PROTECTED        = 0x00000004;public static final int STATIC           = 0x00000008;public static final int FINAL            = 0x00000010;public static final int SYNCHRONIZED     = 0x00000020;public static final int VOLATILE         = 0x00000040;public static final int TRANSIENT        = 0x00000080;public static final int NATIVE           = 0x00000100;public static final int INTERFACE        = 0x00000200;public static final int ABSTRACT         = 0x00000400;public static final int STRICT           = 0x00000800;public static String toString(int mod) {StringBuilder sb = new StringBuilder();int len;if ((mod & PUBLIC) != 0)        sb.append("public ");if ((mod & PROTECTED) != 0)     sb.append("protected ");if ((mod & PRIVATE) != 0)       sb.append("private ");/* Canonical order */if ((mod & ABSTRACT) != 0)      sb.append("abstract ");if ((mod & STATIC) != 0)        sb.append("static ");if ((mod & FINAL) != 0)         sb.append("final ");if ((mod & TRANSIENT) != 0)     sb.append("transient ");if ((mod & VOLATILE) != 0)      sb.append("volatile ");if ((mod & SYNCHRONIZED) != 0)  sb.append("synchronized ");if ((mod & NATIVE) != 0)        sb.append("native ");if ((mod & STRICT) != 0)        sb.append("strictfp ");if ((mod & INTERFACE) != 0)     sb.append("interface ");if ((len = sb.length()) > 0)    /* trim trailing space */return sb.toString().substring(0, len-1);return "";}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

调用 Modifier.toString() 方法就可以打印出一个类的所有修饰符。

当然,Modifier 还提供了一系列的静态工具方法用来对修饰符进行操作。

public static boolean isPublic(int mod) {return (mod & PUBLIC) != 0;}public static boolean isPrivate(int mod) {return (mod & PRIVATE) != 0;
}public static boolean isProtected(int mod) {return (mod & PROTECTED) != 0;
}public static boolean isStatic(int mod) {return (mod & STATIC) != 0;
}public static boolean isFinal(int mod) {return (mod & FINAL) != 0;
}public static boolean isSynchronized(int mod) {return (mod & SYNCHRONIZED) != 0;
}public static boolean isVolatile(int mod) {return (mod & VOLATILE) != 0;
}public static boolean isTransient(int mod) {return (mod & TRANSIENT) != 0;
}public static boolean isNative(int mod) {return (mod & NATIVE) != 0;
}public static boolean isInterface(int mod) {return (mod & INTERFACE) != 0;
}public static boolean isAbstract(int mod) {return (mod & ABSTRACT) != 0;
}public static boolean isStrict(int mod) {return (mod & STRICT) != 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

这些代码的作用,一看就懂,所以不再多说。

获取 Class 的成员

一个类的成员包括属性(有人翻译为字段或者域)、方法。对应到 Class 中就是 Field、Method、Constructor。

获取 Filed

获取指定名字的属性有 2 个 API

public Field getDeclaredField(String name)throws NoSuchFieldException,SecurityException;public Field getField(String name)throws NoSuchFieldException,SecurityException
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

两者的区别就是 getDeclaredField() 获取的是 Class 中被 private 修饰的属性。 getField() 方法获取的是非私有属性,并且 getField() 在当前 Class 获取不到时会向祖先类获取。

获取所有的属性。

//获取所有的属性,但不包括从父类继承下来的属性
public Field[] getDeclaredFields() throws SecurityException {}//获取自身的所有的 public 属性,包括从父类继承下来的。
public Field[] getFields() throws SecurityException {
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

可以用一个例子,给大家加深一下理解。

public class Farther {public int a;private int b;}public class Son extends Farther {int c;private String d;protected float e;
}package com.frank.test;import java.lang.reflect.Field;public class FieldTest {public static void main(String[] args) {// TODO Auto-generated method stubClass cls = Son.class;try {Field field = cls.getDeclaredField("b");} catch (NoSuchFieldException e) {// TODO Auto-generated catch blocke.printStackTrace();System.out.println("getDeclaredField "+e.getMessage());} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();System.out.println("getDeclaredField "+e.getMessage());}try {Field field = cls.getField("b");} catch (NoSuchFieldException e) {// TODO Auto-generated catch blocke.printStackTrace();System.out.println("getField "+e.getMessage());} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();System.out.println("getField "+e.getMessage());}Field[] filed1 = cls.getDeclaredFields();for ( Field f : filed1 ) {System.out.println("Declared Field :"+f.getName());}Field[] filed2 = cls.getFields();for ( Field f : filed2 ) {System.out.println("Field :"+f.getName());}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

代码打印结果:

java.lang.NoSuchFieldException: bat java.lang.Class.getDeclaredField(Unknown Source)at com.frank.test.FieldTest.main(FieldTest.java:13)
java.lang.NoSuchFieldException: bgetDeclaredField bat java.lang.Class.getField(Unknown Source)at com.frank.test.FieldTest.main(FieldTest.java:26)
getField bDeclared Field :c
Declared Field :d
Declared Field :eField :a
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

大家细细体会一下,不过需要注意的是 getDeclaredFileds() 方法获取不到 public 或者是默认的属性,也获取不到从父类继承下来的属性。

获取 Method

类或者接口中的方法对应到 Class 就是 Method。 
相应的 API 如下:

public Method getDeclaredMethod(String name, Class<?>... parameterTypes)public Method getMethod(String name, Class<?>... parameterTypes)public Method[] getDeclaredMethods() throws SecurityExceptionpublic Method getMethod(String name, Class<?>... parameterTypes) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

因为跟 Field 类似,所以不做过多的讲解。parameterTypes 是方法对应的参数。

获取 Constructor

Java 反射把构造器从方法中单独拎出来了,用 Constructor 表示。

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)public Constructor<T> getConstructor(Class<?>... parameterTypes)public Constructor<?>[] getDeclaredConstructors() throws SecurityException public Constructor<?>[] getConstructors() throws SecurityException
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

仍然以前面的 Father 和 Son 两个类为例。

public class Farther {public int a;private int b;public Farther() {super();// TODO Auto-generated constructor stub}}public class Son extends Farther {int c;private String d;protected float e;private Son() {super();// TODO Auto-generated constructor stub}public Son(int c, String d) {super();this.c = c;this.d = d;}}public class ConstructorTest {public static void main(String[] args) {// TODO Auto-generated method stubClass clz = Son.class;Constructor[] constructors = clz.getConstructors();for ( Constructor c : constructors ) {System.out.println("getConstructor:"+c.toString());}constructors = clz.getDeclaredConstructors();for ( Constructor c : constructors ) {System.out.println("getDeclaredConstructors:"+c.toString());}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

测试程序代码的打印结果如下:

getConstructor:public com.frank.test.Son(int,java.lang.String)getDeclaredConstructors:private com.frank.test.Son()
getDeclaredConstructors:public com.frank.test.Son(int,java.lang.String)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

因为,Constructor 不能从父类继承,所以就没有办法通过 getConstructor() 获取到父类的 Constructor。

我们获取到了 Field、Method、Constructor,但这一是终点,相反,这正是反射机制中开始的地方,我们运用反射的目的就是为了获取和操控 Class 对象中的这些成员。

Field 的操控

我们在一个类中定义字段时,通常是这样。

public class Son extends Farther {int c;private String d;protected float e;Car car;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

像 c、d、e、car 这些变量都是属性,在反射机制中映射到 Class 对象中都是 Field,很显然,它们也有对应的类别。

它们要么是 8 种基础类型 int、long、float、double、boolean、char、byte 和 short。或者是引用,所有的引用都是 Object 的后代。

Field 类型的获取

获取 Field 的类型,通过 2 个方法:

public Type getGenericType() {}public Class<?> getType() {}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

注意,两者返回的类型不一样,getGenericType() 方法能够获取到泛型类型。大家可以看下面的代码进行理解:

public class Son extends Farther {int c;private String d;protected float e;public List<Car> cars;public HashMap<Integer,String> map;private Son() {super();// TODO Auto-generated constructor stub}public Son(int c, String d) {super();this.c = c;this.d = d;}}public class FieldTest {public static void main(String[] args) {// TODO Auto-generated method stubClass cls = Son.class;Field[] filed2 = cls.getFields();for ( Field f : filed2 ) {System.out.println("Field :"+f.getName());System.out.println("Field type:"+f.getType());System.out.println("Field generic type:"+f.getGenericType());System.out.println("-------------------");}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

打印结果:

Field :cars
Field type:interface java.util.List
Field generic type:java.util.List<com.frank.test.Car>
-------------------
Field :map
Field type:class java.util.HashMap
Field generic type:java.util.HashMap<java.lang.Integer, java.lang.String>
-------------------
Field :a
Field type:int
Field generic type:int
-------------------
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

可以看到 getGenericType() 确实把泛型都打印出来了,它比 getType() 返回的内容更详细。

Field 修饰符的获取

同 Class 一样,Field 也有很多修饰符。通过 getModifiers() 方法就可以轻松获取。

public int getModifiers() {}
  • 1
  • 2
  • 1
  • 2

这个与前面 Class 获取修饰符一致,所以不需要再讲,不清楚的同学翻看前面的内容就好了。

Field 内容的读取与赋值

这个应该是反射机制中对于 Field 最主要的目的了。

Field 这个类定义了一系列的 get 方法来获取不同类型的值。


public Object get(Object obj);public int getInt(Object obj);public long getLong(Object obj)throws IllegalArgumentException, IllegalAccessException;public float getFloat(Object obj)throws IllegalArgumentException, IllegalAccessException;public short getShort(Object obj)throws IllegalArgumentException, IllegalAccessException;public double getDouble(Object obj)throws IllegalArgumentException, IllegalAccessException;public char getChar(Object obj)throws IllegalArgumentException, IllegalAccessException;public byte getByte(Object obj)throws IllegalArgumentException, IllegalAccessException;public boolean getBoolean(Object obj)throws IllegalArgumentException, IllegalAccessException
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

Field 又定义了一系列的 set 方法用来对其自身进行赋值。

public void set(Object obj, Object value);public void getInt(Object obj,int value);public void getLong(Object obj,long value)throws IllegalArgumentException, IllegalAccessException;public void getFloat(Object obj,float value)throws IllegalArgumentException, IllegalAccessException;public void getShort(Object obj,short value)throws IllegalArgumentException, IllegalAccessException;public void getDouble(Object obj,double value)throws IllegalArgumentException, IllegalAccessException;public void getChar(Object obj,char value)throws IllegalArgumentException, IllegalAccessException;public void getByte(Object obj,byte b)throws IllegalArgumentException, IllegalAccessException;public void getBoolean(Object obj,boolean b)throws IllegalArgumentException, IllegalAccessException
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

可能有同学会对方法中出现的 Object 参数有疑问,它其实是类的实例引用,这里涉及一个细节。

Class 本身不对成员进行储存,它只提供检索,所以需要用 Field、Method、Constructor 对象来承载这些成员,所以,针对成员的操作时,一般需要为成员指定类的实例引用。如果难于理解的话,可以这样理解,班级这个概念是一个类,一个班级有几十名学生,现在有A、B、C 3 个班级,将所有班级的学生抽出来集合到一个场地来考试,但是学生在试卷上写上自己名字的时候,还要指定自己的班级,这里涉及到的 Object 其实就是类似的作用,表示这个成员是具体属于哪个 Object。这个是为了精确定位。

下面用代码来说明:

A testa = new A();
testa.a = 10;System.out.println("testa.a = "+testa.a);Class c = A.class;try {Field fielda = c.getField("a");int ra = fielda.getInt(testa);System.out.println("reflection testa.a = "+ra);fielda.setInt(testa, 15);System.out.println("testa.a = "+testa.a);} catch (NoSuchFieldException e) {// TODO Auto-generated catch blocke.printStackTrace();
} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();
} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();
} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

打印结果如下:

testa.a = 10
reflection testa.a = 10
testa.a = 15
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

我们再来看看 Field 被 private 修饰的情况

public class A {public int a;private int b;public int getB() {return b;}public void setB(int b) {this.b = b;}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

再编写测试代码

A testa = new A();
testa.setB(3);System.out.println("testa.b = "+testa.getB());Class c = A.class;try {Field fieldb = c.getDeclaredField("b");int rb = fieldb.getInt(testa);System.out.println("reflection testa.b = "+rb);fieldb.setInt(testa, 20);System.out.println("testa.b = "+testa.getB());} catch (NoSuchFieldException e) {// TODO Auto-generated catch blocke.printStackTrace();
} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();
} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();
} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

打印的结果如下:

testa.b = 3
java.lang.IllegalAccessException: Class com.frank.test.FieldTest can not access a member of class com.frank.test.A with modifiers "private"at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(Unknown Source)at java.lang.reflect.AccessibleObject.checkAccess(Unknown Source)at java.lang.reflect.Field.getInt(Unknown Source)at com.frank.test.FieldTest.main(FieldTest.java:20)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

抛异常了。这是因为在反射中访问了 private 修饰的成员,如果要消除异常的话,需要添加一句代码。

fieldb.setAccessible(true);
  • 1
  • 1

再看打印结果

testa.b = 3
reflection testa.b = 3
testa.b = 20
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

Method 的操控

Method 对应普通类的方法。 
我们看看一般普通类的方法的构成。


public int add(int a,int b);
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

方法由下面几个要素构成: 
- 方法名 
- 方法参数 
- 方法返回值 
- 方法的修饰符 
- 方法可能会抛出的异常

很显然,反射中 Method 提供了相应的 API 来提取这些元素。

Method 获取方法名

通过 getName() 这个方法就好了。

以前面的 Car 类作为测试对象。

public class MethodTest {public static void main(String[] args) {// TODO Auto-generated method stubCar car = new Car();Class clz = car.getClass();Method methods[] = clz.getDeclaredMethods();for ( Method m : methods ) {System.out.println("method name:"+m.getName());} }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

打印结果如下:

method name:toString
method name:drive
  • 1
  • 2
  • 1
  • 2

Method 获取方法参数

涉及到的 API 如下:

public Parameter[] getParameters() {}
  • 1
  • 2
  • 1
  • 2

返回的是一个 Parameter 数组,在反射中 Parameter 对象就是用来映射方法中的参数。经常使用的方法有:

Parameter.java

// 获取参数名字
public String getName() {}// 获取参数类型
public Class<?> getType() {}// 获取参数的修饰符
public int getModifiers() {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

当然,有时候我们不需要参数的名字,只要参数的类型就好了,通过 Method 中下面的方法获取。 
Method.java

// 获取所有的参数类型
public Class<?>[] getParameterTypes() {}// 获取所有的参数类型,包括泛型
public Type[] getGenericParameterTypes() {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

下面,同样进行测试。

public class Car {private String mBand;private Color mColor;public enum Color {RED,WHITE,BLACK,BLUE,YELLOR}public Car() {super();// TODO Auto-generated constructor stub}public Car(String mBand) {this.mBand = mBand;}public void drive() {System.out.println("di di di,开车了!");}@Overridepublic String toString() {return "Car [mBand=" + mBand + ", mColor=" + mColor + "]";}public void test(String[] paraa,List<String> b,HashMap<Integer,Son> maps) {}}public class MethodTest {public static void main(String[] args) {// TODO Auto-generated method stubCar car = new Car();Class clz = car.getClass();Method methods[] = clz.getDeclaredMethods();for ( Method m : methods ) {System.out.println("method name:"+m.getName());Parameter[] paras = m.getParameters();for ( Parameter p : paras ) {System.out.println(" parameter :"+p.getName()+" "+p.getType().getName());}Class[] pTypes = m.getParameterTypes();System.out.println("method para types:");for ( Class type : pTypes ) {System.out.print(" "+ type.getName());}System.out.println();Type[] gTypes = m.getGenericParameterTypes();System.out.println("method para generic types:");for ( Type type : gTypes ) {System.out.print(" "+ type.getTypeName());}System.out.println();System.out.println("==========================================");} }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83

打印结果如下:

method name:toString
method para types:method para generic types:==========================================
method name:testparameter :arg0 [Ljava.lang.String;parameter :arg1 java.util.Listparameter :arg2 java.util.HashMap
method para types:[Ljava.lang.String; java.util.List java.util.HashMap
method para generic types:java.lang.String[] java.util.List<java.lang.String> java.util.HashMap<java.lang.Integer, com.frank.test.Son>
==========================================
method name:drive
method para types:method para generic types:==========================================
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

Method 获取返回值类型

// 获取返回值类型
public Class<?> getReturnType() {}// 获取返回值类型包括泛型
public Type getGenericReturnType() {}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Method 获取修饰符

public int getModifiers() {}
  • 1
  • 2
  • 1
  • 2

这部分内容前面已经讲过。

Method 获取异常类型

public Class<?>[] getExceptionTypes() {}public Type[] getGenericExceptionTypes() {}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

Method 方法的执行

这个应该是整个反射机制的核心内容了,很多时候运用反射目的其实就是为了以常规手段执行 Method。

public Object invoke(Object obj, Object... args) {}
  • 1
  • 2
  • 1
  • 2

Method 调用 invoke() 的时候,存在许多细节:

  • invoke() 方法中第一个参数 Object 实质上是 Method 所依附的 Class 对应的类的实例,如果这个方法是一个静态方法,那么 ojb 为 null,后面的可变参数 Object 对应的自然就是参数。

  • invoke() 返回的对象是 Object,所以实际上执行的时候要进行强制转换。

  • 在对 Method 调用 invoke() 的时候,如果方法本身会抛出异常,那么这个异常就会经过包装,由 Method 统一抛出 InvocationTargetException。而通过 InvocationTargetException.getCause() 可以获取真正的异常。

下面同样通过例子来说明,我们新建立一个类,要添加一个 static 修饰的静态方法,一个普通的方法和一个会抛出异常的方法。

public class TestMethod {public static void testStatic () {System.out.println("test static");}private  int add (int a,int b ) {return a + b;}public void testException () throws IllegalAccessException {throw new IllegalAccessException("You have some problem.");}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

我们编写测试代码:

public class InvokeTest {public static void main(String[] args) {// TODO Auto-generated method stubClass testCls = TestMethod.class;try {Method mStatic = testCls.getMethod("testStatic",null);// 测试静态方法mStatic.invoke(null, null);} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}TestMethod t = new TestMethod();try {Method mAdd = testCls.getDeclaredMethod("add",int.class,int.class);// 通过这句代码才能访问 private 修饰的 MethodmAdd.setAccessible(true);int result = (int) mAdd.invoke(t, 1,2);System.out.println("add method result:"+result);} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}try {Method testExcep = testCls.getMethod("testException",null);try {testExcep.invoke(t, null);} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch block//e.printStackTrace();// 通过 InvocationTargetException.getCause() 获取被包装的异常System.out.println("testException occur some error,Error type is :"+e.getCause().getClass().getName());System.out.println("Error message is :"+e.getCause().getMessage());}} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

打印结果如下:

test static
add method result:3
testException occur some error,Error type is :java.lang.IllegalAccessException
Error message is :You have some problem.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

Constructor 的操控

在平常开发的时候,构造器也称构造方法,但是在反射机制中却把它与 Method 分离开来,单独用 Constructor 这个类表示。

Constructor 同 Method 差不多,但是它特别的地方在于,它能够创建一个对象。

在 Java 反射机制中有两种方法可以用来创建类的对象实例:Class.newInstance() 和 Constructor.newInstance()。官方文档建议开发者使用后面这种方法,下面是原因。

  • Class.newInstance() 只能调用无参的构造方法,而 Constructor.newInstance() 则可以调用任意的构造方法。
  • Class.newInstance() 通过构造方法直接抛出异常,而 Constructor.newInstance() 会把抛出来的异常包装到 InvocationTargetException 里面去,这个和 Method 行为一致。
  • Class.newInstance() 要求构造方法能够被访问,而 Constructor.newInstance() 却能够访问 private 修饰的构造器。

还是通过代码来验证。

public class TestConstructor {private String self;public TestConstructor() {self = " Frank ";}public TestConstructor(String self) {this.self = self;}@Overridepublic String toString() {return "TestConstructor [self=" + self + "]";}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

上面的类中有 2 个构造方法,一个无参,一个有参数。编写测试代码:

public class NewInstanceTest {public static void main(String[] args) {// TODO Auto-generated method stubClass clz = TestConstructor.class;try {TestConstructor test1 = (TestConstructor) clz.newInstance();System.out.println(test1.toString());} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}try {Constructor con = clz.getConstructor(String.class);TestConstructor test2 = (TestConstructor) con.newInstance("Zhao");System.out.println(test2.toString());} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

分别用 Class.newInstance() 和 Constructor.newInstance() 方法来创建类的实例,打印结果如下:

TestConstructor [self= Frank ]
TestConstructor [self=Zhao]
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

可以看到通过 Class.newInstance() 方法调用的构造方法确实是无参的那个。

现在,我们学习了 Class 对象的获取,也能够获取它内部成员 Filed、Method 和 Constructor 并且能够操作它们。在这个基础上,我们已经能够应付普通的反射开发了。

但是,Java 反射机制还另外细分了两个概念:数组和枚举。

反射中的数组

数组本质上是一个 Class,而在 Class 中存在一个方法用来识别它是否为一个数组。 
Class.java

public native boolean isArray();
  • 1
  • 2
  • 1
  • 2

为了便于测试,我们创建一个新的类

public class Shuzu {private int[] array;private Car[] cars;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

其中有一个 int 型的数组属性,它的名字叫做 array。还有一个 cars 数组,它的类型是 Car,是之前定义好的类。 当然,array 和 cars 是 Shuzu 这个类的 Field,对于 Field 的角度来说,它是数组类型,我们可以这样理解数组可以同 int、char 这些基本类型一样成为一个 Field 的类别。

我们可能通过一系列的 API 来获取它的具体信息,刚刚有提到它本质上还是一个 Class 而已。

getName();getComponentType();
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

第二个方法是获取数组的里面的元素的类型,比如 int[] 数组的 componentType 自然就是 int。

按照惯例,写代码验证。

public class ArraysTest {public static void main(String[] args) {Class clz = Shuzu.class;Field[] fields = clz.getDeclaredFields();for ( Field f : fields ) {// 获取 Field 的类型Class c = f.getType();// 判断这个类型是不是数组类型if ( c.isArray()) {System.out.println("Type is "+c.getName());System.out.println("ComponentType type is :"+c.getComponentType());}}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

打印结果如下:

Type is [I
ComponentType type is :int
Type is [Lcom.frank.test.Car;
ComponentType type is :class com.frank.test.Car
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

反射中动态创建数组

反射创建数组是通过 Array.newInstance() 这个方法。 
Array.java

public static Object newInstance(Class<?> componentType, int... dimensions)throws IllegalArgumentException, NegativeArraySizeException {}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

第一个参数指定的是数组内的元素类型,后面的是可变参数,表示的是相应维度的数组长度限制。

比如,我要创建一个 int[2][3] 的数组。

Array.newInstance(int.class,2,3);
  • 1
  • 2
  • 1
  • 2

Array 的读取与赋值

首先,对于 Array 整体的读取与赋值,把它作为一个普通的 Field,根据 Class 中相应获取和设置就好了。调用的是 Field 中对应的方法。

public void set(Object obj,Object value)throws IllegalArgumentException,IllegalAccessException;public Object get(Object obj)throws IllegalArgumentException,IllegalAccessException;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

还需要处理的情况是对于数组中指定位置的元素进行读取与赋值,这要涉及到 Array 提供的一系列 setXXX() 和 getXXX() 方法。因为和之前 Field 相应的 set 、get 方法类似,所以我在下面只摘抄典型的几种,大家很容易知晓其它类型的怎么操作。

public static void set(Object array,int index,Object value)throws IllegalArgumentException,ArrayIndexOutOfBoundsException;public static void setBoolean(Object array,int index,boolean z)throws IllegalArgumentException,ArrayIndexOutOfBoundsException;public static Object get(Object array,int index)throws IllegalArgumentException,ArrayIndexOutOfBoundsException;public static short getShort(Object array,int index)throws IllegalArgumentException,ArrayIndexOutOfBoundsException;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

进行代码测试:

public class ArraysTest {public static void main(String[] args) {Class clz = Shuzu.class;try {Shuzu shu = (Shuzu) clz.newInstance();Field arrayF = clz.getDeclaredField("array");arrayF.setAccessible(true);Object o = Array.newInstance(int.class, 3);Array.set(o, 0, 1);Array.set(o, 1, 3);Array.set(o, 2, 3);arrayF.set(shu, o);int[] array = shu.getArray();for ( int i = 0;i < array.length;i++) {System.out.println("array index "+i+" value:"+array[i]);}} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NoSuchFieldException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

打印结果如下:

array index 0 value:1
array index 1 value:3
array index 2 value:3
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

反射中的枚举 Enum

同数组一样,枚举本质上也是一个 Class 而已,但反射中还是把它单独提出来了。

我们来看一般程序开发中枚举的表现形式。

public enum State {IDLE,DRIVING,STOPPING,test();int test1() {return 0;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

枚举真的跟类很相似,有修饰符、有方法、有属性字段甚至可以有构造方法。

在 Java 反射中,可以把枚举看成一般的 Class,但是反射机制也提供了 3 个特别的的 API 用于操控枚举。

// 用来判定 Class 对象是不是枚举类型
Class.isEnum()// 获取所有的枚举常量
Class.getEnumConstants()// 判断一个 Field 是不是枚举常量
java.lang.reflect.Field.isEnumConstant()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

枚举的获取与设定

因为等同于 Class,所以枚举的获取与设定就可以通过 Field 中的 get() 和 set() 方法。

需要注意的是,如果要获取枚举里面的 Field、Method、Constructor 可以调用 Class 的通用 API。

用例子来加深理解吧。

public enum State {IDLE,DRIVING,STOPPING,test();int test1() {return 0;}}public class Meiju {private State state = State.DRIVING;public State getState() {return state;}public void setState(State state) {this.state = state;}
}public static void main(String[] args) {Class clz = State.class;if ( clz.isEnum()){System.out.println(clz.getName()+" is Enum");System.out.println(Arrays.asList(clz.getEnumConstants()));// 获取枚举中所有的 FieldField[] fs = clz.getDeclaredFields();for ( Field f : fs ) {if ( f.isEnumConstant()){System.out.println(f.getName()+" is EnumConstant");}else {System.out.println(f.getName()+" is not EnumConstant");}}Class cMeiju = Meiju.class;Meiju meiju = new Meiju();try {Field f = cMeiju.getDeclaredField("state");f.setAccessible(true);try {State state = (State) f.get(meiju);System.out.println("State current is "+state);f.set(meiju, State.STOPPING);System.out.println("State current is "+meiju.getState());} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}} catch (NoSuchFieldException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84

打印结果如下:

com.frank.test.State is Enum
[IDLE, DRIVING, STOPPING, test]
IDLE is EnumConstant
DRIVING is EnumConstant
STOPPING is EnumConstant
test is EnumConstant
ENUM$VALUES is not EnumConstant
State current is DRIVING
State current is STOPPING
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

到这里,反射的所有知识基本上讲完了。下面进行模拟实战。

反射与自动驾驶

文章开头,我用自动驾驶的技术来比喻反射,实际上的目的是为了给初学者一个大体的印象和一个模糊的轮廓,实际上反射不是自动驾驶,它是什么取决于你自己对它的理解。

下段代码的目标是为了对比,先定义一个类 AutoDrive,这个类有一系列的属性,然后有一系列的方法,先用普通编码的方式来创建这个类的对象,调用它的方法。然后用反射的机制模拟自动驾驶。

汽车开动的步骤,以手动档为例。 
1. 空档发动。 
2. 打左转向灯。 
3. 踩离合挂一档。 
4. 起步松手铩。

现在代码模拟

public class AutoDrive {public enum Color {WHITE,REN,BLUE}private String vendor;private Color color;public AutoDrive(String vendor, Color color) {super();this.vendor = vendor;this.color = color;}public AutoDrive() {vendor = "Nissan";color = Color.WHITE;}public void drive(){boot();turnOnLeftLight();cailiheguayidang();songshousha();tips();}private void tips() {System.out.println("您正在驾驶 "+color+" "+vendor+" 汽车,小心行驶。");}private void songshousha() {// TODO Auto-generated method stubSystem.out.println("起步松手铩。");}private void cailiheguayidang() {// TODO Auto-generated method stubSystem.out.println("踩离合器,挂一档");}private void turnOnLeftLight() {// TODO Auto-generated method stubSystem.out.println("打左向灯");}private void boot() {// TODO Auto-generated method stubSystem.out.println("空档发动汽车");}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

我们只要创建一个 AutoDrive 的对象,调用它的 drive() 方法就好了。

public class DriveTest {public static void main(String[] args) {AutoDrive car = new AutoDrive();car.drive();}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

结果如下:

空档发动汽车
打左向灯
踩离合器,挂一档
起步松手铩。
您正在驾驶 WHITE Nissan 汽车,小心行驶。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们现在要使用自动驾驶技术,具体到代码就是反射,因为非常规嘛。

public class DriveTest {public static void main(String[] args) {AutoDrive car = new AutoDrive();car.drive();Class cls = AutoDrive.class;try {Constructor cons = cls.getConstructor(String.class,AutoDrive.Color.class);// 利用反射技术创建 AutoDrive 对象AutoDrive autoDrive = (AutoDrive) cons.newInstance("Tesla",AutoDrive.Color.RED);// 获取能够驱动汽车的 drive 方法Method method = cls.getMethod("drive");System.out.println("=====================\n自动驾驶马上开始\n================");// 通过反射调用 Method 方法,最终车子跑去起来method.invoke(autoDrive, null);} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

最后,打印结果:

空档发动汽车
打左向灯
踩离合器,挂一档
起步松手铩。
您正在驾驶 WHITE Nissan 汽车,小心行驶。
=====================
自动驾驶马上开始
================
空档发动汽车
打左向灯
踩离合器,挂一档
起步松手铩。
您正在驾驶 RED Tesla 汽车,小心行驶。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

总结

  1. Java 中的反射是非常规编码方式。
  2. Java 反射机制的操作入口是获取 Class 文件。 有 Class.forName()、 .class 和 Object.getClass() 3 种。
  3. 获取 Class 对象后还不够,需要获取它的 Members,包含 Field、Method、Constructor。
  4. Field 操作主要涉及到类别的获取,及数值的读取与赋值。
  5. Method 算是反射机制最核心的内容,通常的反射都是为了调用某个 Method 的 invoke() 方法。
  6. 通过 Class.newInstance() 和 Constructor.newInstance() 都可以创建类的对象实例,但推荐后者。因为它适应于任何构造方法,而前者只会调用可见的无参数的构造方法。
  7. 数组和枚举可以被看成普通的 Class 对待。

最后,需要注意的是。

反射是非常规开发手段,它会抛弃 Java 虚拟机的很多优化,所以同样功能的代码,反射要比正常方式要慢,所以考虑到采用反射时,要考虑它的时间成本。另外,就如无人驾驶之于汽车一样,用着很爽的同时,其实风险未知。

洋洋洒洒已经 2000 多行了,本来还有东西没有写完,因为这一块内容实在太多了。只能另外写一篇文章了,讲得是反射中一些常见的细节和容易出错的地方。不过,这篇文章的内容已经足够应付平常开发中所需要的反射知识了。

原文: http://blog.csdn.net/briblue/article/details/74616922

细说反射,Java 和 Android 开发者必须跨越的坎相关推荐

  1. Java、Android注解代码生成(ButterKnife原理、ViewBinding)

    前言 首先需要一些先验知识: 浅谈Java/Android下的注解 Java.Android基础之-反射 Java.Android静态代理与动态代理 简介 在我们常用的框架中注解和自动生成代码的身影很 ...

  2. android反射开启通知_作为Android开发者 你真的知道app从启动到主页显示的过程吗?...

    前言 之前我跟大家说过,在一个夜黑风高的晚上,我的男同事突然给我发了一条微信,我点开来看,他竟然问我Android从按下开机键到启动到底发生了什么?此刻我的内心如下图: 然后就在昨天晚上,我又收到了他 ...

  3. 那些java半路转Android开发者现状;后悔转行,java开发最吃香

    前言 大概是去年底开始,身边不断有人跟我抱怨,之前是做java开发的,看到Android特别火,就奋不顾身转行到Android开发中来了.当初是想着变身成为风口上的猪,是准备起飞的. 没想到这么快就摔 ...

  4. android java 调试工具_调试应用  |  Android 开发者  |  Android Developers

    Auto 如果您希望 Android Studio 自动为您要调试的代码选择最合适的选项,请选择此调试类型.例如,如果您的项目包含任何 C 或 C++ 代码,Android Studio 会自动使用 ...

  5. Android开发者进退两难的出路;转行还是进阶?转行转什么最好

    [](()转行转什么最好? 每个人的自身情况不同,转的方向也会有所区别,转人工智能相关开发肯定是最好的,毕竟风口上的猪也能飞,跟着风口无可厚非.只可惜人工智能相关对学历和算法要求很高,最起码得是个硕士 ...

  6. 临近2022年末: Android 开发者的出路在哪?转行还是进阶?

    你可能知道 Android 的近况,大体上现在移动开发已经不像以前那么活跃了,完全没有了几年安卓开发的那种火热势头. 与此同时,有很多发言说 Android 的开发冷却了,不行了,没有前途了 那么到底 ...

  7. Java开源Android开源项目

    FBReaderJ FBReaderJ用于Android平台的电子书阅读器,它支持多种电子书籍格式包括:oeb.ePub和fb2.此外还支持直接读取zip.tar和gzip等压缩文档. 更多FBRea ...

  8. 给 Android 开发者的第一堂课

    安卓(Android)是一种基于 Linux 内核(不包含 GNU 组件)的自由及开放源代码的操作系统.主要使用于移动设备,如智能手机和平板电脑,由美国 Google 公司和 开放手机联盟 领导及开发 ...

  9. Android开发者库整理

    =================================================================================== 本文转自:http://blog ...

最新文章

  1. 吸烟检测 yolov5开源推荐
  2. python是一种语言还是一个软件-python和GO语言应该选择哪一个?老男孩教育
  3. Robust 源代码分析之gradle-plugin
  4. 【STM32】SPI 实验代码详解
  5. git:如何让不同开发者提交在同一条直线上
  6. inotifypropertychanged接受不执行_scp客户端现多个漏洞,可执行恶意脚本
  7. java实现蛇蛇大作战_蛇蛇大作战3D旋涡版
  8. 智慧楼宇、消防系统、门禁管理、暖通空调、给排水、变配电、设备管理、停车管理、能源管理、故障检测、客流统计、运行控制、权限分配、物联网、Axure原型、rp原型、产品原型
  9. php框架 dirname,PHP目录函数basename()与dirname()
  10. 51汇编与c语言混合编程,C51与汇编混合编程详解
  11. Android原生TTS的基本使用以及配合中文语音包实现中文TTS
  12. GandCrab勒索病毒
  13. 分享2020年第三方支付产品服务流程
  14. 基于图的广度优先搜索策略(耿7.11)
  15. ubuntu上通过命令行导出mysql数据库文件到widows系统上
  16. web页面实现剪切板图片黏贴功能
  17. Canvas绘制大时钟
  18. Python输出[m,n]既能被3整除又能被7整除的数的个数
  19. 泰山OFFICE技术讲座:为字体调整字间距的研究,设置值何时生效
  20. oracle云erp产品有哪些,云ERP与传统ERP的区别有哪些百洋智能科技为您分析

热门文章

  1. socket编程之select()
  2. Unity的Lerp函数实现缓动
  3. (转载)adb模拟按键事件KeyCode
  4. CUDA程序编写具体参数设置
  5. Linux双网卡NAT共享上网
  6. Python的C/C++扩展
  7. 写python的c扩展简介
  8. Matlab中数据拟合函数lsqcurvefit的使用方法与常见问题
  9. CSDN转载别人文章的详细步骤
  10. [云炬创业基础笔记]第二章创业者测试13