JAVA : 反射機制


反射機制

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。

JAVA反射(放射)機制:“程序運行時,允許改變程序結構或變量類型,這種語言稱爲動態語言”。從這個觀點看,Perl,Python,Ruby是動態語言,C++,Java,C#不是動態語言。但是JAVA有着一個非常突出的動態相關機制:Reflection,用在Java身上指的是我們可以於運行時加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。


功能

java反射機制主要提供了以下功能: 在運行時判斷任意一個對象所屬的類;在運行時構造任意一個類的對象;在運行時判斷任意一個類所具有的成員變量和方法;在運行時調用任意一個對象的方法;生成動態代理。

它有着一個非常突出的動態相關機制:Reflection。這個字的意思是“反射、映象、倒影”,用在Java身上指的是我們可以於運行時加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。這種“看透class”的能力(the ability of the program to examine itself)被稱爲introspection(內省、內觀、反省)。Reflection和introspection是常被並提的兩個術語。


動態綁定

要想深入研究java的反射機制,首先要了解什麼是動態綁定。動態是相對於靜態來說的。二者的區別主要在於創建對象的時間不一同,靜態綁定是編譯時創建對象,而動態綁定是在運行時創建對象。

public class TestReflect  {

    public static void main(String[] args) throws Exception {
        String message = null;
        ManPerson m1 = new ManPerson();
        message = m1.eat("aaa");//靜態方式調用
        System.out.println(message);

        Class<?> clazz = Class.forName("com.kblsoft.reflect.one.ManPerson");
        ManPerson m2 = (ManPerson) clazz.newInstance();//動態方式創建對象調用
        message = m2.eat("bbbb");
        System.out.println(message);
    }
}
interface Person{
    public abstract String eat(String food);
}

class ManPerson implements Person{
    @Override
    public String eat(String food) {
        return "man eat " + food;
    }
}

通過一個對象獲得完整的包名和類名

public class TestReflect {
    public static void main(String[] args) {
        TestReflect testReflect = new TestReflect();
        System.out.println(testReflect.getClass().getSimpleName());//類名
        System.out.println(testReflect.getClass().getName());//包名+類名
        //output:
        //TestReflect
        // com.kblsoft.reflect.one.TestReflect
    }
}

實例化Class類對象

public class TestReflect {
    public static void main(String[] args) throws Exception {
        Class<?> class1 = null;
        Class<?> class2 = null;
        Class<?> class3 = null;

        class1 = Class.forName("com.kblsoft.reflect.one.TestReflect");   // 一般採用這種形式
        class2 = new TestReflect().getClass();//可用下面這種代替
        class3 = TestReflect.class;
        System.out.println("類名稱   " + class1.getName());
        System.out.println("類名稱   " + class2.getName());
        System.out.println("類名稱   " + class3.getName());

        //output
        //類名稱   com.kblsoft.reflect.one.TestReflect
        //類名稱   com.kblsoft.reflect.one.TestReflect
        //類名稱   com.kblsoft.reflect.one.TestReflect
    }
}

獲取某個類中的全部構造函數

public class TestReflect {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.kblsoft.reflect.one.Student");
        //第一種方法,實例化
        Student student = (Student) clazz.newInstance();
        student.setAge(24);
        student.setName("black");
        System.out.println(student.getAge() + "::" + student.getName());

        //output     24::black

        //第二種   獲取所有構造函數,逐個分析
        Constructor<?> cons[] = clazz.getConstructors();
        for (Constructor<?> con : cons) {
            Parameter[] paras = con.getParameters();
            System.out.println(con);
            for (Parameter para : paras) {
                System.out.println(para.getType().getName());
            }
        }
        //output
        //  public com.kblsoft.reflect.one.Student(int,java.lang.String)
        //  int
        //  java.lang.String
        //  public com.kblsoft.reflect.one.Student(java.lang.String)
        //  java.lang.String
        //  public com.kblsoft.reflect.one.Student()

    }
}

class Student {
    public int age;
    public String name;

    public Student() {
    }

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

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

獲取某個類的全部屬性

權限修飾符在這裏有用

public class TestReflect extends Param {

    private int iSelf = 0;

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName("com.kblsoft.reflect.one.TestReflect");
        System.out.println("===============本類屬性:getDeclaredFields===============");
        // 取得本類的全部屬性
        Field[] field = clazz.getDeclaredFields();
        for (Field f : field) {
            // 權限修飾符
            int mo = f.getModifiers();
            String priv = Modifier.toString(mo);
            // 屬性類型
            Class<?> type = f.getType();
            System.out.println(priv + " " + type.getName() + " " + f.getName() + ";");
        }

        System.out.println("==========實現的接口或者父類的屬性:getFields==========");
        // 取得實現的接口或者父類的屬性
        Field[] filed1 = clazz.getFields();
        for (Field f : filed1) {
            // 權限修飾符
            int mo = f.getModifiers();
            String priv = Modifier.toString(mo);
            // 屬性類型
            Class<?> type = f.getType();
            System.out.println(priv + " " + type.getName() + " " + f.getName() + ";");
        }

    }
}

獲取某個類的全部方法

public class TestReflect extends Param {

    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.kblsoft.reflect.one.TestReflect");
        Method[] methods = clazz.getMethods();//獲取全部方法,然後循環信息
        for (Method method : methods) {
            Class<?> returnType = method.getReturnType();
            Class<?> paras[] = method.getParameterTypes();
            int temp = method.getModifiers();

            //獲取方法修飾符和名稱
            //output public static void  main
            System.out.print(Modifier.toString(temp) + " ");
            System.out.print(returnType.getName() + "  ");
            System.out.print(method.getName() + " ");

            //獲取方法參數
            for (Class<?> para : paras) {
                System.out.print(para.getName());
            }

            //獲取方法的異常
            Class<?> exces[] = method.getExceptionTypes();
            for (Class<?> exce : exces) {
                System.out.println(exce.getName());
            }

            System.out.println();

        }
    }

    public static void testMethod(String param1,int param2) {

    }

    public String testMethod2(){
        return "SUCCESS";
    }
}

通過反射機制調用某個類的方法

public class TestReflect extends Param {

    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.kblsoft.reflect.one.TestReflect");
        Method method1 = clazz.getMethod("reflect1");
        method1.invoke(clazz.newInstance());

        Method method2 = clazz.getMethod("reflect2",int.class,String.class);
        method2.invoke(clazz.newInstance(),22,"black");//invoke  反射
        //output  
        //Java 反射機制 - 調用某個類的方法1.
        //Java 反射機制 - 調用某個類的方法2.
        //age -> 22. name -> black
    }

    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);
    }
}

通過反射機制操作某個類的屬性

private String proprety = null;

    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.kblsoft.reflect.one.TestReflect");
        Object obj = clazz.newInstance();
        // 可以直接對 private 的屬性賦值
        Field field = clazz.getDeclaredField("proprety");//獲取指定的屬性值
        field.setAccessible(true);//設置是否允許訪問,而不是修改原來的訪問權限修飾詞。
        field.set(obj, "Java反射機制");//set值
        System.out.println(field.get(obj));//get值
    }

反射機制的動態代理

代理設計模式 : 定義:爲其他對象提供一種代理以控制對這個對象的訪問。
一個典型的動態代理創建對象過程可分爲以下四個步驟:

1、通過實現InvocationHandler接口創建自己的調用處理器 IvocationHandler handler = new InvocationHandlerImpl(…);

2、通過爲Proxy類指定ClassLoader對象和一組interface創建動態代理類
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});

3、通過反射機制獲取動態代理類的構造函數,其參數類型是調用處理器接口類型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

4、通過構造函數創建代理類實例,此時需將調用處理器對象作爲參數被傳入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
爲了簡化對象創建過程,Proxy類中的newInstance方法封裝了2~4,只需兩步即可完成代理對象的創建。
生成的ProxySubject繼承Proxy類實現Subject接口,實現的Subject的方法實際調用處理器的invoke方法,而invoke方法利用反射調用的是被代理對象的的方法(Object result=method.invoke(proxied,args))

public class TestReflect  {

    public static void main(String[] args) throws Exception {
        MyInvocationHandler demo = new MyInvocationHandler();
        Person sub = (Person) demo.bind(new RealPerson());
        String info = sub.eat("children");
        System.out.println(info);
    }
}
interface Person{
    public String eat(String food);
}

class RealPerson implements Person{

    @Override
    public String eat(String food) {
        return "eat " + food;
    }
}

class MyInvocationHandler implements InvocationHandler{//定義一個InvocationHandler接口的子類

    private Object obj = null;

    public Object bind(Object obj){//具體的實現
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);//創建動態代理
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(this.obj,args);//對接口的調用重定向爲對代理的調用
    }
}

通過反射取得並修改數組的信息

Array的使用

public class TestReflect  {

    public static void main(String[] args) throws Exception {
        int[] temp = { 1, 2, 3, 4, 5 };
        Class<?> demo = temp.getClass().getComponentType();
        System.out.println("數組類型: " + demo.getName());
        System.out.println("數組長度  " + Array.getLength(temp));
        System.out.println("數組的第一個元素: " + Array.get(temp, 0));
        Array.set(temp, 0, 100);
        System.out.println("修改之後數組第一個元素爲: " + Array.get(temp, 0));
        //output
        //數組類型: int
        //數組長度  5
        //數組的第一個元素: 1
        //修改之後數組第一個元素爲: 100
    }
}

通過反射機制修改數組的大小

public class TestReflect  {

    public static void main(String[] args) throws Exception {
        int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        int[] newTemp = (int[]) arrayInc(temp, 15);
        print(newTemp);
        String[] atr = { "a", "b", "c" };
        String[] str1 = (String[]) arrayInc(atr, 8);
        print(str1);
        //output
        //數組長度爲: 15
        //1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 
        //數組長度爲: 8
        //a b c null null null null null 
    }
    // 修改數組大小
    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;
        }
        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();
    }
}

將反射機制應用於工廠模式

//現在我們利用反射機制實現工廠模式,可以在不修改工廠類的情況下添加任意多個子類。
//但是有一點仍然很麻煩,就是需要知道完整的包名和類名,這裏可以使用properties配置文件來完成。
public class TestReflect  {

    public static void main(String[] args) throws Exception {
        Person p = Factory.getInstance("com.kblsoft.reflect.one.ManPerson");//接口接收,創建的是子類
        if (null != p) {
            System.out.println(p.eat("children"));
        }
    }
}
interface Person{
    public abstract String eat(String food);
}

class ManPerson implements Person{
    @Override
    public String eat(String food) {
        return "man eat " + food;
    }
}
class WomanPerson implements Person{
    @Override
    public String eat(String food) {
        return "woman eat " + food;
    }
}

class Factory {
    public static Person getInstance(String className){
        Person person = null;
        try {
            person = (Person) Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return person;
    }
}

參考
1. http://www.cnbl/lzq198754/p/5780331.html
2. java編程思想

發佈了33 篇原創文章 · 獲贊 10 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章