一、基礎知識
1.Java類用于描述一類事物的共性,該類事物有什麼屬性,沒有什麼屬性,至于這個屬性的值是什麼,則是由這個類的執行個體對象來确定的,不同的執行個體對象有不同的屬性值。
2.Java程式中各個Java類,它們是屬于同一類事物,可以用一個類來描述這類事物,這個類的名字就是Class。Class類描述了類的名字,類的通路屬性,類所屬于的包名,字段名稱的清單、方法名稱的清單,等等。反射就是把Java類中的各個成分映射成相應的java類。例如,一個Java類中用一個Class類的對象來表示,一個類中的組成部分:成員變量,方法,構造汽車,包等信心也用一個個的java類來表示,就像汽車是一個類,汽車中俄發動機,變速箱等等也是一個個的類。表示JAVA類的Class類顯然要提供一些列的方法,來獲得其中的變量,方法,構造方法,修飾符,包等資訊,這些資訊就是用相應類的執行個體對象來表示,他們是Field、Method、Constructor、Package等等。
3.一個類中的每個成員都可以用相應的反射API類的一個執行個體對象來表示,通過調用Class類的方法可以得到這些執行個體對象。每個java類都是Class的一個執行個體對象,它們的内容不同,但是,它們的特征相同,比如都有方法,有字段等。
Class的執行個體對象代表記憶體中的位元組碼 :
二、位元組碼
總結:位元組碼:.class加載到記憶體中才可以建立對象 eg: Class s=Date.class//位元組碼
得到各個位元組碼對應執行個體的對象的三種方法:
1、類名.Class 例如:System.class;
2、對象名.Class 例如 new Date().getClass();
3、靜态方法Class.forName("類名"); 例如,Class.forName("java.util.Date");
Class.forName()得到位元組碼的情況:
1、位元組碼已經加載到java虛拟機中,去得到位元組碼
2、java虛拟機中還沒有生成位元組碼 用類加載器進行加載,加載的位元組碼緩沖到虛拟機中
九個預定義Class的執行個體對象(byte.class char.classshort.class int.class long.class float.class double.class boolean.class和void.class)八個基本資料類型和void類型
在源程式中出現的類型,都有各自的Class執行個體對象,如int[],void…
isPrimitive();判斷是否是基本類型的位元組碼
int.class和Integer.class不是同一份位元組碼,Integer.TYPE,TYPE代表包裝類對應的基本類的位元組碼 int.class==Integer.TYPE
數組類型的Class執行個體對象 用到Class的isArray()
總結:隻要在源程式中國出現的類型,都有各自的Class執行個體對象,例如,int[],void…
構造方法的反射:
思路:class------>constructor-------->new object
Constructor類代表某個類中的一個構造方法
1.得到某個類所有的構造方法
Constructor[] constructors=Class.forName("java.lang.String").getConstructors();
2.得到某一個構造方法
Constructor constructor=Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
3.建立執行個體對象:
通常方式:String str=new String("abc");
反射方式:String str=(String)constructor.newInstance(newStringBuffer("abc"));
Constructorconstructor1=String.class.getConstructor(StringBuffer.class);
String str2=(String)constructor1.newInstance(newStringBuffer("abc"));
第一個StringBuffer代表選擇哪個構造方法
第二個StringBuffer代表用這個StringBuffer時還要傳遞一個StringBuffer對象
Class.newInstance()方法:
例子:String obj=(String)Class.forName(“java.lang.String”).newInstance();
1. 該方法内部先得到貓人的構造方法,然後調用該方法建立執行個體對象。
2. 該方法内部代碼,用到了緩存機制來儲存預設構造方法的執行個體對象。
Field成員變量的反射:
Field類代表某個類的中一個成員變量
publicclass ReflectpoiSecond {
public intx;
privateinty;
public ReflectpoiSecond(int x, int y) {
super();
this.x = x;
this.y = y;
}}
--------------------------------------
publicclass ReflectTest {
public static void main(String[] args) throws Exception {
ReflectpoiSecondrs=new ReflectpoiSecond(4, 5);
Field fieldX=rs.getClass().getField("x");//getField提供可見的 Field fieldY=rs.getClass().getDeclaredField("y");//getDeclaredField得到聲明過的屬性
fieldY.setAccessible(true);//設定通路權限 ---暴力反射
System.out.println(fieldX.get(rs));
System.out.println(fieldY.get(rs));
}}
成員方法的反射:Method類代表某個類中的一個成員方法:
一.得到類中的某一個方法:
Method methodCharAt=Class.forName("java.lang.String").getMethod("charAt",int.class);
System.out.println(methodCharAt.invoke(str,1));//調用str對象中charAt(1)
二.調用方法:
1. 通常方式:System.out.println(str.charAt(1));
2.反射方式:System.out.println(charAt.invoke(str,1));//str是一個對象,這裡str可以是null,說明invoke()方法是一個靜态方法
Jdk1.4和jdk1.5的invoke方法的差別:
JDK1.5:public Object invoke(Object obj,Obj…args);
JDK1.4:public Object invoke(Object obj,Object[] args);
即按JDK1.4的文法,需要講一個數組作為參數傳遞給invoke方法時,數組中的每個元素分别對應被調用方法中的一個參數,是以,charAt方法的代碼也可以用JDK1.4改寫為charAt.invoke(“str”,newObject[]{1})形式。
執行個體應用:用反射方式執行某個類中的main方法
1. 目标:
寫一個程式,這個程式能夠根據使用者提供的類名,去執行該類中的Main方法
作用:
2.問題
啟動java程式的main方法的參數是一個字元串數組, 即public static void main(String[]args),通過反射方式來調用這個main方法時,按jdk1.5的文法,整個數組是一個參數,而jdk1.4的文法,數組的每個元素對應一個參數,當做把一個字元串數組作為參數傳遞給invoke方法如何處理(注意相容)。是以,在給main方法傳遞參數時,不能使用代碼mainMthod.invoke(null,newString[]{“xxxx”}),javac隻把它當做JDK1.4的文法進行了解,。而不能把它當做JDK1.5的文法解釋,是以,會出現參數類型不對的問題。
3.解決方法:
方法一:mainMethod.invoke(null,newObject[]{new String[]{xxxx}});
方法二:mainMethod.invoke)((Object)newString[]{"xxxx"});編譯器會做特殊處理,編譯時不把參數當做數組看待,也就不會數組達三成若幹參數
數組的的反射
1. 具有相同維數和元素類型的數組屬于同一個類型,即具有相同的Class執行個體對象。
2. 代表數組的Class執行個體對象的getSuperClass()方法,傳回的父類為Object類對應的Class
3. 基本類型的一維數組可以被當做Object類型使用,不能作為Object[]類型使用,不能當做Object[]類型使用,非基本類型的一維數組,既可以當做Object類型使用,又可以當做Object[]類使用
4. 注意差別 Array.asList()方法處理int[]和String[]時的差異
5. Array工具類用于完成對數組的反射操作