Java反射(Reflect)和Type

反射(Reflect)

我在java注解文章中, 有提到 RetentionPolicy.RUNTIME, 主要是用在反射技術(shù)上. 顧名思義JAVA反射機(jī)制是在運(yùn)行狀態(tài)中的技術(shù).反射就是在運(yùn)行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意方法和屬性; 并且能改變它的屬性. 在Java中被視為動態(tài)語言的關(guān)鍵

現(xiàn)在我們思考一個問題:既然在運(yùn)行狀態(tài)中可以反射的Class類的信息,那么這些類的信息到底存在JVM的哪里?
在搞清楚上面之前, 我們先來簡單了解一下JVM虛擬機(jī). 如果想詳細(xì)了解JVM虛擬機(jī)請關(guān)注我后面的文章.

JVM簡單概括(除HostSpot JVM用戶)

JVM 從線程角度來看, 分為線程私有和線程共享區(qū),
其中線程共享區(qū):分為堆,方法區(qū)
線程私有包含: 程序計(jì)數(shù)器,本地方法棧,虛擬機(jī)棧; 據(jù)說待考證, 在jdk8 之后,把本地方法棧和虛擬機(jī)棧合并了.
這里我們只簡單的介紹線程共享區(qū)域里面的方法區(qū),
方法區(qū):是各個線程共享的內(nèi)存區(qū)域,它主要是用于存儲已被虛擬機(jī)加載的類信息,常量,靜態(tài)變量,及時編譯器編譯后的代碼數(shù)據(jù).

Class類

Class類是一個Java基礎(chǔ)類, 每當(dāng)裝載一個新的類型的時候,java虛擬機(jī)都會在java堆中創(chuàng)建一個對應(yīng)于新類型的Class, 該實(shí)例就代表此類型,通過該Class實(shí)例我們就可以訪問該類型的基本信息. 上面說到在方法區(qū)中存儲被裝載類的類型信息, 我們可以通過Class實(shí)例來訪問這些信息.
下面是獲取類信息的對應(yīng)的少部分的API,其他的API可以查看JDK幫助文檔:
1:getName() 獲取這個類的全限定名
2:getSuperClass() 獲取這個類型的直接父類的全限定名
3:isInterface() 判斷這個類型是否是接口類型
4:getTypeParamters() 獲取這個類型的范圍修飾符, 本文后面會講到Type
5:getInterfaces() 獲取這個類型的超接口的全限定名的列表
6:getFields() 獲取公有字段,包括父類
7,getDeclaredFields() 獲取當(dāng)前類所有的字段 不包括父類的公有字段
8,getMethods()獲取公有方法,包括父類
9,getDeclaredMethods() 獲取當(dāng)前類所有的方法, 不包括父類的公有方法.

怎樣獲取到Class的對象?

獲取Class對象API 有:

類名.Class
對象.getClass()
Class.forName("全限定名")
類.getClassLoader().loadClass("全限定名");
子類.class.getSuperClass()
包裝類.class

這是帶有注釋的JDK8里面的loadClass()方法源碼, 在源碼中可以看出, 雙親委派機(jī)制,通過父對象獲取到Class對象. 在Android中經(jīng)常會遇到.

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) {//這里是典型的雙親委派機(jī)制孔祸, 如果父親有就直接拿父親的脸候。 也叫坑爹機(jī)制
                        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()數(shù)據(jù)類型
4,set(對象名,屬性) 這是給對象的屬性賦值
5,get(對象名) 對象名.get(), 取屬性值
6,setAccessible(true) 設(shè)置私有屬性能訪問.

Method類(方法) API

getMethod(方法名,參數(shù)數(shù)據(jù)類型(無參數(shù)傳null))獲取公有方法;
getDeclareMethod(方法名,參數(shù)數(shù)據(jù)類型(無參數(shù)傳null)) 獲取私有方法
invoke(對象名,參數(shù)數(shù)據(jù)類型) 等于 對象名.方法名; 執(zhí)行方法
getParameterTypes() 得到返回參數(shù)列表.
getRetrumType() 得到返回值方法的數(shù)據(jù)類型.

Constructor(構(gòu)造)API

Class對象.getConstructor(參數(shù)類型的class....) 得到構(gòu)造方法
Class對象.getConstructors()等到所有的構(gòu)造的方法.

Type 類

結(jié)構(gòu)圖

E1197A32F094119F750BF61005319A30.png

1,TypeVariable 泛型類型變量. 可以泛型上下限等信息
2,ParameterizedType 具體的泛型類型, 可以獲得元數(shù)據(jù)中泛型簽名類型(泛型真實(shí)類型)
3,GenericArrayType 當(dāng)需要描述的類型是泛型類的數(shù)組時,比如List[] Map[], 此接口會作為Type的實(shí)現(xiàn).
4,WildcardType 通配符泛型,獲得上下限信息 .
至于什么是上下限信息,建議大家看java泛型

下面是對這幾個Type使用示例,有詳細(xì)的注釋.

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
 * 泛型變量, 泛型信息在編譯是會被轉(zhuǎn)換為一個特定的類型, 而TypeVariable就是
 * 用來反映在JVM編譯該泛型前的信息.
 * TypeVariable 就是<T> <C extends Collection>中的變量T,C本身; 他有如下方法
 *
 * Type[] getBounds(); 獲取類型變量的上邊界,若為明確聲明上邊界則默認(rèn)為Object
 * D getGenericDeclaration() 獲取聲明該類型的變量的類型
 *
 * String getName(); 獲取在源碼中定義時的名字.
 *
 * 類型變量在定義的時候, 只能使用extends進(jìn)行(多)邊界限定, 不能用super;
 *
 * 為什么邊界是一個數(shù)組? 因?yàn)轭愋妥兞靠梢酝ㄟ^& 進(jìn)行多個上邊界限定, 因此上邊界有多個.
 */
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(); 返回實(shí)際泛型類型列表, 如上面那個Map<String,String>
 實(shí)際泛型列表中有兩個元素,就是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
 泛型數(shù)組, 組成數(shù)組的元素中有泛型則實(shí)現(xiàn)了該接口; 它的組成元素是
 ParameterizedType 或TypeVariable類型, 他只有一個方法:
 Type getGenericComponentType();返回?cái)?shù)組對象
 */
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
 * 內(nèi)容:
 * 注意:
 * 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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恬涧,一起剝皮案震驚了整個濱河市恢氯,隨后出現(xiàn)的幾起案子杉辙,更是在濱河造成了極大的恐慌,老刑警劉巖班缰,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贤壁,死亡現(xiàn)場離奇詭異,居然都是意外死亡埠忘,警方通過查閱死者的電腦和手機(jī)脾拆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來莹妒,“玉大人名船,你說我怎么就攤上這事≈嫉。” “怎么了包帚?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長运吓。 經(jīng)常有香客問我渴邦,道長,這世上最難降的妖魔是什么拘哨? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任谋梭,我火速辦了婚禮,結(jié)果婚禮上倦青,老公的妹妹穿的比我還像新娘瓮床。我一直安慰自己,他們只是感情好产镐,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布隘庄。 她就那樣靜靜地躺著,像睡著了一般癣亚。 火紅的嫁衣襯著肌膚如雪丑掺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天述雾,我揣著相機(jī)與錄音街州,去河邊找鬼兼丰。 笑死,一個胖子當(dāng)著我的面吹牛唆缴,可吹牛的內(nèi)容都是我干的鳍征。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼面徽,長吁一口氣:“原來是場噩夢啊……” “哼艳丛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起趟紊,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤质礼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后织阳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砰粹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年唧躲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碱璃。...
    茶點(diǎn)故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡弄痹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嵌器,到底是詐尸還是另有隱情肛真,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布爽航,位于F島的核電站蚓让,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏讥珍。R本人自食惡果不足惜历极,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衷佃。 院中可真熱鬧趟卸,春花似錦、人聲如沸氏义。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惯悠。三九已至邻邮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間克婶,已是汗流浹背饶囚。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工帕翻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萝风。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓嘀掸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親规惰。 傳聞我的和親對象是個殘疾皇子睬塌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評論 2 350