Java—反射

Java–反射

反射


一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的,并且能够获得此类的引用。于是我们直接
对这个类进行实例化,之后使用这个类对象进行操作。

反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用new关键字来创建对象了。这时候,我们
使用 JDK 提供的反射AP进行反射调用。反射就是在运行状态中对于任意一个类都能够知道这个类的所有属性和
**方法对于任意一个对象都能够调用它的任意方法和属性:并且能改变它的属性。**是Java被视为动态语言的关键。

Java的反射机制提供了以下功能:

  • 在运行时构造任意一个类对象
  • 在运行时获取或修改任意一个类所具有的成员变量和成员方法。
  • 在运行时调用任意一个对象的方法。

Class

反射始于 Class,Class是一个类,封装了当前对象所对应的类的信息。—个类中有属性,方法,构造器等,比如说
有一个 Person类,一个 Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是
Cass,它应该有类名,属性,方法,构造器等。Cass是用来描述类的类。
Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性,方法,构造器,实现了哪些接口等等。对于毎
个类而言,JRE都为其保留一个不变的Cass类型的对象。一个cass对象包含了特定某个类的有关信息。对象只
能由系统建立对象,一个类(而不是一个对象)在M中只会有一个Cass实例。

获取Class对象

获取Class对象的三种方式

  1. 通过类名获取 类名.class
  2. 通过对象获取 对象.getClass()
  3. 通过全类名获取 Class.forName(全类名) classLoader.loadClass(全类名)
  • 使用Class类的forName静态方法
public static Class<?> forName(String className)
  • 直接获取某个对象的Class
Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;
  • 调用某个对象的getClass()方法
StringBuilder stringBuilder = new StringBuilder();
Class<?> klass = stringBuilder.getClass();

判断是否为某个类的实例

一般情况下判断是否为某个类的实例是用 instanceof 关键字。同事我们也可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,他是一个native方法:

public native boolean isInstance(Object obj);

判断是否为某个类型

public boolean isAssignableFrom(Class<?> cls);

创建实例

通过反射来生成对象主要有两种方式。

  • 使用 Class 对象的 newInstance()方法来创建Class对象对应类的实例。
Class<?> c = String.class;
Object obj = c.newInstance();
  • 先通过 Class 对象获取指定的 Constructor 对象,再调用 newInstance() 方法来创建实例。这种方法可以用指定的构造器构造类的实例。
Class<?> c = String.class;
Constructor<?> constructor = c.getConstructor(String.class);
Object obj = constructor.newInstance("23333");
System.out.println(obj);

获取构造器信息

得到构造器的方法

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

获取构造器的方法与上述获取方法的用法类相似。主要通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例。

public T newInstance(Object... initargs)

获取类的成员变量(字段)信息

获取字段信息的方法

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() -- 获得类声明的所有方法。

当我们从类中获取一个方法后,我们就可以用 invoke() 方法来调用这个方法。invoke 方法的原型是

public object invoke(Object obj, Object... args)

利用反射创建数组

数字在Java里是比较特殊的一种类型,他可以赋值给一个Object Reference 其中的Array类为java.lang.reflect.Array类。我们通过Array.newInstance() 创建数组对象,他的原型是:

public static Object newInstance(Class<?> componentType, int length);

反射代码示例


获取被注解标注的字段值

/**
 *	判断是否被注解标注主要使用isAnnotationPresent()来判断
 *  也可以通过 getAnnotation() != null 来判断是否被注解标注
 */
Field[] declaredFields = object.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
    //判断是否被注解声明
    if (declaredField.isAnnotationPresent(UserName.class)){
        //设置可以访问
        declaredField.setAccessible(true);
        //获取被注解标注的值
        String userName = declaredField.get(object).toString();
        String value = "";
        if (declaredField.getAnnotation(UserName.class) != null) {
            //获取注解中传入的值 value
            UserName annotation = declaredField.getAnnotation(UserName.class);
            value = annotation.value();
        }
        System.out.println("被注解@UserName的值为:" + userName + //被注解@UserName的值为:张三
                   "\n注解@UserName的Value为:" + value + // 注解@UserName的Value为:name
                   "\n被注解标注的值名称为:" + declaredField.getName() + //被注解标注的值名称为:name
                   "\n被标注的类型为:" + declaredField.getGenericType()); //被标注的类型为:class java.lang.String
    }
}

泛型通配符 WildcardType

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

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];
// 方法测试
System.out.println(wTypeA.getUpperBounds()[0]);   // class java.lang.Number
System.out.println(wTypeB.getLowerBounds()[0]);   // class java.lang.String
// 看看通配符类型到底是什么, 打印结果为: ? extends java.lang.Number
System.out.println(wTypeA);

泛型变量 TypeVariable

K key;
V value;

// 获取字段的类型
Field fk = TestType.class.getDeclaredField("key");
Field fv = TestType.class.getDeclaredField("value");

TypeVariable keyType = (TypeVariable)fk.getGenericType();
TypeVariable valueType = (TypeVariable)fv.getGenericType();
// getName 方法 获取在源码中定义时的名字
System.out.println(keyType.getName());                 // K
System.out.println(valueType.getName());               // V
// getGenericDeclaration 方法 获取声明该类型变量的类型
System.out.println(keyType.getGenericDeclaration());   // class com.test.TestType
System.out.println(valueType.getGenericDeclaration()); // class com.test.TestType
// getBounds 方法 获取类型变量的上边界, 若未明确声明上边界则默认为Object
System.out.println("K 的上界:");                        // 有两个
for (Type type : keyType.getBounds()) {                // interface java.lang.Comparable
System.out.println(type);                          // interface java.io.Serializable
}
System.out.println("V 的上界:");                        // 没明确声明上界的, 默认上界是 Object
for (Type type : valueType.getBounds()) {              // class java.lang.Object
System.out.println(type);
}

具体类型泛型 ParameterizedType

Map<String, String> map;

Field f = TestType.class.getDeclaredField("map");
System.out.println(f.getGenericType());                               // java.util.Map<java.lang.String, java.lang.String>
ParameterizedType pType = (ParameterizedType) f.getGenericType();
//getRawType() 返回承载该泛型信息的对象, 如上面那个Map<String, String>承载范型信息的对象是Map 
System.out.println(pType.getRawType());                               // interface java.util.Map
//getActualTypeArguments() 返回实际泛型类型列表, 如上面那个Map<String, String>实际范型列表中有两个元素, 都是String
for (Type type : pType.getActualTypeArguments()) {
System.out.println(type);                                         // 打印两遍: class java.lang.String
}

泛型数组 GenericArrayType

List<String>[] lists;

Field f = TestType.class.getDeclaredField("lists");
GenericArrayType genericType = (GenericArrayType) f.getGenericType();
//getGenericComponentType() 返回数组的组成对象
System.out.println(genericType.getGenericComponentType()); // java.util.List<java.lang.String>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值