你知道的反射是這樣嗎?(二)

Mr.棟發表於2020-09-14

在上一篇文章,已經講到了反射的一些基本概念以及基本的使用,也介紹到了一些反射中常用的方法以及區別,今天我們再次進入反射的第二期,更進一步的瞭解一下反射。
我們在使用反射去建立一個類的例項的時候一般都是使用:

Class clazz = Test.class;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object obj = constructor.newInstance();

這種寫法稍顯繁雜,我們可以通過另一種便捷的方式去實現:


Class clazz = Test.class;
Test instance1 = clazz.newInstance();

這種方式雖然不直接使用構造器物件,但是這種方式是有缺陷的:
1、如果需要例項化的Test類無參構造方法使用private修飾則會出現java.lang.IllegalAccessException異常,因為沒有許可權訪問。
2、如果我們沒有無參構造方法,因為newInstance()是不需要傳參的,在執行時newInstance()只會尋找公共的、無參的構造方法。

注意:在這裡我們一定要區分newInstance這個方法,這裡的newInstance方法是Class下的一個方法,與Constructor中的newInstance方法是不一樣的。

Method物件的使用
如果我們需要獲取某一個類中的所有Method物件,我們還是需要通過位元組碼物件去操作。比如我們現在需要和獲取到Test2類中的所有Method物件。


public class Test2 {

   private String name;

   private Integer age;

   public String getName() {
    return name;
   }

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

   public Integer getAge() {
    return age;
   }

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

   @Override
   public String toString() {
    return "Test2{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
   }

  private void test(){
    System.out.println("I'm test method");
  }
}

我們在Test2中提供了get、set方法,也重寫了toString方法,並且也建立了一個私有的test方法,然後再通過使用getDeclaredMethods()方法獲取到了Test2中所有的Method物件。


Class clazz = Test2.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
    System.out.println(declaredMethod);
}

但是如果使用getMethods()這個時候我們就不能獲取到私有Method物件了,如果我們需要獲取指定的方法則可以使用getMethod(name)或者getDeclaredMethod(name),至於他們兩個的區別就不再贅述。

那麼又有一個問題出現了,Java是有多型特性的,如果我們有某一個方法進行了方法過載,那麼這個時候我們如果根據方法名稱去獲取不就有問題了嗎?不用急,我們可以通過getMethod(name,paramClassType)或者getDeclaredMethod(name,paramClassType)獲取到我們指定的過載方法。如:


public class Test{
   private void test(){
     System.out.println("I'm test method");
   }

   private void test(String name, Integer age){
    System.out.println("I'm test method:"+name+age+"歲");
   }
}

// 通過此方式便可以獲取到我們指定的過載方法
Method test = clazz.getDeclaredMethod("test", String.class, Integer.class);

獲取到指定的方法後我們要如何使用?
我們一般在使用一個方法的時候都是obj.method(param);但是如果我們需要用反射進行呼叫則需要,method.invoke(obj,param);


Class clazz = Test2.class;
Method test = clazz.getDeclaredMethod("test", String.class, Integer.class);
test.setAccessible(true);
test.invoke(clazz.newInstance(), "zhangsan", 12);

Field物件如何使用呢?

這裡其實與Class、Method物件都是大同小異了,都是同樣的玩法。


Class clazz = Test2.class;
Field field1 = clazz.getDeclaredField("name");
Field field2 = clazz.getDeclaredField("age");

Test2 test2 = clazz.newInstance();
field1.setAccessible(true);
field1.set(test2, "張三");
field2.setAccessible(true);
field2.set(test2, 12);
System.out.println(test2);

至此我們的反射也就已經整體結束了,後續將繼續努力給大家帶來一些易懂的Java知識。如果你有興趣,可以關注我後續將帶來更多精彩的文章。

相關文章