反射的基石——Class類
Java程序中的各個Java類屬於同一類事物,描述這類事物的Java類名就是Class。比如:衆多的人可以用一個Person類來表示,而這個類就可以描述人的信息(姓名、年齡、性別等等),而衆多的Java類就可以用Class來表示。
Question:Person類代表人,它的實例對象可以是張三、李四,Class類代表Java類,它的各個實例對象有分別代表什麼呢?
Answer: 對應各個類在內存中的字節碼。一個類被類加載器加載到內存中,佔用一份存儲空間,這個空間裏面的內容就是類的字節碼,不同的類的字節碼是不同的,所以它們在內存中的內容是不同的,這一個個空間可分別用一個個的對象來表示,這些對象顯然具有相同的類型,即Class類型。
如何得到各個字節碼對應的實例對象,有三種方法:
1、類名.class,例如:System.class
2、對象.getClass(),例如:new Data().getClass()
3、Class.forName("類名"),例如:Class.forName("java.util.Date")
Class類的屬性和方法請參見jdk
反射
反射就是把Java類中的各種成分映射成相應的java類。例如:一個Java類中用一個Class類的對象來表示,一個類中的組成部分:成員變量,方法,構造方法,包等等信息也用一個個的Java類來表示。表示java類的Class類顯然要提供一系列的方法來獲得其中的變量,方法,構造方法,修飾符,包等信息,這些信息就是用相應類的實例對象來表示,它們是Field、Method、Contructor、Package等等。
Contructor類
1、得到某個類的所有構造方法:
Contructor[] contructors = lass.forName("類名").getContructors();
2、得到某個特定的構造方法:
//獲得方法時要用到類型
Contructor contructors = Class.forName("類名").getContructor(參數類型.class,...);
3、創建實例對象
通常方式:String str = new String(new StringBuffer("abc"));
//調用獲得的方法時要用到上面相同類型的實例
反射方式:String str = (String)contructor.newInstance(new StringBuffer("abc"));
4、Class.forName();
·示例:String obj = (String)Class.forName("java.lang.String").newInstence();
·該方法內部先得到默認的構造方法,然後用該構造方法創建實例
·該方法的內部具體代碼是怎樣的呢?用到了緩存機制來保存默認構造方法的對象
Field類
Filed類代表某個類中的成員變量
示例:
RefletPoint.java
private int x;
public int y;
public ReflectPoint() {}
public ReflectPoint(int x,int y) {
this.x = x;
this.y = y;
}
FieldTest.java
ReflectPoint p1 = new ReflectPoint(2,8);
Field fy = Class.forName("com.zouc.reflect.ReflectPoint").getField("y");
System.out.println(fy.get(p1));; //8
Field fx = Class.forName("com.zouc.reflect.ReflectPoint").getDeclaredField("x");
fx.setAccessible(true);
System.out.println(fx.get(p1)); //2
Question: 得到的Field對象是對應於類上的成員變量?還是對應到對象上的成語變量?
Anwser:是類上的成員變量。因爲對象可以有多個,而類只有一個,所以fx.fy是得到的類上的那一份字節碼。
Method類
Method類代表某個類中一個成員方法
·得到類中的某個方法
例子:Method charAt = Class.forName("java.lang.String").getMethod("charAt",int.class);
·調用方法:
通常方式:str.charAt(1);
反射方式:charAt.invoke(str,1);
注意:如果傳遞給Method對象的invoke()方法的第一個參數是null,這說明該Method對象對應的是一個靜態方法
示例: 對接收數組參數的成員方法進行反射
package com.zouc.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws SecurityException, NoSuchMethodException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
String startingClassName = args[0];
Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
mainMethod.invoke(null, (Object)new String[]{"abc","def","xyz"});
}
}
class Argumenmts {
public static void main(String[] args) {
for(String arg : args) {
System.out.println(arg);
}
}
}
數組的反射
·具有相同維數和元素類型的數組屬於同一個類型,即具有相同的Class實例對象
·代表數組的Class實例對象的getSuperclass()方法返回的父類爲Object類對應的Class
·基本類型的一維數組可以當做Ojbect類型使用,不能當做Object[]類型使用,而二維數組可以當做 Object類型使用,也可以當做Object[]類型使用
示例:
int[] a = new int[3];
int[] b = new int[4];
int[][] c = new int[3][4];
String[] s = new String[5];
System.out.println(a.getClass() == b.getClass()); //true
System.out.println(a.getClass() == c.getClass()); //false
System.out.println(c.getClass() == s.getClass()); //false
·Array工具類用於完成對數組的反射操作
public static void printObject(Object obj) {
Class obyte = obj.getClass();
if (obyte.isArray()) {
int len = Array.getLength(obj);
for (int i = 0; i < len; i++) {
System.out.println(Array.get(obj, i));
}
} else {
System.out.println(obj);
}
}