深入分析Java反射(二)-数组和枚举
前提
本文主要介绍反射中可能用到的两个比较特殊的类型,数组和枚举,分别对应
java.lang.reflect.Array
和
java.lang.Enum
,后者其实并不是反射类库包中的类,但是反射的基础类库里面有使用枚举类型的方法。
数组类型
使用非反射方式创建数组实例的过程如下:
fully_qualified_class_name[] variable_name = {val1,val2,val3,...}; |
使用反射方式就是使用
java.lang.reflect.Array
中的相关方法:
Class<?> c = Class.forName(cName); |
下面列举一下
java.lang.reflect.Array
中的方法:
这里省略了一部分对于
Int
、
Boolean
等原始类型的Setter和Getter方法。
在
java.lang.Class
和数组相关的方法:
这里举个例子加深下印象:
public class ArrayCreationMain { |
运行后输出:
class java.math.BigInteger[] = [123, 234, 345] |
需要注意的是,
java.lang.reflect.Array
中的Setter和Getter方法如果越界操作数组元素,会抛出
ArrayIndexOutOfBoundsException
,通过Setter设置和数组初始化时候的组件类型不一致的元素会抛出
IllegalArgumentException
。
细议数组类型
前面说到了数组类型的一些基础特性,这里补充一些比较高级的使用方法。
创建特定元素类型的数组:
因为Java泛型擦除的问题,实际上我们使用
Array#newInstance
方法只能得到一个Object类型的结果实例,其实这个结果实例的类型就是
ComponentType[]
,这里只是返回了它的父类(Object)类型实例,因此我们可以直接强转,例如:
String[] strArray = (String[]) Array.newInstance(String.class, 3); |
获取数组类型:
在非反射方式下,我们可以通过
数组实例.class
通过class字面量直接获取数组类型,例如:
Class stringArrayClass = String[].class; |
反射条件下,可以通过
Class.forName()
获取数组类型,但是调用此方法的时候有个限制,类名必须使用JVM可以识别的签名形式,就是
[L${ComponentType};
,注意
Class.forName()
无法获取原始类型(如int、boolean)的类型,例如:
// 不能漏了左边的[L和右边的; |
获取数组元素(组件)类型:
目前获取数组组件类型只能通过数组类型实例去调用
Class#getComponentType()
。
枚举类型
因为枚举就是普通的Java类,因此反射相关类库中并没有添加一个
java.lang.reflect.Enum
类型,反射中的API和枚举相关的有:
boolean java.lang.Class#isEnum()
:判断类型是否枚举类型。
T[] java.lang.Class#getEnumConstants()
:获取类型中所有的枚举常量。
boolean java.lang.reflect.Field#isEnumConstant()
:判断属性是否枚举类型。
如果实例中的成员属性为枚举,那么枚举的反射操作实际上就是
java.lang.reflect.Field
的相关操作。
举个例子:
public class EnumerationMain { |
运行后输出:
Color class is enum:true |
之前写过一篇文章《JDK中枚举的底层实现》,从枚举类的字节码翻译出类的代码逻辑,这里翻出来那个例子(手机操作系统枚举)说一下:
public enum PhoneOsEnum { |
这个是我们使用Java的关于枚举的语法创建出来的枚举类型,是编译前我们看到的Java类文件,实际上,编译完成之后,枚举类型会变成一个普通的Java类,它有以下特点:
java.lang.Enum
,并且把自身类型作为泛型参数类型,构造函数中必定包含name(字符串类型String)、ordinal(整型int)参数,因为父类
java.lang.Enum
的构造要求传入这两个参数。
static final
修饰的在第1步中提到的Java类的实例,属性的名称和原来枚举的名字一致,实例在静态代码块中创建。
static final
修饰的第1步中提到的Java类的数组实例,名称为
$VALUES
,此数组在静态代码块中创建,基于此数组还新增了一个静态方法
values()
,此方法就是直接返回数组的克隆。
也就是上面提到的
PhoneOsEnum
在编译完成之后会变成: