反射与泛型
i.e.Reflection And Generic
反射1
反射,i.e.Reflection,Java 的反射是指程序在运行期可以拿到一个对象(实例)的所有信息。
正常情况下,如果我们要调用一个对象的方法,或者访问一个对象的字段,通常会传入对象实例。反射是为了解决在运行期,对某个实例一无所知的情况下,如何调用其方法。
除了 int
等基本类型外,Java 的其他类型全部都是 class
(包括 interface
), class
的本质是数据类型。
class
在由 JVM 在执行过程中动态加载的。
JVM 在第一次读取到一种 class
类型时,将其加载进内存。每加载一种 class
,JVM 就为其创建一个 Class
类型的实例,并关联起来。
*注意:这里的 Class
类型是一个名叫 Class
的 class
,它长这样:
1: public final class Class { 2: private Class() {} 3: }
以 String
类为例,当 JVM 加载 String
类时,它首先读取 String.class
文件到内存,然后,为 String
类创建一个 Class
实例并关联起来:
1: Class cls = new Class(String);
这个 Class
实例是由 JVM 内部创建的,其构造方法是 private
,只有 JVM 能创建 Class
实例,我们自己的 Java 程序是无法创建 Class
实例的。
所以,JVM 持有的每个 Class
实例都指向一个数据类型( class
或 interface
),一个 Class
实例包含了该 class
的所有完整信息:

由于 JVM 为每个加载的 class
创建了对应的 Class
实例,并在实例中保存了该 class
的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等。因此,如果获取了某个 Class
实例,我们就可以通过这个 Class
实例获取到该实例对应的 class
的所有信息。
这种通过 Class
实例获取 class
信息的方法称为反射(Reflection)。
如何获取一个 class
的 Class
实例呢?
1: // 方法一 2: // 直接通过一个 class 的静态变量 class 获取 3: Class cls = String.class; 4: 5: // 方法二 6: // 通过该实例变量(如果有一个实例变量)提供的 getClass() 方法获取 7: String s = "Hello"; 8: Class cls = s.getClass(); 9: 10: // 方法三 11: // 通过静态方法(如果知道一个 class 的完整类名) Class.forName() 获取 12: Class cls = Class.forName("java.lang.String");
JVM 在执行 Java 程序的时候,并不是一次性把所有用到的 class
全部加载到内存,而是第一次需要用到 class
时才加载。
泛型2
泛型是一种“代码模板”,可以用一套代码套用各种类型。
在讲解什么是泛型之前,我们先观察 Java 标准提供的 ArrayList
,可以看作“可变长度”的数组,实际上它内部就是一个 Object[]
数组,配合存储一个当前分配的长度。
1: public class ArrayList { 2: private Object[] array; 3: private int size; 4: public void add(Object e) { ... } 5: public void remove(int index) { ... } 6: public Object get(int index) { ... } 7: }
如上所示,ArrayList 的 get
方法返回的是一个 Object
类型的数据。如此,当你用它存储 String
类型的时候,获取的结果其实需要强制转型(由 Object
转型为 String
)。如下:
1: ArrayList list = new ArrayList(); 2: list.add("Hello"); 3: // 获取到 Object ,必须强制转型为 String 4: String first = (String) list.get(0); 5: 6: list.add(new Interger(123)); 7: // ERROR: ClassCastException 8: String second = (String) list.get(1);
并且很容易出现误转型 ClassCastException
。
要解决上述问题,我们可以为 String
单独编写一种 ArrayList :
1: public class StringArrayList { 2: private String[] array; 3: private int size; 4: public void add(String e) { ... } // 存 String 5: public void remove(int index) { ... } 6: public String get(int index) { ... } // 取 String 7: }
存取 String
的问题暂时解决了,然而存取 Integer
、 Person
等其他类型呢?当然,我们可以用上述方式为其创建对应的类,然而实际上这实际上是不科学的,太多了。
为了解决这个新的问题,我们必须把 ArrayList
变成一种模板: ArrayList<T>
,代码如下:
1: public class ArrayList<T> { 2: private T[] array; 3: private int size; 4: public void add(T e) { ... } 5: public void remove(int index) { ... } 6: public T get(int index) { ... } 7: }
如此,我们在存取 String
类型的时候,就可以用 ArrayList<String>
了,如下:
1: ArrayList<String> strList = new ArrayList<String>(); 2: 3: strList.add("hello"); // ✔ 4: String s = strList.get(0); // ✔ 5: strList.add(new Integer(123)); // ✘ compile error 6: Integer n = strList.get(0); // ✘ compile error