本教程是针对JDK 8编写的。本页中描述的示例和实践不利用后续版本引入的改进,并且可能使用不再可用的技术。
有关Java SE 9及后续版本中更新的语言特性的摘要,请参见Java语言更改。
有关所有JDK版本的新功能、增强功能以及已删除或已弃用选项的信息,请参见JDK发行说明。
与非反射代码一样,数组字段可以一次性设置或按组件进行设置或检索。要一次性设置整个数组,请使用java.lang.reflect.Field.set(Object obj, Object value)
。要检索整个数组,请使用Field.get(Object)
。可以使用java.lang.reflect.Array
中的方法设置或检索单个组件。
Array
提供了setFoo()
和getFoo()
形式的方法,用于设置和获取任何基本类型的组件。例如,可以使用Array.setInt(Object array, int index, int value)
设置int
数组的组件,并可以使用Array.getInt(Object array, int index)
检索组件。
这些方法支持自动的扩宽数据类型。因此,可以使用Array.getShort()
来设置int
数组的值,因为16位的short
可以扩宽为32位的int
而不会丢失数据;另一方面,对int
数组调用Array.setLong()
将抛出IllegalArgumentException
,因为64位的long
不能缩小为32位的int
而不会丢失信息。无论实际传递的值是否可以准确表示为目标数据类型,这都是正确的。The Java Language Specification, Java SE 7 Edition,第5.1.2节和5.1.3节包含了关于扩宽和缩窄转换的完整讨论。
引用类型的数组(包括数组的数组)的组成部分可以使用Array.set(Object array, int index, int value)
和Array.get(Object array, int index)
来设置和检索。
示例演示了如何替换类型为数组的字段的值。在这种情况下,代码将一个GrowBufferedReader
java.io.BufferedReader
的后备数组替换为一个更大的数组。(这假设原始BufferedReader
的创建在不可修改的代码中;否则,可以简单地使用接受输入缓冲区大小的另一个构造函数BufferedReader(java.io.Reader in, int size)
)
import java.io.BufferedReader; import java.io.CharArrayReader; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.Field; import java.util.Arrays; import static java.lang.System.out; public class GrowBufferedReader { private static final int srcBufSize = 10 * 1024; private static char[] src = new char[srcBufSize]; static { src[srcBufSize - 1] = 'x'; } private static CharArrayReader car = new CharArrayReader(src); public static void main(String... args) { try { BufferedReader br = new BufferedReader(car); Class<?> c = br.getClass(); Field f = c.getDeclaredField("cb"); // cb是一个私有字段 f.setAccessible(true); char[] cbVal = char[].class.cast(f.get(br)); char[] newVal = Arrays.copyOf(cbVal, cbVal.length * 2); if (args.length > 0 && args[0].equals("grow")) f.set(br, newVal); for (int i = 0; i < srcBufSize; i++) br.read(); // 看看是否正在使用新的后备数组 if (newVal[srcBufSize - 1] == src[srcBufSize - 1]) out.format("使用新的后备数组,大小=%d%n", newVal.length); else out.format("使用原始后备数组,大小=%d%n", cbVal.length); // 生产代码应该更优雅地处理这些异常 } catch (FileNotFoundException x) { x.printStackTrace(); } catch (NoSuchFieldException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } catch (IOException x) { x.printStackTrace(); } } }
$ java GrowBufferedReader grow 使用新的支持数组,大小为16384 $ java GrowBufferedReader 使用原始的支持数组,大小为8192
请注意,上面的示例使用了数组工具方法java.util.Arrays.copyOf()
。java.util.Arrays
包含许多在操作数组时非常方便的方法。
多维数组只是嵌套的数组。二维数组是一个数组的数组。三维数组是一个二维数组的数组,以此类推。示例
演示了如何使用反射创建和初始化多维数组。CreateMatrix
import java.lang.reflect.Array; import static java.lang.System.out; public class CreateMatrix { public static void main(String... args) { Object matrix = Array.newInstance(int.class, 2, 2); Object row0 = Array.get(matrix, 0); Object row1 = Array.get(matrix, 1); Array.setInt(row0, 0, 1); Array.setInt(row0, 1, 2); Array.setInt(row1, 0, 3); Array.setInt(row1, 1, 4); for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) out.format("matrix[%d][%d] = %d%n", i, j, ((int[][])matrix)[i][j]); } }
$ java CreateMatrix matrix[0][0] = 1 matrix[0][1] = 2 matrix[1][0] = 3 matrix[1][1] = 4
可以使用以下代码片段获得相同的结果:
Object matrix = Array.newInstance(int.class, 2); Object row0 = Array.newInstance(int.class, 2); Object row1 = Array.newInstance(int.class, 2); Array.setInt(row0, 0, 1); Array.setInt(row0, 1, 2); Array.setInt(row1, 0, 3); Array.setInt(row1, 1, 4); Array.set(matrix, 0, row0); Array.set(matrix, 1, row1);
可变参数Array.newInstance(Class<?> componentType, int... dimensions)
提供了一种方便的方式来创建多维数组,但是组件仍然需要使用嵌套数组的原则进行初始化。(反射不提供多个索引的get
/set
方法来完成此任务。)