參考鏈接: Java中的反射數組類reflect.Array
數組的反射有什么用呢?何時需要使用數組的反射呢?先來看下下面的代碼:?
?
? ? ? ? Integer[] nums = {1, 2, 3, 4};
? ? ? ? Object[] objs = nums; //這里能自動的將Integer[]轉成Object[]
? ? ? ? Object obj = nums; //Integer[]當然是一個Object
? ? ? ? int[] ids = {1, 2, 3, 4};
? ? ? ? //Object[] objs2 = ids; //這里不能將int[]轉換成Object[]
? ? ? ? Object obj2 = ids; //int[] 是一個Object?
上面的例子表明:基本類型的一維數組只能當做Object,而不能當作Object[]。?
?
? ? ? ? int[][] intArray = {{1, 2}, {3, 4}};
? ? ? ? Object[] oa = intArray;
? ? ? ? Object obj = intArray;
? ? ? ? //Integer[][] integerArray = intArray; int[][] 不是 Integer[][]
? ? ? ? Integer[][] integerArray2 = new Integer[][]{{1, 2}, {3, 4}};
? ? ? ? Object[][] oa2 = integerArray2;
? ? ? ? Object[] oa3 = integerArray2;
? ? ? ? Object obj2 = integerArray2;從上面的例子可以看出java的二位數組是數組的數組。下面來看下對數組進行反射的例子:?
?
?
package cn.zq.array.reflect;
?
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Random;
?
public class ArrayReflect {
? ? public static void main(String[] args) {
? ? ? ? Random rand = new Random(47);
? ? ? ? int[] is = new int[10];
? ? ? ? for(int i = 0; i < is.length; i++) {
? ? ? ? ? ? is[i] = rand.nextInt(100);
? ? ? ? }
? ? ? ? System.out.println(is);
? ? ? ? System.out.println(Arrays.asList(is));
? ? ? ? /*以上的2個輸出都是輸出類似"[[I@14318bb]"的字符串,
? ? ? ? ? ?不能顯示數組內存放的內容,當然我們采用遍歷的方式來輸出數組內的內容*/
? ? ? ? System.out.println("--1.通過常規方式遍歷數組對數組進行打印--");
? ? ? ? for(int i = 0; i < is.length; i++) {
? ? ? ? ? ? System.out.print(is[i] + " ");
? ? ? ? }
? ? ? ? System.out.println();
? ? ? ? System.out.println("--2.通過數組反射的方式遍歷數組對數組進行打印--");
? ? ? ? Object obj = is; //將一維的int數組向上轉為Object
? ? ? ? System.out.println("obj isArray:" + obj.getClass().isArray());
? ? ? ? for(int i = 0; i < Array.getLength(obj); i++) {
? ? ? ? ? ? int num = Array.getInt(obj, i);
? ? ? ? ? ? //也能通過這個常用的方法來獲取對應索引位置的值
? ? ? ? ? ? //Object value = Array.get(obj, i); //如果數組存放的是基本類型,那么返回的是基本類型對應的包裝類型
? ? ? ? ? ? System.out.print(num + " ");
? ? ? ? }
? ? }
}
輸出:?
?
?
[I@14318bb
[[I@14318bb]
--1.通過常規方式遍歷數組對數組進行打印--
58 55 93 61 61 29 68 0 22 7?
--2.通過數組反射的方式遍歷數組對數組進行打印--
obj isArray:true
58 55 93 61 61 29 68 0 22 7上面的例子首先創建了一個int的一維數組,然后隨機的像里面填充0~100的整數,接著通過System.out.println()方法直接對數組輸出或者用Arrays.asList方法(
如果不是基本類型的一維數組此方法能按照期望轉成List,如果是二維數組也不能按照我們期望轉成List)將數組轉成List再輸出,通過都不是我們期望的輸出結果。接下來以常規的數組的遍歷方式來輸出數組內的內容,然后將int[]看成是一個Object,利用反射來遍歷其內容。Class.isArray()可以用來判斷是對象是否為一個數組,假如是一個數組,那么在通過java.lang.reflect.Array這個對數組反射的工具類來獲取數組的相關信息,這個類通過了一些get方法,可以用來獲取數組的長度,各個版本的用來獲取基本類型的一維數組的對應索引的值,通用獲取值的方法get(Object array, int index),設置值的方法,還有2個用來創建數組實例的方法。通過數組反射工具類,可以很方便的利用數組反射寫出通用的代碼,而不用再去判斷給定的數組到底是那種基本類型的數組。
?
package cn.zq.array.reflect;
?
import java.lang.reflect.Array;
?
public class NewArrayInstance {
? ? public static void main(String[] args) {
? ? ? ? Object o = Array.newInstance(int.class, 20);
? ? ? ? int[] is = (int[]) o;
? ? ? ? System.out.println("is.length = " + is.length);
? ? ? ? Object o2 = Array.newInstance(int.class, 10, 8);
? ? ? ? int[][] iss = (int[][]) o2;
? ? ? ? System.out.println("iss.length = " + iss.length?
? ? ? ? ? ? ? ? + ", iss[0].lenght = " + iss[0].length);
? ? }
}
is.length = 20
iss.length = 10, iss[0].lenght = 8
Array總共通過了2個方法來創建數組?
?
Object newInstance(Class<?> componentType, int length),根據提供的class來創建一個指定長度的數組,如果像上面那樣提供int.class,長度為10,相當于new int[10];Object newInstance(Class<?> componentType, int... dimensions),根據提供的class和維度來創建數組,可變參數dimensions用來指定數組的每一維的長度,像上面的例子那樣相當于創建了一個new int[10][8]的二維數組,但是不能創建每一維長度都不同的多維數組。通過第一種創建數組的方法,可以像這樣創建數組Object o = Array.newInstance(int[].class, 20)可以用來創建二維數組,這里相當于Object o = new int[20][];??
當然通過上面例子那樣來創建數組的用法是很少見的,其實也是多余的,為什么不直接通過new來創建數組呢?反射創建數組不僅速度沒有new快,而且寫的程序也不易讀,還不如new來的直接。事實上通過反射創建數組確實很少見,是有何種變態的需求需要用反射來創建數組呢!?
由于前面對基本類型的數組進行輸出時遇到一些障礙,下面將利用數組反射來實現一個工具類來實現期望的輸出:?
?
package cn.zq.util;
?
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Array;
?
public class Print {
? ? public static void print(Object obj) {
? ? ? ? print(obj, System.out);
? ? }
? ? public static void print(Object obj, PrintStream out) {
? ? ? ? out.println(getPrintString(obj));
? ? }
? ? public static void println() {
? ? ? ? print(System.out);
? ? }
? ? public static void println(PrintStream out) {
? ? ? ? out.println();
? ? }
? ? public static void printnb(Object obj) {
? ? ? ? printnb(obj, System.out);
? ? }
? ? public static void printnb(Object obj, PrintStream out) {
? ? ? ? out.print(getPrintString(obj));
? ? }
? ? public static PrintStream format(String format, Object ... objects) {
? ? ? ? return format(System.out, format, objects);
? ? }
? ? public static PrintStream format(PrintStream out, String format, Object ... objects) {
? ? ? ? Object[] handleObjects = new Object[objects.length];
? ? ? ? for(int i = 0; i < objects.length; i++) {
? ? ? ? ? ? Object object = objects[i];
? ? ? ? ? ? if(object == null || isPrimitiveWrapper(object)) {
? ? ? ? ? ? ? ? handleObjects[i] = object;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ByteArrayOutputStream bos = new ByteArrayOutputStream();
? ? ? ? ? ? ? ? PrintStream ps = new PrintStream(bos);
? ? ? ? ? ? ? ? printnb(object, ps);
? ? ? ? ? ? ? ? ps.close();
? ? ? ? ? ? ? ? handleObjects[i] = new String(bos.toByteArray());
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? out.format(format, handleObjects);
? ? ? ? return out;
? ? }
? ? /**
? ? ?* 判斷給定對象是否為基本類型的包裝類。
? ? ?* @param o 給定的Object對象
? ? ?* @return 如果是基本類型的包裝類,則返回是,否則返回否。
? ? ?*/
? ? private static boolean isPrimitiveWrapper(Object o) {
? ? ? ? return o instanceof Void || o instanceof Boolean
? ? ? ? ? ? ? ? || o instanceof Character || o instanceof Byte?
? ? ? ? ? ? ? ? || o instanceof Short || o instanceof Integer?
? ? ? ? ? ? ? ? || o instanceof Long || o instanceof Float
? ? ? ? ? ? ? ? || o instanceof Double;
? ? }
? ? public static String getPrintString(Object obj) {
? ? ? ? StringBuilder result = new StringBuilder();
? ? ? ? if(obj != null && obj.getClass().isArray()) {
? ? ? ? ? ? result.append("[");
? ? ? ? ? ? int len = Array.getLength(obj);
? ? ? ? ? ? for(int i = 0; i < len; i++) {
? ? ? ? ? ? ? ? Object value = Array.get(obj, i);
? ? ? ? ? ? ? ? result.append(getPrintString(value));
? ? ? ? ? ? ? ? if(i != len - 1) {
? ? ? ? ? ? ? ? ? ? result.append(", ");
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? result.append("]");
? ? ? ? } else {
? ? ? ? ? ? result.append(String.valueOf(obj));
? ? ? ? }
? ? ? ? return result.toString();
? ? }
}上面的Print工具類提供了一些實用的進行輸出的靜態方法,并且提供了一些重載版本,可以根據個人的喜歡自己編寫一些重載的版本,支持基本類型的一維數組的打印以及多維數組的打印,看下下面的Print工具進行測試的示例:?
?
?
package cn.zq.array.reflect;
?
import static cn.zq.util.Print.print;
?
import java.io.PrintStream;
?
import static cn.zq.util.Print.*;
?
public class PrintTest {
? ? static class Person {
? ? ? ? private static int counter;
? ? ? ? private final int id = counter ++;
? ? ? ? public String toString() {
? ? ? ? ? ? return getClass().getSimpleName() + id;
? ? ? ? }
? ? }
? ??
? ? public static void main(String[] args) throws Exception {
? ? ? ? print("--打印非數組--");
? ? ? ? print(new Object());
? ? ? ? print("--打印基本類型的一維數組--");
? ? ? ? int[] is = new int[]{1, 22, 31, 44, 21, 33, 65};
? ? ? ? print(is);
? ? ? ? print("--打印基本類型的二維數組--");
? ? ? ? int[][] iss = new int[][]{
? ? ? ? ? ? ? ? {11, 12, 13, 14},
? ? ? ? ? ? ? ? {21, 22,},
? ? ? ? ? ? ? ? {31, 32, 33}
? ? ? ? };
? ? ? ? print(iss);
? ? ? ? print("--打印非基本類型的一維數組--");
? ? ? ? Person[] persons = new Person[10];
? ? ? ? for(int i = 0; i < persons.length; i++) {
? ? ? ? ? ? persons[i] = new Person();
? ? ? ? }
? ? ? ? print(persons);
? ? ? ? print("--打印非基本類型的二維數組--");
? ? ? ? Person[][] persons2 = new Person[][]{
? ? ? ? ? ? ? ? {new Person()},
? ? ? ? ? ? ? ? {new Person(), new Person()},
? ? ? ? ? ? ? ? {new Person(), new Person(), new Person(),},
? ? ? ? };
? ? ? ? print(persons2);
? ? ? ? print("--打印empty數組--");
? ? ? ? print(new int[]{});
? ? ? ? print("--打印含有null值的數組--");
? ? ? ? Object[] objects = new Object[]{
? ? ? ? ? ? ? ? new Person(), null, new Object(), new Integer(100)
? ? ? ? };
? ? ? ? print(objects);
? ? ? ? print("--打印特殊情況的二維數組--");
? ? ? ? Object[][] objects2 = new Object[3][];
? ? ? ? objects2[0] = new Object[]{};
? ? ? ? objects2[2] = objects;
? ? ? ? print(objects2);
? ? ? ? print("--將一維數組的結果輸出到文件--");
? ? ? ? PrintStream out = new PrintStream("out.c");
? ? ? ? try {
? ? ? ? ? ? print(iss, out);
? ? ? ? } finally {
? ? ? ? ? ? out.close();
? ? ? ? }
? ? ? ? print("--格式化輸出--");
? ? ? ? format("%-6d%s %B %s", 10086, "is", true, iss);
? ? ? ? /**
? ? ? ? ?* 上面列出了一些Print工具類的一些常用的方法,
? ? ? ? ?* 還有一些未列出的方法,請自行查看。
? ? ? ? ?*/
? ? }
}
輸出:?
?
?
--打印非數組--
java.lang.Object@61de33
--打印基本類型的一維數組--
[1, 22, 31, 44, 21, 33, 65]
--打印基本類型的二維數組--
[[11, 12, 13, 14], [21, 22], [31, 32, 33]]
--打印非基本類型的一維數組--
[Person0, Person1, Person2, Person3, Person4, Person5, Person6, Person7, Person8, Person9]
--打印非基本類型的二維數組--
[[Person10], [Person11, Person12], [Person13, Person14, Person15]]
--打印empty數組--
[]
--打印含有null值的數組--
[Person16, null, java.lang.Object@ca0b6, 100]
--打印特殊情況的二維數組--
[[], null, [Person16, null, java.lang.Object@ca0b6, 100]]
--將一維數組的結果輸出到文件--
--格式化輸出--
10086 is TRUE [[11, 12, 13, 14], [21, 22], [31, 32, 33]]輸出文件:?
?
?
可見Print工具類已經具備打印基本類型的一維數組以及多維數組的能力了,總體來說上面的工具類還是挺實用的,免得每次想要看數組里面的內容都有手動的去編寫代碼,那樣是在是太麻煩了,以后直接把Print工具類拿過去用就行了,多么的方便啊。?
上面的工具類確實能很好的工作,但是假如有這樣一個需求:給你一個數組(也有可能是其他的容器),你給我整出一個List。那么我們應該怎樣做呢?事實上Arrays.asList不總是能得到我們所期望的結果,java5雖然添加了泛型,但是是有限制的,并不能像c++的模板那樣通用,正是因為java中存在基本類型,即使有自動包裝的機制,與泛型一起并不能使用,參數類型必須是某種類型,而不能是基本類型。下面給出一種自己的解決辦法:??
?
package cn.zq.util;
?
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
?
public class CollectionUtils {
? ??
? ? public static List<?> asList(Object obj) {
? ? ? ? return convertToList(
? ? ? ? ? ? ? ? makeIterator(obj));
? ? }
? ? public static <T>List<T> convertToList(Iterator<T> iterator) {
? ? ? ? if(iterator == null) {
? ? ? ? ? ? return null;
? ? ? ? }
? ? ? ? List<T> list = new ArrayList<T>();
? ? ? ? while(iterator.hasNext()) {
? ? ? ? ? ? list.add(iterator.next());
? ? ? ? }
? ? ? ? return list;
? ? }
? ? @SuppressWarnings({ "rawtypes", "unchecked" })
? ? public static Iterator<?> makeIterator(Object obj) {
? ? ? ? if(obj instanceof Iterator) {
? ? ? ? ? ? return (Iterator<?>) obj;
? ? ? ? }
? ? ? ? if(obj == null) {
? ? ? ? ? ? return null;
? ? ? ? }
? ? ? ? if(obj instanceof Map) {
? ? ? ? ? ? obj = ((Map<?, ?>)obj).entrySet();
? ? ? ? }
? ??????
? ? ? ? Iterator<?> iterator = null;
? ? ? ? if(obj instanceof Iterable) {
? ? ? ? ? ? iterator = ((Iterable<?>)obj).iterator();
? ? ? ? } else if(obj.getClass().isArray()) {
? ? ? ? ? ? //Object[] objs = (Object[]) obj; //原始類型的一位數組不能這樣轉換
? ? ? ? ? ? ArrayList list = new ArrayList(Array.getLength(obj));
? ? ? ? ? ? for(int i = 0; i < Array.getLength(obj); i++) {
? ? ? ? ? ? ? ? list.add(Array.get(obj, i));
? ? ? ? ? ? }
? ? ? ? ? ? iterator = list.iterator();
? ? ? ? } else if(obj instanceof Enumeration) {
? ? ? ? ? ? iterator = new EnumerationIterator((Enumeration) obj);
? ? ? ? } else {
? ? ? ? ? ? iterator = Arrays.asList(obj).iterator();
? ? ? ? }
? ? ? ? return iterator;
? ? }
? ??
? ? public static class EnumerationIterator<T> implements Iterator<T> {
? ? ? ? private Enumeration<T> enumeration;
? ? ? ? public EnumerationIterator(Enumeration<T> enumeration) {
? ? ? ? ? ? this.enumeration = enumeration;
? ? ? ? }
? ? ? ? public boolean hasNext() {
? ? ? ? ? ? return enumeration.hasMoreElements();
? ? ? ? }
? ? ? ? public T next() {
? ? ? ? ? ? return enumeration.nextElement();
? ? ? ? }
? ? ? ? public void remove() {
? ? ? ? ? ? throw new UnsupportedOperationException();
? ? ? ? }
? ? }
}
?
?測試代碼:?
?
?
package cn.zq.array.reflect;
?
import java.util.Iterator;
import java.util.List;
?
import cn.zq.array.reflect.PrintTest.Person;
import cn.zq.util.CollectionUtils;
?
public class CollectionUtilsTest {
? ? public static void main(String[] args) {
? ? ? ? System.out.println("--基本類型一維數組--");
? ? ? ? int[] nums = {1, 3, 5, 7, 9};
? ? ? ? List<?> list = CollectionUtils.asList(nums);
? ? ? ? System.out.println(list);
? ? ? ? System.out.println("--非基本類型一維數組--");
? ? ? ? Person[] persons = new Person[]{
? ? ? ? ? ? ? ? new Person(),
? ? ? ? ? ? ? ? new Person(),
? ? ? ? ? ? ? ? new Person(),
? ? ? ? };
? ? ? ? List<Person> personList = (List<Person>) CollectionUtils.asList(persons);
? ? ? ? System.out.println(personList);
? ? ? ? System.out.println("--Iterator--");
? ? ? ? Iterator<Person> iterator = personList.iterator();
? ? ? ? List<Person> personList2 = (List<Person>) CollectionUtils.asList(iterator);
? ? ? ? System.out.println(personList2);
?
? ? }
}
輸出:?
?
?
--基本類型一維數組--
[1, 3, 5, 7, 9]
--非基本類型一維數組--
[Person0, Person1, Person2]
--Iterator--
[Person0, Person1, Person2]
在java的容器類庫中可以分為Collection,Map,數組,由于Iterator(以及早期的遺留接口Enumeration)是所有容器的通用接口并且Collection接口從Iterable(該接口的iterator將返回一個Iterator),所以在makeIterator方法中對這些情形進行了一一的處理,對Map類型,只需要調用其entrySet()方法,對于實現了Iterable接口的類(Collection包含在內),調用iterator()直接得到Iterator對象,對于Enumeration類型,利用適配器EnumerationIterator進行適配,對于數組,利用數組反射遍歷數組放入ArrayList中,對于其他的類型調用Arrays.asList()方法創建一個List。CollectionUtils還提供了一些其他的方法來進行轉換,可以根據需要添加自己需要的方法。?
?
總結:數組的反射對于那些可能出現數組的設計中提供更方便、更靈活的方法,以免寫那些比較麻煩的判斷語句,這種靈活性付出的就是性能的代價,對于那些根本不需要數組反射的情況下用數組的反射實在是不應該。是否使用數組的反射,在實際的開發中仁者見仁智者見智,根據需要來選擇是否使用數組的反射,最好的方式就是用實踐來探路,先按照自己想到的方式去寫,在實踐中不斷的完善。