java筆記--反射進階之總結與詳解


一.反射進階之動態設置類的私有域

 

"封裝"是Java的三大特性之一,為了能更好保證其封裝性,我們往往需要將域設置成私有的,

然后通過提供相對應的set和get方法來操作這個域。但是我們仍然可以用java的反射機制來

修改類的私有域,由於修改類的私有域會破壞Java"封裝"的特性,故請慎重操作。

主要技術:
    Field類提供有關類或接口的單個字段的信息,以及對它的動態訪問權限。
    訪問的字段可能是一個類(靜態)字段或實例字段。
   
        常用方法:
        set(Object obj,Object value)-----: 將指定對象變量上此Field對象表示的字段設置為指定的新值
        setBoolean(Object obj,boolean z)-: 將字段使得設置為指定對象上的一個boolean值
        setDouble(Object obj,double d)---: 將字段的值設置為指定對象上的一個double值
        setInt(Object obj,int i)---------: 將字段的值設置為指定對象上的一個int值
        setAccessible(boolean flag)------: 將此對象的accessible標志設置為指定的布爾值
       
       注:對於私有域,外部類調用的時候一定要使用setAccessible()方法將其可見性設置為true才能設置新值,
            否則將會拋出異。

--支持知識共享,轉載請標注地址"http://www.cnblogs.com/XHJT/p/3922160.html "——和佑博客園,謝謝~~-- 

實例代碼:

package com.xhj.reflection_excetion;

import java.lang.reflect.Field;

/**
 * 動態修改類的私有域
 * 
 * @author XIEHEJUN
 * 
 */
public class DynamicChangePrivate {
    private String userName;
    private int userAge;
    private String userAddress;
    private boolean userGender;

    public DynamicChangePrivate(String userName, int userAge,
            String userAddress, boolean userGender) {
        super();
        this.userName = userName;
        this.userAge = userAge;
        this.userAddress = userAddress;
        this.userGender = userGender;
    }

    public DynamicChangePrivate() {
        super();
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getUserAge() {
        return userAge;
    }

    public void setUserAge(int userAge) {
        this.userAge = userAge;
    }

    public String getUserAddress() {
        return userAddress;
    }

    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }

    public boolean isUserGender() {
        return userGender;
    }

    public void setUserGender(boolean userGender) {
        this.userGender = userGender;
    }

    /**
     * 根據獲得的性別的真假,獲取其String值: true--為男 false--為女
     * 
     * @param userGender
     * @return
     */
    public String getGender(boolean userGender) {
        if (userGender) {
            return "男";
        } else {
            return "女";
        }
    }

    public static void main(String[] args) {
        DynamicChangePrivate user = new DynamicChangePrivate("小黑", 21,
                "北京路華西小區3單元446", true);
        Class<?> clazz = user.getClass();
        System.out.println("通過反射取得的對象全稱為:" + clazz.getName());
        try {

            // 獲取要修改的類的字段名稱
            Field userName = clazz.getDeclaredField("userName");
            Field userAge = clazz.getDeclaredField("userAge");
            Field userAddress = clazz.getDeclaredField("userAddress");
            Field userGender = clazz.getDeclaredField("userGender");

            // 修改並輸出新舊名稱
            System.out.print("原名稱為:" + user.getUserName());
            userName.set(user, "曉曉");
            System.out.println("\t\t\t修改后的名稱為:" + user.getUserName());

            // 修改並輸出新舊年齡
            System.out.print("原年齡為:" + user.getUserAge());
            userAge.set(user, 24);
            System.out.println("\t\t\t修改后的年齡為:" + user.getUserAge());

            // 修改並輸出新舊地址
            System.out.print("原地址為:" + user.getUserAddress());
            userAddress.set(user, "石景山八角南里3單元506");
            System.out.println("\t修改后的地址為:" + user.getUserAddress());

            // 修改並輸出新舊性別
            System.out.print("原性別為:" + user.getGender(user.isUserGender()));
            userGender.set(user, false);
            System.out.println("\t\t\t修改后的性別為:"
                    + user.getGender(user.isUserGender()));

        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

}

結果為:

image

 

 

注:任何類型的域,都可以通過set(Object obj, Object value) 方法將指定對象變量上此 Field 對象表示的字段設置為指定的新值。
但是Field本身也提供了其他類型相對應的set方法,如 setBoolean(Object obj, boolean z),setDouble(Object obj, double d)

等。另外,通過Field也可以設置public.protected域,但一般情況下很少這么設置,尤其是public域。在這里特別要注意的是:一定

要明確修改的含義,不要輕易的通過反射來修改類的私有域,因為這破壞了java面向對象"封裝"的特性。

 

二、反射進階之動態調用類的方法

 

我們知道Java是一種面向對象的語言,對他而言,一切都是對象,因此要調用類的方法,只能通過建立類的對象來調用。當然如果是
靜態的方法,那就可以直接通過類本身來調用,而不需要建立類的對象。那么還有沒有其他可以調用類方法的方式呢??

 

在Java的反射的機制中,提供了比較另類的調用方式,既可以根據需要指定要調用的方法,而不必在編程時確定。調用的方法不僅權限於public
的,還可以是private的。

 

Method類提供類或接口上單獨某個方法(以及如何訪問該方法)的信息,所反映的方法可能是類方法或實例方法(包括抽象方法)。它允許在匹配
要調用的實參與底層方法的形參時進行擴展轉換,但是如果要進行收縮轉換,則會拋出"非法參數異常"--IllegalArgumentExcetion。使用invoke()
可以實現動態調用方法:
   public Object invoke(Object obj,Object...args)throws IllegalArgumentException,IllegalAccessException,InvocationTargetExcetion
    obj--要調用的方法的類對象
    args--方法調用的參數

   
   注:對於私有域,外部類調用的時候一定要使用setAccessible,並且設置為true
   
實例代碼:

package com.xhj.reflection_excetion;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 實現動態調用類的方法
 * 
 * @author XIEHEJUN
 * 
 */
public class DynamicCallMethod {

    public static void main(String[] args) {
        try {
            // 運行時動態調用Math的abs()靜態方法
            System.out.println("運行時動態調用Math的abs()靜態方法");
            Method meth = Math.class.getDeclaredMethod("abs", Integer.TYPE);
            Integer a = (Integer) meth.invoke(null, new Integer(-1));
            System.out.println("-1的絕對值為:" + a);

            // 運行時動態調用String的非靜態方法contains()
            System.out.println("運行時動態調用String的非靜態方法contains()");
            Method strMeth = String.class.getDeclaredMethod("contains",
                    CharSequence.class);
            boolean str = (Boolean) strMeth.invoke(new String("xhjit"), "xhj");
            System.out.println("xhjit中是否包含有xhj——" + str);

        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

結果為:

image

 

三、反射進階之動態實例化類

 

在Java中,要實例化一個類即創建一個類的對象,需要通過構造方法來實現。但是在Java中還可以使用另外一種方式來實現,

那就是通過反射來實例化一個類。
   
    Constructor是Java中提供類的單個構造方法的信息以及訪問權限的封裝類。
    它允許在將實參與帶有底層構造方法的形參的newInstance()匹配時進行擴展轉換,
    但是如果發生收縮轉換,則拋出IllegalArgumentExcetion。newInstance()方法可以
    使用指定的參數來創建對象:
   public T newInstance(Object...initargs)throws InstantiationException,IllegalAccessException,

                            IllegalArgumentException,InvocationTargetException
       
    initargs: 將作為變量傳遞給構造方法調用的對象數組。
   
    注:對於私有域,外部類調用的時候一定要使用setAccessible,並且設置為true。
   
代碼實例為:

package com.xhj.reflection_excetion;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 動態實例化類
 * 
 * @author XIEHEJUN
 * 
 */
public class DynamiCreateClass {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        try {
            // 動態建立一個String對象,
            Constructor<?> construtor = String.class
                    .getConstructor(String.class);
            String str = (String) construtor.newInstance("000123");
            System.out.println(Integer.parseInt(str));

            // 動態建立一個txt文件,並將上面初始化后的String值寫入文件的當中
            construtor = File.class.getConstructor(String.class);
            String url = "C:\\Users\\XIEHEJUN\\Desktop\\XHj.txt";
            File file = (File) construtor
                    .newInstance(url);
            file.createNewFile();
            if (file.exists()) {
                str += "---文件創建成功";
                System.out.println(str);
                OutputStream os = new FileOutputStream(url);
                os.write(str.getBytes());
                os.close();
            } else {
                System.out.println("創建文件失敗");
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

結果為:

控制台輸出

image

生成的文件:

image

注:在java中有兩種不用new就可以建立類對象的方法,即Class.newInstance()Constructor.newInstance();
       區別在於:

                  前者只能調用無參構造方法,而后者卻能調用有參構造方法;
                  且前者需要被調用的構造方法可見,后者則在特定情況下運行調用不可見的構造方法

 

四、反射進階之自定義可變數組工具類 

 

在Java中,要創建可變數組可通過ArraryList類來實現。除此之外,我們也可以用自定義的方法來實現可變數組。
這里,我們將使用Java的反射機制實現一個工具類,通過這個工具類,我們就能實現可變數組的創建。

 

主要技術:


    Array類提供了動態創建和訪問Java數組的方法。它允許在執行get,set操作期間進行擴展轉換,但若發生收縮
    轉換將拋出IllegalArgumentExcetion。為了創建新的數組對象,需要使用newInstance()方法,它可以根據指定
    的元素類型和長度創建新的數組:


   public static Object newInstance(Class<?> componentType,int length)throws NegativeArraySizeException


        componentType: 表示新數組的組件類型的Class對象
        length: 新數組的長度

 

     注:Java 中的數組不管是幾維的,都是Object類型的s

 

代碼實例:
可變數組工具類的實現:

package com.xhj.reflection_excetion;

import java.lang.reflect.Array;

/**
 * 用反射機制實現可變數組工具類
 * 
 * @author XIEHEJUN
 * 
 */
public class VariableArrayUtil {
    /**
     * 增加的數組長度值
     */
    private int addLength;
    /**
     * 需要增加長度的原數組
     */
    private Object array;

    public int getaddLength() {
        return addLength;
    }

    public Object getArray() {
        return array;
    }

    /**
     * 可變數組初始化
     * @param addLength 需要增加的數組的長度
     * @param array 需要增加長度的原數組
     */
    public VariableArrayUtil(int addLength, Object array) {
        super();
        this.addLength = addLength;
        this.array = array;
    }

    /**
     * 可變數組的實現
     * 
     * @return
     */
    public Object newArrary() {
        Class<?> clazz = array.getClass();
        if (clazz.isArray()) {
            Class<?> type = clazz.getComponentType();
            int length = Array.getLength(array);
            Object new_Array = Array.newInstance(type, length + addLength);
            System.arraycopy(array, 0, new_Array, 0, length);
            return new_Array;
        }
        return null;
    }

}

測試類:

package com.xhj.test;

import com.xhj.reflection_excetion.VariableArrayUtil;

/**
 * 可變數組工具類的測試類
 * 
 * @author XIEHEJUN
 * 
 */
public class Test {

    public static void main(String[] args) {
        int[] a = new int[10];
        System.out.println("原數組為:");
        for (int i = 0; i < 10; i++) {
            a[i] = i;
            System.out.print("  " + a[i]);
        }
        System.out.println("\n數組長度為:" + a.length);
        VariableArrayUtil util = new VariableArrayUtil(5, (Object) a);
        int[] b = (int[]) util.newArrary();
        System.out
                .println("==================================================\n"
                        + "更改后的數組長度為:" + b.length);
        for (int i = 10; i < 15; i++) {
            b[i] = i;
        }
        System.out.println("更改后的數組為:");
        for (int i : b) {
            System.out.print("  " + i);
        }
    }
    
}

結果為:

image

 

 

五、反射進階之重寫toString方法


為了方便輸出對象,在Object中定義了toString的方法,其默認值由類名和哈希碼組成。但是很多時候,為了能更好的滿足我們的需求,

我們都是需要重寫這個方法的。下面我們將利用Java的反射機制,重寫這個方法,並輸出類的相關信息。

 

代碼實例:

package com.xhj.reflection_excetion;

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

/**
 * 利用反射重寫Object的toString方法
 * 
 * @author XIEHEJUN
 * 
 */
public class RewriteToString {
    public String toString(Object obj) {
        Class<?> clazz = obj.getClass();
        // 建立一個容器用來存儲類的信息
        StringBuilder strBuilder = new StringBuilder();
        strBuilder.append("以下是類的信息:");
        // 類名
        String className = clazz.getSimpleName();
        // 包名
        Package packageName = clazz.getPackage();
        // 公共構造方法
        Constructor<?>[] constructors = clazz.getConstructors();
        // 公共域
        Field[] fields = clazz.getFields();
        // 接口
        Type[] interfaces = clazz.getInterfaces();
        // 公共方法
        Method[] methods = clazz.getMethods();

        strBuilder.append("\n此類的簡單名稱為--" + className);

        strBuilder.append("\n此類的包名為--" + packageName);

        strBuilder.append("\n此類的公共構造方法有:");
        if (constructors.length > 0) {
            for (Constructor<?> constructor : constructors) {
                strBuilder.append("\n\t" + constructor);
            }
        } else {
            strBuilder.append("空");
        }

        strBuilder.append("\n此類的公共域有:");
        if (fields.length > 0) {
            for (Field field : fields) {
                strBuilder.append("\n\t" + field);
            }
        } else {
            strBuilder.append("空");
        }

        strBuilder.append("\n此類的接口有:");
        if (fields.length > 0) {
            for (Type type : interfaces) {
                strBuilder.append("\n\t" + type);
            }
        } else {
            strBuilder.append("空");
        }

        strBuilder.append("\n此類的公共方法有:");
        if (methods.length > 0) {
            for (Method method : methods) {
                strBuilder.append("\n\t" + method);
            }
        } else {
            strBuilder.append("空");
        }
        return strBuilder.toString();
    }

    public static void main(String[] args) {
        RewriteToString rts = new RewriteToString();
        System.out.println(rts.toString(new StringBuilder()));
    }

}

結果為:

image

 

注:在實際開發當中,一般而言都是需要重寫toString方法的,為了更好的開發,可使用Commons Lang 組件提供的工具類來重寫此方法。

 

六、反射進階之動態代理


   代理是Java中很重要的一個特性,使用代理可以在程序運行時創建一個實現指定接口的新類。一般而言,我們只有在程序編譯時

    無法確定需要使用哪些接口時才會使用代理機制。代理更多的是用在系統的開發上,它可以為工具類提供更加靈活的特性。

 

    InvocationHandle 接口是代理實例的調用處理程序實現的接口。每個代理實例都具有一個關聯的調用處理程序。

   對代理實例調用方法時,將對方法調用進行編碼並將其指派到它的調用處理程序的invoke()方法:


    Object invoke(Object proxy,Method method,Object[]args)throws Throwable
    proxy:代理類
    method: 代理實例上要被調用的方法
    args: 代理實例上方法調用的參數數組

 

    Proxy接口提供了用於創建動態代理類和實例的靜態方法,它是所有動態代理類的父類。


    獲得一個指定接口的代理類實例:
   public static Object newProxyInstance(ClassLoader loader,Class<?> [] interfaces,InvocationHandle h)throws IllegalArgumentException
    loader:定義代理類的類加載器
    interfaces:代理類要實現的接口列表
    h:指派方法調用的代理處理程序

 

代碼實例:

接口:

package com.xhj.reflection_excetion.dynamicProxy.bean;

/**
 * 代理類和被代理類的共同接口
 * 
 * @author XIEHEJUN
 * 
 */
public interface DoBusiness {
    /**
     * 商品交易方式
     */
    public void trading();
}

被代理類:

package com.xhj.reflection_excetion.dynamicProxy.service;

import com.xhj.reflection_excetion.dynamicProxy.bean.DoBusiness;

/**
 * 被代理類--廠家
 * 
 * @author XIEHEJUN
 * 
 */
public class Product implements DoBusiness {

    @Override
    public void trading() {
        System.out.println("廠家直銷");
    }

}

代理處理器:

package com.xhj.reflection_excetion.dynamicProxy.proxy;

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

/**
 * 代理處理器--商家代理
 * 
 * @author XIEHEJUN
 * 
 */
public class DynamicProxy implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("商家代理定點銷售");
        return null;
    }
}

測試類:

package com.xhj.reflection_excetion.dynamicProxy;

import java.lang.reflect.Proxy;

import com.xhj.reflection_excetion.dynamicProxy.bean.DoBusiness;
import com.xhj.reflection_excetion.dynamicProxy.proxy.DynamicProxy;
import com.xhj.reflection_excetion.dynamicProxy.service.Product;

public class Test {

    public static void main(String[] args) {
        DoBusiness p = new Product();
        System.out.print("沒有啟用代理模式---");
        p.trading();
        ClassLoader loader = p.getClass().getClassLoader();
        p = (DoBusiness) Proxy.newProxyInstance(loader,
                Product.class.getInterfaces(), new DynamicProxy());
        System.out.print("啟用動態代理模式---");
        p.trading();
    }

}

結果為:

image

 

注:
java動態代理類位於java.lang.reflect包下,一般主要涉及到以下兩個類:

Interface InvocationHandler:該接口中僅定義了一個方法Object:invoke(Object obj,Method method, Object[] args)。
                                            在實際使用時,第一個參數obj一般是指代理類,method是被代理的方法,如上例中的trading(),

                                            args為該方法的參數數組。這個抽象方法在代理類(代理處理類)中動態實現。
Proxy:該類即為動態代理類。
    Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):
        返回代理類的一個實例,返回后的代理類可以當作被代理類使用 (可使用被代理類的在接口中聲明過的方法)。

 

 

總結:
  動態代理類是一個實現在創建類並運行時指定接口列表的類,
    1.代理接口是代理類所實現的一個接口。
    2.代理實例是代理類的一個實例。
    3.每一個代理實例都有一個關聯的調用處理程序對象,它可以實現接口InvocationHandler。
    4.通過調用代理處理器中的Invoke方法實現代理,並傳遞代理實例,識別調用方法以及方法上的參數數組。
    5.調用對象加載器以及代理處理器中的方法,並以代理實例為結果返回。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM