淺析JAVA反射

廢話不多說,直切進入正題。先來一張反射機制的圖-->

image

認識反射

  • 首先反射我們通過“反”來理解,既然有“反”就一定有“正”,在正常情況下,我們認為一定是先有類再有對象。如下:
    import java.util.Date;//先有類

    public class ReflectTest1 {
    public static void main(String[] args) {
        Date date = new Date();//后有對象
        System.out.println(date);
    }
    }
  • ** 所謂的“反”就是指利用對象找到此對象的出處。在Object類里提供有一個方法:**

    取得Class對象:public final Class<?> getClass()。
    范例:觀察“反”

    import java.util.Date;

    public class ReflectTest2 {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.getClass());
    }
    }
    "class java.util.Date"
     發(fā)現(xiàn)調(diào)用了getClas()方法后的輸出,就輸出了類的完整名稱,等于找到了對象的出處。
  • Class類對象實例化

    java.lang.Class是一個類,這個類是反射操作的源頭,即:所有的反射都要從類開始進行,而最關鍵的是這個類有三種實例化方法:

1.****調(diào)用Object類中的getClass()方法:

    import java.util.Date;

    public class ReflectTest3 {
    public static void main(String[] args) {
        Date date = new Date();
        Class<?> cls =  date.getClass();
        System.out.println(cls);
    }
    }
    "class java.util.Date"

2. 使用“類.class”取得:

    import java.util.Date;

    public class ReflectTest4 {
    public static void main(String[] args) {
        Class<?> cls =  Date.class;
        System.out.println(cls);
    }
    }
    "class java.util.Date"
     之前是在實例化了Date對象之后取得的Class類對象,但是現(xiàn)在并沒有實例化對象的產(chǎn)生。這種方法用在Hibernate、Mybatis、Spring等。

**3. 調(diào)用Class類提供的一個方法--實例化Class對象(在java.lang.Object下****public ****static **

** Class<?> forName(String className) throws ClassNotFoundException):**

    public class ReflectTest5 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> cls =  Class.forName("java.util.Date");
        System.out.println(cls);
    }
    }
    "class java.util.Date"
      此時可以不使用import語句導入一個明確的類,而類的名稱是采用字符串的形式進行描述的。

反射實例化對象

     當拿到一個類的時候,肯定要直接使用關鍵字new 進行對象實例化操作這屬于我們習慣性的做法。如果拿到Class類對象,那么就可以做到利用反射來實例化對象操作:

public T newInstance() throws InstantiationException,IllegalAccessException

范例: 利用反射來實例化對象

    /*正常實例化*/
    class Book{
    public Book(){
        System.out.println("*** Book的構(gòu)造方法 ***");
    }
    @Override
    public String toString() {
        return "--- 這是Book方法 ---";
    }
    }
    public class ReflectTest6 {
    public static void main(String[] args) throws ClassNotFoundException {
        Book b = new Book();
        System.out.println(b);
    }
    }
    "*** Book的構(gòu)造方法 ***
     --- 這是Book方法 ---"復制代碼
    package com.jkx.lzh.test;    
    /*反射實例化*/
    class Book{
    public Book(){
        System.out.println("*** Book的構(gòu)造方法 ***");
    }
    @Override
    public String toString() {
        return "--- 這是Book方法 ---";
    }
    }
    public class ReflectTest7 {
    public static void main(String[] args) throws InstantiationException, 
            IllegalAccessException, ClassNotFoundException{
        Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
        Object obj = cls.newInstance();
    }
    }
    "*** Book的構(gòu)造方法 ***"復制代碼
     class Book{
    public Book(){
        System.out.println("*** Book的構(gòu)造方法 ***");
    }
    @Override
    public String toString() {
        return "--- 這是Book方法 ---";
    }
    }
    public class ReflectTest8 {
    public static void main(String[] args) throws InstantiationException, 
            IllegalAccessException, ClassNotFoundException{
        Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
        Object obj = cls.newInstance();
        Book b = (Book) obj;
        System.out.println(b);
    }
    }
"*** Book的構(gòu)造方法 ***
     --- 這是Book方法 ---"復制代碼
    有了反射之后,以后進行對象化實例化的操作不在只是單獨的依靠關鍵字new完成了,反射也同樣可以,但是這并不表示new就被完全取代了。

    這樣有人就會說:“下面兩行代碼相當于:Book b = new  Book();這樣不是更復雜了,么“。
    Class<?> cls = Class.forName("com.jkx.lzh.test.Book");     
    Object obj = cls.newInstance();
     PS: 在任何的開發(fā)之中,new是造成耦合的最大元兇。一切的耦合都起源于new。

    ** 范例:**觀察工廠模式-->
    interface Fruit{
    void eat();
    }
    class Apple implements Fruit{
    @Override
    public void eat() {
        System.out.println("* 吃蘋果 *");
    }
    }
    class Factory{
    public static Fruit getInstance(String className){
        if("apple" == className){
        return new Apple();
        }
        return null;
    }
    }
    public class TestFactory {
    public static void main(String[] args) {
        Fruit f = Factory.getInstance("apple");
        f.eat();
    }
    }
    "* 吃蘋果 *"
     代碼如上,但是此時,如果我們要增加一個Fruit接口子類“orange”,就意味著我們就要修改工廠模式的方法。
    class Orange implements Fruit{
    @Override
    public void eat() {
        System.out.println("* 吃橘子 *");
    }
    }
    class Factory{
    public static Fruit getInstance(String className){
        if("apple" == className){
        return new Apple();
        }else if("orange" == className){
        return new Orange();    
            }
        return null;
    }
    }
      由此可見,每增加一個Fruit接口子類,就要修改工廠類,那么如果隨時需要增加子類呢?

      因為現(xiàn)在工廠類都是new關鍵字直接實例化的,所以new就造成了所有問題的關鍵點。要想解決這一問題,就只能依靠反射完成。

修改工廠模式的方法如下:

    package com.jkx.lzh.test;

    interface Fruit{
    void eat();
    }
    class Apple implements Fruit{
    @Override
    public void eat() {
        System.out.println("* 吃蘋果 *");
    }
    }
    class Orange implements Fruit{
    @Override
    public void eat() {
        System.out.println("* 吃橘子 *");
    }
    }
    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 class TestFactory {
    public static void main(String[] args) {
        Fruit f = Factory.getInstance("com.jkx.lzh.test.Apple");
        f.eat();
    }
    }
    此時的程序就真正的完成了解耦合的目的,而且可擴展性非常強。

使用反射調(diào)用構(gòu)造

    在之前所編寫的代碼中,我們都默認調(diào)用了類中的無參構(gòu)造方法??墒穷愔幸灿锌赡懿惶峁o參構(gòu)造方法。

范例:觀察當前程序的問題

Book.java

    package com.jkx.lzh.po;

    public class Book {
    private String title;
    private double price;
    public Book(String title,double price){
        this.title = title;
        this.price = price;
    }
    public String toString(String title,double price) {
        return "書名:"+ this.title + "價格: " + this.price ;
    }
    }

ReflectTest9.java

    package com.jkx.lzh.test;

    public class ReflectTest9 {
    public static void main(String[] args) throws InstantiationException, 
            IllegalAccessException, ClassNotFoundException{
        Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
        Object obj = cls.newInstance();
        System.out.println(obj);
    }
    }

以上代碼由于Book沒有無參構(gòu)造方法(<init>()),所以拋出以下異常:

Exception in thread "main" java.lang.InstantiationException: com.jkx.lzh.po.Book
    at java.lang.Class.newInstance(Class.java:427)
    at com.jkx.lzh.test.ReflectTest1.main(ReflectTest1.java:7)
Caused by: java.lang.NoSuchMethodException: com.jkx.lzh.po.Book.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.newInstance(Class.java:412)
    ... 1 more

那么怎么解決這個問題呢?

在Class類中提供了這么一個構(gòu)造方法可以取得構(gòu)造:

  1. 取得全部構(gòu)造方法:public Constructor<?>[] getConstructors() throws SecurityException;
  2. 取得一個指定參數(shù)順序的構(gòu)造:public Constructor<T>(Class<?> ... ParameterTypes) throws NoSuchMethodException,SecurityException;

以上兩個方法返回都是“java.lang.reflect.Constructor”類的對象。在這個類中我們提供一個明確傳遞有參構(gòu)造內(nèi)容實例化對象方法:

public T newInstance(Object... initargs) throws InstantiationException,IllegalAccessException,IllegalAccessException,IllegalArgumentException,InvocationTargetException;

范例:明確調(diào)用類中的構(gòu)造方法

ReflectTest9.java修改如下:

    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;

    public class ReflectTest9 {
    public static void main(String[] args) throws InstantiationException, 
            IllegalAccessException, ClassNotFoundException, NoSuchMethodException, 
            SecurityException, IllegalArgumentException, InvocationTargetException{

                Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
            //public Book(String title,double price);Constructor傳的是有參構(gòu)造的參數(shù)類型
            Constructor<?> con = cls.getConstructor(String.class,double.class);
            Object obj = con.newInstance("JAVA 反射,",82.22);
            System.out.println(obj);
    }
    }
    "書名:JAVA 反射價格: 82.22"
     以上給我一個警示:在我們的簡單java類的開發(fā)中不管提供多少構(gòu)造方法,我們至少要保留無參構(gòu)造方法。

反射調(diào)用方法

** 類中普通方法只有在這個類實例化對象之后才能被調(diào)用。并且實例化方法有三種(new,conle,反射)**

范例:定義一個類

    public class Book {
    private String title;
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    }

這個類有無參構(gòu)造方法,所以實例化對象的時候可以直接利用Class提供的newInstance方法

在Class類里面提供有以下取得類中Method()的操作:

1. 取得一個類中的全部方法:
public Method[] getMethods() throws SecurityException;

2. 取得類中指定方法:
public Method getMethod(String name,Class<?> ... ParameterTypes) throws NoSuchMethodException,SecurityException;

以上兩個操作返回的是“java.lang.reflect.Method”類對象,在這里類里面我們重點關注一個方法:調(diào)用方法-->

public Object invoke(Object obj,Object args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException;

范例:反射調(diào)用方法

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

    public class ReflectTest10 {
    public static void main(String[] args) throws InstantiationException, 
            IllegalAccessException, ClassNotFoundException, NoSuchMethodException, 
            SecurityException, IllegalArgumentException, InvocationTargetException{
        String fileName = "title";//要操作的成員
        Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
        Object obj = cls.newInstance();//必須實例化對象
        Method setMet = cls.getMethod("set"+ inticap(fileName) , String.class);
        Method getMet = cls.getMethod("get"+ inticap(fileName));
        setMet.invoke(obj, "JAVA 反射");
        System.out.println(getMet.invoke(obj));
    }
    public static String inticap(String str){
        return str.substring(0,1).toUpperCase() + str.substring(1);
    }
    }
"JAVA 反射"復制代碼

此時我們完全看不到操作類型,也就是說利用反射可以實現(xiàn)任意類的指定方法調(diào)用。

反射調(diào)用成員

  • 類中屬性一定要在本類實例化對象產(chǎn)生之后才可以分配內(nèi)存空間。在Class類中提供了取得成員的方法:

    1. 取得全部成員:public Filed[] getDeclaredFileds() throws SecurityException;

    1. 取得指定成員:public Filed getDeclaredFiled(String name) throws NoSuchMethodException,SecurityException;
  • 返回的類型是“java.lang.reflect.Filed”類,在這個類里面有兩個重要的方法:

    1. 取得屬性的內(nèi)容:public Object getObject(Object obj) throws IllegalAccessException,IllegalArgumentException

    2. 設置屬性的內(nèi)容:public void Object getObject(Object obj,Object values) throws IllegalAccessException,IllegalArgumentException

  • 在“java.lang.reflect.AccessibleObject”類下面(在JDK1.8之后修改):

    1. Executable下面繼承了Constructor,Method;

    2. Filed: 在這個類中提供有一個方法:public void setAccessible(boolean flag) throws SecurityException;設置是否取消封裝

范例:現(xiàn)在提供如下類

    package com.jkx.lzh.po;

    public class Book {
        private String title;
    }

這個類中定義了一個私有屬性,按照我們原始的做法,此時它一定無法被外部所使用。

范例: 但是使用setAccessible取消封裝就可以使用了

import java.lang.reflect.Field;

public class ReflectTest11 {
    public static void main(String[] args) throws NoSuchFieldException, 
            SecurityException, ClassNotFoundException, 
            InstantiationException, IllegalAccessException {
        Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
        Object obj = cls.newInstance();//必須實例化對象
        Field titleField = cls.getDeclaredField("title");
        titleField.setAccessible(true);
        titleField.set(obj, "JAVA 反射");//相當于:Book對象.title = "JAVA 反射";
        System.out.println(titleField.get(obj));//相當于:Book對象.title;
    }
} 
"JAVA 反射"

構(gòu)造方法和普通方法同樣可以取消,只不過我們很少這樣去做,而且對于屬性的訪問還是建議使用settr、gettr

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 導語 反射機制是Java程序開發(fā)語言的重要特征之一。 主要內(nèi)容 認識反射 理解反射作用 利用反射來調(diào)用類的結(jié)構(gòu) 具...
    一個有故事的程序員閱讀 721評論 2 5
  • 反射機制的功能 Java反射機制主要提供了以下功能:在運行時判斷任意一個對象所屬的類;在運行時構(gòu)造任意一個類的對象...
    Anderson大碼渣閱讀 2,966評論 1 43
  • 學習Android的同學注意了?。?!學習過程中遇到什么問題或者想獲取學習資源的話,歡迎加入Android學習交流群...
    kingZXY2009閱讀 355評論 0 0
  • 對于之前的程序?qū)嶋H上采用的模式是“程序類+配置文件”,但這種也是需要手工維護配置文件,所以現(xiàn)在考慮另一種方式。 C...
    tingtingtina閱讀 286評論 0 0
  • 表情是什么,我認為表情就是表現(xiàn)出來的情緒。表情可以傳達很多信息。高興了當然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,666評論 2 7

友情鏈接更多精彩內(nèi)容