获取 Class 的成员
一个类的成员包括属性(有人翻译为字段或者域)、方法。对应到 Class 中就是 Field、Method、Constructor。
获取 Filed
获取指定名字的属性有 2 个 API
public Field getDeclaredField(String name)
throws NoSuchFieldException,
SecurityException;
public Field getField(String name)
throws NoSuchFieldException,
SecurityException
两者的区别就是 getDeclaredField() 获取的是 Class 中被 private 修饰的属性。 getField() 方法获取的是非私有属性,并且 getField() 在当前 Class 获取不到时会向祖先类获取。
获取所有的属性。
//获取所有的属性,但不包括从父类继承下来的属性
public Field[] getDeclaredFields() throws SecurityException {}
//获取自身的所有的 public 属性,包括从父类继承下来的。
public Field[] getFields() throws SecurityException {
可以用一个例子,给大家加深一下理解。
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 stub
Class cls = Son.class;
try {
Field field = cls.getDeclaredField("b");
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("getDeclaredField "+e.getMessage());
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("getDeclaredField "+e.getMessage());
}
try {
Field field = cls.getField("b");
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("getField "+e.getMessage());
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.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());
}
}
}
代码打印结果:
java.lang.NoSuchFieldException: b
at java.lang.Class.getDeclaredField(Unknown Source)
at com.frank.test.FieldTest.main(FieldTest.java:13)
java.lang.NoSuchFieldException: bgetDeclaredField b
at java.lang.Class.getField(Unknown Source)
at com.frank.test.FieldTest.main(FieldTest.java:26)
getField b
Declared Field :c
Declared Field :d
Declared Field :e
Field :a
大家细细体会一下,不过需要注意的是 getDeclaredFileds() 方法可以获取 private、protected、public 和 default 属性,但是它获取不到从父类继承下来的属性。
获取 Method
类或者接口中的方法对应到 Class 就是 Method。
相应的 API 如下:
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
public Method getMethod(String name, Class<?>... parameterTypes)
public Method[] getDeclaredMethods() throws SecurityException
public Method getMethod(String name, Class<?>... parameterTypes)
因为跟 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
仍然以前面的 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 stub
Class 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());
}
}
}
测试程序代码的打印结果如下:
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)
因为,Constructor 不能从父类继承,所以就没有办法通过 getConstructor() 获取到父类的 Constructor。
我们获取到了 Field、Method、Constructor,但这一是终点,相反,这正是反射机制中开始的地方,我们运用反射的目的就是为了获取和操控 Class 对象中的这些成员。
Field 的操控
我们在一个类中定义字段时,通常是这样。
public class Son extends Farther {
int c;
private String d;
protected float e;
Car car;
}
像 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() {}
注意,两者返回的类型不一样,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 stub
Class 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("-------------------");
}
}
}
打印结果:
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
-------------------
可以看到 getGenericType() 确实把泛型都打印出来了,它比 getType() 返回的内容更详细。
Field 修饰符的获取
同 Class 一样,Field 也有很多修饰符。通过 getModifiers() 方法就可以轻松获取。
public int getModifiers() {}
这个与前面 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
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
可能有同学会对方法中出现的 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 block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
打印结果如下:
testa.a = 10
reflection testa.a = 10
testa.a = 15
我们再来看看 Field 被 private 修饰的情况
public class A {
public int a;
private int b;
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
}
再编写测试代码
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 block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
打印的结果如下:
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)
抛异常了。这是因为在反射中访问了 private 修饰的成员,如果要消除异常的话,需要添加一句代码。
fieldb.setAccessible(true);
再看打印结果
testa.b = 3
reflection testa.b = 3
testa.b = 20
Method 的操控
Method 对应普通类的方法。
我们看看一般普通类的方法的构成。
public int add(int a,int b);
方法由下面几个要素构成:
- 方法名
- 方法参数
- 方法返回值
- 方法的修饰符
- 方法可能会抛出的异常
很显然,反射中 Method 提供了相应的 API 来提取这些元素。
Method 获取方法名
通过 getName() 这个方法就好了。
以前面的 Car 类作为测试对象。
public class MethodTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Car car = new Car();
Class clz = car.getClass();
Method methods[] = clz.getDeclaredMethods();
for ( Method m : methods ) {
System.out.println("method name:"+m.getName());
}
}
}
打印结果如下:
method name:toString
method name:drive
Method 获取方法参数
涉及到的 API 如下:
public Parameter[] getParameters() {}
返回的是一个 Parameter 数组,在反射中 Parameter 对象就是用来映射方法中的参数。经常使用的方法有:
Parameter.java
// 获取参数名字
public String getName() {}
// 获取参数类型
public Class<?> getType() {}
// 获取参数的修饰符
public int getModifiers() {}
当然,有时候我们不需要参数的名字,只要参数的类型就好了,通过 Method 中下面的方法获取。
Method.java
// 获取所有的参数类型
public Class<?>[] getParameterTypes() {}
// 获取所有的参数类型,包括泛型
public Type[] getGenericParameterTypes() {}
下面,同样进行测试。
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,开车了!");
}
@Override
public 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 stub
Car 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("==========================================");
}
}
}
打印结果如下:
method name:toString
method para types:
method para generic types:
==========================================
method name:test
parameter :arg0 [Ljava.lang.String;
parameter :arg1 java.util.List
parameter :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:
==========================================
Method 获取返回值类型
// 获取返回值类型
public Class<?> getReturnType() {}
// 获取返回值类型包括泛型
public Type getGenericReturnType() {}
Method 获取修饰符
public int getModifiers() {}
这部分内容前面已经讲过。
Method 获取异常类型
public Class<?>[] getExceptionTypes() {}
public Type[] getGenericExceptionTypes() {}
Method 方法的执行
这个应该是整个反射机制的核心内容了,很多时候运用反射目的其实就是为了以常规手段执行 Method。
public Object invoke(Object obj, Object... args) {}
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.");
}
}
我们编写测试代码:
public class InvokeTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Class testCls = TestMethod.class;
try {
Method mStatic = testCls.getMethod("testStatic",null);
// 测试静态方法
mStatic.invoke(null, null);
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
TestMethod t = new TestMethod();
try {
Method mAdd = testCls.getDeclaredMethod("add",int.class,int.class);
// 通过这句代码才能访问 private 修饰的 Method
mAdd.setAccessible(true);
int result = (int) mAdd.invoke(t, 1,2);
System.out.println("add method result:"+result);
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Method testExcep = testCls.getMethod("testException",null);
try {
testExcep.invoke(t, null);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.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 block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
打印结果如下:
test static
add method result:3
testException occur some error,Error type is :java.lang.IllegalAccessException
Error message is :You have some problem.
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;
}
@Override
public String toString() {
return "TestConstructor [self=" + self + "]";
}
}
上面的类中有 2 个构造方法,一个无参,一个有参数。编写测试代码:
public class NewInstanceTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Class clz = TestConstructor.class;
try {
TestConstructor test1 = (TestConstructor) clz.newInstance();
System.out.println(test1.toString());
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.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 block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
分别用 Class.newInstance() 和 Constructor.newInstance() 方法来创建类的实例,打印结果如下:
TestConstructor [self= Frank ]
TestConstructor [self=Zhao]
可以看到通过 Class.newInstance() 方法调用的构造方法确实是无参的那个。
现在,我们学习了 Class 对象的获取,也能够获取它内部成员 Filed、Method 和 Constructor 并且能够操作它们。在这个基础上,我们已经能够应付普通的反射开发了。
但是,Java 反射机制还另外细分了两个概念:数组和枚举。
反射中的数组
数组本质上是一个 Class,而在 Class 中存在一个方法用来识别它是否为一个数组。
Class.java
public native boolean isArray();
为了便于测试,我们创建一个新的类
public class Shuzu {
private int[] array;
private Car[] cars;
}
其中有一个 int 型的数组属性,它的名字叫做 array。还有一个 cars 数组,它的类型是 Car,是之前定义好的类。 当然,array 和 cars 是 Shuzu 这个类的 Field,对于 Field 的角度来说,它是数组类型,我们可以这样理解数组可以同 int、char 这些基本类型一样成为一个 Field 的类别。
我们可能通过一系列的 API 来获取它的具体信息,刚刚有提到它本质上还是一个 Class 而已。
getName();
getComponentType();
第二个方法是获取数组的里面的元素的类型,比如 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());
}
}
}
}
打印结果如下:
Type is [I
ComponentType type is :int
Type is [Lcom.frank.test.Car;
ComponentType type is :class com.frank.test.Car
反射中动态创建数组
反射创建数组是通过 Array.newInstance() 这个方法。
Array.java
public static Object newInstance(Class<?> componentType, int... dimensions)
throws IllegalArgumentException, NegativeArraySizeException {}
第一个参数指定的是数组内的元素类型,后面的是可变参数,表示的是相应维度的数组长度限制。
比如,我要创建一个 int[2][3] 的数组。
Array.newInstance(int.class,2,3);
Array 的读取与赋值
首先,对于 Array 整体的读取与赋值,把它作为一个普通的 Field,根据 Class 中相应获取和设置就好了。调用的是 Field 中对应的方法。
public void set(Object obj,
Object value)
throws IllegalArgumentException,
IllegalAccessException;
public Object get(Object obj)
throws IllegalArgumentException,
IllegalAccessException;
还需要处理的情况是对于数组中指定位置的元素进行读取与赋值,这要涉及到 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;
进行代码测试:
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 block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
打印结果如下:
array index 0 value:1
array index 1 value:3
array index 2 value:3
反射中的枚举 Enum
同数组一样,枚举本质上也是一个 Class 而已,但反射中还是把它单独提出来了。
我们来看一般程序开发中枚举的表现形式。
public enum State {
IDLE,
DRIVING,
STOPPING,
test();
int test1() {
return 0;
}
}
枚举真的跟类很相似,有修饰符、有方法、有属性字段甚至可以有构造方法。
在 Java 反射中,可以把枚举看成一般的 Class,但是反射机制也提供了 3 个特别的的 API 用于操控枚举。
// 用来判定 Class 对象是不是枚举类型
Class.isEnum()
// 获取所有的枚举常量
Class.getEnumConstants()
// 判断一个 Field 是不是枚举常量
java.lang.reflect.Field.isEnumConstant()
枚举的获取与设定
因为等同于 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()));
// 获取枚举中所有的 Field
Field[] 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 block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
打印结果如下:
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