淺談Java的反射機制和作用

發表於 2021-05-03

淺談Java的反射機制和作用

作者:Java大師

歡迎轉載,轉載請註明出處

很多剛學Java反射的同學可能對反射技術一頭霧水,為什麼要學習反射,學習反射有什麼作用,不用反射,通過new也能建立使用者物件。

那麼接下來大師就帶你們瞭解一下反射是什麼,為什麼要學習反射?

下面我們首先通過一個例項來說明反射的好處:

方法1、不用反射技術,建立使用者物件,呼叫sayHello方法

1.1 我們首先建立一個User類

package com.dashi;

/**
 * Author:Java大師
 * User物件,包含使用者的id和姓名以及sayHello方法
 */
public class User {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String sayHello(String who) {
        return who+ "{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

 

1.2 建立測試用例

package com.dashi;

import org.junit.Test;

/**
 * 建立Juinit測試物件
 */
public class Test01 {

    @Test
    public void test01(){
        User user = new User();
        user.setId(1);
        user.setName("Java大師");
        //呼叫sayHello方法
        System.out.println(user.sayHello("user1"));
    }
}

 

1.3執行結果如下,列印出sayHello結果:

user1{id=1, name='Java大師'}

Process finished with exit code 0

方法2、通過反射技術,建立使用者物件,呼叫sayHello方法

2.1 呼叫測試用例

 @Test
    public void test02(){
        try {
            //建立使用者物件字串
            String obj = "com.dashi.User";
            //通過使用者物件字串載入類
            Class clz = Class.forName(obj);
            //通過newInstance方法,建立使用者物件
            User user = (User)clz.newInstance();
            user.setId(2);
            user.setName("Java大師2");
            //呼叫sayHello方法
            System.out.println(user.sayHello("user2"));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

 

2.2 執行結果如下,列印出sayHello結果:

user1{id=1, name='Java大師'}
user2{id=2, name='Java大師2'}

Process finished with exit code 0

 

通過兩者以上對比,發現方法1和方法2都能建立使用者物件,並呼叫sayHello方法,並且列印的結果都正確。但是方法2比方法1先進的地方是方法2針對字串程式設計,方法1針對實體類程式設計。

那麼針對字串程式設計有什麼好處呢,小夥伴們耐心接著往下看:

我們通過一個Dao層來演示下針對字串程式設計的好處:

假設我們有一個IUserDao介面,裡面有一個load方法,程式碼如下:

package com.dashi;

public interface IUserDao {
    public void load();
}

 

有兩個實現類來實現該IUserDao介面,實現類如下:

package com.dashi;

/**
 * A實現類
 */
public class AUserDao implements IUserDao{
    @Override
    public void load() {
        System.out.println("這是AUserDao");
    }
}
package com.dashi;

/**
 * B實現類
 */
public class BUserDao implements IUserDao{
    @Override
    public void load() {
        System.out.println("這是BUserDao");
    }
}

 

方法3、不通過反射技術,建立IUserDao,呼叫load方法

@Test
    public void testDao01(){
        IUserDao userDao = new AUserDao();
        userDao.load();
    }

 

列印結果如下:

這是AUserDao

Process finished with exit code 0

 

方法4、通過反射技術,建立IUserDao,呼叫load方法

@Test
    public void testDao02(){
        try {
            //建立介面實現類字串
            String dao_str = "com.dashi.AUserDao";
            //通過類載入的方式建立IUserDao
            IUserDao userDao = (IUserDao) Class.forName(dao_str).newInstance();
            //呼叫load方法
            userDao.load();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

 

列印結果如下:

這是AUserDao
這是AUserDao

Process finished with exit code 0

 

通過類載入的方式,我們也建立了IUserDao物件,呼叫了load方法,和方法3的執行結果一樣

方法5、通過反射技術,建立IUserDao,呼叫load方法

@Test
    public void testDao03(){
        try {
            //建立介面實現類字串
            String dao_str = "com.dashi.BUserDao";
            //通過類載入的方式建立IUserDao
            Class clz = Class.forName(dao_str);
            IUserDao userDao= (IUserDao)clz.newInstance();
            //建立呼叫方法字串
            String mm = "load";
            //建立method物件
            Method method =  clz.getMethod(mm);
            //呼叫通過反射呼叫invoke方法
            method.invoke(userDao);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

 

執行結果如下:

這是AUserDao
這是AUserDao
這是BUserDao

Process finished with exit code 0

 

通過method.invoke方法也可以實現load方法的呼叫

方法5比方法4和方法3更加靈活,不需要知道AUserDao和BUserDao實體類,只提供類的字串和類的方法名稱,通過反射就可以實現方法的呼叫

實戰中的實際意義

假設我們的Dao層,從mysql遷移導oracle,SQL server等

運用反射技術,通過字串程式設計,那麼我們不需要進行Dao層實體類的更改,只需要改動我們的字串名字就可以進行Dao層的更新。比如:

1、不通過反射技術,我們需要修改實現類中的AUserDao改為BUserDao
IUserDao userDao = new AUserDao();
userDao.load();
``如果有幾百個Dao,我們需要修改幾百次``
``
2、運用發射技術通過字串程式設計,我們可以把字串定義在properties檔案中,通過修改properties檔案中的配置即可實現Dao的更新
 //建立介面實現類字串
 String dao_str = "com.dashi.AUserDao"; //可以改寫為:String dao_str = PropertyUtil.get("dao");
 //通過類載入的方式建立IUserDao
 IUserDao userDao = (IUserDao) Class.forName(dao_str).newInstance();
 //呼叫load方法
 userDao.load();

 

這就是反射技術的實際運用,通過以上例項就可以看出字串程式設計和通過實現類程式設計的最大的區別和實際的意義

並且通過反射技術可以使我們的程式設計更加靈活

靈活運用反射技術,我們可以設計出更加靈活的框架哦~

關注博主公眾號, 每日推送乾貨文章, 回覆「資源」, 即可領取全網最火的Java學習&面試核心資源!~。

淺談Java的反射機制和作用