java反射機制分析

來源:互聯網
上載者:User

標籤:

本文轉自:http://www.cnblogs.com/gulvzhe/archive/2012/01/27/2330001.html

淺顯易懂,值得收藏 

Java反射機制是在運行狀態中,對於任意一個,都能夠知道這個類的所有屬性和方法;對於任意一個對象, 都能夠調用它的任意一個方法和屬性;這種動態擷取的資訊以及動態調用對象的方法的功能稱為Java語言的反射機制。反射的概念是由Smith在1982年 首次提出的,主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力。這一概念的提出很快引發了電腦科學領域關於應用反射性的研究。它首先被程式 語言的設計領域所採用,並在Lisp和物件導向方面取得了成績。當然反射本身並不是一個新概念,它可能會使我們聯想到光學中的反射概念,儘管電腦科學賦 予了反射概念新的含義,但是,從現象上來說,它們確實有某些相通之處,這些有助於我們的理解。

      Java反射機制主要提供下面幾種用途:

  • 在運行時判斷任意一個對象所屬的類
  • 在運行時構造任意一個類的對象
  • 在運行時判斷任意一個類所具有的成員變數和方法
  • 在運行時調用任意一個對象的方法

      首先看一個簡單的例子,通過這個例子來理解Java的反射機制是如何工作的。

package com.wanggc.reflection;

import java.lang.reflect.Method;

/**
* Java 反射練習。
*
* @author Wanggc
*/
public class ForNameTest {

/**
* 入口函數。
*
* @param args
* 參數
* @throws Exception
* 錯誤資訊
*/
public static void main(String[] args) throws Exception {
// 獲得Class
Class<?> cls = Class.forName(args[0]);
// 通過Class獲得所對應對象的方法
Method[] methods = cls.getMethods();
// 輸出每個方法名
for (Method method : methods) {
System.out.println(method);
}
}
}

      當傳入的參數是java.lang.String時,會輸出如下結果

public boolean java.lang.String.equals(java.lang.Object)
public java.lang.String java.lang.String.toString()
public int java.lang.String.hashCode()
public int java.lang.String.compareTo(java.lang.String)
public int java.lang.String.compareTo(java.lang.Object)
public int java.lang.String.indexOf(int)
public int java.lang.String.indexOf(int,int)
public int java.lang.String.indexOf(java.lang.String)
public int java.lang.String.indexOf(java.lang.String,int)
public static java.lang.String java.lang.String.valueOf(int)
public static java.lang.String java.lang.String.valueOf(char)
public static java.lang.String java.lang.String.valueOf(boolean)
public static java.lang.String java.lang.String.valueOf(float)
public static java.lang.String java.lang.String.valueOf(char[],int,int)
public static java.lang.String java.lang.String.valueOf(double)
public static java.lang.String java.lang.String.valueOf(char[])
public static java.lang.String java.lang.String.valueOf(java.lang.Object)
public static java.lang.String java.lang.String.valueOf(long)
public char java.lang.String.charAt(int)
public int java.lang.String.codePointAt(int)
public int java.lang.String.codePointBefore(int)
public int java.lang.String.codePointCount(int,int)
public int java.lang.String.compareToIgnoreCase(java.lang.String)
public java.lang.String java.lang.String.concat(java.lang.String)
public boolean java.lang.String.contains(java.lang.CharSequence)
public boolean java.lang.String.contentEquals(java.lang.CharSequence)
public boolean java.lang.String.contentEquals(java.lang.StringBuffer)
public static java.lang.String java.lang.String.copyValueOf(char[])
public static java.lang.String java.lang.String.copyValueOf(char[],int,int)
public boolean java.lang.String.endsWith(java.lang.String)
public boolean java.lang.String.equalsIgnoreCase(java.lang.String)
public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[])
public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[])
public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException
public void java.lang.String.getBytes(int,int,byte[],int)
public byte[] java.lang.String.getBytes()
public byte[] java.lang.String.getBytes(java.nio.charset.Charset)
public void java.lang.String.getChars(int,int,char[],int)
public native java.lang.String java.lang.String.intern()
public boolean java.lang.String.isEmpty()
public int java.lang.String.lastIndexOf(java.lang.String)
public int java.lang.String.lastIndexOf(int,int)
public int java.lang.String.lastIndexOf(int)
public int java.lang.String.lastIndexOf(java.lang.String,int)
public int java.lang.String.length()
public boolean java.lang.String.matches(java.lang.String)
public int java.lang.String.offsetByCodePoints(int,int)
public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int)
public boolean java.lang.String.regionMatches(int,java.lang.String,int,int)
public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence)
public java.lang.String java.lang.String.replace(char,char)
public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String)
public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String)
public java.lang.String[] java.lang.String.split(java.lang.String)
public java.lang.String[] java.lang.String.split(java.lang.String,int)
public boolean java.lang.String.startsWith(java.lang.String)
public boolean java.lang.String.startsWith(java.lang.String,int)
public java.lang.CharSequence java.lang.String.subSequence(int,int)
public java.lang.String java.lang.String.substring(int)
public java.lang.String java.lang.String.substring(int,int)
public char[] java.lang.String.toCharArray()
public java.lang.String java.lang.String.toLowerCase()
public java.lang.String java.lang.String.toLowerCase(java.util.Locale)
public java.lang.String java.lang.String.toUpperCase()
public java.lang.String java.lang.String.toUpperCase(java.util.Locale)
public java.lang.String java.lang.String.trim()
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

      這樣就列出了java.lang.String類的所有方法名、及其限制符、傳回型別及拋出的異常。這個程式使用Class類forName方法載入指定的類,然後調用getMethods方法返回指定類的方法列表。java.lang.reflect.Method用來表述某個類中的單一方法。

      使用java的反射機制,一般需要遵循三步:

    1. 獲得你想操作類的Class對象
    2. 通過第一步獲得的Class對象去取得操作類的方法或是屬性名稱
    3. 操作第二步取得的方法或是屬性

     Java啟動並執行時候,某個類無論產生多少個對象,他們都會對應同一個Class對象,它表示正在運行程式中的類和介面。如何取得操作類的Class對象,常用的有三種方式:

    1. 調用Class的靜態方法forName,如上例;
    2. 使用類的.class文法,如:Class<?> cls = String.class;
    3. 調用對象的getClass方法,如:String str = "abc";Class<?> cls = str .getClass();

      下面將通過執行個體講述如何通過前面所訴的三步來執行某對象的某個方法:

 1 package com.wanggc.reflection;
2
3 import java.lang.reflect.Method;
4
5 /**
6 * Java 反射練習。
7 *
8 * @author Wanggc
9 */
10 public class ReflectionTest {
11 public static void main(String[] args) throws Exception {
12 DisPlay disPlay = new DisPlay();
13 // 獲得Class
14 Class<?> cls = disPlay.getClass();
15 // 通過Class獲得DisPlay類的show方法
16 Method method = cls.getMethod("show", String.class);
17 // 調用show方法
18 method.invoke(disPlay, "Wanggc");
19 }
20 }
21
22 class DisPlay {
23 public void show(String name) {
24 System.out.println("Hello :" + name);
25 }
26 }

      前面說過,Java程式的每個類都會有個Class對象與之對應。Java反射的第一步就是獲得這個Class對象,如代碼14行。當然,每個類的方法也必有一個Method對象與之對應。要通過反射的方式調用這個方法,就要首先獲得這個方法的Method對象,如代碼16行,然後用Method對象反過來調用這個方法,如代碼18行。注意16行getMethod方法的第一個參數是方法名,第二個是此方法的參數類型,如果是多個參數,接著添加參數就可以了,因為getMethod是可變參數方法。執行18行代碼的invoke方法,其實也就是執行show方法,注意invoke的第一個參數,是DisPlay類的一個對象,也就是調用DisPlay類哪個對象的show方法,第二個參數是給show方法傳遞的參數。類型和個數一定要與16行的getMethod方法一直。

      上例講述了如何通過反射調用某個類的方法,下面將再通過一個執行個體講述如何通過反射給某個類的屬性賦值:

  1 package com.wanggc.reflection;
2
3 import java.lang.reflect.Field;
4
5 /**
6 * Java 反射之屬性練習。
7 *
8 * @author Wanggc
9 */
10 public class ReflectionTest {
11 public static void main(String[] args) throws Exception {
12 // 建立學生對象
13 Student student = new Student();
14 // 為學生對象賦值
15 student.setStuName("Wanggc");
16 student.setStuAge(24);
17 // 建立拷貝目標對象
18 Student destStudent = new Student();
19 // 拷貝學生對象
20 copyBean(student, destStudent);
21 // 輸出拷貝結果
22 System.out.println(destStudent.getStuName() + ":"
23 + destStudent.getStuAge());
24 }
25
26 /**
27 * 拷貝學生對象資訊。
28 *
29 * @param from
30 * 拷貝來源物件
31 * @param dest
32 * 拷貝目標對象
33 * @throws Exception
34 * 例外
35 */
36 private static void copyBean(Object from, Object dest) throws Exception {
37 // 取得拷貝來源物件的Class對象
38 Class<?> fromClass = from.getClass();
39 // 取得拷貝來源物件的屬性列表
40 Field[] fromFields = fromClass.getDeclaredFields();
41 // 取得拷貝目標對象的Class對象
42 Class<?> destClass = dest.getClass();
43 Field destField = null;
44 for (Field fromField : fromFields) {
45 // 取得拷貝來源物件的屬性名稱字
46 String name = fromField.getName();
47 // 取得拷貝目標對象的相同名稱的屬性
48 destField = destClass.getDeclaredField(name);
49 // 設定屬性的可訪問性
50 fromField.setAccessible(true);
51 destField.setAccessible(true);
52 // 將拷貝來源物件的屬性的值賦給拷貝目標對象相應的屬性
53 destField.set(dest, fromField.get(from));
54 }
55 }
56 }
57
58 /**
59 * 學生類。
60 */
61 class Student {
62 /** 姓名 */
63 private String stuName;
64 /** 年齡 */
65 private int stuAge;
66
67 /**
68 * 擷取學生姓名。
69 *
70 * @return 學生姓名
71 */
72 public String getStuName() {
73 return stuName;
74 }
75
76 /**
77 * 設定學生姓名
78 *
79 * @param stuName
80 * 學生姓名
81 */
82 public void setStuName(String stuName) {
83 this.stuName = stuName;
84 }
85
86 /**
87 * 擷取學生年齡
88 *
89 * @return 學生年齡
90 */
91 public int getStuAge() {
92 return stuAge;
93 }
94
95 /**
96 * 設定學生年齡
97 *
98 * @param stuAge
99 * 學生年齡
100 */
101 public void setStuAge(int stuAge) {
102 this.stuAge = stuAge;
103 }
104 }

       Java的反射機制中類有Class對應,類的方法有Method對應,當然屬性也有Field與之對應。代碼中注釋已經做了詳細的注釋,在此不再贅述。但要注意,Field提供了get和set方法擷取和設定屬性的值,但是由於屬性是私人類型,所以需要設定屬性的可訪問性為true,如代碼50~51行。也可以在為整個fields設定可訪問性,在40行下面使用AccessibleObject的靜態方法setAccessible,如:AccessibleObject.setAccessible(fromFields, true);

      前面講述了如何用Java反射機制操作一個類的方法和屬性,下面再通過一個執行個體講述如何在運行時建立類的一個對象:

 1 package com.wanggc.reflection;
2
3 import java.lang.reflect.Field;
4
5 /**
6 * Java 反射之屬性練習。
7 *
8 * @author Wanggc
9 */
10 public class ReflectionTest {
11 public static void main(String[] args) throws Exception {
12 // 建立學生對象
13 Student student = new Student();
14 // 為學生對象賦值
15 student.setStuName("Wanggc");
16 student.setStuAge(24);
17 // 建立拷貝目標對象
18 Student destStudent = (Student) copyBean(student);
19 // 輸出拷貝結果
20 System.out.println(destStudent.getStuName() + ":"
21 + destStudent.getStuAge());
22 }
23
24 /**
25 * 拷貝學生對象資訊。
26 *
27 * @param from
28 * 拷貝來源物件
29 * @param dest
30 * 拷貝目標對象
31 * @throws Exception
32 * 例外
33 */
34 private static Object copyBean(Object from) throws Exception {
35 // 取得拷貝來源物件的Class對象
36 Class<?> fromClass = from.getClass();
37 // 取得拷貝來源物件的屬性列表
38 Field[] fromFields = fromClass.getDeclaredFields();
39 // 取得拷貝目標對象的Class對象
40 Object ints = fromClass.newInstance();
41 for (Field fromField : fromFields) {
42 // 設定屬性的可訪問性
43 fromField.setAccessible(true);
44 // 將拷貝來源物件的屬性的值賦給拷貝目標對象相應的屬性
45 fromField.set(ints, fromField.get(from));
46 }
47
48 return ints;
49 }
50 }
51
52 /**
53 * 學生類。
54 */
55 class Student {
56 /** 姓名 */
57 private String stuName;
58 /** 年齡 */
59 private int stuAge;
60
61 /**
62 * 擷取學生姓名。
63 *
64 * @return 學生姓名
65 */
66 public String getStuName() {
67 return stuName;
68 }
69
70 /**
71 * 設定學生姓名
72 *
73 * @param stuName
74 * 學生姓名
75 */
76 public void setStuName(String stuName) {
77 this.stuName = stuName;
78 }
79
80 /**
81 * 擷取學生年齡
82 *
83 * @return 學生年齡
84 */
85 public int getStuAge() {
86 return stuAge;
87 }
88
89 /**
90 * 設定學生年齡
91 *
92 * @param stuAge
93 * 學生年齡
94 */
95 public void setStuAge(int stuAge) {
96 this.stuAge = stuAge;
97 }
98 }

      此例和上例啟動並執行結果是相同的。但是copyBean方法返回的對象不再是外面傳入的,而是由方法內部產生的,如第40行代碼所示。注意:Class的newInstance方法,只能建立只包含無參數的建構函式的類,如果某類只有帶參數的建構函式,那麼就要使用另外一種方式:fromClass.getDeclaredConstructor(int.class,String.class).newInstance(24,"wanggc");

      至此,Java反射機制的常用機能(運行時調用對象的方法、類屬性的使用、創類類的對象)已經介紹完了。

      補充:在獲得類的方法、屬性、建構函式時,會有getXXX和getgetDeclaredXXX兩種對應的方法。之間的區別在於前者返回的是存取權限為public的方法和屬性,包括父類中的;但後者返回的是所有存取權限的方法和屬性,不包括父類的。

java反射機制分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.