1. 程式人生 > >有關Java反射的使用看這一篇就夠了

有關Java反射的使用看這一篇就夠了

1. 簡介

本篇文章不探討反射的實現機制或者說實現原理,僅僅從使用的角度去講解我們常用的一些API介面,方便自己以後需要使用時信手拈來,同時也方便廣大博友能夠快速瞭解API的使用。

什麼是反射?

反射是java語言的一個特性,它允許一個java的類獲取他所有的成員變數和方法並且顯示出來,這樣說起來有些抽象,例如我們可以通過反射去例項化一個物件,並不非得使用new這個關鍵字來例項化,同時我們也可以通過反射知道Java類中有哪些變數、哪些方法等等,這些特性在C或者C++語言中是不存在的。

反射中,我們會常用到三個類,分別為Class、Method和Field,通過類名相信大家已經能夠知道大概意思,分別代表類、方法和變數,本篇文章將會用大量的Demo來講解這三個類中的部分常用重要API的作用,Demo中用到的一個實體類如下:

  1. public class Person {

  2. private String name;

  3. public Person() {

  4. System.out.println("Person()...");

  5. }

  6. public Person(String name) {

  7. this.name = name;

  8. System.out.println("Person(String name)...");

  9. }

  10. private void privateMethod(){

  11. System.out.println("privateMethod-->name="+this.name);

  12. }

  13. protected void protectedMethod(){

  14. System.out.println("protectedMethod-->name="+this.name);

  15. }

  16. void defaultMethod(){

  17. System.out.println("defaultMethod-->name="+this.name);

  18. }

  19. public void setName(String name) {

  20. this.name = name;

  21. System.out.println("setName()...");

  22. }

  23. public String getName() {

  24. System.out.println("getName()...");

  25. return name;

  26. }

  27. @Override

  28. public String toString() {

  29. return "name: "+this.name;

  30. }

  31. }

2. Class 類

2.1 獲取Class例項

  1. // 方法一 forName函式

  2. Class c= Class.forName("Person");

  3. // 方法二 getClass()函式

  4. Person person = new Person();

  5. Class c = person.getClass();

  6. // 方法三 使用類字面常量

  7. Class c=Person.class;

2.2 通過反射來例項化物件

  1. // 方法一

  2. try {

  3. Class c = Person.class;

  4. Person person = (Person) c.newInstance();

  5. System.out.println(person instanceof Person); // true

  6. } catch (InstantiationException e) {

  7. e.printStackTrace();

  8. } catch (IllegalAccessException e) {

  9. e.printStackTrace();

  10. }

  11. // 方法二

  12. try {

  13. Class c = Class.forName("reflect.demo.Person");

  14. Person person = (Person) c.newInstance();

  15. System.out.println(person instanceof Person); // true

  16. } catch (ClassNotFoundException e) {

  17. e.printStackTrace();

  18. } catch (IllegalAccessException e) {

  19. e.printStackTrace();

  20. } catch (InstantiationException e) {

  21. e.printStackTrace();

  22. }

以上方式都是呼叫的Person類無參構造,如果需要呼叫Person(String name),改如何呢?

  1. // 首先獲取到Person類中的有參構造,通過建構函式例項化物件

  2. try {

  3. Class c = Person.class;

  4. Constructor constructor = c.getConstructor(new Class[]{String.class});

  5. // 呼叫的是有參構造

  6. Person person = (Person) constructor.newInstance("jack");

  7. System.out.println(person.toString()); // name: jack

  8. } catch (NoSuchMethodException e) {

  9. e.printStackTrace();

  10. } catch (IllegalAccessException e) {

  11. e.printStackTrace();

  12. } catch (InstantiationException e) {

  13. e.printStackTrace();

  14. } catch (InvocationTargetException e) {

  15. e.printStackTrace();

  16. }

2.3 獲取類中所有的方法物件

  1. try {

  2. Class c = Person.class;

  3. // 方式一

  4. Method [] methods = c.getMethods(); // 獲取所有公共方法,且包括父類的共有方法

  5. for (Method method:methods) {

  6. System.out.print(method.getName()+", ");

  7. }

  8. System.out.println("");

  9. // 方式二

  10. methods = c.getDeclaredMethods(); // 獲取所有方法,包括私有、共有等,但是隻定義在該類中

  11. for (Method method:methods) {

  12. System.out.print(method.getName()+", ");

  13. }

  14. } catch (Exception e) {

  15. e.printStackTrace();

  16. }

  17. 輸出:

  18. toString, getName, setName, wait, wait, wait, equals, hashCode, getClass, notify, notifyAll,

  19. toString, getName, setName, protectedMethod, privateMethod, defaultMethod,

  20. // 其他

  21. Method getMethod(String name, Class<?>... parameterTypes) // 獲取某個方法物件,只能是共有的

  22. Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 獲取獲取某個方法物件,只能是當前類的,可以是private、public等

2.4 獲取類中欄位物件

  1. try {

  2. Class c = Person.class;

  3. Field[] fields = c.getFields(); // 同 getMethods

  4. for (Field field : fields) {

  5. System.out.print(field.getName() + ", ");

  6. }

  7. System.out.println("5***************");

  8. fields = c.getDeclaredFields(); // 同 getDeclaredMethods

  9. for (Field field : fields) {

  10. System.out.print(field.getName() + ", ");

  11. }

  12. } catch (Exception e) {

  13. e.printStackTrace();

  14. }

2.5 其他方法

  • getName : 返回類名,包括包名
  • getSimpleName: 返回類名,不包含包名
  • cast(Object obj):將obj型別物件轉化為當前class型別物件
  1. Class cc = Person.class;

  2. // getName: reflect.demo.Person getSimpleName: Person

  3. System.out.println("getName: "+cc.getName()+" getSimpleName: "+cc.getSimpleName());

3. Method類

  • getName() 獲取方法名
  • invoke(Object obj, Object... args) 執行obj中該方法
  • Parameter[] getParameters() 獲取方法中的引數
  • setAccessible(boolean flag) 設定訪問許可權
  1. Person per = new Person();

  2. Class c = Person.class;

  3. Method method;

  4. try {

  5. method = c.getDeclaredMethod("setName", String.class);

  6. System.out.println(method.getName()); // 輸出: setName

  7. method.invoke(per, "hello reflect"); // 呼叫了 setName方法

  8. System.out.println(per); // 輸出 name: hello reflect

  9. Parameter[] parameters = method.getParameters();

  10. for (Parameter parameter:parameters) {

  11. System.out.print(parameter.getName()+", "); // 輸出:arg0

  12. }

  13. } catch (Exception e) {

  14. e.printStackTrace();

  15. }

  16. // 如何反射呼叫私有方法

  17. try {

  18. method = c.getDeclaredMethod("privateMethod");

  19. method.setAccessible(true); // 私有方法必須設定訪問許可權為true

  20. method.invoke(per); // 呼叫privateMethod方法

  21. } catch (Exception e) {

  22. e.printStackTrace();

  23. }

4. Field API

  • String getName() 獲取變數名
  • setAccessible(boolean flag) 設定訪問許可權
  • Object get(Object obj) 獲取obj物件中改變數值
  • set(Object obj, Object value) 將obj物件中的改變數值設定為value
  1. Class c = Person.class;

  2. Person person = new Person("lvjie");

  3. try {

  4. // Field field = c.getField("name"); // 只能獲取到共有變數,包括父類

  5. Field field = c.getDeclaredField("name"); // 可以獲取當前類中定義的任何變數,包括private、public等

  6. System.out.println(field.getName()); // name

  7. field.setAccessible(true); // 私有變數必須設定訪問許可權為true

  8. System.out.println(field.get(person)); // 獲取該物件中的變數值,私有變數需要新增訪問許可權

  9. System.out.println(person); // name: lvjie

  10. field.set(person, "jack"); // 對該物件的變數設定值, 私有變數需要新增訪問許可權

  11. System.out.println(person); // name: jack

  12. } catch (Exception e) {

  13. e.printStackTrace();

  14. }

以上相關API在使用反射過程中是經常遇見的,當然還有其他相關API,例如獲取該類、方法和變數上使用到的註解等等,這些在註解的使用上會做講解。反射配合註解,會產生巨大的功能,目前許多開源庫也是基於這兩點技術來實現,例如Android中的 EventBus、ButterKnife、DBFlow等等。

如果對Java註解感興趣,請看下一篇Java註解全面總結