一般情况下,我们使用某个类时必须知道它是什么类,是用来做什么的,并且能够获得此类的引用。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
反射则是一开始并不知道我们要初始化的类的对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用JDK提供的反射API进行反射调用。反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有方法和属性。对于任意一个对象都能够调用它的任意方法和属性,并且能改变它的属性。是 Java 被视为动态语言的关键。
Java 的反射主要提供了以下功能:
- 在运行时构造任意一个类的对象
- 在运行时获取或者修改任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法(属性)
Class
反射始于Class,Class是一个类,封装了当前对象所对应的类的信息。一个类中有属性,方法,构造器等。比如说有一个 Student 类,一个 Animal 类,这些都是不同的类。现在需要一个类,它的作用是用来描述这些类,比如一个普通的类应该有构造方法,成员属性,方法等。那么这个能描述类的类就是Class。
Class类是一个对象照镜子的结果,对象可以看到自己有哪些方法,属性,构造器,实现了哪些接口等等。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。对象只能由系统建立对象,一个类(而不是一个对象)在JVM中只会有一个 Class 实例,不管这个类被 new 了多少次。
获得 Class 对象
- 通过类名获取:类名.class
Class<Fruit> fruitClazz = Fruit.class;
- 通过对象获取:对象.getClass()
Fruit fruit = new Fruit();
Class<? extends Fruit> fruitClazz = fruit.getClass();
- 通过全类名获取:Class.forName(全类名),classLoader.loadClass(全类名)
// 通过静态方法
try {
Class<?> fruitClazz = Class.forName("com.example.Fruit");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 通过ClassLoader
try {
ClassLoader classLoader = getClassLoader();
Class<?> fruitClazz = classLoader.loadClass("com.example.Fruit");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
判断是否为某个类的实例
一般的,我们使用 instanceof 关键字来判断是否为某个类的实例。同时为我们也可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法。
public boolean isInstance(Object obj);
判断是否为某个类的类型:
public boolean isAssignableFrom(Class<?> cls);
创建实例
通过反射来生成对象实例主要有两种方式:
- 使用 Class 对象的 newInstance() 方法来创建 Class 对象对应类的实例。
Class<Fruit> fruitClazz = Fruit.class;
Fruit fruit = fruitClazz.newInstance();
- 先通过 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()方法来创建实例。这种方法可以用指定的构造器来构造类的实例。
Class<Fruit> fruitClazz = Fruit.class;
// 获取构造参数为 String 类型的构造器
Constructor<Fruit> constructor = fruitClazz.getConstructor(String.class);
// 使用构造器创建实例
Fruit fruit = constructor.newInstance("name");
获取构造器信息
- 获得类的所有公共构造函数
public Constructor<?>[] getConstructors()
- 获得使用特殊的参数类型的public构造函数(包括父类)
public Constructor<T> getConstructor(Class<?>... parameterTypes)
- 获得类的所有构造函数(与接入级别无关)
public Constructor<?>[] getDeclaredConstructors()
- 获得使用特定参数类型的构造函数(包括私有)
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取类的成员变量
- 获得命名的公共字段
public Field getField(String name)
- 获得类的所有公共字段
public Field[] getFields()
- 获得类声明的命名的字段
public native Field getDeclaredField(String name)
- 获得类声明的所有字段
public native Field[] getDeclaredFields();
调用方法
- 获得类的所有公共方法
public Method[] getMethods()
- 使用特定的参数类型,获得命名的公共方法
public Method getMethod(String name, Class<?>... parameterTypes)
- 获得类声明的所有方法
public Method[] getDeclaredMethods()
- 使用特定的参数类型,获得类声明的命名的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
当我们从类中获取了一个方法后,我们就可以使用 invoke() 方法来调用这个方法:
public native Object invoke( Object obj, Object... args)
利用反射创建数组
数组在 Java 里是比较特殊的一种类型,它可以赋值给一个 Object Reference 其中的 Array 类为 java.lang.reflect.Array 类。我们通过 Array.newInstance()创建数组对象,它的原型是:
public static Object newInstance(Class<?> componentType,int length);
反射获取真实类型
当我们对一个泛型类进行反射时,需要得到泛型中的真实数据类型,来完成如json反序列化的操作。此时需要通过 Type 体系来完成。Type 接口包含了一个实现类(Class) 和四个实现接口,它们分别是:
- TypeVariable 泛型类型变量,可以获得泛型上下限等信息
public class TypeStudy<K extends Comparable & Serializable, V> {
public K mKey;
public V mValue;
public static void main(String[] args) throws NoSuchFieldException {
// 通过反射获取字段
Field keyField = TypeStudy.class.getDeclaredField("mKey");
Field valueField = TypeStudy.class.getDeclaredField("mValue");
// 强转为TypeVariable
TypeVariable keyType = (TypeVariable) keyField.getGenericType();
TypeVariable valueType = (TypeVariable) valueField.getGenericType();
// 获取泛型变量的名称
String keyName = keyType.getName();
String valueName = valueType.getName();
System.out.println("KeyName:" + keyName);
System.out.println("ValueName:" + valueName +"\n");
// 获取泛型声明信息
GenericDeclaration keyDeclaration = keyType.getGenericDeclaration();
GenericDeclaration valueDeclaration = valueType.getGenericDeclaration();
System.out.println("KeyDeclaration:" + keyDeclaration);
System.out.println("ValueDeclaration:" + valueDeclaration +"\n");
// 获取边界
Type[] keyBounds = keyType.getBounds();
Type[] valueBounds = valueType.getBounds();
for (Type keyBound : keyBounds) {
System.out.println("KeyBound:" + keyBound);
}
for (Type valueBound : valueBounds) {
System.out.println("ValueBound:" + valueBound);
}
}
}
输出结果
- ParameterizedType 具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
public class TypeStudy{
public Map<String,Integer> map;
public static void main(String[] args) throws NoSuchFieldException {
Field mapField = TypeStudy.class.getDeclaredField("map");
Type type = mapField.getGenericType();
System.out.println("Type: " + type + "\n");
ParameterizedType parameterizedType = (ParameterizedType) mapField.getGenericType();
Type ownerType = parameterizedType.getOwnerType();
System.out.println("OwnerType: " + ownerType+ "\n");
Type rawType = parameterizedType.getRawType();
System.out.println("RawType: " + rawType+ "\n");
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("ActualTypeArgument: " + actualTypeArgument);
}
}
输出结果
- GenericArrayType 当需要描述的是泛型类的数组时,比如List[],Map[],此接口会作为 Type 的实现
public class TypeStudy{
List<String>[] lists;
public static void main(String[] args) throws NoSuchFieldException {
Field field = TypeStudy.class.getDeclaredField("lists");
GenericArrayType genericType = (GenericArrayType) field.getGenericType();
Type genericComponentType = genericType.getGenericComponentType();
System.out.println(genericComponentType);
}
输出结果
- WildcardType 通配符泛型,获取上下限信息
public class TypeStudy{
List<? extends Number> numbers;
List<? super String> strs;
public static void main(String[] args) throws NoSuchFieldException {
Field numbersField = TypeStudy.class.getDeclaredField("numbers");
Field strsField = TypeStudy.class.getDeclaredField("strs");
// 先拿到泛型类型
ParameterizedType numbersType = (ParameterizedType) numbersField.getGenericType();
ParameterizedType strsType = (ParameterizedType) strsField.getGenericType();
// 再从泛型里拿到通配符类型
WildcardType numbersWildcardType = (WildcardType) numbersType.getActualTypeArguments()[0];
WildcardType strsWildcardType = (WildcardType) strsType.getActualTypeArguments()[0];
System.out.println(numbersWildcardType.getUpperBounds()[0]);
System.out.println(strsWildcardType.getLowerBounds()[0] + "\n");
System.out.println(numbersWildcardType);
System.out.println(strsWildcardType);
}
输出结果
Gson反序列化
public class Response<T> {
T data;
int code;
String msg;
public Response(T data, int code, String msg) {
this.data = data;
this.code = code;
this.msg = msg;
}
@Override
public String toString() {
return "Response{" +
"data=" + data +
", code=" + code +
", msg='" + msg + '\'' +
'}';
}
}
public class Data {
String result;
public Data(String result) {
this.result = result;
}
@Override
public String toString() {
return "Data{" +
"result='" + result + '\'' +
'}';
}
}
public class TypeStudy {
public static void main(String[] args) {
Gson gson = new Gson();
Data data = new Data("i am result");
Response<Data> response = new Response<>(data, 200, "ok");
// 序列化数据
String json = gson.toJson(response);
System.out.println("json: " + json);
// 反序列化数据
Response<Data> resp = gson.fromJson(json, new TypeToken<Response<Data>>() {
}.getType());
System.out.println(resp.data.result);
}
对于经常进行网络请求的 coder 来说,上面的代码肯定不会陌生。可是 TypeToken 为什么要被定义称抽象类呢?这是因为只有抽象类或者接口在使用时需要创建对应的实现类,此时确定泛型类型,编译器才能将泛型 signature 信息记录到 Class 元数据中。
网友评论