1. 程式人生 > >Java 基礎之詳解 Java 反射機制

Java 基礎之詳解 Java 反射機制

一行代碼 strac classname for 內部 系統資源 用戶 管理 ann

一、什麽是 Java 的反射機制?

??反射(Reflection)是Java的高級特性之一,是框架實現的基礎,定義:JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。
??一般而言,當用戶使用一個類的時候,應該先知道這個類,而後通過這個類產生實例化對象,但是使用反射則可以相反的通過對象找到類。
??通俗的講反射就是可以在程序運行的時候動態裝載類,查看類的信息,生成對象,或操作生成的對象。它允許運行中的 Java 程序獲取自身的信息,自己能看到自己,就像照鏡子一樣。

二、Java 反射機制常見方法介紹。

1、Java反射實現的關鍵點之Class
??Class 類的實例表示正在運行的 Java 應用程序中的類和接口。jvm中有N多的實例,每個類的實例都有Class對象。(包括基本數據類型)
??Class 沒有公共構造方法。Class 對象是在加載類時由 Java 虛擬機以及通過調用類加載器中的defineClass 方法自動構造的。也就是這不需要我們自己去處理創建,JVM已經幫我們創建好了。
??如果知道一個實例,那麽可以通過實例的“getClass()”方法獲得運行實例的Class(該類型的字節碼文件對象),如果你知道一個類型,那麽你可以使用“.class”的方法獲得運行實例的Class(該類型的字節碼文件對象)。

方法2~17都是類 Class的方法
java.lang.Class 繼承自java.lang.Object

2、getName()方法
String getName();返回此 Member 表示的底層成員或構造方法的簡單名稱。

3、forName()方法
public static Class <?> forName(String className)
throws ClassNotFoundException
返回與帶有給定字符串名的類或接口相關聯的 Class 對象。
參數:className - 所需類的完全限定名。

4、getSuperclass()方法

public Class<? super T> getSuperclass()
返回:此對象所表示的類的超類。

5、getInterfaces()方法
public Class< ? >[] getInterfaces()
返回:該類所實現的接口的一個數組。

6、getConstructors()方法
public Constructor< ? >[] getConstructors() throws SecurityException
返回:表示此類公共構造方法的 Constructor 對象數組

7、newInstance()方法
public T newInstance() throws InstantiationException, IllegalAccessException
創建此 Class 對象所表示的類的一個新實例。如同用一個帶有一個空參數列表的 new 表達式實例化該類。如果該類尚未初始化,則初始化這個類。
返回:此對象所表示的類的一個新分配的實例。

8、getDeclaredConstructors()方法
public Constructor<?>[] getDeclaredConstructors() throws SecurityException
返回 Constructor 對象的一個數組,這些對象反映此 Class 對象表示的類聲明的所有構造方法。它們是公共、保護、默認(包)訪問和私有構造方法。返回數組中的元素沒有排序,也沒有任何特定的順序。如果該類存在一個默認構造方法,則它包含在返回的數組中。 如果此 Class 對象表示一個接口、一個基本類型、一個數組類或 void,則此方法返回一個長度為 0 的數組。

返回:表示此類所有已聲明的構造方法的 Constructor 對象的數組

9、getFields()方法
public Field[] getFields() throws SecurityException

返回:表示公共字段的 Field 對象的數組

10、getDeclaredFields()方法
public Field[] getDeclaredFields() throws SecurityException
返回:表示此類所有已聲明字段的 Field 對象的數組,與getFields()方法的區別是getFields()方法只返回公有屬性,
而getDeclaredFields()方法返回所有屬性。

11、getMethods()方法
public Method[] getMethods() throws SecurityException
返回一個包含某些 Method 對象的數組,這些對象反映此 Class 對象所表示的類或接口(包括那些由該類或接口聲明的以及從超類和超接口繼承的那些的類或接口)的公共 member 方法。數組類返回從 Object 類繼承的所有(公共)member 方法。返回數組中的元素沒有排序,也沒有任何特定的順序。如果此 Class 對象表示沒有公共成員方法的類或接口,或者表示一個基本類型或 void,則此方法返回長度為 0 的數組。
類初始化方法

返回:表示此類中公共方法的 Method 對象的數組

12、getDeclaredMethods()方法
public Method[] getDeclaredMethods() throws SecurityException
返回:表示此類所有聲明方法的 Method 對象的數組

13、getClassLoader()方法
public ClassLoader getClassLoader()
返回該類的類加載器。有些實現可能使用 null 來表示引導類加載器。如果該類由引導類加載器加載,則此方法在這類實現中將返回 null。
如果存在安全管理器,並且調用者的類加載器不是 null,也不同於或是請求其類加載器的類的類加載器的祖先,
則此方法通過 RuntimePermission("getClassLoader") 權限調用此安全管理器checkPermission 方法,以確保可以訪問該類的類加載器。
如果此對象表示一個基本類型或 void,則返回 null。

返回:加載此對象所表示的類或接口的類加載器。

14、getInterfaces()方法
public Class<?>[] getInterfaces()

返回:該類所實現的接口的一個數組。

15、getComponentType()方法
public Class<?> getComponentType()
返回表示數組組件類型的 Class。如果此類不表示數組類,則此方法返回 null。
返回:如果此類是數組,則返回表示此類組件類型的 Class

16、getResourceAsStream()方法
public InputStream getResourceAsStream(String name)
查找具有給定名稱的資源。查找與給定類相關的資源的規則是通過定義類的 class loader 實現的。此方法委托此對象的類加載器。
如果此對象通過引導類加載器加載,則此方法將委托給 ClassLoader.getSystemResourceAsStream(java.lang.String)。

參數:name - 所需資源的名稱
返回:一個 InputStream 對象;如果找不到帶有該名稱的資源,則返回 null

17、isInstance()方法
public boolean isInstance(Object obj)

參數:obj - 要檢查的對象
返回:如果 obj 是此類的實例,則返回 true

一般地,我們用instanceof關鍵字來判斷是否為某個類的實例。同時我們也可以借助反射中Class對象的isInstance()方法來判斷是否為某個類的實例,它是一個Native方法.

18、invoke()方法

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
參數:
obj - 從中調用底層方法的對象
args - 用於方法調用的參數
返回:
使用參數 args 在 obj 上指派該對象所表示方法的結果

java.lang.reflect
類 Method
java.lang.Object
繼承者 java.lang.reflect.AccessibleObject
繼承者 java.lang.reflect.Method

19、newProxyInstance()方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
返回一個指定接口的代理類實例,該接口可以將方法調用指派到指定的調用處理程序。此方法相當於:
Proxy.getProxyClass(loader, interfaces). getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler });
參數:
loader - 定義代理類的類加載器
interfaces - 代理類要實現的接口列表
h - 指派方法調用的調用處理程序
返回:
一個帶有代理類的指定調用處理程序的代理實例,它由指定的類加載器定義,並實現指定的接口

java.lang.reflect
類 Proxy
java.lang.Object
繼承者 java.lang.reflect.Proxy

三、Java 反射機制常見用法舉例。

1、使用 Java 反射通過一個對象獲取某個類的完整包名和類名。
??通過實例的getClass()方法獲得運行實例的字節碼文件對象,然後通過getName()方法獲得類的完整包名和類名。

    public static void test1(){
        ReflectTest testReflect = new ReflectTest();
        System.out.println(testReflect.getClass().getName());
    }
    //結果:com.channelsoft.ssm.controller.ReflectTest

2、使用 Java 反射獲取 Class 對象(三種方式)
方式1.通過Class類的靜態方法獲取Class類對象 (推薦)
方式2.因為所有類都繼承Object類。因而可通過調用Object類中的getClass方法來獲取
方式3.任何數據類型(包括基本數據類型)都有一個“靜態”的class屬性

    public static void test2() throws Exception{
            Class<?> class1 = null;
            Class<?> class2 = null;
            Class<?> class3 = null;
            class1 =  Class.forName("com.channelsoft.ssm.controller.ReflectTest");
            //這一new 產生一個ReflectTest對象,一個Class對象。
            class2 = new ReflectTest().getClass();
            class3 = ReflectTest.class;
            System.out.println("類名稱 1:   " + class1.getName());
            System.out.println("類名稱 2:  " + class2.getName());
            System.out.println("類名稱 3:  " + class3.getName());
    }

運行結果:
技術分享圖片
3、使用 Java 反射獲取一個對象的父類和實現的接口。

public static void test3() throws ClassNotFoundException{
        Class<?> clazz = Class.forName("com.channelsoft.ssm.controller.ReflectTest");
        // 取得父類
        Class<?> parentClass = clazz.getSuperclass();
        System.out.println("父類為:" + parentClass.getName());
        // 獲取所有的接口
        Class<?> intes[] = clazz.getInterfaces();
        System.out.println("實現的接口有:");
        for (int i = 0; i < intes.length; i++) {
            System.out.println((i + 1) + ":" + intes[i].getName());
        }
}

註:ReflectTest實現了Serializable接口
運行結果:
技術分享圖片
4、使用 Java 反射獲取某個類的全部構造函數並調用私有構造方法。
Student類:

package com.channelsoft.ssm.domain;

public class Student {

 public Student(){
        System.out.println("調用了公有、無參構造方法。。。");
    }

    public Student(char name){
        System.out.println("姓名:" + name);
    }

    public Student(String name ,int age){
        System.out.println("姓名:"+name+"年齡:"+ age);
    }

    //受保護的構造方法
    protected Student(boolean b){
        System.out.println("受保護的構造方法 b = " + b);
    }

    //私有構造方法
    private Student(int age){
        System.out.println("私有的構造方法,年齡:"+ age);
    }
}

獲取Student類的全部構造函數,並使用Class對象的newInstance()方法來創建Class對象對應類Student的實例來調用私有構造方法

package com.channelsoft.ssm.controller;

import java.lang.reflect.Constructor;

public class Constructors {
    public static void main(String[] args) throws Exception {
        //1.加載Class對象
        Class clazz = Class.forName("com.channelsoft.ssm.domain.Student");
        //2.獲取所有公有構造方法
        System.out.println("**********************所有公有構造方法****************************");
        Constructor[] conArray = clazz.getConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }

        System.out.println("************所有的構造方法(包括:私有、受保護、默認、公有)***************");
        conArray = clazz.getDeclaredConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }

        System.out.println("*****************獲取公有、無參的構造方法***************************");
        //1、因為是無參的構造方法所以類型是一個null,不寫也可以:這裏需要的是一個參數的***類型***,
        //2、返回的是描述這個無參構造函數的類對象。
        Constructor con = clazz.getConstructor(null);
        System.out.println("con = " + con);
        //調用構造方法
        Object obj = con.newInstance();
        System.out.println("obj = " + obj);

        System.out.println("******************獲取私有構造方法,並調用**************************");
        con = clazz.getDeclaredConstructor(int.class);
        System.out.println(con);
        //調用構造方法
        con.setAccessible(true);//暴力訪問(忽略掉訪問修飾符)
        obj = con.newInstance(26);
    }
}

運行結果:
技術分享圖片
5、使用 Java 反射獲取某個類的全部屬性。
Student類

package com.channelsoft.ssm.domain;

public class Student {

    public String name;
    protected int age;
    char sex;
    private String phoneNum;

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ", sex=" + sex
                + ", phoneNum=" + phoneNum + "]";
    }
}

獲取Student類的全部屬性並調用

package com.channelsoft.ssm.controller;

import java.lang.reflect.Field;

import com.channelsoft.ssm.domain.Student;

public class Fields {
    public static void main(String[] args) throws Exception {
        //1.獲取Class對象
        Class stuClass = Class.forName("com.channelsoft.ssm.domain.Student");
        //2.獲取字段
        System.out.println("************************獲取所有公有的字段**************************************");
        Field[] fieldArray = stuClass.getFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }
        System.out.println();
        System.out.println("************************獲取所有的字段(包括私有、受保護、默認的)**********************");
        fieldArray = stuClass.getDeclaredFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }
        System.out.println();
        System.out.println("*************************獲取公有字段並調用**************************************");
        Field f = stuClass.getField("name");
        System.out.println(f);
        //獲取一個對象
        Object obj = stuClass.getConstructor().newInstance();
        f.set(obj, "laughitover");
        //驗證
        Student stu = (Student)obj;
        System.out.println("驗證姓名:" + stu.name);

        System.out.println();
        System.out.println("**************************獲取私有字段並調用**************************************");
        f = stuClass.getDeclaredField("phoneNum");
        System.out.println(f);
        f.setAccessible(true);//暴力反射,解除私有限定
        f.set(obj, "13812345678");
        System.out.println("驗證電話:" + stu);
    }
}

運行結果:
技術分享圖片
6、使用 Java 反射獲取某個類的全部方法。

package com.channelsoft.ssm.domain;

public class Student {
    public void show1(String s){
        System.out.println("調用了:公有的,String參數的show1(): s = " + s);
    }
    protected void show2(){
        System.out.println("調用了:受保護的,無參的show2()");
    }
    void show3(){
        System.out.println("調用了:默認的,無參的show3()");
    }
    private String show4(int age){
        System.out.println("調用了,私有的,並且有返回值的,int參數的show4(): age = " + age);
        return "abcd";
    }
}

獲取某個類的全部方法並且調用共有方法show1()和私有方法show4()

public static void test6() throws Exception {
        //1.獲取Class對象
        Class stuClass = Class.forName("fanshe.method.Student");
        //2.獲取所有公有方法
        System.out.println("***************獲取所有的”公有“方法*******************");
        stuClass.getMethods();
        Method[] methodArray = stuClass.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("***************獲取所有的方法,包括私有的*******************");
        methodArray = stuClass.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("***************獲取公有的show1()方法*******************");
        Method m = stuClass.getMethod("show1", String.class);
        System.out.println(m);
        //實例化一個Student對象
        Object obj = stuClass.getConstructor().newInstance();
        m.invoke(obj, "laughitover");

        System.out.println("***************獲取私有的show4()方法******************");
        m = stuClass.getDeclaredMethod("show4", int.class);
        System.out.println(m);
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj, 20);//需要兩個參數,一個是要調用的對象(獲取有反射),一個是實參
        System.out.println("返回值:" + result);
}

運行結果:
技術分享圖片
7、使用 Java 反射調用某個類的方法。
Java 反射獲取 Class 對象並使用invoke()方法調用方法reflect1()和reflect2()

public static void test7() throws Exception{
        Class<?> clazz = Class.forName("com.channelsoft.ssm.controller.ReflectTest");
        Method method = clazz.getMethod("reflect1");
        method.invoke(clazz.newInstance());
        method = clazz.getMethod("reflect2", int.class, String.class);
        method.invoke(clazz.newInstance(), 20, "張三");
    }

    public void reflect1() {
        System.out.println("Java 反射機制 - 調用某個類的方法1.");
    }
    public void reflect2(int age, String name) {
        System.out.println("Java 反射機制 - 調用某個類的方法2.");
        System.out.println("age -> " + age + ". name -> " + name);
    }

運行結果:
技術分享圖片
8、反射機制的動態代理實例。
如果想獲取實現Subject()接口的其它實例,只需新建類實現接口Subject(),使用MyInvocationHandler.bind()方法獲取即可實現調用。

 public static void test8(){
        MyInvocationHandler demo = new MyInvocationHandler();
        Subject sub = (Subject) demo.bind(new RealSubject());
        String info = sub.say("語文", 90);
        System.out.println(info);
    }

如果想要完成動態代理,首先需要定義一個InvocationHandler接口的子類,完成代理的具體操作。

package com.channelsoft.ssm.controller;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler {
    private Object obj = null;

    public Object bind(Object obj) {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object temp = method.invoke(this.obj, args);
        return temp;
    }
}
package com.channelsoft.ssm.service;

public interface Subject {
    public String say(String name, int score);
}
package com.channelsoft.ssm.controller;

import com.channelsoft.ssm.service.Subject;

public class RealSubject implements Subject {
    public String say(String name, int score) {
            return name + "  " + score;
    }
}

運行結果:
技術分享圖片
9、通過反射越過泛型檢查。
泛型用在編譯期,編譯過後泛型擦除(消失掉)。所以是可以通過反射越過泛型檢查的

    public static void test10() throws Exception{
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(100);
        list.add(200);
        Method method = list.getClass().getMethod("add", Object.class);
        method.invoke(list, "Java反射機制實例。");
        //遍歷集合
        for(Object obj : list){
            System.out.println(obj);
        }
    }

運行結果:
技術分享圖片
10、通過反射機制獲得數組信息並修改數組的大小和值。
通過反射機制分別修改int和String類型的數組的大小並修改int數組的第一個值

 public static void test12() throws Exception{
         int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
         int[] newTemp = (int[]) arrayInc(temp, 15);
         print(newTemp);
         Array.set(newTemp, 0, 100);
         System.out.println("修改之後數組第一個元素為: " + Array.get(newTemp, 0));
         print(newTemp);
         String[] atr = { "a", "b", "c" };
         String[] str1 = (String[]) arrayInc(atr, 8);
         print(str1);
    }
    // 修改數組大小
    public static Object arrayInc(Object obj, int len) {
        Class<?> arr = obj.getClass().getComponentType();
        Object newArr = Array.newInstance(arr, len);
        int co = Array.getLength(obj);
        System.arraycopy(obj, 0, newArr, 0, co);
        return newArr;
    }
    // 打印
    public static void print(Object obj) {
        Class<?> c = obj.getClass();
        if (!c.isArray()) {
            return;
        }
        Class<?> arr = obj.getClass().getComponentType();
        System.out.println("數組類型: " + arr.getName());
        System.out.println("數組長度為: " + Array.getLength(obj));
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i) + " ");
        }
        System.out.println();
    }

運行結果:
技術分享圖片
11、將反射機制應用於工廠模式。
對於普通的工廠模式當我們在添加一個子類的時候,就需要對應的修改工廠類。 當我們添加很多的子類的時候,會很麻煩

package com.channelsoft.ssm.service;

public interface Fruit {
    public abstract void eat();
}
package com.channelsoft.ssm.controller;

import com.channelsoft.ssm.service.Fruit;

public class Orange implements Fruit {
    public void eat() {
        System.out.println("Orange");
    }
}
package com.channelsoft.ssm.controller;

import com.channelsoft.ssm.service.Fruit;

public class Apple implements Fruit {
    public void eat() {
        System.out.println("Apple");
    }
}
package com.channelsoft.ssm.controller;

import com.channelsoft.ssm.service.Fruit;

public class Factory {
    public static Fruit getInstance(String ClassName) {
        Fruit f = null;
        try {
            f = (Fruit) Class.forName(ClassName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}
    public static void test13(String fruit) throws Exception{
        Fruit f = Factory.getInstance(fruit);
        if (f != null) {
            f.eat();
        }
   }

還可以通過加載配置文件的方式

    public static String getValue(String key) throws IOException{
        Properties pro = new Properties();//獲取配置文件的對象
        InputStream in=new Demo().getClass().getResourceAsStream("/pro.txt");
        pro.load(in);//將流加載到配置文件對象中
        in.close();
        return pro.getProperty(key);//返回根據key獲取的value值
    }

pro.txt內容如下:

className = com.channelsoft.ssm.domain.Apple

運行結果:
技術分享圖片

12、通過Java反射機制獲取ClassLoader類加載器

技術分享圖片

package com.channelsoft.ssm.controller;

public class ReflectTest{

    public static void main(String[] args) throws Exception {
        testClassLoader();
    }

    public static void testClassLoader() throws ClassNotFoundException {
        //1、獲取一個系統的類加載器
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        System.out.println("系統的類加載器-->" + classLoader);

        //2、獲取系統類加載器的父類加載器(擴展類加載器(extensions classLoader))
        classLoader = classLoader.getParent();
        System.out.println("擴展類加載器-->" + classLoader);

        //3、獲取擴展類加載器的父類加載器
        //輸出為Null,無法被Java程序直接引用
        classLoader = classLoader.getParent();
        System.out.println("啟動類加載器-->" + classLoader);

        //4、測試當前類由哪個類加載器進行加載 ,結果就是系統的類加載器
        classLoader = Class.forName("com.channelsoft.ssm.controller.ReflectTest").getClassLoader();
        System.out.println("當前類由哪個類加載器進行加載-->"+classLoader);

        //5、測試JDK提供的Object類由哪個類加載器負責加載的
        classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println("JDK提供的Object類由哪個類加載器加載-->" + classLoader);
    }
}

技術分享圖片

四、Java 反射機制相關面試題。

1、反射創建類實例的三種方式是什麽?

答案參見:本篇文章:Java 反射機制常見用法舉例2

2、反射中,Class.forName 和 ClassLoader 區別。

Class.forName(className)方法,其實調用的方法是Class.forName(className,true,classloader);第2個參數表示在loadClass後必須初始化。根據jvm加載類的知識,我們知道在執行過此方法後,目標對象的 static塊代碼已經被執行,static參數也已經被初始化。
而ClassLoader.loadClass(className)方法,實際調用的方法是ClassLoader.loadClass(className,false);第2個 參數表示目標對象被裝載後不進行鏈接,這就意味著不會去執行該類靜態塊中間的內容。
例如,在JDBC使用中,常看到這樣的用法,Class.forName("com.mysql.jdbc.Driver"),如果換成了 getClass().getClassLoader().loadClass("com.mysql.jdbc.Driver"),就不行。 根據com.mysql.jdbc.Driver的源代碼,Driver在static塊中會註冊自己到java.sql.DriverManager。而static塊就是在Class的初始化中被執行。所以這個地方就只能用Class.forName(className)。
com.mysql.jdbc.Driver的源代碼:

// Register ourselves with the DriverManager
static {
    try {
        java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
        throw new RuntimeException("Can‘t register driver!");
    }
}

java類的加載過程:
在Java中,類裝載器把一個類裝入Java虛擬機中,要經過三個步驟來完成:裝載、鏈接和初始化,其中鏈接又可以分成校驗、準備和解析三
步.
  裝載:查找和導入類或接口的二進制數據;
  鏈接:執行下面的校驗、準備和解析步驟,其中解析步驟是可以選擇的;
  校驗:檢查導入類或接口的二進制數據的正確性;
  準備:給類的靜態變量分配並初始化存儲空間;
  解析:將符號引用轉成直接引用;
  初始化:激活類的靜態變量的初始化Java代碼和靜態Java代碼塊。

3、描述動態代理的幾種實現方式,分別說出相應的優缺點。

jdk動態代理和cglib動態代理。jdk動態代理是由Java內部的反射機制來實現的,cglib動態代理底層則是借助asm框架來實現的,實現了無反射機制進行代理,利用空間來換取了時間,代理效率高於jdk 。總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行緩存,這樣解決asm生成類過程低效問題)。還有一點必須註意:jdk動態代理的應用前提,必須是目標類基於統一的接口。如果沒有上述前提,jdk動態代理不能應用。由此可以看出,jdk動態代理有一定的局限性,cglib這種第三方類庫實現的動態代理應用更加廣泛,且在效率上更有優勢。

五、總結。

??很多人認為反射在實際的Java開發應用中並不廣泛,其實不然。我們常用的jdbc中有一行代碼:
Class.forName(‘com.MySQL.jdbc.Driver.class‘).newInstance();
這裏用到的就是Java反射機制,除此之外很多框架也都用到反射機制。

??實際上反射機制就是非常規模式,如果說方法的調用是 Java 正確的打開方式,那反射機制就是Java的後門。為什麽會有這個後門呢?這涉及到了靜態和動態的概念:
靜態編譯:在編譯時確定類型,綁定對象
動態編譯:運行時確定類型,綁定對象
??兩者的區別在於,動態編譯可以最大程度地支持多態,多態最大的意義在於降低類的耦合性,而Java語言確是靜態編譯(如:int a=3;要指定類型),Java反射很大程度上彌補了這一不足:解耦以及提高代碼的靈活性,所以Java也被稱為是準動態語言。
盡管反射有很多優點,但是反射也有缺點:
1、由於反射會額外消耗一定的系統資源,因此,反射操作的效率要比那些非反射操作低得多。
2、由於反射允許代碼執行一些在正常情況下不被允許的操作(比如訪問私有的屬性和方法),所以使用反射可能會導致意料之外的副作用--代碼有功能上的錯誤。
所以能用其它方式實現的就盡量不去用反射。

Java 基礎之詳解 Java 反射機制