反射包中的Array类
在Java的java.lang.reflect包中存在着一个可以动态操作数组的类,Array,它提供了动态创建和访问 Java 数组的方法。Array 允许在执行 get 或 set 操作进行取值和赋值。在Class类中与数组关联的方法是:
方法返回值 | 方法名称 | 方法说明 |
---|---|---|
Class<?> |
getComponentType() |
返回表示数组元素类型的 Class,即数组的类型 |
boolean |
isArray() |
判定此 Class 对象是否表示一个数组类。 |
java.lang.reflect.Array中的常用静态方法如下:
方法返回值 | 方法名称 | 方法说明 |
---|---|---|
static Object |
set(Object array, int index) |
返回指定数组对象中索引组件的值。 |
static int |
getLength(Object array) |
以 int 形式返回指定数组对象的长度 |
static object |
newInstance(Class<?> componentType, int... dimensions) |
创建一个具有指定类型和维度的新数组。 |
static Object |
newInstance(Class<?> componentType, int length) |
创建一个具有指定的组件类型和长度的新数组。 |
static void |
set(Object array, int index, Object value) |
将指定数组对象中索引组件的值设置为指定的新值。 |
下面通过一个简单例子来演示这些方法
import java.lang.reflect.Array;
public class ReflectArray {
public static void main(String[] args) throws ClassNotFoundException {
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
//获取数组类型的Class 即int.class
Class<?> clazz = array.getClass().getComponentType();
//创建一个具有指定的组件类型和长度的新数组。
//第一个参数:数组的类型,第二个参数:数组的长度
Object newArr = Array.newInstance(clazz, 15);
//获取原数组的长度
int co = Array.getLength(array);
//赋值原数组到新数组
System.arraycopy(array, 0, newArr, 0, co);
for (int i:(int[]) newArr) {
System.out.print(i+",");
}
//创建了一个长度为10 的字符串数组,
//接着把索引位置为6 的元素设为"hello world!",然后再读取索引位置为6 的元素的值
Class clazz2 = Class.forName("java.lang.String");
//创建一个长度为10的字符串数组,在Java中数组也可以作为Object对象
Object array2 = Array.newInstance(clazz2, 10);
//把字符串数组对象的索引位置为6的元素设置为"hello"
Array.set(array2, 6, "hello world!");
//获得字符串数组对象的索引位置为5的元素的值
String str = (String)Array.get(array2, 6);
System.out.println();
System.out.println(str);//hello
}
}
输出结果:
1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
hello world!
通过上述代码演示,确实可以利用Array类和反射相结合动态创建数组,也可以在运行时动态获取和设置数组中元素的值,其实除了上的set/get外Array还专门为8种基本数据类型提供特有的方法,如setInt/getInt、setBoolean/getBoolean,其他依次类推,需要使用是可以查看API文档即可。除了上述动态修改数组长度或者动态创建数组或动态获取值或设置值外,可以利用泛型动态创建泛型数组如下:
/**
* 接收一个泛型数组,然后创建一个长度与接收的数组长度一样的泛型数组,
* 并把接收的数组的元素复制到新创建的数组中,
* 最后找出新数组中的最小元素,并打印出来
* @param a
* @param <T>
*/
public <T extends Comparable<T>> void min(T[] a) {
//通过反射创建相同类型的数组
T[] b = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length);
for (int i = 0; i < a.length; i++) {
b[i] = a[i];
}
T min = null;
boolean flag = true;
for (int i = 0; i < b.length; i++) {
if (flag) {
min = b[i];
flag = false;
}
if (b[i].compareTo(min) < 0) {
min = b[i];
}
}
System.out.println(min);
}
毕竟我们无法直接创建泛型数组,有了Array的动态创建数组的方式这个问题也就迎刃而解了。
//无效语句,编译不通
T[] a = new T[];
ok~,到这反射中几个重要并且常用的类我们都基本介绍完了,但更重要是,我们应该认识到反射机制并没有什么神奇之处。当通过反射与一个未知类型的对象打交道时,JVM只会简单地检查这个对象,判断该对象属于那种类型,同时也应该知道,在使用反射机制创建对象前,必须确保已加载了这个类的Class对象,当然这点完全不必由我们操作,毕竟只能JVM加载,但必须确保该类的”.class”文件已存在并且JVM能够正确找到。关于Class类的方法在前面我们只是分析了主要的一些方法,其实Class类的API方法挺多的,建议查看一下API文档,浏览一遍,有个印象也是不错的选择,这里仅列出前面没有介绍过又可能用到的API:
/**
* 修饰符、父类、实现的接口、注解相关
*/
//获取修饰符,返回值可通过Modifier类进行解读
public native int getModifiers();
//获取父类,如果为Object,父类为null
public native Class<? super T> getSuperclass();
//对于类,为自己声明实现的所有接口,对于接口,为直接扩展的接口,不包括通过父类间接继承来的
public native Class<?>[] getInterfaces();
//自己声明的注解
public Annotation[] getDeclaredAnnotations();
//所有的注解,包括继承得到的
public Annotation[] getAnnotations();
//获取或检查指定类型的注解,包括继承得到的
public <A extends Annotation> A getAnnotation(Class<A> annotationClass);
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
/**
* 内部类相关
*/
//获取所有的public的内部类和接口,包括从父类继承得到的
public Class<?>[] getClasses();
//获取自己声明的所有的内部类和接口
public Class<?>[] getDeclaredClasses();
//如果当前Class为内部类,获取声明该类的最外部的Class对象
public Class<?> getDeclaringClass();
//如果当前Class为内部类,获取直接包含该类的类
public Class<?> getEnclosingClass();
//如果当前Class为本地类或匿名内部类,返回包含它的方法
public Method getEnclosingMethod();
/**
* Class对象类型判断相关
*/
//是否是数组
public native boolean isArray();
//是否是基本类型
public native boolean isPrimitive();
//是否是接口
public native boolean isInterface();
//是否是枚举
public boolean isEnum();
//是否是注解
public boolean isAnnotation();
//是否是匿名内部类
public boolean isAnonymousClass();
//是否是成员类
public boolean isMemberClass();
//是否是本地类
public boolean isLocalClass();
完结。