Java反射(Reflect)和Type

反射(Reflect)

我在java注解文章中, 有提到 RetentionPolicy.RUNTIME, 主要是用在反射技术上. 顾名思义JAVA反射机制是在运行状态中的技术.反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性; 并且能改变它的属性. 在Java中被视为动态语言的关键

现在我们思考一个问题:既然在运行状态中可以反射的Class类的信息,那么这些类的信息到底存在JVM的哪里?
在搞清楚上面之前, 我们先来简单了解一下JVM虚拟机. 如果想详细了解JVM虚拟机请关注我后面的文章.

JVM简单概括(除HostSpot JVM用户)

JVM 从线程角度来看, 分为线程私有和线程共享区,
其中线程共享区:分为堆,方法区
线程私有包含: 程序计数器,本地方法栈,虚拟机栈; 据说待考证, 在jdk8 之后,把本地方法栈和虚拟机栈合并了.
这里我们只简单的介绍线程共享区域里面的方法区,
方法区:是各个线程共享的内存区域,它主要是用于存储已被虚拟机加载的类信息,常量,静态变量,及时编译器编译后的代码数据.

Class类

Class类是一个Java基础类, 每当装载一个新的类型的时候,java虚拟机都会在java堆中创建一个对应于新类型的Class, 该实例就代表此类型,通过该Class实例我们就可以访问该类型的基本信息. 上面说到在方法区中存储被装载类的类型信息, 我们可以通过Class实例来访问这些信息.
下面是获取类信息的对应的少部分的API,其他的API可以查看JDK帮助文档:
1:getName() 获取这个类的全限定名
2:getSuperClass() 获取这个类型的直接父类的全限定名
3:isInterface() 判断这个类型是否是接口类型
4:getTypeParamters() 获取这个类型的范围修饰符, 本文后面会讲到Type
5:getInterfaces() 获取这个类型的超接口的全限定名的列表
6:getFields() 获取公有字段,包括父类
7,getDeclaredFields() 获取当前类所有的字段 不包括父类的公有字段
8,getMethods()获取公有方法,包括父类
9,getDeclaredMethods() 获取当前类所有的方法, 不包括父类的公有方法.

怎样获取到Class的对象?

获取Class对象API 有:

类名.Class
对象.getClass()
Class.forName("全限定名")
类.getClassLoader().loadClass("全限定名");
子类.class.getSuperClass()
包装类.class

这是带有注释的JDK8里面的loadClass()方法源码, 在源码中可以看出, 双亲委派机制,通过父对象获取到Class对象. 在Android中经常会遇到.

File:JDK1.8-->ClassLoader.java
   protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name); //首先从缓存中获取class。 
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {//这里是典型的双亲委派机制, 如果父亲有就直接拿父亲的。 也叫坑爹机制
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);//抛出ClassNotFoundException

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);// 这是native方法,将JVM链接指定的 Java 类
            }
            return c;
        }
    }
Field类(属性)

获取Field对象

import java.lang.reflect.Field;

public class MyClass extends ChildClass {

    private String pri_field = "private_field";
    public String pub_field = "public_field";

    public static void main(String[] args) throws NoSuchFieldException {
        Field[] declaredFields = MyClass.class.getDeclaredFields();
        Field[] fields = MyClass.class.getFields();
        Field pri_field = MyClass.class.getDeclaredField("pri_field");
        Field pub_field = MyClass.class.getField("pub_field");
        System.out.println("-----declaredFields-------");
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField.getName());
        }
        System.out.println("-----Fields-------");
        for (Field field : fields) {
            System.out.println(field.getName());
        }
        System.out.println("-------DeclaredField-------");
        System.out.println(pri_field.getName());
        System.out.println("-------Field-------");
        System.out.println(pub_field.getName());
        System.out.println("-------全限定名-------");
        System.out.println(MyClass.class.getName());
    }
}
class ChildClass{
    private String pri_childField = "private_child_field";
    public String pub_childField = "public_child_field";
}
image.png

Field类使用的一些API,大家可以自己试着去打印这些方法的值

1,getName() 获取属性名
2,getModifiers()获取修饰符
3,getType()数据类型
4,set(对象名,属性) 这是给对象的属性赋值
5,get(对象名) 对象名.get(), 取属性值
6,setAccessible(true) 设置私有属性能访问.

Method类(方法) API

getMethod(方法名,参数数据类型(无参数传null))获取公有方法;
getDeclareMethod(方法名,参数数据类型(无参数传null)) 获取私有方法
invoke(对象名,参数数据类型) 等于 对象名.方法名; 执行方法
getParameterTypes() 得到返回参数列表.
getRetrumType() 得到返回值方法的数据类型.

Constructor(构造)API

Class对象.getConstructor(参数类型的class....) 得到构造方法
Class对象.getConstructors()等到所有的构造的方法.

Type 类

结构图

E1197A32F094119F750BF61005319A30.png

1,TypeVariable 泛型类型变量. 可以泛型上下限等信息
2,ParameterizedType 具体的泛型类型, 可以获得元数据中泛型签名类型(泛型真实类型)
3,GenericArrayType 当需要描述的类型是泛型类的数组时,比如List[] Map[], 此接口会作为Type的实现.
4,WildcardType 通配符泛型,获得上下限信息 .
至于什么是上下限信息,建议大家看java泛型

下面是对这几个Type使用示例,有详细的注释.

1,TypeVariable使用

package com.zyang.reflect.type.typeVariable;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
/**
 * TypeVariable
 * 泛型变量, 泛型信息在编译是会被转换为一个特定的类型, 而TypeVariable就是
 * 用来反映在JVM编译该泛型前的信息.
 * TypeVariable 就是<T> <C extends Collection>中的变量T,C本身; 他有如下方法
 *
 * Type[] getBounds(); 获取类型变量的上边界,若为明确声明上边界则默认为Object
 * D getGenericDeclaration() 获取声明该类型的变量的类型
 *
 * String getName(); 获取在源码中定义时的名字.
 *
 * 类型变量在定义的时候, 只能使用extends进行(多)边界限定, 不能用super;
 *
 * 为什么边界是一个数组? 因为类型变量可以通过& 进行多个上边界限定, 因此上边界有多个.
 */
public class TestType<K extends Comparable & Serializable,V> {
    K key;
    V value;

    public static void main(String[] args) throws NoSuchFieldException {
        Field fieldk = TestType.class.getDeclaredField("key");
        Field fieldv = TestType.class.getDeclaredField("value");

        TypeVariable keyType = (TypeVariable) fieldk.getGenericType();

        TypeVariable valueType = (TypeVariable) fieldv.getGenericType();

        System.out.println(keyType.getName());
        System.out.println(valueType.getName());


        System.out.println(keyType.getGenericDeclaration());
        System.out.println(valueType.getGenericDeclaration());

        System.out.println("K的上界");
        for (Type type: keyType.getBounds()){
            System.out.println(type);
        }

        System.out.println("V的上界");
        for (Type type:valueType.getBounds()){
            System.out.println(type);
        }
    }
}
image.png

2,ParameterizedType使用

package com.zyang.reflect.type.parameterizedType;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

/**
        ParameterizedType
 具体的泛型类型, 如Map<String,String>

 具体方法
 Type getRawType(); 返回承载该泛型信息的对象, 如上面的那个Map<String,String>承载泛型信息
 的对象是Map
 Type[] getActualTypeArguments(); 返回实际泛型类型列表, 如上面那个Map<String,String>
 实际泛型列表中有两个元素,就是String
 */
public class TestType {
    Map<String,String> map;

    public static void main(String[] args) throws NoSuchFieldException {
        Field field = TestType.class.getDeclaredField("map");
        System.out.println(field.getGenericType());

        ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
        System.out.println(parameterizedType.getRawType());
        for(Type type:parameterizedType.getActualTypeArguments()){
            System.out.println("---->"+type);
        }
    }
}
image.png

3,GenericArrayType使用

package com.zyang.reflect.type.genericArrayType;

import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Type;
import java.util.List;

/**
GenericArrayType
 泛型数组, 组成数组的元素中有泛型则实现了该接口; 它的组成元素是
 ParameterizedType 或TypeVariable类型, 他只有一个方法:
 Type getGenericComponentType();返回数组对象
 */
public class TestType {

    List<String>[] lists;

    public static void main(String[] args) throws NoSuchFieldException {
        Field field = TestType.class.getDeclaredField("lists");
        Type genericType1 = field.getGenericType();
        GenericArrayType genericType = (GenericArrayType) genericType1;
        System.out.println(genericType.getGenericComponentType());
    }
}
image.png

4, WildcardType使用

package com.zyang.reflect.type.wildcardType;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.WildcardType;
import java.util.List;

/**
 * 作者:zhengyang on 2020-04-30
 * 邮箱:lzy@winwayit.com.cn
 * 内容:
 * 注意:
 * WindcardType
 * 该接口表示通配符泛型, 比如? extends Number 和 ? super Integer它又如下方法
 *  Type[] getUpperBounds(); 获取泛型变量的上界
 *  Type[] getLowerBounds(); 获取泛型变量的下界
 *
 */
public class TestType {
    private List<? extends Number> a;

    private List<? super String> b;

    public static void main(String[] args) throws NoSuchFieldException {
        Field a = TestType.class.getDeclaredField("a");
        Field b = TestType.class.getDeclaredField("b");

        ParameterizedType parameterizedType_a = (ParameterizedType) a.getGenericType();

        ParameterizedType parameterizedType_b = (ParameterizedType) b.getGenericType();

        WildcardType wildcardType_a = (WildcardType) parameterizedType_a.getActualTypeArguments()[0];
        WildcardType actualTypeArgument = (WildcardType) parameterizedType_b.getActualTypeArguments()[0];

        System.out.println(wildcardType_a.getUpperBounds()[0]);
        System.out.println(actualTypeArgument.getLowerBounds()[0]);

        System.out.println(wildcardType_a);
    }
}
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342