Java類加載與反射

Java類加載與反射

類加載、連接、初始化

JVM和類

當(dāng)運(yùn)行某個Java程序時左医,將會啟動一個Java虛擬機(jī)進(jìn)程奴紧,不管Java程序里面多么復(fù)雜特姐,都處于該Java虛擬機(jī)進(jìn)程里面。

當(dāng)Java程序運(yùn)行結(jié)束黍氮,JVM進(jìn)程結(jié)束唐含,該進(jìn)程在內(nèi)存中的狀態(tài)將丟失


public class A {

    // 定義該類的類變量
    public static int a = 6;

    
}

接下來創(chuàng)建A類的實例

public class ATest1 {

    public static void main(String[] args)
    {
        // 創(chuàng)建A類的實例
        A a = new A();
        // 讓a實例的類變量a的值自加
        a.a ++;
        System.out.println(a.a);
    }


}

下面這個也進(jìn)行同樣操作

public class ATest2 {

    public static void main(String[] args) {

        // 創(chuàng)建A類的實例
        A b = new A();
        // 輸出b實例的類變量a的值
        System.out.println(b.a);

    }
}

當(dāng)運(yùn)行第二個ATest2時浅浮,程序再次創(chuàng)建了A對象,并輸出a的值捷枯,為6滚秩,這是因為運(yùn)行ATest1和ATest2是兩次運(yùn)行JVM進(jìn)程,第一次JVM結(jié)束后淮捆,對A類的修改全部丟失

類加載

當(dāng)主動使用某個類時郁油,如果該類還未被加載到內(nèi)存中,則系統(tǒng)會通過加載攀痊、連接已艰、初始化三個步驟來對該類進(jìn)行初始化

類加載是將類的class文件讀入內(nèi)存,并為之創(chuàng)建一個java.lang.Class對象

類的加載由類加載器完成蚕苇,類加載器通常由JVM提供

類連接

當(dāng)類被加載后哩掺,系統(tǒng)為之生成一個對應(yīng)的Class對象,接著進(jìn)入連接階段涩笤,類連接又分為:

  • 驗證

  • 準(zhǔn)備

  • 解析

類初始化

虛擬機(jī)負(fù)責(zé)對類進(jìn)行初始化嚼吞,主要是對類變量進(jìn)行初始化

public class Test {

    static
    {
        // 使用靜態(tài)初始化塊為變量b指定出初始值
        b = 6;
        System.out.println("----------");
    }
    // 聲明變量a時指定初始值
    static int a = 5;
    static int b = 9;         // ①
    static int c;
    public static void main(String[] args)
    {
        System.out.println(Test.b);
    }
}

類初始化時機(jī)

當(dāng)Java程序首次通過下面6鐘方式來使用某個類或接口,系統(tǒng)就會初始化該類或接口

  • 創(chuàng)建類實例(通過new操作符蹬碧、反射舱禽、反序列化)

  • 調(diào)用某個類的類方法(靜態(tài)方法)

  • 訪問某個類或接口的類變量

  • 反射強(qiáng)制創(chuàng)建

  • 初始化某個類的子類

  • 直接使用java.exe運(yùn)行某個主類

class MyTest
{
    static
    {
        System.out.println("靜態(tài)初始化塊...");
    }
    // 使用一個字符串直接量為static final的類變量賦值
    static final String compileConstant = "Java";
}
public class CompileConstantTest
{
    public static void main(String[] args)
    {
        // 訪問、輸出MyTest中的compileConstant類變量
        System.out.println(MyTest.compileConstant);   // ①
    }
}


System.out.println(MyTest.compileConstant)不會導(dǎo)致初始化MyTest類

類加載器

類加載器負(fù)責(zé)將.class文件加載到內(nèi)存恩沽,并為之生成對應(yīng)的java.lang.Class對象

類加載機(jī)制

類加載器負(fù)責(zé)加載所有類誊稚,系統(tǒng)為載入內(nèi)存的類生成實例,一旦一個類被載入JVM罗心,同一個類就不會被再次載入

JVM啟動里伯,會形成三個類加載器組成的初始類加載器層次結(jié)構(gòu)

  • 根類加載器

  • 擴(kuò)展類加載器

  • 系統(tǒng)類加載器

public class ClassLoaderPropTest {
    public static void main(String[] args)
            throws IOException
    {
        // 獲取系統(tǒng)類加載器
        ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
        System.out.println("系統(tǒng)類加載器:" + systemLoader);
        /*
        獲取系統(tǒng)類加載器的加載路徑——通常由CLASSPATH環(huán)境變量指定
        如果操作系統(tǒng)沒有指定CLASSPATH環(huán)境變量,默認(rèn)以當(dāng)前路徑作為
        系統(tǒng)類加載器的加載路徑
        */
        Enumeration<URL> em1 = systemLoader.getResources("");
        while(em1.hasMoreElements())
        {
            System.out.println(em1.nextElement());
        }
        // 獲取系統(tǒng)類加載器的父類加載器:得到擴(kuò)展類加載器
        ClassLoader extensionLader = systemLoader.getParent();
        System.out.println("擴(kuò)展類加載器:" + extensionLader);
        System.out.println("擴(kuò)展類加載器的加載路徑:"
                + System.getProperty("java.ext.dirs"));
        System.out.println("擴(kuò)展類加載器的parent: "
                + extensionLader.getParent());
    }
}

反射

Java中許多對象在運(yùn)行時都會出現(xiàn)兩種類型

  • 編譯時類型

  • 運(yùn)行時類型

有時我們需要在運(yùn)行時發(fā)現(xiàn)對象和類的真實信息

  • 假設(shè)在編譯和運(yùn)行時都完全知道了類型的具體信息渤闷,這樣可以先使用instanceof運(yùn)算符進(jìn)行判斷疾瓮,再進(jìn)行強(qiáng)制類型轉(zhuǎn)換
  • 編譯時無法預(yù)知該對象和類可能屬于哪些類,程序只依靠運(yùn)行時信息來發(fā)現(xiàn)該對象和類的真實信息飒箭,這就要使用反射了

獲取Class對象

每個類被加載后狼电,系統(tǒng)會為該類生成一個對應(yīng)的Class對象,通過該Class對象就可以訪問到JVM中的這個類弦蹂,獲取Class對象有三種方式:

  • Class類的forName()靜態(tài)方法肩碟,傳入字符串參數(shù)

  • 調(diào)用某個類的class屬性來獲取,如Person.class將會返回Person類對應(yīng)的class對象

  • 調(diào)用某個對象的getClass()

從Class獲取信息

例子:


// 定義可重復(fù)注解
@Repeatable(Annos.class)
@interface Anno {}
@Retention(value= RetentionPolicy.RUNTIME)
@interface Annos {
    Anno[] value();
}
// 使用4個注解修飾該類
@SuppressWarnings(value="unchecked")
@Deprecated
// 使用重復(fù)注解修飾該類
@Anno
@Anno
public class ClassTest
{
    // 為該類定義一個私有的構(gòu)造器
    private ClassTest()
    {
    }
    // 定義一個有參數(shù)的構(gòu)造器
    public ClassTest(String name)
    {
        System.out.println("執(zhí)行有參數(shù)的構(gòu)造器");
    }
    // 定義一個無參數(shù)的info方法
    public void info()
    {
        System.out.println("執(zhí)行無參數(shù)的info方法");
    }
    // 定義一個有參數(shù)的info方法
    public void info(String str)
    {
        System.out.println("執(zhí)行有參數(shù)的info方法"
                + "凸椿,其str參數(shù)值:" + str);
    }
    // 定義一個測試用的內(nèi)部類
    class Inner
    {
    }


    public static void main(String[] args)
            throws Exception
    {
        // 下面代碼可以獲取ClassTest對應(yīng)的Class
        Class<ClassTest> clazz = ClassTest.class;
        // 獲取該Class對象所對應(yīng)類的全部構(gòu)造器
        Constructor[] ctors = clazz.getDeclaredConstructors();
        System.out.println("ClassTest的全部構(gòu)造器如下:");
        for (Constructor c : ctors)
        {
            System.out.println(c);
        }


        System.out.println("");
        System.out.println("======================================================");
        System.out.println("");



        // 獲取該Class對象所對應(yīng)類的全部public構(gòu)造器
        Constructor[] publicCtors = clazz.getConstructors();
        System.out.println("ClassTest的全部public構(gòu)造器如下:");
        for (Constructor c : publicCtors)
        {
            System.out.println(c);
        }


        System.out.println("");
        System.out.println("======================================================");
        System.out.println("");



        // 獲取該Class對象所對應(yīng)類的全部public方法
        Method[] mtds = clazz.getMethods();
        System.out.println("ClassTest的全部public方法如下:");
        for (Method md : mtds)
        {
            System.out.println(md);
        }


        System.out.println("");
        System.out.println("======================================================");
        System.out.println("");


        // 獲取該Class對象所對應(yīng)類的指定方法
        System.out.println("ClassTest里帶一個字符串參數(shù)的info()方法為:"
                + clazz.getMethod("info" , String.class));



        System.out.println("");
        System.out.println("======================================================");
        System.out.println("");



        // 獲取該Class對象所對應(yīng)類的上的全部注解
        Annotation[] anns = clazz.getAnnotations();
        System.out.println("ClassTest的全部Annotation如下:");
        for (Annotation an : anns)
        {
            System.out.println(an);
        }

        System.out.println("");
        System.out.println("======================================================");
        System.out.println("");


        System.out.println("該Class元素上的@SuppressWarnings注解為:"
                + Arrays.toString(clazz.getAnnotationsByType(SuppressWarnings.class)));

        System.out.println("");
        System.out.println("======================================================");
        System.out.println("");


        System.out.println("該Class元素上的@Anno注解為:"
                + Arrays.toString(clazz.getAnnotationsByType(Anno.class)));

        System.out.println("");
        System.out.println("======================================================");
        System.out.println("");


        // 獲取該Class對象所對應(yīng)類的全部內(nèi)部類
        Class<?>[] inners = clazz.getDeclaredClasses();
        System.out.println("ClassTest的全部內(nèi)部類如下:");
        for (Class c : inners)
        {
            System.out.println(c);
        }


        System.out.println("");
        System.out.println("======================================================");
        System.out.println("");


        // 使用Class.forName方法加載ClassTest的Inner內(nèi)部類
        Class inClazz = Class.forName("C_2.c_2_3.ClassTest$Inner");
        // 通過getDeclaringClass()訪問該類所在的外部類
        System.out.println("inClazz對應(yīng)類的外部類為:" +
                inClazz.getDeclaringClass());


        System.out.println("");
        System.out.println("======================================================");
        System.out.println("");


        System.out.println("ClassTest的包為:" + clazz.getPackage());

        System.out.println("");
        System.out.println("======================================================");
        System.out.println("");


        System.out.println("ClassTest的父類為:" + clazz.getSuperclass());

        System.out.println("");
        System.out.println("======================================================");
        System.out.println("");
    }
}

Java 8 新增方法參數(shù)反射


class Test_1
{
    public void replace(String str, List<String> list){}
}
public class MethodParameterTest
{
    public static void main(String[] args)throws Exception
    {
        // 獲取String的類
        Class<Test_1> clazz = Test_1.class;


        // 獲取String類的帶兩個參數(shù)的replace()方法
        Method replace = clazz.getMethod("replace"
                , String.class, List.class);


        // 獲取指定方法的參數(shù)個數(shù)
        System.out.println("replace方法參數(shù)個數(shù):" + replace.getParameterCount());


        // 獲取replace的所有參數(shù)信息
        Parameter[] parameters = replace.getParameters();


        int index = 1;
        // 遍歷所有參數(shù)
        for (Parameter p : parameters)
        {
            if (p.isNamePresent())
            {
                System.out.println("---第" + index++ + "個參數(shù)信息---");
                System.out.println("參數(shù)名:" + p.getName());
                System.out.println("形參類型:" + p.getType());
                System.out.println("泛型類型:" + p.getParameterizedType());
            }
        }
    }
}

使用反射生成并操作對象

創(chuàng)建對象

通過反射生成對象需要先使用Class對象獲取指定的Constructor對象削祈,再用Constructor對象的newInstance()創(chuàng)建該Class對象對應(yīng)類的實例

public class ObjectPoolFactory {

    // 定義一個對象池,前面是對象名削饵,后面是實際對象
    private Map<String ,Object> objectPool = new HashMap<>();
    // 定義一個創(chuàng)建對象的方法
    // 該方法只要傳入一個字符串類名岩瘦,程序可以根據(jù)該類名生成Java對象
    private Object createObject(String clazzName)
            throws Exception
            , IllegalAccessException , ClassNotFoundException
    {
        // 根據(jù)字符串來獲取對應(yīng)的Class對象
        Class<?> clazz = Class.forName(clazzName);
        // 使用clazz對應(yīng)類的默認(rèn)構(gòu)造器創(chuàng)建實例
        return clazz.getConstructor().newInstance();
    }
    // 該方法根據(jù)指定文件來初始化對象池
    // 它會根據(jù)配置文件來創(chuàng)建對象
    public void initPool(String fileName)
            throws InstantiationException
            , IllegalAccessException ,ClassNotFoundException
    {
        try(
                FileInputStream fis = new FileInputStream(fileName))
        {
            Properties props = new Properties();
            props.load(fis);
            for (String name : props.stringPropertyNames())
            {
                // 每取出一對key-value對,就根據(jù)value創(chuàng)建一個對象
                // 調(diào)用createObject()創(chuàng)建對象窿撬,并將對象添加到對象池中
                objectPool.put(name ,
                        createObject(props.getProperty(name)));
            }
        }
        catch (Exception ex)
        {
            System.out.println("讀取" + fileName + "異常");
        }
    }
    public Object getObject(String name)
    {
        // 從objectPool中取出指定name對應(yīng)的對象
        return objectPool.get(name);
    }
    public static void main(String[] args)
            throws Exception
    {
        ObjectPoolFactory pf = new ObjectPoolFactory();
        pf.initPool("obj.txt");
        System.out.println(pf.getObject("a"));      // ①
        System.out.println(pf.getObject("b"));      // ②
    }
}


調(diào)用方法

獲取某個類對應(yīng)的Class對象后启昧,就可以通過該Class對象的getMethods()獲取全部方法

Method里面有一個invoke()方法:

Object invoke(Object obj,Object...args);

obj是執(zhí)行方法的主調(diào),args是執(zhí)行該方法時傳入該方法的實參


public class ExtendedObjectPoolFactory {


    // 定義一個對象池劈伴,前面是對象名密末,后面是實際對象
    private Map<String ,Object> objectPool = new HashMap<>();
    private Properties config = new Properties();
    // 從指定屬性文件中初始化Properties對象
    public void init(String fileName)
    {
        try(
                FileInputStream fis = new FileInputStream(fileName))
        {
            config.load(fis);
        }
        catch (IOException ex)
        {
            System.out.println("讀取" + fileName + "異常");
        }
    }
    // 定義一個創(chuàng)建對象的方法
    // 該方法只要傳入一個字符串類名,程序可以根據(jù)該類名生成Java對象
    private Object createObject(String clazzName)
            throws Exception
    {
        // 根據(jù)字符串來獲取對應(yīng)的Class對象
        Class<?> clazz =Class.forName(clazzName);
        // 使用clazz對應(yīng)類的默認(rèn)構(gòu)造器創(chuàng)建實例
        return clazz.getConstructor().newInstance();
    }
    // 該方法根據(jù)指定文件來初始化對象池
    // 它會根據(jù)配置文件來創(chuàng)建對象
    public void initPool()throws Exception
    {
        for (String name : config.stringPropertyNames())
        {
            // 每取出一個key-value對跛璧,如果key中不包含百分號(%)
            // 這就表明是根據(jù)value來創(chuàng)建一個對象
            // 調(diào)用createObject創(chuàng)建對象严里,并將對象添加到對象池中
            if (!name.contains("%"))
            {
                objectPool.put(name ,
                        createObject(config.getProperty(name)));
            }
        }
    }
    // 該方法將會根據(jù)屬性文件來調(diào)用指定對象的setter方法
    public void initProperty()throws InvocationTargetException
            ,IllegalAccessException,NoSuchMethodException
    {
        for (String name : config.stringPropertyNames())
        {
            // 每取出一對key-value對,如果key中包含百分號(%)
            // 即可認(rèn)為該key用于控制調(diào)用對象的setter方法設(shè)置值
            // %前半為對象名字追城,后半控制setter方法名
            if (name.contains("%"))
            {
                // 將配置文件中的key按%分割
                String[] objAndProp = name.split("%");
                // 取出調(diào)用setter方法的參數(shù)值
                Object target = getObject(objAndProp[0]);
                // 獲取setter方法名:set + "首字母大寫" + 剩下部分
                String mtdName = "set" +
                        objAndProp[1].substring(0 , 1).toUpperCase()
                        + objAndProp[1].substring(1);
                // 通過target的getClass()獲取它的實現(xiàn)類所對應(yīng)的Class對象
                Class<?> targetClass = target.getClass();
                // 獲取希望調(diào)用的setter方法
                Method mtd = targetClass.getMethod(mtdName , String.class);
                // 通過Method的invoke方法執(zhí)行setter方法
                // 將config.getProperty(name)的值作為調(diào)用setter方法的參數(shù)
                mtd.invoke(target , config.getProperty(name));
            }
        }
    }
    public Object getObject(String name)
    {
        // 從objectPool中取出指定name對應(yīng)的對象
        return objectPool.get(name);
    }
    public static void main(String[] args)
            throws Exception
    {
        ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory();
        epf.init("extObj.txt");
        epf.initPool();
        epf.initProperty();
        System.out.println(epf.getObject("a"));
    }
}

訪問成員變量值

通過Class對象的getFields()可以獲取該類全部成員變量


class Person
{
    private String name;
    private int age;
    public String toString()
    {
        return "Person[name:" + name +
                " , age:" + age + " ]";
    }
}
public class FieldTest
{
    public static void main(String[] args)
            throws Exception
    {
        // 創(chuàng)建一個Person對象
        Person p = new Person();
        // 獲取Person類對應(yīng)的Class對象
        Class<Person> personClazz = Person.class;
        // 獲取Person的名為name的成員變量
        // 使用getDeclaredField()方法表明可獲取各種訪問控制符的成員變量
        Field nameField = personClazz.getDeclaredField("name");
        // 設(shè)置通過反射訪問該成員變量時取消訪問權(quán)限檢查
        nameField.setAccessible(true);
        // 調(diào)用set()方法為p對象的name成員變量設(shè)置值
        nameField.set(p , "Yeeku.H.Lee");
        // 獲取Person類名為age的成員變量
        Field ageField = personClazz.getDeclaredField("age");
        // 設(shè)置通過反射訪問該成員變量時取消訪問權(quán)限檢查
        ageField.setAccessible(true);
        // 調(diào)用setInt()方法為p對象的age成員變量設(shè)置值
        ageField.setInt(p , 30);
        System.out.println(p);
    }
}

操作數(shù)組

java.lang.reflect下有一個Array類刹碾,Array對象可以代表所有數(shù)組,可以使用Array動態(tài)創(chuàng)建數(shù)組


public class ArrayTest1 {

    public static void main(String[] args) {

        try
        {
            // 創(chuàng)建一個元素類型為String 座柱,長度為10的數(shù)組
            Object arr = Array.newInstance(String.class, 10);
            // 依次為arr數(shù)組中index為5迷帜、6的元素賦值
            Array.set(arr, 5, "Java");
            Array.set(arr, 6, "Java EE");
            // 依次取出arr數(shù)組中index為5、6的元素的值
            Object book1 = Array.get(arr , 5);
            Object book2 = Array.get(arr , 6);
            // 輸出arr數(shù)組中index為5色洞、6的元素
            System.out.println(book1);
            System.out.println(book2);
        }
        catch (Throwable e)
        {
            System.err.println(e);
        }

    }
}

下面創(chuàng)建一個三維數(shù)組


public class ArrayTest2 {

    public static void main(String[] args) {
/*
          創(chuàng)建一個三維數(shù)組戏锹。
          根據(jù)前面介紹數(shù)組時講的:三維數(shù)組也是一維數(shù)組,
          是數(shù)組元素是二維數(shù)組的一維數(shù)組火诸,
          因此可以認(rèn)為arr是長度為3的一維數(shù)組
        */
        Object arr = Array.newInstance(String.class, 3, 4, 10);
        // 獲取arr數(shù)組中index為2的元素锦针,該元素應(yīng)該是二維數(shù)組
        Object arrObj = Array.get(arr, 2);
        // 使用Array為二維數(shù)組的數(shù)組元素賦值。二維數(shù)組的數(shù)組元素是一維數(shù)組置蜀,
        // 所以傳入Array的set()方法的第三個參數(shù)是一維數(shù)組奈搜。
        Array.set(arrObj , 2 , new String[]
                {
                        "Java",
                        "Java EE"
                });
        // 獲取arrObj數(shù)組中index為3的元素,該元素應(yīng)該是一維數(shù)組盯荤。
        Object anArr  = Array.get(arrObj, 3);
        Array.set(anArr , 8  , "Android");
        // 將arr強(qiáng)制類型轉(zhuǎn)換為三維數(shù)組
        String[][][] cast = (String[][][])arr;
        // 獲取cast三維數(shù)組中指定元素的值
        System.out.println(cast[2][3][8]);
        System.out.println(cast[2][2][0]);
        System.out.println(cast[2][2][1]);


    }
}


動態(tài)代理

java.lang.reflect提供了一個Proxy類和一個InvocationHandler接口

用Proxy和InvocationHandler創(chuàng)建動態(tài)代理

Proxy是所有動態(tài)代理的父類媚污,可以使用它創(chuàng)建動態(tài)代理類以及動態(tài)代理實例

Proxy提供了下面兩個方法創(chuàng)建動態(tài)代理類以及動態(tài)代理實例

  • static Class < ? > getProxyClass ( Cl assLoader loader , Class < ? > . . .
    interfaces ) 返回實現(xiàn)指定接口的代理類
  • staticObject newProxyInstance ( ClassLoader loader , Class < ? > [ ]
    interfaces , InvocationHandler handler ) 構(gòu)造實現(xiàn)指定接口的代理類的一個新實例 所有方法會調(diào)用給定處理器對象的 invoke 方法

系統(tǒng)生成的每個代理對象都有一個與之關(guān)聯(lián)的InvocationHandler對象

例子:

interface Person
{
    void walk();
    void sayHello(String name);
}
class MyInvokationHandler implements InvocationHandler
{
    /*
    執(zhí)行動態(tài)代理對象的所有方法時,都會被替換成執(zhí)行如下的invoke方法
    其中:
    proxy:代表動態(tài)代理對象
    method:代表正在執(zhí)行的方法
    args:代表調(diào)用目標(biāo)方法時傳入的實參廷雅。
    */
    public Object invoke(Object proxy, Method method, Object[] args)
    {
        System.out.println("----正在執(zhí)行的方法:" + method);
        if (args != null)
        {
            System.out.println("下面是執(zhí)行該方法時傳入的實參為:");
            for (Object val : args)
            {
                System.out.println(val);
            }
        }
        else
        {
            System.out.println("調(diào)用該方法沒有實參耗美!");
        }
        return null;
    }
}
public class ProxyTest
{
    public static void main(String[] args)
            throws Exception
    {
        // 創(chuàng)建一個InvocationHandler對象
        InvocationHandler handler = new MyInvokationHandler();
        // 使用指定的InvocationHandler來生成一個動態(tài)代理對象
        Person p = (Person) Proxy.newProxyInstance(Person.class.getClassLoader()
                , new Class[]{Person.class}, handler);
        // 調(diào)用動態(tài)代理對象的walk()和sayHello()方法
        p.walk();
        p.sayHello("孫悟空");
    }
}

動態(tài)代理和AOP

假設(shè)有三個模塊需要用到一個公共的代碼段,我們可以把該公共代碼段定義成一個方法航缀,然后讓這三個模塊直接調(diào)用就可以了商架,但是這三個模塊又和一個特定的方法耦合了侣诵,最好的情況就是模塊可以使用到這段公共代碼嗜桌,但又不需要在三個模塊中以硬編碼的方式直接調(diào)用這段公共代碼,這個時候就可以使用動態(tài)代理了

先上一個Dog接口

public interface Dog {

    // info方法聲明
    void info();
    // run方法聲明
    void run();


}

再來一個獵狗實現(xiàn)類

public class GunDog implements Dog{

    // 實現(xiàn)info()方法影所,僅僅打印一個字符串
    public void info()
    {
        System.out.println("我是一只獵狗");
    }
    // 實現(xiàn)run()方法灿巧,僅僅打印一個字符串
    public void run()
    {
        System.out.println("我奔跑迅速");
    }


}


現(xiàn)在需求來了赶袄,假設(shè)info和run就是前面例子中的三個模塊中的兩個揽涮,我們想在這兩個方法中實現(xiàn)一段公共代碼,但又不想以硬編碼方式直接調(diào)用饿肺,怎么做呢蒋困?

假設(shè)下面這個就是需要插入的公共代碼


public class DogUtil {

    // 第一個攔截器方法
    public void method1()
    {
        System.out.println("=====模擬第一個通用方法=====");
    }
    // 第二個攔截器方法
    public void method2()
    {
        System.out.println("=====模擬通用方法二=====");
    }

    
}

關(guān)鍵實現(xiàn):

public class MyInvokationHandler implements InvocationHandler {

    // 需要被代理的對象
    private Object target;
    public void setTarget(Object target)
    {
        this.target = target;
    }
    // 執(zhí)行動態(tài)代理對象的所有方法時,都會被替換成執(zhí)行如下的invoke方法
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Exception
    {
        DogUtil du = new DogUtil();
        // 執(zhí)行DogUtil對象中的method1敬辣。
        du.method1();
        // 以target作為主調(diào)來執(zhí)行method方法
        Object result = method.invoke(target , args);
        // 執(zhí)行DogUtil對象中的method2雪标。
        du.method2();
        return result;
    }
}


接下來:


public class MyProxyFactory {

    // 為指定target生成動態(tài)代理對象
    public static Object getProxy(Object target)
            throws Exception
    {
        // 創(chuàng)建一個MyInvokationHandler對象
        MyInvokationHandler handler =
                new MyInvokationHandler();
        // 為MyInvokationHandler設(shè)置target對象
        handler.setTarget(target);
        // 創(chuàng)建、并返回一個動態(tài)代理
        return Proxy.newProxyInstance(target.getClass().getClassLoader()
                , target.getClass().getInterfaces() , handler);
    }
}

測試:


public class Test {

    public static void main(String[] args)
            throws Exception
    {
        // 創(chuàng)建一個原始的GunDog對象溉跃,作為target
        Dog target = new GunDog();
        // 以指定的target來創(chuàng)建動態(tài)代理
        Dog dog = (Dog)MyProxyFactory.getProxy(target);
        dog.info();
        dog.run();
    }
}

dog對象實際是動態(tài)代理對象村刨,只是該動態(tài)代理對象也實現(xiàn)了Dog接口

反射與泛型

從Java5開始允許使用泛型來限制Class類,如:String.class的類型實際上就是CLass< Sring >

泛型與Class類

使用Class< T >可以避免強(qiáng)制類型轉(zhuǎn)換

下面創(chuàng)建一個簡單對象工廠撰茎,該對象工廠可以根據(jù)指定類來提供該類的實例


public class CrazyitObjectFactory {

    public static Object getInstance(String clsName)
    {
        try
        {
            // 創(chuàng)建指定類對應(yīng)的Class對象
            Class cls = Class.forName(clsName);
            // 返回使用該Class對象所創(chuàng)建的實例
            return cls.newInstance();
        }
        catch(Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }
}

將上面的改寫成使用泛型的Class:

public class CrazyitObjectFactory2 {

    public static <T> T getInstance(Class<T> cls)
    {
        try
        {
            return cls.newInstance();
        }
        catch(Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }
    public static void main(String[] args)
    {
        // 獲取實例后無須類型轉(zhuǎn)換
        Date d = CrazyitObjectFactory2.getInstance(Date.class);
        JFrame f = CrazyitObjectFactory2.getInstance(JFrame.class);
    }
}

對Array的newInstance()進(jìn)行包裝

public class CrazyitArray {

    // 對Array的newInstance方法進(jìn)行包裝
    @SuppressWarnings("unchecked")
    public static <T> T[] newInstance(Class<T> componentType, int length)
    {
        return (T[]) Array.newInstance(componentType , length);  //①
    }
    public static void main(String[] args)
    {
        // 使用CrazyitArray的newInstance()創(chuàng)建一維數(shù)組
        String[] arr = CrazyitArray.newInstance(String.class , 10);
        // 使用CrazyitArray的newInstance()創(chuàng)建二維數(shù)組
        // 在這種情況下嵌牺,只要設(shè)置數(shù)組元素的類型是int[]即可。
        int[][] intArr = CrazyitArray.newInstance(int[].class , 5);
        arr[5] = "Java";
        // intArr是二維數(shù)組龄糊,初始化該數(shù)組的第二個數(shù)組元素
        // 二維數(shù)組的元素必須是一維數(shù)組
        intArr[1] = new int[]{23, 12};
        System.out.println(arr[5]);
        System.out.println(intArr[1][1]);
    }
}

反射獲取泛型信息

public class GenericTest {

    private Map<String , Integer> score;
    public static void main(String[] args)
            throws Exception
    {
        Class<GenericTest> clazz = GenericTest.class;
        Field f = clazz.getDeclaredField("score");
        // 直接使用getType()取出的類型只對普通類型的成員變量有效
        Class<?> a = f.getType();
        // 下面將看到僅輸出java.util.Map
        System.out.println("score的類型是:" + a);
        // 獲得成員變量f的泛型類型
        Type gType = f.getGenericType();
        // 如果gType類型是ParameterizedType對象
        if(gType instanceof ParameterizedType)
        {
            // 強(qiáng)制類型轉(zhuǎn)換
            ParameterizedType pType = (ParameterizedType)gType;
            // 獲取原始類型
            Type rType = pType.getRawType();
            System.out.println("原始類型是:" + rType);
            // 取得泛型類型的泛型參數(shù)
            Type[] tArgs = pType.getActualTypeArguments();
            System.out.println("泛型信息是:");
            for (int i = 0; i < tArgs.length; i++)
            {
                System.out.println("第" + i + "個泛型類型是:" + tArgs[i]);
            }
        }
        else
        {
            System.out.println("獲取泛型類型出錯髓梅!");
        }
    }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绎签,隨后出現(xiàn)的幾起案子枯饿,更是在濱河造成了極大的恐慌,老刑警劉巖诡必,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奢方,死亡現(xiàn)場離奇詭異,居然都是意外死亡爸舒,警方通過查閱死者的電腦和手機(jī)蟋字,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扭勉,“玉大人鹊奖,你說我怎么就攤上這事⊥垦祝” “怎么了忠聚?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長唱捣。 經(jīng)常有香客問我两蟀,道長,這世上最難降的妖魔是什么震缭? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任赂毯,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘党涕。我一直安慰自己烦感,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布膛堤。 她就那樣靜靜地躺著手趣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪骑祟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天气笙,我揣著相機(jī)與錄音次企,去河邊找鬼。 笑死潜圃,一個胖子當(dāng)著我的面吹牛缸棵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谭期,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼堵第,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了隧出?” 一聲冷哼從身側(cè)響起踏志,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胀瞪,沒想到半個月后针余,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凄诞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年圆雁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帆谍。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡伪朽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出汛蝙,到底是詐尸還是另有隱情烈涮,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布窖剑,位于F島的核電站跃脊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏苛吱。R本人自食惡果不足惜酪术,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绘雁,春花似錦橡疼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至挪略,卻和暖如春历帚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杠娱。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工挽牢, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人摊求。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓禽拔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親室叉。 傳聞我的和親對象是個殘疾皇子睹栖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容