Day15 JavaSE 反射機制(下)

JavaSE 反射機制(下)

四、獲取類的方法

  • 獲取方法函數:

    • public Method[] getDeclaredMethods()

      返回此Class對象所表示的類或接口的全部方法。

    • public Method[] getMethods()

      返回此Class對象所表示的類或接口的public的方法。

  • 常用方法:(在獲取方法過程中)

    方法名 功能
    Method[] getMethods() 獲取類的所有公有方法
    Method[] getDeclaredMethods() 獲取類的所有方法(公有+私有)
    getName() 獲取方法名稱
    getReturnType() 獲取返回值類型
    getModifiers() 獲取修飾符(public–>1; private–>2)
    getParameterTypes() 獲取方法的參數類型,返回Class[], 數組元素個數爲參數個數
  • 上述方法案例:

    package com.reflection;
    //父類
    public class Person {
        public String name;
        int age;
    }
    
    package com.reflection;
    //子類
    public class Student extends Person{
        String school;
      	private String privateField;
        //無參構造
        public Student(){
            System.out.println("調用的是public Student()");
        }
        //有參構造
        public Student(String school){
            System.out.println("調用的是public Student(String school)");
            this.school = school;
        }
        //私有有參構造
        private Student(String name, int age){
            System.out.println("調用的是private Student(String name, int age)");
            this.name = name;
            this.age = age;
        }
        	
     		public void moveType() {
            System.out.println("騎自行車上學");
        }
      
      	public void studyInfo() { System.out.println("學習中學知識"); }
    
        public String getSchool(){
            return this.school;
        }
      
      	private void test(String name){
        }
    }
    
    package com.reflection;
    
    import java.lang.reflect.Method;
    
    public class Test3 {
        public static void main(String[] args) {
            try {
                Class clazz3 =  Class.forName("com.reflection.Student");
              
    	          //獲取到類的所有公有方法
                Method[] ms = clazz3.getMethods(); 
    
                for (Method m : ms) {
                    System.out.println("方法名: "+ m.getName());
                    System.out.println("返回值類型: "+ m.getReturnType());
                    System.out.println("修飾符: "+ m.getModifiers());
    
                    Class[] pcs = m.getParameterTypes();//獲取方法的參數類型,返回Class數組,方法參數個數等於數組元素個數
                    if (pcs.length>0){
                        for (Class pc : pcs) {
                            System.out.println("參數類型: " + pc.getName());
                        }
                    }
                    System.out.println("-----------------------------------------------");
                }
    
                System.out.println("\n========分==================割===================線===========\n");
    
    	          //獲取類的所有方法,包含公有私有
                Method[] ams = clazz3.getDeclaredMethods();
    
                for (Method m : ams) {
                    System.out.println("方法名: "+ m.getName());
                    System.out.println("返回值類型: "+ m.getReturnType());
                    System.out.println("修飾符: "+ m.getModifiers());
    
                    Class[] pcs = m.getParameterTypes();//獲取方法的參數類型,返回Class數組,方法參數個數等於數組元素個數
                    if (pcs.length>0){
                        for (Class pc : pcs) {
                            System.out.println("參數類型: " + pc.getName());
                        }
                    }
                    System.out.println("-----------------------------------------------");
                }
    
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    /*運行結果:
    方法名: moveType
    返回值類型: void
    修飾符: 1
    -----------------------------------------------
    方法名: studyInfo
    返回值類型: void
    修飾符: 1
    -----------------------------------------------
    方法名: getSchool
    返回值類型: class java.lang.String
    修飾符: 1
    -----------------------------------------------
    
    ========分==================割===================線===========
    
    方法名: test
    返回值類型: void
    修飾符: 2
    參數類型: java.lang.String
    -----------------------------------------------
    方法名: moveType
    返回值類型: void
    修飾符: 1
    -----------------------------------------------
    方法名: studyInfo
    返回值類型: void
    修飾符: 1
    -----------------------------------------------
    方法名: getSchool
    返回值類型: class java.lang.String
    修飾符: 1
    -----------------------------------------------
    */
    

五、獲取類的屬性和包

  • 獲取屬性函數:

    • public Field[] getDeclaredFields()

      返回此Class對象所表示的類或接口的全部屬性。

    • public Field[] getFields()

      返回此Class對象所表示的類或接口的public的屬性。

    注意:1. getDeclaredFields()僅獲取本類中所有屬性,不包含父類。

    2. getFields()獲取本類及其父類中的public修飾屬性。

  • 獲取包函數:

    • Package getPackage()

      返回此Class對象所在的包名稱。

  • 常用方法:(在獲取屬性和包中)

    方法名 功能
    getFields() 獲取類(本類及父類)的公有屬性
    getDeclaredFields() 獲取本類中所有的屬性(公有+私有)
    getModifiers() 獲取屬性修飾符
    getType() 獲取屬性的類型
    getName() 獲取屬性名稱
    Package getPackage() 獲取該類所屬包名
  • 以上方法案例展示:

    注:父類Person()、子類Student()同(四)中案例。

    package com.reflection;
    
    import java.lang.reflect.Field;
    
    public class Test5 {
        public static void main(String[] args) {
            try {
                Class clazz4 = Class.forName("com.reflection.Student");
                Field[] fs = clazz4.getFields(); //獲取類中所有公有屬性,同時包含父類的屬性
    
                for (Field f : fs) {
                    System.out.println("修飾符: " + f.getModifiers());
                    System.out.println("屬性類型: " + f.getType());
                    System.out.println("屬性名稱: " + f.getName());
                    System.out.println("-----------------------------------");
                }
    
                System.out.println("\n==========分===========割===========線===========\n");
    
                Field[] afs = clazz4.getDeclaredFields(); //獲取類中所有屬性(私有+公有),不包含父類的屬性。
    
                for (Field af : afs) {
                    System.out.println("修飾符: " + af.getModifiers());
                    System.out.println("屬性類型: "+af.getType());
                    System.out.println("屬性名稱: " + af.getName());
                    System.out.println("-----------------------------------");
                }
    
                System.out.println("\n==========分===========割===========線===========\n");
    
    
                Package p = clazz4.getPackage();
                System.out.println(p.getName());
    
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    /*運行結果:
    修飾符: 1
    屬性類型: class java.lang.String
    屬性名稱: school
    -----------------------------------
    修飾符: 1
    屬性類型: class java.lang.String
    屬性名稱: name
    -----------------------------------
    
    ==========分===========割===========線===========
    
    修飾符: 1
    屬性類型: class java.lang.String
    屬性名稱: school
    -----------------------------------
    修飾符: 2
    屬性類型: class java.lang.String
    屬性名稱: privateField
    -----------------------------------
    
    ==========分===========割===========線===========
    
    com.reflection
    
     */
    

六、調用類中指定方法、指定屬性

– 調用指定方法

注:方法調用前需要通過構造器創建obj對象,爲之後的方法調用做準備!

實際調用分以下四種情況討論:

  • 調用公有方法

    Method m = clazz.getMethod("調用方法名","參數.class"...);

    m.invoke(obj,"args"...); //此處的invoke英文意思有“喚起”的意思,此處表示向對象obj中注入參數。

  • 調用私有方法

    需要額外註明m.setAccessible(true),該語句表示解除私有封裝,強行調用。

    其他語句同上。

  • 調用重載方法

    只需注意填入參數的不同即可。

  • 調用有返回值的方法

    需要注意對返回值進行接收,接受過程中可能需要對對象進行強轉。

以上情況案例展示:

注:該案例引入Teacher類,其父類Person同上。

package com.reflection;
//Teacher類
public class Teacher extends Person{

    public String school;
	  private String privateField;

  	//私有方法
    private void test(String name){
        this.name = name;
        System.out.println("這裏調用private void test(String name)方法");
    }

  	//公有方法
    public void setInfo(String name, String school){
        this.name = name;
        this.school = school;
        System.out.println("這裏調用public void setInfo(String name, String school)方法");
    }

    //重載方法
    public void SetInfo(String school){
        this.school = school;
        System.out.println("這裏調用public void setInfo(String school)方法");
    }
	
  	//有返回值方法
    public String getSchool(){
        System.out.println("這裏調用public String getSchool()方法");
        return school;
    }

}
package com.reflection;
//調用指定方法
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Test6 {
    public static void main(String[] args) {
        try {
            /*
                以下不論是反射調用setInfo還是test方法
                都調用的obj對象的方法,obj對象實際上就是Teacher對象
             */
            Class clazz5 = Class.forName("com.reflection.Teacher");

            Constructor con = clazz5.getConstructor(); //獲取無參構造
            Object obj = con.newInstance(); //使用無參構造創建對象

            //調用公有方法
            Method m = clazz5.getMethod("setInfo", String.class, String.class); //得到名稱爲setInfo,參數是(String,String)的方法
            m.invoke(obj,"卡莉","第一中學");//參數1爲需要實例化的對象,參數2爲調用當前方法的實際參數

            //調用私有方法(需要解封)
            Method m1 = clazz5.getDeclaredMethod("test", String.class);
            m1.setAccessible(true); //解除私有封裝,則可調用私有方法
            m1.invoke(obj,"蘇瞻");

            //調用重載方法,只需注意填入參數不同即可
            Method m2 = clazz5.getMethod("SetInfo", String.class);
            m2.invoke(obj,"第二中學");

            //調用有返回值的方法,需要對返回值進行接收
            Method m3 = clazz5.getMethod("getSchool");
            String school = (String)m3.invoke(obj);//調用有返回值但是沒有參數的方法
            System.out.println(school);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

– 調用指定屬性

注:方法調用前需要通過構造器創建obj對象,此處案例強轉爲Teacher對象,爲之後調用屬性準備!

實際調用分兩種情況討論:

  • 調用公有屬性

    Field f = clazz.getField("屬性名稱");

    f.set(對象名,“傳入參數”); //設置對應屬性值

    f.get(對象名);//獲取對象的對應屬性值

  • 調用私有屬性

    Field f = clazz.getDeclaredField("私有屬性名稱");

    f.setAccessible(true) //非常重要!!!進行解除封裝

    f.set(對象名,“傳入參數”)//設置對應屬性值

    f.get(對象名);//獲取對象的對應屬性值

以上情況案例展示:

注:Teacher類同上

package com.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class Test7 {
    public static void main(String[] args) {
        try {
            Class clazz6 = Class.forName("com.reflection.Teacher");
            //反射創建一個對象
            Constructor con = clazz6.getConstructor();
            Teacher tea = (Teacher)con.newInstance();

            //調用公有屬性
            Field f = clazz6.getField("school");
            f.set(tea,"第三中學");//對tea對象的school屬性設置值 "第三中學"
            String school = (String)f.get(tea); //獲取tea對象的school屬性值
            System.out.println(school);

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

            //調用私有屬性:使用getDeclareField()方法,並解除封裝
            Field f1 = clazz6.getDeclaredField("privateField");
            f1.setAccessible(true);
            f1.set(tea,"測試私有屬性");
            String field = (String)f1.get(tea);
            System.out.println(field);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/*運行結果:
第三中學
----------------
測試私有屬性
 */

七、動態代理

什麼是動態代理?

例:一個Java項目,其中有100個java類,每個java有10個方法,總共1000個方法。現在有一個需求,需要在每個java方法上加上2句話,在方法執行前輸出“這個方法開始執行”,在方法執行後輸出“這個方法已經完成”。此時就需要使用到動態代理!

Proxy :專門完成代理的操作類,是所有動態代理類的父類。通過此類爲一個或多個接口動態地生成實現類。

動態代理步驟:

  1. 創建需要被代理的類以及接口。

  2. 創建一個實現接口InvocationHandler的類,它必須實現invoke方法,以完成代理的具體操作。(ProxyDemo爲實現接口的類,test爲被代理對象),則使用如下語句生成代理對象handler。

    InvocationHandler handler = new ProxyDemo(test);

  3. 通過Proxy的靜態方法,返回一個Subject接口代理。

    newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

  4. 通過Subject代理調用RealSubject實現類的方法。

注意如果一個對象想要通過Proxy.newProxyInstance方法被代理,那麼這個對象的類一定要有相應的接口。

案例展示:

package com.proxy;
//被代理的接口
public interface ITestDemo {
    void test1();
    void test2();
}
package com.proxy;
//實現被代理接口的類
public class TestDemoImpl implements ITestDemo{
    @Override
    public void test1() {
        System.out.println("執行test1()方法");
    }

    @Override
    public void test2() {
        System.out.println("執行test2()方法");
    }
}
package com.proxy;
//實現了InvocationHandler接口的動態代理類
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 動態代理類
 */
public class ProxyDemo implements InvocationHandler {
    Object obj; //被代理的對象

  	//有參構造器
    public ProxyDemo(Object obj){
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()+"方法開始執行");

        Object result = method.invoke(this.obj,args); //執行的是指定代理對象的指定的方法

        System.out.println(method.getName()+"方法執行完成");
        return result; //返回Object對象
    }
}

注意:其中invoke()方法有三個參數:

  1. 動態代理類的引用,通常情況下不需要它。但可以使用getClass()方法,得到proxy的Class類從而取得實例的類信息,如方法列表,annotation等。

  2. 方法對象的引用,代表被動態代理類調用的方法。從中可得到方法名,參數類型,返回類型等等

  3. args對象數組,代表被調用方法的參數。注意基本類型(int,long)會被裝箱成對象類型(Interger, Long)

package com.proxy;
//測試類
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Test1 {
    public static void main(String[] args) {
        //如果一個對象想要通過Proxy.newProxyInstance方法被代理
        //那麼這個對象的類一定要有相應的接口
        //就像本類中ITestDemo接口和實現類TestDemoImpl
        ITestDemo test = new TestDemoImpl();

        test.test1();
        test.test2();
        System.out.println("====================================");
        /**
         * 需求:
         * 在執行test1和test2方法時,需要加入一些東西
         * 在執行方法前打印test1/test2開始執行
         * 在執行方法後打印test1/test2執行完成
         * 打印的方法名要和當時調用的方法保存一致
         */
        InvocationHandler handler = new ProxyDemo(test); //handler爲代理對象,代理test

        //參數1爲代理對象的類加載器
        //參數2爲被代理的對象的接口
        //參數3爲代理對象
        //返回的值是成功被代理後的對象,返回Object類型,需要根據當時的情況去轉換類型
        ITestDemo t = (ITestDemo) Proxy.newProxyInstance(handler.getClass().getClassLoader(),test.getClass().getInterfaces(),handler);
        t.test1();
        System.out.println("====================================");
        t.test2();
    }
}
/*測試結果:
執行test1()方法
執行test2()方法
====================================
test1方法開始執行
執行test1()方法
test1方法執行完成
====================================
test2方法開始執行
執行test2()方法
test2方法執行完成
*/

注意:Proxy.newProxyInstance()方法有三個參數:

  1. 代理對象類加載器(Class Loader)

  2. 被代理對象的接口

  3. InvocationHandler接口實例對象(也稱代理對象)。所有動態代理類的方法調用,都會交由InvocationHandler接口實現類裏的invoke()方法去處理。這是動態代理的關鍵所在。

寫在最後

我以後會經常抱你。

To Dottie!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章