黑馬程式設計師 反射學習筆記
----------android培訓、java培訓、java學習型技術部落格、期待與您交流!----------
反射:
“反射就是把Java類中的各種成分對映成相應的java類”,而在此之前,首先是必須獲得類的Class物件,再呼叫Class的相關方法,獲取例項類中的各種成分,如構造方法Constructor、域Field及包等資訊。
具體過程:反射就是把Java類中的各種成分對映成相應的java類。具體就是先得到一個Field(欄位,通常都是變數)們對映的Class的例項物件--一份位元組碼。然後在通過它們操作具體的類物件。
比如:
Constructor:得到一個Constructor[] constructors= Class.forName(“java.lang.String”).getConstructors();,通過呼叫它的newInstance得到這個constructors所在的類的新物件。String str =(String)constructor.newInstance(new StringBuffer(“abc”))。
Field:得到一個類中的Field,再通過Field操作這個類中的Field對應的具體成員變數。如果這個變數是私有的,還可以通過暴力反射對這個變數進行操作。
Method:得到一個類中的Method()包括main方法,在通過得到的method對這個類對應的一個具體物件進行操作(通過invoke方法)。
那麼,常見的獲取Class例項的方法有哪些呢,通過學習,一般的,有如下三種方法:
(1)對於類,格式為:類名.class,例如,System.class ,Person.class
(2)對於具體的物件,需使用getClass()方法,格式為:物件.getClass(),例如,new Date().getClass(),new Person().getClass()。
(3當然,若給定類名,則可使用Class類的靜態方法forName(),格式為:Class.forName("類名"),例如,Class.forName("java.util.Date");
1.獲取構造方法(Constructor):
通過反射可以獲取到java類中的構造方法,通過構造方法可以在執行時動態的建立java物件,而不用通過new來建立。對於Class類,有四個方法可以獲取構造方法,getConstructors返回的是類中所有public許可權的構造方法陣列,getConstructor根據引數的列表返回打個public許可權的構造方法。此外,getDeclaredConstructors和getDeclaredConstructor與之前的兩個類似,但是,這兩個獲取的是類中真正宣告的除private許可權的構造方法,且忽略從父類中繼承下來的構造方法。構造方法獲取後,便可以呼叫構造方法了。
import java.lang.reflect.Constructor;
public class ReflectGetConst {
public static void main(String[] args) throws Exception{
//反射獲取構造方法
Constructor<MathMethods> constructor = MathMethods.class.getDeclaredConstructor(String.class,String.class);
//利用構造方法建立例項物件
MathMethods mathMethods = constructor.newInstance("myAdd","add");
System.out.println("name:"+mathMethods.getName()+"\nforWhat:"+mathMethods.getForWhat());
}
}
class MathMethods{
private static String id = "001";//私有靜態域
public String name = "shinian";
private String forWhat = "No";//私有域
MathMethods(){}
MathMethods(String name,String forWhat){
this.name = name;
this.forWhat = forWhat;
}
public int add(int a,int b){
return a+b;
}
public double add(double ...b){
double sum = 0.0;
for(int i=0;i<b.length;i++){
sum+=b[i];
}
return sum;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getForWhat() {
return forWhat;
}
public void setForWhat(String forWhat) {
this.forWhat = forWhat;
}
}
}
2、 獲取域(Field)
反射不僅能夠獲取上述的構造方法,也能獲取類中public修飾的域,若是private,則需通過getDeclaredField(),再經setAccessible(true)之後進行暴力似獲取。而對於getField()方法,暴力獲取是無效的,其只能訪問public修飾的域。請看下錶
private |
預設 |
protected |
public |
|
getField() |
× |
× |
× |
√ |
getField(),再經setAccessible(true)之後 |
× |
× |
× |
√ |
getDeclaredField() |
× |
√ |
√ |
√ |
getDeclaredField(),再經setAccessible(true)之後 |
√ |
√ |
√ |
√ |
public static void main(String[] args) throws Exception{
Field staticField =MathMethods.class.getDeclaredField("id");
staticField.setAccessible(true);
//通過反射改變id域的值
System.out.println(staticField.get(new MathMethods()));
//通過反射改變id域的值,無需建立具體物件
staticField.set(null, "007");
System.out.println(staticField.get(new MathMethods()));
System.out.println("---分割線---");
Field nameField =MathMethods.class.getDeclaredField("name");
MathMethods mathMethods = new MathMethods();
//通過反射改變id域的值,需要具體的物件
nameField.set(mathMethods, "張三");
System.out.println(nameField.get(mathMethods));
}
執行結果:
001
007
---分割線---
張三
3、 獲取方法(Method)
這裡需要注意兩點,其一是,利用getMethod(String name,Class<?>... parameterTypes),獲取某個方法的物件時,對於引數是陣列或者是可變引數的,需要的是同類型的陣列的Class物件。其二是對某個方法物件呼叫
方法Objectinvoke(Object obj,Object... args)時,務必注意該方法的返回值是Object型別,引數也均為Object型別,必要的時候是需要對引數進行型別強制轉換成Object型別的。
/*獲取MathMethods類中的public double add(double ...b){}方法並呼叫*/
public class ReflectGetConst {
public static void main(String[] args) throws Exception{
//反射獲取類的成員方法
Method method = MathMethods.class.getMethod("add", double[].class);
double result = (Double) method.invoke(new MathMethods("myAdd","add"), (Object)new double[]{1.1,2.1,3.5});//第二個引數強轉成Object類型別,返回值Object型強轉成Double型別
System.out.println(result);//列印結果:6.7
}
4、 運算元組
使用反射對陣列進行操作是經過專門的java.lang.reflect.Array這個工具類來實現的,import java.lang.reflect.Array;
public class ReflectArray {
public static void main(String[] args) {
ArrayDemo();
}
public static void ArrayDemo(){
//一維陣列
String[] strArr = (String[])Array.newInstance(String.class, 2);
Array.set(strArr, 0, "張三");//反射賦值
strArr[1] = "李四";//普通賦值
print(strArr);
print("---1---");
//二維陣列
int[][] intArr = (int[][])Array.newInstance(int.class, 1,1);
intArr[0][0] = 1;
print(intArr[0][0]);
print("---2---");
//類似int[][][] intArr2 = new int[1][1][1]方式
int[][][] intArr2 = (int[][][])Array.newInstance(int.class, 2,2,2);
intArr2[0][0][0] = 11;
intArr2[0][0][1] = 22;
print(intArr2[0][0][1]);
print("---3---");
int[] notObject = new int[]{13,23,33};
Object[] objcetes1 = strArr;
//Object[] objcetes2 = notObject;//該句報錯,原因是int等基本資料型別不是直接繼承自Object
/*那麼下面這句為什麼又不報錯呢,因為intArr是二維陣列,可以視為二維數組裡面儲存的元素是一維陣列,
* 是一個物件,它預設繼承Object*
*/
Object[] objectes3 = intArr;
print(objectes3.length);//從列印結果為1也可以看出,intArr裡實際存放的元素是一維陣列
}
public static void print(Object[] obj){
for(int i = 0;i<obj.length;i++){
System.out.println(obj[i]);
}
}
public static void print(Object obj){
System.out.println(obj);
}
}
總結:反射還是比較難的,要好好練習才能好好掌握。