黑馬程式設計師——Java高新技術之反射學習總結一
反射學習總結
基礎補充:
Java程式中的各個Java類屬於同一類事務,描述這類事務的Java類名就是Class。
Class物件代表記憶體中的一份位元組碼,所以不能直接通過new 關鍵字來例項化。
位元組碼:.class檔案編譯好後是以二進位制形式存放在記憶體中的。就是首相要將類的位元組碼載入到記憶體中去,之後用這個位元組碼去複製複製出一個個物件來。有幾個類就會有幾份位元組碼,就會有幾份例項物件。
得到位元組碼對應的例項物件的方式(Class
①類名.class
Class clazz = Date.class; //代表Date型別的位元組碼
②物件.getClass()
Person p1 = new Person();
p1.getClass(); //雖然有了物件,但是也可以呼叫物件的.getClass獲取位元組碼
③Class.forName(“類名”)
Class.forName(“類的完整路徑”); //得到類的位元組碼
可以發現得到類的位元組碼中有兩種情況:
情況一:這個類的位元組碼已經載入到記憶體中去了,這時只需找到位元組碼並返回
情況二:獲取這個類的位元組碼時發現虛擬機器中還沒有該類的位元組碼,這時用類載入器進 行載入,這時就用載入進來的位元組碼,呼叫該類的相應方法進行返回位元組碼。
檢視是否該類是否為基本資料型別時,呼叫Class.isPrimitive方法,如果是返回true,如果不是返回false
只要在源程式中出現的型別,都有各自的Class例項物件,比如int[],void,等等
反射篇開始:
反射就是把Java類中的各種成分對映成相應的java類。
1.Constructor類:代表某個類中的一個構造方法。
可以得到某個類所有的構造方法:
Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
得到某一個構造方法:
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
建立例項物件:
String str = (String)construtctor.newInstance(new StringBuffer("abc"));
//new String(new StringBuffer("abc"));
Constructor constructor1 = String.class.getConstructor(StringBuffer.class); //這裡的StringBuffer代
//表String的構造方法選擇的是StringBuffer型別
String str = (String)constructor1.newInstance(new StringBuffer("abc")); //表示用這個StringBuffer時
// 要傳一個StringBuffer進去,在建立時編譯器不知道是String型別的,只有在執行時才會執行
//String.class.getConstructor(StringBuffer.class);但在編譯時沒有讀取constructor1後面的內容。
Class.newInstance()方法:
該方法內部先得到預設的構造方法,然後用該構造方法建立例項物件。該方法內不的具體程式碼用到了快取機制來儲存預設構造方法的例項物件。
比如:String obj = (String)Class.forName(“java.lang.String”).newInstance();
2.Field類:代表某個類中的一個成員變數。
ReflectPoint類:
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
Reflect測試類:
import java.lang.reflect.Field;
public class ReflectTest {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
ReflectPoint rp = new ReflectPoint(2,5);
Field fieldY = rp.getClass().getField("y");
//fieldY不是物件身上的變數,而是類上,要用它去取某個物件上對應的值
System.out.println(fieldY.get(rp));
}
}
這時如果獲取x變數時,會報java.lang.NoSuchFieldException:x異常。
可以將原來的.getField(“x”) 方法變為.getDeclaredField(“x”);即不論方法可見不可見都可以通過這個方法獲取到,但是這是不可以取出來。這時可以用暴力反射。
Field fieldX = rp.getClass().getDeclaredField("x");
fieldX.setAccessible(true); //這叫暴力反射
System.out.println(fieldX.get(rp));
含有String型別的變數,進行反射
ReflectPoint類中:
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "itcast";
@Override
public String toString() {
return "ReflectPoint [str1=" + str1
+ ", str2=" + str2 + ", str3=" + str3 + "]";
}
在ReflectTest類中:
Field[] fields = rp.getClass().getFields();
for(Field field : fields){
//if(field.getType().equals(String.class)){
//位元組碼用等號比
if(field.getType() == String.class){
String oldValue = (String)field.get(rp);
String newValue = oldValue.replace('b', 'a');
field.set(rp, newValue);
}
}
3.Method類:代表某個類中的一個成員方法。
得到類中的某一個方法:
Method charAt = Class.forName("java.lang.String").getMthod("charAt",int.class);
普通方法:
System.out.println(str.charAt(1));
呼叫反射方法:
System.out.println(charAt.invoke(str,1));
提示:如果傳遞給Method物件的invoke()方法的一個引數為null,說明該Method物件對應的是一個靜態方法!也可以理解為,靜態方法呼叫時不需要物件。
4.用反射方式執行某個類中的main方法
String startingClassName = args[0];
Method mainMethod =Class.forName(startingClassName).getMethod("main",String[].class);
//mainMethod.invoke(null,new Objectp[]{new String[]{"111","222","333"}});
mainMethod.invoke(null,(Object)new String[]{"111","222","333"});
5.陣列的反射
具有相同維數和元素型別的陣列屬於同一個型別,即具有相同的Class例項物件。
代表陣列的Class例項物件的getSuperClass()方法返回的父類為Object類對應的Class。
int[][] a1 = new int[2][4]; //因為int[]屬於Object型別的
Object[] abj = a1;