Java高级语言特性 ---- 反射

        Java反射机制是一种运行期的行为,即是在运行时,能够知道任意一个类的所有属性和方法;访问任意一个对象的任意属性;调用任意一个对象的任意方法。它是Java被视为准动态语言的关键特性。Java反射机制主要提供以下功能:

        1、在运行时构造任意一个类的对象;
        2、在运行时获取或者修改任意一个类所具有的成员变量和方法;
        3、在运行时调用任意一个对象的方法和访问对象的属性。

Class对象

        反射始于Class对象,它是类类型的对象,Class对象在JVM进行类加载的时候创建,只会被创建一次,一个类也只会对应一个Class对象。Class对象封装了类的信息,是对类的描述,如类所既具有的方法、属性、构造器等。因此,通过Class类,对象可以看到其所具有的所有信息,如同照镜子一样。

        获取Class对象有三种方式:1、通过对象获取,"对象名.getClass()";2、通过类名获取,“类名.class”;3、通过全类名获取,"Class.forName(全类名)"、"classLoader.loadClass(全类名)"。

        判断某个对象是否为某个类的实例,可以通过instanceof关键字,或者Class对象的isInstance()方法、isAssignableFrom()方法,后者理解为判断是否为某个类的类型。

// instanceof方法
if (obj instanceof Object) {}

// Class对象的方法,方法传递“对象”作为参数
public native boolean isInstance(Object obj);

// Class对象的方法,方法传递“Class对象”作为参数
public boolean isAssignableFrom(Class<?> cls)

反射的使用

        通过反射创建对象,有两种方式:1、用Class对象的newInstance()方法创建;2、先获取Class对象的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。后者可以任意指定构造器进行对象的创建。

// 方法一:通过Class对象的newInstance()创建
Class<?> c = String.class;
Object str = c.newInstance();

// 方法二:先通过Class对象获取Constructor对象,再用该对象创建实例
Class<?> c = String.class; // 获取String所对应的Class对象
Constructor constructor = c.getConstructor(String.class); // 获取构造器对象
Object obj = constructor.newInstance("test"); // 根据构造器创建实例

        获取构造对象的方法:

// 获得使用特殊的参数类型的public构造函数(包括父类)
Constructor getConstructor(Class[] params)
// 获得类的所有公共构造函数
Constructor[] getConstructors()
// 获得使用特定参数类型的构造函数(包括私有)
Constructor getDeclaredConstructor(Class[] params)
// 获得类的所有构造函数(与接入级别无关)
Constructor[] getDeclaredConstructors()

         获得变量对象的方法:

// 获得指定名称的公共字段
Field getField(String name)
// 获得类的所有公共字段,包括父类
Field[] getFields()
// 获得类声明的命名的字段
Field getDeclaredField(String name)
// 获得类声明的所有字段,包括私有
Field[] getDeclaredFields()

        获得方法对象的方法:

// 使用特定的参数类型,获得命名的公共方法
Method getMethod(String name, Class[] params)
// 获得类的所有公共方法 (包含父类)
Method[] getMethods()
// 用特定参数类型,获得类声明的命名的方法
Method getDeclaredMethod(String name, Class[] params)
// 获得类的所有方法,包括私有
Method[] getDeclaredMethods() 

         当获取到Method对象后,就可以用 invoke() 方法来进行调用。invoke方法的签名是:“public Object invoke(Object obj, Object... args)”,第一个调用参数是对象类型,第二个是参数列表。

        反射创建数组:通过Array.newInstance()创建数组对象。newInstance方法的签名是:“public static Object newInstance(Class<?> componentType, int length)”,第一个参数是数组元素类型,第二个参数是数组的长度。

反射与泛型辨析

        前面在“泛型”的博客中提到,泛型在擦除时会保留一部分信息到Signature参数,这使得在运行时(反射调用),获得参数化类型信息成为可能。        

        在反射时获取泛型信息,需要通过Type体系来完成。Type是一个接口,Type体系包含了一个实现类(上面说的Class类型,既是其实现类)和四个实现接口,分别是:
        TypeVariable泛型类型变量,可以得到泛型界限、名称、声明泛型类的信息;
        ParameterizedType具体的泛型类型,可以获得元数据中泛型真实类型(签名类型);
        GenericArrayType 用于泛型类是数组的情形;
        WildcardType带有通配符泛型的,用于获得上下限信息;

TypeVariable,获取泛型类型变量信息。

// TypeVariable接口的定义
public interface TypeVariable<D extends GenericDeclaration> extends Type {
    Type[] getBounds(); // 获取泛型上界的信息
    D getGenericDeclaration(); // 获取泛型声明处类的信息
    String getName(); // 获取泛型名称
}

// TypeVariable接口的使用 
package com.kelly.generic;
public class TestType<V extends Comparable & Serializable> {
    V value;
    public void test() throws Exception {
        Field fv = TestType.class.getDeclaredField("value");
        // 获取泛型对应的 TypeVariable
        TypeVariable valueType = (TypeVariable) fv.getGenericType();
        String name = valueType.getName(); // name == "V"
        // valueType.getGenericDeclaration() == "com.kelly.generic.TestType"
        Log.e(TAG, "GenericDeclaration= " + valueType.getGenericDeclaration());
        // getBounds() 获取界限信息,
        // 此处返回 “interface java.lang.Comparable”和“interface java.io.Serializable”
        for (Type type : valueType.getBounds()) {
            Log.e(TAG, "type= " + type);
        }
    }
}

ParameterizedType,获得具现化泛型的真实类型相关信息。

public interface ParameterizedType extends Type {
    Type[] getActualTypeArguments(); // 
    Type getRawType();
    Type getOwnerType();
}

public class TestType {
    List<String> list;
    public void test() throws Exception {
        Field fList = TestType.class.getDeclaredField("list");
        // 获取 ParameterizedType
        ParameterizedType pType = (ParameterizedType) fList.getGenericType();
        // 获取泛型的原始类型,type = “interface java.util.List”
        Type type = pType.getRawType()); 
        // 获取泛型具体类型,此处 type= "class java.lang.String"
        for (Type type : pType.getActualTypeArguments()) {
            Log.e(TAG, "type= " + type);
        }
    }
}

GenericArrayType,获得数组中具现化泛型真实类型信息

public interface GenericArrayType extends Type {
    Type getGenericComponentType(); // 获取数组中实例化泛型的真实类型信息
}

public class TestType {
    Generator<String>[] array;
    public void test() throws Exception {
        Field f = TestType.class.getDeclaredField("array");
        GenericArrayType genericType = (GenericArrayType) f.getGenericType();
        // GenericComponentType= com.kelly.generic.Generator<java.lang.String>
        Log.e(TAG, "GenericComponentType= " + genericType.getGenericComponentType());
    }
}

WildcardType,获得带有通配符泛型的上下限信息

public interface WildcardType extends Type {
    Type[] getUpperBounds(); // 获取通配符上限
    Type[] getLowerBounds(); // 获取通配符下限
}

public class TestType {
    private List<? extends Number> a; // 上限
    private List<? super String> b; //下限

    public void main() throws Exception {
        Field fieldA = TestType.class.getDeclaredField("a");
        Field fieldB = TestType.class.getDeclaredField("b");
        // 获取范型类型
        ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
        ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
        // 从范型类型得到通配符类型
        WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
        WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
        // 查看泛型信息,mTypeA = "? extends java.lang.Number"
        Log.e(TAG, "wTypeA= " + wTypeA);
        // 获得上限信息,upper = "class java.lang.Number"
        Log.e(TAG, "upper= " + wTypeA.getUpperBounds()[0]);
        // 获得下限信息,lower = "class java.lang.String"
        Log.e(TAG, "lower= " + wTypeB.getLowerBounds()[0]);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值