实例讲解Java编程中数组反射的使用方法

作者:RiccioZhang


这篇文章主要介绍了Java编程中数组反射的使用方法,通过编写数组反射工具类可以重用许多基础代码,减少对类型的判断过程,需要的朋友可以参考下



什么是反射“反射(Reflection)能够让运行于JVM中的程序检测和修改运行时的行为。”这个概念常常会和内省(Introspection)混淆,以下是这两个术语在Wikipedia中的解释:

  • 内省用于在运行时检测某个对象的类型和其包含的属性;
  • 反射用于在运行时检测和修改某个对象的结构及其行为。
  • 从它们的定义可以看出,内省是反射的一个子集。有些语言支持内省,但并不支持反射,如C++。

内省示例:instanceof 运算符用于检测某个对象是否属于特定的类。



if           (obj           instanceof           Dog) {         
                    Dog d = (Dog) obj;         
                    d.bark();         
          }



反射示例:Class.forName()方法可以通过类或接口的名称(一个字符串或完全限定名)来获取对应的Class对象。forName方法会触发类的初始化。



// 使用反射         
          Class<?> c = Class.forName(          "classpath.and.classname"          );         
          Object dog = c.newInstance();         
          Method m = c.getDeclaredMethod(          "bark"          ,           new          Class<?>[          0          ]);         
          m.invoke(dog);




在Java中,反射更接近于内省,因为你无法改变一个对象的结构。虽然一些API可以用来修改方法和属性的可见性,但并不能修改结构。

数组的反射数组的反射有什么用呢?何时需要使用数组的反射呢?先来看下下面的代码:



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]]



输出文件:

java反射重新设值_数组

可见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还提供了一些其他的方法来进行转换,可以根据需要添加自己需要的方法。

总结:数组的反射对于那些可能出现数组的设计中提供更方便、更灵活的方法,以免写那些比较麻烦的判断语句,这种灵活性付出的就是性能的代价,对于那些根本不需要数组反射的情况下用数组的反射实在是不应该。是否使用数组的反射,在实际的开发中仁者见仁智者见智,根据需要来选择是否使用数组的反射,最好的方式就是用实践来探路,先按照自己想到的方式去写,在实践中不断的完善。