反射

Prerequisites:

img

Classloader文章已經說明Class類別載入的過程。

反射英文是Reflection

Class

Class知道類別的所有屬性、方法,透過Class類別可以取得屬性、方法、建構子。

測試的程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Duck extends Animal implements Fly, Swim{
  public String name;
  private String info;

  static {
    System.out.println("Duck 被classloader載入");
  }

  public Duck() {
    System.out.println("物件被jvm建立");
  }

  private Duck(String name) {
    this.name = name;
  }

  @Override
  public void eat() {
    System.out.println("鴨子吃");
  }

  @Override
  public void fly() {
    System.out.println("鴨子飛");
  }

  @Override
  public void swim() {
    System.out.println("鴨子游泳");
  }

  public String getName() {
    return name;
  }

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

  public String getInfo() {
    return info;
  }

  public void setInfo(String info) {
    this.info = info;
  }
}

Class.forName

forName(““)的參數是package名字.類別名組成,用點來區隔。

執行結果會被classLoader載入

1
2
Class class1 = Class.forName("reflect.Duck");
System.out.println(class1);
Duck 被classloader載入
class reflect.Duck

類別名.class

執行結果「不會」被classLoader載入

1
2
Class class2 = Duck.class;
System.out.println(class2);
class reflect.Duck

getClass()

執行結果會被classLoader載入

1
2
3
Duck duck = new Duck();
Class class3 = duck.getClass();
System.out.println(class3);
Duck 被classloader載入
物件被jvm建立
class reflect.Duck

取得父類別

1
2
3
4
5
6
7
public class Test {
  public static void main(String[] args) throws Exception {
    Class class1 = Duck.class;
    Class superclass = class1.getSuperclass();
    System.out.println(superclass);
  }
}
class reflect.Animal

取得介面

1
2
3
4
5
6
7
8
9
public class Test {
  public static void main(String[] args) throws Exception {
    Class class1 = Duck.class;
    Class[] interfaces = class1.getInterfaces();
    for (Class cls : interfaces) {
      System.out.println(cls);
    }
  }
}
interface reflect.Fly
interface reflect.Swim

取得public屬性

1
2
3
4
5
  Class class1 = Duck.class;
  Field[] fields = class1.getFields();
  for (Field field : fields) {
    System.out.println(field);
  }
public java.lang.String reflect.Duck.name

取得所有屬性

1
2
3
4
  Field[] dfields = class1.getDeclaredFields();
  for (Field field : dfields) {
    System.out.println(field);
  }
public java.lang.String reflect.Duck.name
private java.lang.String reflect.Duck.info

取得public方法與所有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Test {
  public static void main(String[] args) throws Exception {
    Class class1 = Duck.class;
    // getMethods只有public的方法
    Method[] methods = class1.getMethods();
    for (Method method : methods) {
      System.out.println(method);
    }

    // getDeclaredMethods() 包含所有public,private方法
    Method[] dmethods = class1.getDeclaredMethods();
    for (Method method : dmethods) {
      System.out.println(method);
    }
  }
}
public java.lang.String reflect.Duck.getInfo()
public void reflect.Duck.eat()
public void reflect.Duck.fly()
public void reflect.Duck.swim()
public void reflect.Duck.setInfo(java.lang.String)

取得父類別所有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Test3 {
  public static void main(String[] args) {
    Class<?> clazz = Dog.class;
    System.out.println("Methods in class: " + clazz.getName());
    for (Method method : clazz.getDeclaredMethods()) {
      System.out.println(method);
    }

    // parent
    Class<?> superclass = clazz.getSuperclass();
    System.out.println("Methods in Parent: " + clazz.getName());
    for (Method method : superclass.getDeclaredMethods()) {
      System.out.println(method);
    }
  }
}

class Animal {
  int i = 10;  // 父類欄位
  void speak() { System.out.println(i); }  // 印出 this.i
}

class Dog extends Animal {
  int i = 5;  // 子類同名欄位
  //void speak() { System.out.println(i); }
}
Methods in class: wrap.Dog
Methods in Parent: wrap.Dog
void wrap.Animal.speak()

將以上取得父類別方法改善如下,方便以後做測試,只要輸入「package.類別名」。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
  public static void main(String[] args) throws ClassNotFoundException {
    // 參數是「package.類別名」
    getClzMethod(Class.forName("inherit.A"));
  }

  public static void getClzMethod(Class<?> clz) {
    Class<?> clazz = clz;
    System.out.println("Methods in class: " + clazz.getName());
    for (Method method : clazz.getDeclaredMethods()) {
      System.out.println(method);
    }
    System.out.println("------------------");
    // parent
    Class<?> superclass = clazz.getSuperclass();
    System.out.println("Methods in Parent: " + clazz.getName());
    for (Method method : superclass.getDeclaredMethods()) {
      System.out.println(method);
    }
  }
}

取得public建構子與private建構子

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
  public static void main(String[] args) throws Exception {
    Class class1 = Duck.class;
    Constructor[] constructors = class1.getConstructors();

    // getDeclaredConstructors() 包含所有public,private
    Constructor[] dconstructors = class1.getDeclaredConstructors();
    for (Constructor constructor : dconstructors) {
      System.out.println(constructor);
    }
  }
}
public reflect.Duck()
private reflect.Duck(java.lang.String)

使用private建構子建立物件並呼叫私有方法

先把public建構子

1
2
3
  public Duck() {
    System.out.println("物件被jvm建立");
  }

改成

1
2
3
  private Duck() {
    System.out.println("物件被jvm建立");
  }

增加private 方法

1
2
3
  private void getAddress() {
    System.out.println("private getAddress() test");
  }

呼叫private建構子與private方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws Exception {
  Class class1 = Duck.class;
  // 呼叫private建構子建立物件
  Constructor dc = class1.getDeclaredConstructor();
  // 把private改成public
  dc.setAccessible(true);
  Object instance = dc.newInstance();
  // 呼叫私有方法
  Method dm1 = class1.getDeclaredMethod("getAddress");
  // 設置成public
  dm1.setAccessible(true);
  // 參數代入instance
  dm1.invoke(instance);
}
Duck 被classloader載入
物件被jvm建立
private getAddress() test

使用public建構子與參數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.lang.reflect.Method;
import java.util.Scanner;
public class Test {
  public static void main(String[] args) throws Exception {
    Scanner  sc= new Scanner(System.in);
    System.out.println("輸入package.類別");
    String strName = sc.next();
    System.out.println("請輸入一個數");
    int a = sc.nextInt();
    System.out.println("請輸入另一個數");
    int b = sc.nextInt();
    Class class1 = Class.forName(strName);
    // 呼叫public建構子,建立物件
    Object ins = class1.newInstance();
    // 取出方法與設定參數類型
    Method md = class1.getDeclaredMethod("sum", int.class, int.class);
    // 呼叫方法,第1個參數是物件,a與b是方法參數
    md.invoke(ins, a, b);
  }
}

編譯,並執行

在另一個Project,跟上一個程式碼同一個package下寫以下程式碼,並編譯

1
2
3
4
5
6
public class Cal {
  public void sum(int a, int b) {
    System.out.println("result = ");
    System.out.println(a + b);
  }
}

熱部署

熱部署(英:Hot deployment)是,伺服器不需要重新啟動的情況下,修改軟體或者軟體。 將上面的程式碼產生出來的class檔放在已經正在運行的class目錄下

回到剛才執行”使用public建構子與參數”,輸入package name與類別

輸入package.類別
reflect.Cal
請輸入一個數
5
請輸入另一個數
3
result = 
8

results matching ""

    No results matching ""