成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java高級(jí)程序員必備:反射、動(dòng)態(tài)代理

church / 3405人閱讀

摘要:相比硬編碼,反射要復(fù)雜的多,但其給我們帶來(lái)了更大的靈活性。實(shí)際上構(gòu)造函數(shù)也是類的靜態(tài)方法,因此使用關(guān)鍵字創(chuàng)建類的新對(duì)象也會(huì)被當(dāng)做對(duì)類的靜態(tài)引用,從而觸發(fā)類加載器對(duì)類的加載?;A(chǔ)基礎(chǔ)主要是為反射提供通用特性的接口或基類。

1. Java類型系統(tǒng)
獲取Java類型系統(tǒng),主要有兩個(gè)方式:一種是傳統(tǒng)的RTTI(Run-Time Type Identification),它假定我們?cè)诰幾g時(shí)已經(jīng)知道了所有的類型信息;另一種是反射(Reflect),它允許我們?cè)诔绦蜻\(yùn)行時(shí)獲取并使用類型信息。

假如有一個(gè)簡(jiǎn)單的繼承體系,讓我們看下在RTTI和Reflect不同情況下如何獲取類型信息。

Animal為接口,定義getType以返回不同動(dòng)物的類型,Cat、Dog、Elephant等為具體實(shí)現(xiàn)類,均實(shí)現(xiàn)getType接口。一般情況下,我們會(huì)創(chuàng)建一個(gè)具體的對(duì)象(Cat,Dog,Elephant等),把它向上轉(zhuǎn)型為Animal,并在程序后面直接使用Animal引用。

具體樣例代碼如下:

/**
 * 動(dòng)物
 */
public interface Animal {
    /**
     * 獲取動(dòng)物類型
     * @return
     */
    String getType();
}

/**
 * 動(dòng)物具體子類 貓
 */
public class Cat implements Animal{

    @Override
    public String getType() {
        return "貓";
    }
}

/**
 * 動(dòng)物具體子類 狗
 */
public class Dog implements Animal{
    @Override
    public String getType() {
        return "狗";
    }
}

/**
 * 動(dòng)物具體實(shí)現(xiàn) 大象
 */
public class Elephant implements Animal{
    @Override
    public String getType() {
        return "大象";
    }
}

讓我們看下相同的功能通過(guò)硬編碼反射兩個(gè)機(jī)制如何實(shí)現(xiàn)。

1.1. 硬編碼
RTTI假定在編譯期,已經(jīng)知道了所有的類型信息。在編碼時(shí),可以直接使用具體的類型信息,這是我們最常見的類型用法。

在編譯期,編譯器通過(guò)容器、泛型保障類型系統(tǒng)的完整性;在運(yùn)行時(shí),由類型轉(zhuǎn)換操作來(lái)確保這一點(diǎn)。

硬編碼樣例如下:

public static void main(String... args){
    List animals = createAnimals();
    for (Animal animal : animals){
        System.out.println(animal.getType());
    }

}

/**
 * RTTI假定我們?cè)诰幾g時(shí)已經(jīng)知道了所有的類型
 * @return
 */
private static List createAnimals() {
    List animals = new ArrayList<>();
    animals.add(new Cat()); // 已知類型Cat
    animals.add(new Elephant()); // 已知類型Elephant
    animals.add(new Dog()); // 已知類型 Dog
    return animals;
}

在這個(gè)例子中,我們把Cat、Elephant、Dog等向上轉(zhuǎn)型為Animal并存放于List中,在轉(zhuǎn)型過(guò)程中丟失了具體的類型信息(只保留了接口信息Animal);當(dāng)我們從List中取出元素時(shí),這時(shí)容器(容器內(nèi)部所有的元素都被當(dāng)做Object)會(huì)自動(dòng)將結(jié)果轉(zhuǎn)型成Animal。這是RTTI最基本的用法,在Java中所有的類型轉(zhuǎn)換都是在運(yùn)行時(shí)進(jìn)行有效性檢查。這也是RTTI的含義,在運(yùn)行時(shí),識(shí)別一個(gè)對(duì)象的類型。

1.2. Reflect
Reflect允許我們?cè)谶\(yùn)行時(shí)獲取并使用類型信息,它主要用于在編譯階段無(wú)法獲得所有的類型信息的場(chǎng)景,如各類框架。

反射樣例如下:

private static final String[] ANIMAL_TYPES = new String[]{
        "com.example.reflectdemo.base.Cat",
        "com.example.reflectdemo.base.Elephant",
        "com.example.reflectdemo.base.Dog"
};

public static void main(String... args){
    List animals = createAnimals();
    for (Object animal : animals){
        System.out.println(invokeGetType(animal));
    }

}

/**
 * 利用反射API執(zhí)行g(shù)etType方法(等同于animal.getType)
 * @param animal
 * @return
 */
private static String invokeGetType(Object animal){
    try {
        Method getTypeMethod = Animal.class.getMethod("getType");
        return (String) getTypeMethod.invoke(animal);
    }catch (Exception e){
        return null;
    }

}

/**
 * 反射允許我們?cè)谶\(yùn)行時(shí)獲取類型信息
 * @return
 */
private static List createAnimals() {
    List animals = new ArrayList<>();
    for (String cls : ANIMAL_TYPES){
        animals.add(instanceByReflect(cls));
    }
    return animals;
}

/**
 * 使用反射機(jī)制,在運(yùn)行時(shí)動(dòng)態(tài)的實(shí)例化對(duì)象(等同于new關(guān)鍵字)
 * @param clsStr
 * @return
 */
private static Object instanceByReflect(String clsStr) {
    try {
        // 通過(guò)反射獲取類型信息
        Class cls = Class.forName(clsStr);
        // 通過(guò)Class實(shí)例化對(duì)象
        Object object = cls.newInstance();
        return object;
    }catch (Exception e){
        e.printStackTrace();
        return null;
    }

}

反射,可以通過(guò)一組特殊的API,在運(yùn)行時(shí),動(dòng)態(tài)執(zhí)行所有Java硬編碼完成的功能(如對(duì)象創(chuàng)建、方法調(diào)用等)。

相比硬編碼,Java反射API要復(fù)雜的多,但其給我們帶來(lái)了更大的靈活性。

2. Class對(duì)象
要理解RTTI在Java中的工作原理,首先需要知道類型信息在Java中是如何表示的。這個(gè)工作是由稱為Class對(duì)象的特殊對(duì)象完成的,它包含了與類相關(guān)的所有信息。Java使用Class對(duì)象來(lái)執(zhí)行RTTI。

類是程序的一部分,每個(gè)類都會(huì)有一個(gè)Class對(duì)象。每當(dāng)編寫并編譯一個(gè)新類(動(dòng)態(tài)代理、CGLIB、運(yùn)行時(shí)編譯都能創(chuàng)建新類),就會(huì)產(chǎn)生一個(gè)Class對(duì)象,為了生成這個(gè)類的對(duì)象,運(yùn)行這個(gè)程序的JVM將使用稱為“類加載器”的子系統(tǒng)。

2.1. Class Loader
類加載器子系統(tǒng),是JVM體系重要的一環(huán),主要完成將class二進(jìn)制文件加載到JVM中,并將其轉(zhuǎn)換為Class對(duì)象的過(guò)程。

類加載器子系統(tǒng)實(shí)際上是一條類加載器鏈,但是只有一個(gè)原生類加載器,它是JVM實(shí)現(xiàn)的一部分。原生類加載器加載的是可信類,包括Java API類,他們通常是從本地加載。在這條鏈中,通常不需要添加額外的類加載器,但是如果有特殊需求,可以掛載新的類加載器(比如Web容器)。

所有的類都是在第一次使用時(shí),動(dòng)態(tài)加載到JVM中的,當(dāng)程序創(chuàng)建第一次對(duì)類的靜態(tài)成員引用時(shí),就會(huì)加載這個(gè)類。實(shí)際上構(gòu)造函數(shù)也是類的靜態(tài)方法,因此使用new關(guān)鍵字創(chuàng)建類的新對(duì)象也會(huì)被當(dāng)做對(duì)類的靜態(tài)引用,從而觸發(fā)類加載器對(duì)類的加載。

Java程序在它開始運(yùn)行之前并非被全部加載,各個(gè)部分是在需要時(shí)按需加載的。類加載器在加載類之前,首先檢查這個(gè)類的Class是否已經(jīng)加載,如果尚未加載,加載器會(huì)按照類名查找class文件,并對(duì)字節(jié)碼進(jìn)行有效性校驗(yàn),一旦Class對(duì)象被載入內(nèi)存,它就用來(lái)創(chuàng)建這個(gè)類的所有對(duì)象。

static初始化塊在類加載時(shí)調(diào)用,因此可以用于觀察類在什么時(shí)候進(jìn)行加載,樣例如下:

static class C1{
    static {
        System.out.println("C1");
    }
}

static class C2{
    static {
        System.out.println("C2");
    }
}

static class C3{
    static {
        System.out.println("C3");
    }
}

public static void main(String... args) throws Exception{
    System.out.println("new start");
    // 構(gòu)造函數(shù)為類的靜態(tài)引用,觸發(fā)類型加載
    new C1();
    new C1();
    System.out.println("new end");

    System.out.println();

    System.out.println("Class.forName start");
    // Class.forName為Class上的靜態(tài)函數(shù),用于強(qiáng)制加載Class
    Class.forName("com.example.reflectdemo.classloader.ClassLoaderTest$C2");
    Class.forName("com.example.reflectdemo.classloader.ClassLoaderTest$C2");
    System.out.println("Class.forName end");

    System.out.println();

    System.out.println("C3.class start");
    // Class引用,會(huì)觸發(fā)Class加載,但是不會(huì)觸發(fā)初始化
    Class c1 = C3.class;
    Class c2 = C3.class;
    System.out.println("C3.class end");

    System.out.println();

    System.out.println("c1.newInstance start");
    // 調(diào)用class上的方法,觸發(fā)初始化邏輯
    c1.newInstance();
    System.out.println("c1.newInstance end");

}

輸出結(jié)果為:

new start
C1
new end

Class.forName start
C2
Class.forName end

C3.class start
C3.class end

c1.newInstance start
C3
c1.newInstance end

看結(jié)果,C3.class的調(diào)用不會(huì)自動(dòng)的初始化該Class對(duì)象(調(diào)用static塊)。為了使用Class而做的準(zhǔn)備工作主要包括三個(gè)步驟:

加載,這個(gè)是由類加載器執(zhí)行。該步驟將查找字節(jié)碼文件,并根據(jù)字節(jié)碼創(chuàng)建一個(gè)Class對(duì)象。

鏈接,在鏈接階段將驗(yàn)證類中的字節(jié)碼,為靜態(tài)域分配存儲(chǔ)空間,如果必要的話,將解析這個(gè)類創(chuàng)建的對(duì)其他類的引用。

初始化,如果該類有超類,則對(duì)其進(jìn)行初始化,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化塊。初始化被延時(shí)到對(duì)靜態(tài)方法或非常數(shù)靜態(tài)域進(jìn)行首次訪問時(shí)才執(zhí)行。

2.2. Class 實(shí)例獲取
Class對(duì)象作為Java類型體系的入口,如何獲取實(shí)例成為第一個(gè)要解決的問題。

Class對(duì)象的獲取主要有以下幾種途徑:

ClassName.class,獲取Class對(duì)象最簡(jiǎn)單最安全的方法,其在編譯時(shí)會(huì)受到編譯檢測(cè),但上例中已經(jīng)證實(shí),該方法不會(huì)觸發(fā)初始化邏輯。

Class.forName,這是反射機(jī)制最常用的方法之一,可以在不知具體類型時(shí),通過(guò)一個(gè)字符串加載所對(duì)應(yīng)的Class對(duì)象。

object.getClass,這也是比較常用的方式之一,通過(guò)一個(gè)對(duì)象獲取生成該對(duì)象的Class實(shí)例。

對(duì)于基本數(shù)據(jù)類型對(duì)于的包裝器類,還提供了一個(gè)TYPE字段,指向?qū)?yīng)的基本類型的Class對(duì)象。

基本類型 TYPE類型
boolean.class Boolean.TYPE
char.class Char.TYPE
byte.class Byte.TYPE
short.class Short.TYPE
int.class Integer.TYPE
long.class Long.TYPE
float.class Float.TYPE
double.class Double.TYPE
void.class Void.TYPE
2.3. Class 類型信息
Class對(duì)象存儲(chǔ)了一個(gè)class的所有類型信息,當(dāng)獲取到Class對(duì)象后,便能通過(guò)API獲取到所有信息。

在進(jìn)入Class類型信息之前,需要簡(jiǎn)單的了解下幾個(gè)反射的基類,以便更好的理解反射實(shí)現(xiàn)體系。

2.3.1 ClassAPI 基礎(chǔ)
Class API基礎(chǔ)主要是為反射API提供通用特性的接口或基類。由于其通用性,現(xiàn)統(tǒng)一介紹,在具體的API中將對(duì)其進(jìn)行忽略。
2.3.1.1 AnnotatedElement
AnnotatedElement為Java1.5新增接口,該接口代表程序中可以接受注解的程序元素,并提供統(tǒng)一的Annotation訪問方式,賦予API通過(guò)反射獲取Annotation的能力,當(dāng)一個(gè)Annotation類型被定義為運(yùn)行時(shí)后,該注解才能是運(yùn)行時(shí)可見,當(dāng)class文件被裝載時(shí)被保存在class文件中的Annotation才會(huì)被虛擬機(jī)讀取。

AnnotatedElement接口是所有注解元素(Class、Method、Field、Package和Constructor)的父接口,所以程序通過(guò)反射獲取了某個(gè)類的AnnotatedElement對(duì)象之后,程序就可以調(diào)用該對(duì)象的下列方法來(lái)訪問Annotation信息:

方法 含義
T getAnnotation(Class annotationClass) 返回程序元素上存在的、指定類型的注解,如果該類型注解不存在,則返回null
Annotation[] getAnnotations() 返回該程序元素上存在的所有注解
boolean is AnnotationPresent(Class annotationClass) 判斷該程序元素上是否包含指定類型的注解,存在則返回true,否則返回false
Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒有注釋直接存在于此元素上,則返回長(zhǎng)度為零的一個(gè)數(shù)組)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會(huì)對(duì)其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響。

AnnotatedElement子類涵蓋所有可以出現(xiàn)Annotation的地方,其中包括:

Constructor 構(gòu)造函數(shù)

Method 方法

Class 類型

Field 字段

Package 包

Parameter 參數(shù)

AnnotatedParameterizedType 泛型

AnnotatedTypeVariable 變量

AnnotatedArrayType 數(shù)組類型

AnnotatedWildcardType

樣例如下:

public class AnnotatedElementTest {

    public static void main(String... args){
        System.out.println("getAnnotations:");
        for (Annotation annotation :  A.class.getAnnotations()){
            System.out.println(annotation);
        }

        System.out.println();
        System.out.println("getAnnotation:" + A.class.getAnnotation(TestAnn1.class));

        System.out.println();
        System.out.println("isAnnotationPresent:" + A.class.isAnnotationPresent(TestAnn1.class));

    }

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnn1{

    }

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnn2{

    }

    @TestAnn1
    @TestAnn2
    public class A{

    }
}

輸出結(jié)果如下:

getAnnotations:
@com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn1()
@com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn2()

getAnnotation:@com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn1()

isAnnotationPresent:true
2.3.1.2. Member
Member用于標(biāo)記反射中簡(jiǎn)單元素。

所涉及方法如下:

方法 含義
getDeclaringClass 元素所在類
getName 元素名稱
getModifiers 元素修飾
isSynthetic 是否為Synthetic,synthetic是由編譯器引入的字段、方法、類或其他結(jié)構(gòu),主要用于JVM內(nèi)部使用。

其子類主要包括:

Class 類型

Field 字段

Method 方法

Constructor 構(gòu)造函數(shù)

2.3.1.3. AccessibleObject
AccessibleObject可訪問對(duì)象,其對(duì)元素的可見性進(jìn)行統(tǒng)一封裝。同時(shí)實(shí)現(xiàn)AnnotatedElement接口,提供對(duì)Annotation元素的訪問。

所涉及方法如下:

方法 含義
isAccessible 是否可訪問
setAccessible 重新訪問性

其中AccessibleObject所涉及的子類主要包括:

Field 字段

Constructor 構(gòu)造函數(shù)

Method 方法

AccessibleObject 對(duì)可見性提供了強(qiáng)大的支持,使我們能夠通過(guò)反射擴(kuò)展訪問限制,甚至可以對(duì)private成員進(jìn)行訪問。

樣例代碼如下:

public class TestBean {
    private String id;

    public String getId() {
        return id;
    }

    private void setId(String id) {
        this.id = id;
    }

}
public class AccessibleObjectBase {

    public static void main(String... args) throws Exception{
        TestBean testBean = new TestBean();
        // private方法, 不能直接調(diào)用
        Method setId = TestBean.class.getDeclaredMethod("setId", String.class);
        System.out.println("setId:" + setId.isAccessible());
        try {
            setId.invoke(testBean, "111");
        }catch (Exception e){
            System.out.println("private不能直接調(diào)用");
        }
        setId.setAccessible(true);
        System.out.println("設(shè)置可訪問:" + setId.isAccessible());

        setId.invoke(testBean, "111");
        System.out.println("設(shè)置可訪問后,可以繞過(guò)private限制,進(jìn)行調(diào)用,結(jié)果為:" + testBean.getId());

    }
}

輸出結(jié)果如下:

setId:false
private不能直接調(diào)用
設(shè)置可訪問:true
設(shè)置可訪問后,可以繞過(guò)private限制,進(jìn)行調(diào)用,結(jié)果為:111
2.3.1.4. Executable
Executable表示可執(zhí)行元素的一種封裝,可以獲取方法簽名相關(guān)信息。

所涉及方法如下:

方法 含義
getName 獲取名稱
getModifiers 獲取修飾符
getTypeParameters 獲取類型參數(shù)(泛型)
getParameterTypes 獲取參數(shù)列表
getParameterCount 獲取參數(shù)數(shù)量
getGenericParameterTypes 獲取參數(shù)類型
getExceptionTypes 獲取異常列表
getGenericExceptionTypes 獲取異常列表

鎖涉及的子類主要有:

Constructor 構(gòu)造函數(shù)

Method 方法

樣例代碼如下:

public class TestBean {
    private String id;

    public TestBean(String id) throws IllegalArgumentException, NotImplementedException {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    private void setId(String id) {
        this.id = id;
    }

}

public class ExecutableTest {
    public static void main(String... args) throws Exception{
        for (Constructor constructor : TestBean.class.getConstructors()){
            System.out.println("getName: " + constructor.getName());

            System.out.println();

            System.out.println("getModifiers: " + Modifier.toString(constructor.getModifiers()));

            System.out.println();

            System.out.println("getTypeParameters:");
            for (TypeVariable t : constructor.getTypeParameters()){
                System.out.println("type var:" + t.getName());
            }

            System.out.println();
            System.out.println("getParameterCount:" + constructor.getParameterCount());

            System.out.println();
            System.out.println("getParameterTypes:");
            for (Class cls : constructor.getParameterTypes()){
                System.out.println(cls.getName());
            }

            System.out.println();
            System.out.println("getExceptionTypes:");
            for (Class cls : constructor.getExceptionTypes()){
                System.out.println(cls.getName());
            }
        }
    }
}

輸出結(jié)果為:

getName: com.example.reflectdemo.reflectbase.TestBean

getModifiers: public

getTypeParameters:
type var:T
type var:R

getParameterCount:1

getParameterTypes:
java.lang.String

getExceptionTypes:
java.lang.IllegalArgumentException
sun.reflect.generics.reflectiveObjects.NotImplementedException
2.3.1.5. 方法命名規(guī)則
整個(gè)反射機(jī)制存在著通用的命名規(guī)則,了解這些規(guī)則,可以大大減少理解方法的阻力。

getXXXgetDeclaredXXX, 兩者主要區(qū)別在于獲取元素的可見性不同,一般情況下getXXX返回public類型的元素,而getDeclaredXXX獲取所有的元素,其中包括private、protected、public和package。

2.3.2. 類型信息
Class自身信息包括類名、包名、父類以及實(shí)現(xiàn)的接口等。

Class類實(shí)現(xiàn)AnnotatedElement接口,以提供對(duì)注解的支持。除此以外,涉及方法如下:

方法 含義
getName 獲取類名
getCanonicalName 得到目標(biāo)類的全名(包名+類名)
getSimpleName 等同于getCanonicalName
getTypeParameters 獲取類型參數(shù)(泛型)
getSuperclass 獲取父類
getPackage 獲取包信息
getInterfaces 獲取實(shí)現(xiàn)接口
getModifiers 獲取修飾符
isAnonymousClass 是否匿名類
isLocalClass 是否局部類
isMemberClass 是否成員類
isEnum 是否枚舉
isInterface 是否是接口
isArray 是否是數(shù)組
getComponentType 獲取數(shù)組元素類型
isPrimitive 是否是基本類型
isAnnotation 是否是注解
getEnumConstants 獲取枚舉所有類型
getClasses 獲取定義在該類中的public類型
getDeclaredClasses 獲取定義在該類中的類型

實(shí)例如下:

class Base implements Callable {

    @Override
    public T call() throws Exception {
        return null;
    }
}
public final class BaseClassInfo extends Base implements Runnable, Serializable {

    @Override
    public void run() {

    }


    public static void main(String... args){
        Class cls = BaseClassInfo.class;

        System.out.println("getName:" + cls.getName());
        System.out.println();
        System.out.println("getCanonicalName:"  + cls.getCanonicalName());
        System.out.println();
        System.out.println("getSimpleName:" + cls.getSimpleName());
        System.out.println();
        System.out.println("getSuperclass:" + cls.getSuperclass());
        System.out.println();
        System.out.println("getPackage:" + cls.getPackage());

        System.out.println();
        for (Class c : cls.getInterfaces()){
            System.out.println("interface : " + c.getSimpleName());
        }

        System.out.println();
        for (TypeVariable> typeVariable : cls.getTypeParameters()){
            System.out.println("type var : " + typeVariable.getTypeName());
        }

        System.out.println();
        System.out.println("getModifiers:" + Modifier.toString(cls.getModifiers()));
    }
}

輸出結(jié)果為:

getName:com.example.reflectdemo.classdetail.BaseClassInfo

getCanonicalName:com.example.reflectdemo.classdetail.BaseClassInfo

getSimpleName:BaseClassInfo

getSuperclass:class com.example.reflectdemo.classdetail.Base

getPackage:package com.example.reflectdemo.classdetail

interface : Runnable
interface : Serializable

type var : T
type var : R

getModifiers:public final

Class類型判斷,實(shí)例如下:

public class ClassTypeTest {

    public static void main(String... args){
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                printClassType(getClass());
            }
        };
        System.out.println("匿名內(nèi)部類");
        runnable.run();

        class M implements Runnable{

            @Override
            public void run() {
                printClassType(getClass());
            }
        }

        System.out.println("方法內(nèi)部類");
        new M().run();

        System.out.println("內(nèi)部類");
        new ClassTypeTest().new T().run();

        System.out.println("靜態(tài)內(nèi)部類");
        new S().run();

        System.out.println("枚舉");
        printClassType(EnumTest.class);


        System.out.println("接口");
        printClassType(Runnable.class);

        System.out.println("數(shù)組");
        printClassType(int[].class);

        System.out.println("int");
        printClassType(int.class);

        System.out.println("注解");
        printClassType(AnnTest.class);

    }


    class T implements Runnable{

        @Override
        public void run() {
            printClassType(getClass());
        }
    }

    static class S implements Runnable{

        @Override
        public void run() {
            printClassType(getClass());
        }
    }

    enum EnumTest{
        A, B, C
    }

    @interface AnnTest{

    }


    private static void printClassType(Class cls){
        System.out.println("Class:" + cls.getName());
        System.out.println("isAnonymousClass:" + cls.isAnonymousClass());
        System.out.println("isLocalClass:" + cls.isLocalClass());
        System.out.println("isMemberClass:" + cls.isMemberClass());
        System.out.println("isEnum:" + cls.isEnum());
        System.out.println("isInterface:" + cls.isInterface());
        System.out.println("isArray:" + cls.isArray());
        System.out.println("isPrimitive:" + cls.isPrimitive());
        System.out.println("isAnnotation:" + cls.isAnnotation());

        if (cls.isEnum()){
            System.out.println("getEnumConstants:");
            for (Object o : cls.getEnumConstants()){
                System.out.println(o);
            }
        }

        if (cls.isArray()){
            System.out.println("getComponentType:" + cls.getComponentType());
        }
        System.out.println();
    }
}

輸出結(jié)果如下:

匿名內(nèi)部類
Class:com.example.reflectdemo.classdetail.ClassTypeTest$1
isAnonymousClass:true
isLocalClass:false
isMemberClass:false
isEnum:false
isInterface:false
isArray:false
isPrimitive:false
isAnnotation:false

方法內(nèi)部類
Class:com.example.reflectdemo.classdetail.ClassTypeTest$1M
isAnonymousClass:false
isLocalClass:true
isMemberClass:false
isEnum:false
isInterface:false
isArray:false
isPrimitive:false
isAnnotation:false

內(nèi)部類
Class:com.example.reflectdemo.classdetail.ClassTypeTest$T
isAnonymousClass:false
isLocalClass:false
isMemberClass:true
isEnum:false
isInterface:false
isArray:false
isPrimitive:false
isAnnotation:false

靜態(tài)內(nèi)部類
Class:com.example.reflectdemo.classdetail.ClassTypeTest$S
isAnonymousClass:false
isLocalClass:false
isMemberClass:true
isEnum:false
isInterface:false
isArray:false
isPrimitive:false
isAnnotation:false

枚舉
Class:com.example.reflectdemo.classdetail.ClassTypeTest$EnumTest
isAnonymousClass:false
isLocalClass:false
isMemberClass:true
isEnum:true
isInterface:false
isArray:false
isPrimitive:false
isAnnotation:false
getEnumConstants:
A
B
C

接口
Class:java.lang.Runnable
isAnonymousClass:false
isLocalClass:false
isMemberClass:false
isEnum:false
isInterface:true
isArray:false
isPrimitive:false
isAnnotation:false

數(shù)組
Class:[I
isAnonymousClass:false
isLocalClass:false
isMemberClass:false
isEnum:false
isInterface:false
isArray:true
isPrimitive:false
isAnnotation:false
getComponentType:int

int
Class:int
isAnonymousClass:false
isLocalClass:false
isMemberClass:false
isEnum:false
isInterface:false
isArray:false
isPrimitive:true
isAnnotation:false

注解
Class:com.example.reflectdemo.classdetail.ClassTypeTest$AnnTest
isAnonymousClass:false
isLocalClass:false
isMemberClass:true
isEnum:false
isInterface:true
isArray:false
isPrimitive:false
isAnnotation:true

內(nèi)部類型樣例如下:

public class InnerClassTest {

    public static void main(String... args){
        System.out.println("getClasses");
        for (Class cls : InnerClassTest.class.getClasses()){
            System.out.println(cls.getName());
        }
    }

    public interface I{

    }

    public class A implements I{

    }

    public class B implements I{

    }
}

輸出結(jié)果如下:

getClasses
com.example.reflectdemo.classdetail.InnerClassTest$B
com.example.reflectdemo.classdetail.InnerClassTest$A
com.example.reflectdemo.classdetail.InnerClassTest$I
2.3.3. 對(duì)象實(shí)例化
對(duì)象實(shí)例化,主要通過(guò)Constructor實(shí)例完成,首先通過(guò)相關(guān)方法獲取Constructor對(duì)象,然后進(jìn)行實(shí)例化操作。

所涉及的方法如下:

方法 含義
newInstance 使用默認(rèn)構(gòu)造函數(shù)實(shí)例化對(duì)象
getConstructors 獲取public構(gòu)造函數(shù)
getConstructor(Class... parameterTypes) 獲取特定public構(gòu)造函數(shù)
getDeclaredConstructors 獲取所有的構(gòu)造函數(shù)
getDeclaredConstructor 獲取特定構(gòu)造函數(shù)

實(shí)例化涉及的核心類為Constructor,Constructor繼承自Executable,擁有AnnotatedElement、AccessibleObject、Executable等相關(guān)功能,其核心方法如下:

方法 含義
newInstance 調(diào)用構(gòu)造函數(shù),實(shí)例化對(duì)象

樣例如下:

public class TestBean {
    private final Integer id;
    private final String name;

    public TestBean(Integer id, String name) throws IllegalArgumentException, NotImplementedException {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "TestBean{" +
                "id=" + id +
                ", name="" + name + """ +
                "}";
    }
}

public class ConstructorTest {
    public static void main(String... args) throws Exception{
        for (Constructor constructor : TestBean.class.getConstructors()){
            TestBean bean = (TestBean) constructor.newInstance(1, "Test");
            System.out.println("newInstance:" + bean);
        }
    }
}

輸出結(jié)果為:

newInstance:TestBean{id=1, name="Test"}
2.3.4. 屬性信息
對(duì)象屬性是類型中最主要的信息之一,主要通過(guò)Field表示,首先通過(guò)相關(guān)方法獲取Field實(shí)例,然后進(jìn)行屬性值操作。

所涉及的方法如下:

方法 含義
getFields 獲取public字段
getField(String name) 獲取特定public字段
getDeclaredFields 獲取所有的的屬性
getDeclaredField 獲取特定字段

Field繼承自AccessibleObject實(shí)現(xiàn)Member接口,擁有AccessibleObject、AnnotatedElement、Member相關(guān)功能,其核心方法如下:

方法 含義
isEnumConstant 是否枚舉常量
getType 獲取類型
get 獲取屬性值
getBoolean 獲取boolean值
getByte 獲取byte值
getChar 獲取chat值
getShort 獲取short值
getInt 獲取int值
getLong 獲取long值
getFloat 獲取float值
getDouble 獲取double值
set 設(shè)置屬性值
setBoolean 設(shè)置boolean值
setByte 設(shè)置byte值
setChar 設(shè)置char值
setShort 設(shè)置short值
setInt 設(shè)置int值
setLong 設(shè)置long值
setFloat 設(shè)置float值
setDouble 設(shè)置double值

實(shí)例如下:

public enum EnumTest {
    A
}

public class FieldBean {
    private EnumTest aEnum;
    private String aString;
    private boolean aBoolean;
    private byte aByte;
    private char aChar;
    private short aShort;
    private int anInt;
    private long aLong;
    private float aFloat;
    private double aDouble;

}


public class FieldTest {
    public static void main(String... args) throws NoSuchFieldException, IllegalAccessException {
        FieldBean fieldBean = new FieldBean();
        Field aEnum = getByName("aEnum");
        Field aString = getByName("aString");
        Field aBoolean = getByName("aBoolean");
        Field aByte = getByName("aByte");
        Field aChar = getByName("aChar");
        Field aShort = getByName("aShort");
        Field anInt = getByName("anInt");
        Field aLong = getByName("aLong");
        Field aFloat = getByName("aFloat");
        Field aDouble = getByName("aDouble");

        aEnum.set(fieldBean, EnumTest.A);
        System.out.println("isEnumConstant: " + aEnum.isEnumConstant());
        System.out.println("set and get enum : " + aEnum.get(fieldBean));

        aString.set(fieldBean, "Test");
        System.out.println("set and get String : " + aString.get(fieldBean));

        aBoolean.setBoolean(fieldBean, true);
        System.out.println("set and get Boolean : " + aBoolean.getBoolean(fieldBean));

        aByte.setByte(fieldBean, (byte) 1);
        System.out.println("set and get Byte : " + aByte.getByte(fieldBean));

        aChar.setChar(fieldBean, "a");
        System.out.println("set and get Char : " + aChar.getChar(fieldBean));

        aShort.setShort(fieldBean, (short) 1);
        System.out.println("set and get Short : " + aShort.getShort(fieldBean));

        anInt.setInt(fieldBean, 1);
        System.out.println("set and get Int : " + anInt.getInt(fieldBean));

        aLong.setLong(fieldBean, 1L);
        System.out.println("set and get Long : " + aLong.getLong(fieldBean));

        aFloat.setFloat(fieldBean, 1f);
        System.out.println("set and get Float : " + aLong.getFloat(fieldBean));

        aDouble.setDouble(fieldBean, 1.1);
        System.out.println("set and get Double : " + aLong.getDouble(fieldBean));

    }

    private static Field getByName(String name) throws NoSuchFieldException {
        Field field = FieldBean.class.getDeclaredField(name);
        field.setAccessible(true);
        return field;
    }
}
2.3.5. 方法信息
類型中的方法通過(guò)Method表示,首先通過(guò)相關(guān)方法獲取Method實(shí)現(xiàn),然后通過(guò)反射執(zhí)行方法。

所涉及的方法如下:

方法 含義
getMethods 獲取public方法
getMethod(String name, Class... parameterTypes) 獲取特定public方法
getDeclaredMethods 獲取所有方法
getDeclaredMethod 獲取特定方法

Method繼承自Executable,擁有AnnotatedElement、AccessibleObject、Executable等相關(guān)功能,其核心方法如下:

方法 含義
getReturnType 獲取方法返回類型
invoke 調(diào)用方法
isBridge 是否為橋接方法。橋接方法是 JDK 1.5 引入泛型后,為了使Java的泛型方法生成的字節(jié)碼和 1.5 版本前的字節(jié)碼相兼容,由編譯器自動(dòng)生成的方法。我們可以通過(guò)Method.isBridge()方法來(lái)判斷一個(gè)方法是否是橋接方法。
isDefault 是否為默認(rèn)方法

實(shí)例如下:

public interface SayHi {
    String get();

    default void hi(){
        System.out.println("Hi " + get());
    }
}
public class MethodBean implements Function, SayHi {
    private final String name;

    public MethodBean(String name) {
        this.name = name;
    }

    @Override
    public String get() {
        return "Hi " + name;
    }

    @Override
    public String apply(String s) {
        return s + name;
    }
}
public class MethodTest {
    public static void main(String... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method strMethod = MethodBean.class.getDeclaredMethod("apply", String.class);
        Method objMethod = MethodBean.class.getDeclaredMethod("apply", Object.class);
        Method hiMethod = SayHi.class.getDeclaredMethod("hi");

        MethodBean methodBean = new MethodBean("張三");

        System.out.println("Return Type:");
        System.out.println("getMethod(String):" + strMethod.getReturnType());
        System.out.println("getMethod(Object):" + objMethod.getReturnType());
        System.out.println("hi():" + hiMethod.getReturnType());

        System.out.println();
        System.out.println("isBridge:");
        System.out.println("getMethod(String):" + strMethod.isBridge());
        System.out.println("getMethod(Object):" + objMethod.isBridge());
        System.out.println("hi():" + hiMethod.isBridge());


        System.out.println();
        System.out.println("isDefault:");
        System.out.println("getMethod(String):" + strMethod.isDefault());
        System.out.println("getMethod(Object):" + objMethod.isDefault());
        System.out.println("hi():" + hiMethod.isDefault());


        System.out.println();
        System.out.println("invoke:");
        System.out.println("invoke(String):" + strMethod.invoke(methodBean, "Test"));
        System.out.println("invoke(Object):" + objMethod.invoke(methodBean, "Test"));
        System.out.println("hi():" + hiMethod.invoke(methodBean));


    }
}

輸出結(jié)果:

Return Type:
getMethod(String):class java.lang.String
getMethod(Object):class java.lang.Object
hi():void

isBridge:
getMethod(String):false
getMethod(Object):true
hi():false

isDefault:
getMethod(String):false
getMethod(Object):false
hi():true

invoke:
invoke(String):Test張三
invoke(Object):Test張三
Hi Hi 張三
hi():null
2.3.6. 其他
除上述核心方法外,Class對(duì)象提供了一些使用方法。

所涉及方法如下:

方法 含義
isInstance 判斷某對(duì)象是否是該類的實(shí)例
isAssignableFrom 判定此 Class 對(duì)象所表示的類或接口與指定的 Class 參數(shù)所表示的類或接口是否相同,或是否是其超類或超接口。如果是則返回 true;否則返回 false。
getClassLoader 獲取加載當(dāng)前類的ClassLoader
getResourceAsStream 根據(jù)該ClassLoader加載資源
getResource 根據(jù)該ClassLoader加載資源
public class Task implements Runnable{
    @Override
    public void run() {

    }
}
public class OtherTest {
    public static void main(String...args){
        Task task = new Task();
        System.out.println("Runnable isInstance Task:" + Runnable.class.isInstance(task));
        System.out.println("Task isInstance Task:" + Task.class.isInstance(task));

        System.out.println("Task isAssignableFrom Task:" + Task.class.isAssignableFrom(Task.class));

        System.out.println("Runnable isAssignableFrom Task :" + Runnable.class.isAssignableFrom(Task.class));
    }
}

輸出結(jié)果:

Runnable isInstance Task:true
Task isInstance Task:true
Task isAssignableFrom Task:true
Runnable isAssignableFrom Task :true
3. 動(dòng)態(tài)代理
代理是基本的設(shè)計(jì)模式之一,它是我們?yōu)榱颂峁╊~外的或不同的操作,而插入的用來(lái)代替“實(shí)際”對(duì)象的對(duì)象。這些操作通常與“實(shí)際”對(duì)象通信,因此代理通常充當(dāng)中間人的角色。

例如,我們已有一個(gè)Handler接口,和一個(gè)實(shí)現(xiàn)類HandlerImpl,現(xiàn)需要對(duì)其進(jìn)行性能統(tǒng)計(jì),使用代理模式,代碼如下:

/**
 * handler接口
 */
public interface Handler {
    /**
     * 數(shù)據(jù)處理
     * @param data
     */
    void handle(String data);
}

/**
 * Handler 實(shí)現(xiàn)
 */
public class HandlerImpl implements Handler{
    @Override
    public void handle(String data) {
        try {
            TimeUnit.MILLISECONDS.sleep(100);
            System.out.println(data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * Handler代理
* 實(shí)現(xiàn)Handler接口,記錄耗時(shí)情況,并將請(qǐng)求發(fā)送給目標(biāo)對(duì)象 */ public class HandlerProxy implements Handler{ private final Handler handler; public HandlerProxy(Handler handler) { this.handler = handler; } @Override public void handle(String data) { long start = System.currentTimeMillis(); this.handler.handle(data); long end = System.currentTimeMillis(); System.out.println("cost " + (end - start) + " ms"); } } public static void main(String... args){ Handler handler = new HandlerImpl(); Handler proxy = new HandlerProxy(handler); proxy.handle("Test"); }

采用代理模式,比較優(yōu)雅的解決了該問題,但如果Handler接口存在多個(gè)方法,并且需要對(duì)所有方法進(jìn)行性能監(jiān)控,那HandlerProxy的復(fù)雜性將會(huì)提高。
Java動(dòng)態(tài)代理比代理更進(jìn)一步,因?yàn)樗梢詣?dòng)態(tài)的創(chuàng)建代理并動(dòng)態(tài)的處理對(duì)所代理方法的調(diào)用。在動(dòng)態(tài)代理上所做的所有調(diào)用都會(huì)被重定向到單一的調(diào)用處理器上。

3.1. InvocationHandler
InvocationHandler 是由動(dòng)態(tài)代理處理器實(shí)現(xiàn)的接口,對(duì)代理對(duì)象的方法調(diào)用,會(huì)路由到該處理器上進(jìn)行統(tǒng)一處理。

其只有一個(gè)核心方法:

/**
* proxy : 代理對(duì)象
* method : 調(diào)用方法
* args : 調(diào)用方法參數(shù)
**/
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
3.2. Proxy
Proxy 用于生成代理對(duì)象。

其核心方法為:

/**
* 獲取代理類
* loader : 類加載器 * interfaces: 類實(shí)現(xiàn)的接口 * */ Class getProxyClass(ClassLoader loader, Class... interfaces); /* * 生成代理對(duì)象
* loader : 類加載器 * interfaces : 類實(shí)現(xiàn)的接口 * h : 動(dòng)態(tài)代理回調(diào) */ Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h); /* * 判斷是否為代理類
* * cl : 待判斷類 */ public static boolean isProxyClass(Class cl); /* * 獲取代理對(duì)象的InvocationHandler
* * proxy : 代理對(duì)象 */ InvocationHandler getInvocationHandler(Object proxy);
3.3. demo
對(duì)于之前的性能監(jiān)控,使用Java動(dòng)態(tài)代理怎么實(shí)現(xiàn)?
/**
 * 定義代理方法回調(diào)處理器
 */
public class CostInvocationHandler implements InvocationHandler {
    // 目標(biāo)對(duì)象
    private final Object target;

    public CostInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("call method " + method + " ,args " + args);
        long start = System.currentTimeMillis();
        try {
            // 將請(qǐng)求轉(zhuǎn)發(fā)給目標(biāo)對(duì)象
            return method.invoke(this.target, args);
        }finally {
            long end = System.currentTimeMillis();
            System.out.println("cost " + (end - start) + "ms");
        }


    }
}
public static void main(String... args){
    Handler handler = new HandlerImpl();

    CostInvocationHandler invocationHandler = new CostInvocationHandler(handler);

    Class cls = Proxy.getProxyClass(DHandlerMain.class.getClassLoader(), Handler.class);

    Handler proxy = (Handler) Proxy.newProxyInstance(DHandlerMain.class.getClassLoader(),
            new Class[]{Handler.class},
            invocationHandler);

    System.out.println("invoke method");
    proxy.handle("Test");
    System.out.println("isProxyClass: " + Proxy.isProxyClass(cls));
    System.out.println("getInvocationHandler: " + (invocationHandler == Proxy.getInvocationHandler(proxy)));
}
4. 基于SPI的Plugin
SPI 全稱為 (Service Provider Interface) ,是JDK內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機(jī)制。 目前有不少框架用它來(lái)做服務(wù)的擴(kuò)展發(fā)現(xiàn),它是一種動(dòng)態(tài)替換發(fā)現(xiàn)的機(jī)制。

具體用法是在JAR包的"META-INF/services/"目錄下建立一個(gè)文件,文件名是接口的全限定名,文件的內(nèi)容可以有多行,每行都是該接口對(duì)應(yīng)的具體實(shí)現(xiàn)類的全限定名。然后使用 ServiceLoader.load(Interface.class) 對(duì)插件進(jìn)行加載。

假定,現(xiàn)有個(gè)場(chǎng)景,需要對(duì)消息進(jìn)行處理,但消息處理器的實(shí)現(xiàn)需要放開,及可以動(dòng)態(tài)的對(duì)處理器進(jìn)行加載,當(dāng)有新消息到達(dá)時(shí),依次調(diào)用處理器對(duì)消息進(jìn)行處理,讓我們結(jié)合SPI和反射構(gòu)造一個(gè)簡(jiǎn)單的Plugin系統(tǒng)。

首先我們需要一個(gè)插件接口和若干個(gè)實(shí)現(xiàn)類:

/**
 * 插件接口
 */
public interface Handler {
    void handle(String msg);
}
/**
 * 實(shí)現(xiàn)1
 */
public class Handler1 implements Handler{
    @Override
    public void handle(String msg) {
        System.out.println("Handler1:" + msg);
    }
}
/**
 * 實(shí)現(xiàn)2
 */
public class Handler2 implements Handler{
    @Override
    public void handle(String msg) {
        System.out.println("Handler2:" + msg);
    }
}

然后,我們添加SPI配置,及在META-INF/services/com.example.reflectdemo.plugin.Handler添加配置信息:

com.example.reflectdemo.plugin.Handler1
com.example.reflectdemo.plugin.Handler2

其次,我們實(shí)現(xiàn)DispatcherInvocationHandler類繼承自InvocationHandler接口,將方法調(diào)用分發(fā)給目標(biāo)對(duì)象。

/**
 * 分發(fā)處理器
* 將請(qǐng)求挨個(gè)轉(zhuǎn)發(fā)給目標(biāo)對(duì)象 */ public class DispatcherInvocationHandler implements InvocationHandler { // 目標(biāo)對(duì)象集合 private final List targets; public DispatcherInvocationHandler(List targets) { this.targets = targets; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { for (Object target : targets){ // 將請(qǐng)求轉(zhuǎn)發(fā)給目標(biāo)對(duì)象 method.invoke(target, args); } return null; } }

實(shí)現(xiàn)主流程,通過(guò)SPI加裝插件,將插件作為轉(zhuǎn)發(fā)對(duì)象實(shí)例化DispatcherInvocationHandler,在通過(guò)Proxy構(gòu)建動(dòng)態(tài)代理對(duì)象,最后調(diào)用handle方法進(jìn)行業(yè)務(wù)處理。

public static void main(String... args){
        // 使用SPI加載插件
        ServiceLoader serviceLoader = ServiceLoader.load(Handler.class);
        List handlers = new ArrayList<>();
        Iterator handlerIterator = serviceLoader.iterator();
        while (handlerIterator.hasNext()){
            Handler handler = handlerIterator.next();
            handlers.add(handler);
        }
        // 將加載的插件組裝成InvocationHandler,以進(jìn)行分發(fā)處理
        DispatcherInvocationHandler invocationHandler = new DispatcherInvocationHandler(handlers);
        // 生成代理對(duì)象
        Handler proxy = (Handler) Proxy.newProxyInstance(HandlerMain.class.getClassLoader(), new Class[]{Handler.class}, invocationHandler);
        // 調(diào)用handle方法
        proxy.handle("Test");
    }

運(yùn)行結(jié)果如下:

Handler1:Test
Handler2:Test
5. 總結(jié)
Java類型系統(tǒng)、反射、動(dòng)態(tài)代理,作為Java的高級(jí)應(yīng)用,大量用于各大框架中。對(duì)其的掌握有助于加深對(duì)框架的理解。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76818.html

相關(guān)文章

  • MyBatis初步

    摘要:本章主要介紹的是的基礎(chǔ)應(yīng)用和源碼涉及的相關(guān)等,主要包含的內(nèi)容有的簡(jiǎn)介反射動(dòng)態(tài)代理包含代理和代理使用和代碼生成器等。組件生命周期,如圖測(cè)試代碼生成器代碼生成器,又稱逆向工程。 本章主要介紹的是MyBatis的基礎(chǔ)應(yīng)用和源碼涉及的相關(guān)等,主要包含的內(nèi)容有MyBatis的簡(jiǎn)介、反射、動(dòng)態(tài)代理(包含JDK代理和cglib代理)、MyBatis使用和代碼生成器等。 1.1 MyBatis簡(jiǎn)介 M...

    MASAILA 評(píng)論0 收藏0
  • Java 高級(jí)面試知識(shí)點(diǎn)匯總!

    摘要:適配器模式將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。適配器模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。這個(gè)主題對(duì)象在狀態(tài)發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象,使它們能夠自動(dòng)更新自己。 1、常用設(shè)計(jì)模式 單例模式:懶漢式、餓漢式、雙重校驗(yàn)鎖、靜態(tài)加載,內(nèi)部類加載、枚舉類加載。保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。 代理模式:動(dòng)態(tài)代理和靜態(tài)代理,什么時(shí)候使用...

    since1986 評(píng)論0 收藏0
  • Java動(dòng)態(tài)編程初探

    摘要:動(dòng)態(tài)編程使用場(chǎng)景通過(guò)配置生成代碼,減少重復(fù)編碼,降低維護(hù)成本。動(dòng)態(tài)生成字節(jié)碼操作字節(jié)碼的工具有,其中有兩個(gè)比較流行的,一個(gè)是,一個(gè)是。 作者簡(jiǎn)介 傳恒,一個(gè)喜歡攝影和旅游的軟件工程師,先后從事餓了么物流蜂鳥自配送和蜂鳥眾包的開發(fā),現(xiàn)在轉(zhuǎn)戰(zhàn) Java,目前負(fù)責(zé)物流策略組分流相關(guān)業(yè)務(wù)的開發(fā)。 什么是動(dòng)態(tài)編程 動(dòng)態(tài)編程是相對(duì)于靜態(tài)編程而言的,平時(shí)我們討論比較多的靜態(tài)編程語(yǔ)言例如Java, 與動(dòng)態(tài)...

    趙連江 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<