---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity開發</a>、<a href="http://www.itheima.com"target="blank">.Net培訓</a>、期待與您交流!
----------------------
什麼是反射?
一,反射就是把java類中的各個成員,提取成其對應的java類對象。
例如:一個java類中用一個Class類的對象來表示,一個類中的組成部分都有對應的類:
Field —— 成員變量;
Method —— 成員方法;
Constructor —— 構造函數;
Package ——包
二,一個類中的每個成員可以用相對應的反射API類的一個實例對象來表示,通過調用Class類的方法可以得到這些
實例對象,就可以執行相應的操作了。
反射的基石 -- Class類
1,java中的各個java類屬於同一類事物,描述這類事物的java類名就是Class。
注:對比:衆多人用一個Person類表示,衆多的java類就用Class表示
a,一個人 -->Person
b,java類 -->Class
2,Class類代表java類,它的實例對象分別對應什麼呢?
1,對應各個類在內存中字節碼,例如,String類的字節碼,Person類的字節碼等。。。
2,一個類被類加載器加載到內存中,佔用一片存儲空間,這個空間的內容就是類的字節碼,不同的類的字節碼是不同的,
所以它們在內存中的內容是不同的,這一個個空間分別用一個個的對象來表示,這些對象具有相同的類型即Class類型。
3,如何得到各個字節碼對應的實例對象(Class類型)
有3中方式:
1:類名.class;例如:System.class
2:對象名.getClass();例如:new Date().getClass()3:Class.forName(類的全名稱(包名+類名)字符串);例如:Class.forName("java.util.Date");
4,九個預定義Class實例對象:
1,boolean.class = Boolean.TYPE,
2,char.class = Character.TYPE,
3,byte.class = Byte.TYPE,
4,short.class = Short.TYPE,
5,int.class = Integer.TYPE,
6,long.class = Long.TYPE,
7,float.class = Float.TYPE,
8,double.class = Double.TYPE,
9,void.class = Void.TYPE
5,數組類型的Class對象實例對象
1,Class.isArray()
2,總之,只要好似在源程序中出現的類型,都有各自的Class實例對象,例如,int[]
構造函數的反射應用
一,Constructor類代表某個類中的一個構造方法
二,得到某個類所有的構造方法:
示例:Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
三,得到某一個構造方法://獲取時要用到參數類型
示例:Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
四,創建示例對象:
示例:
1,普通方式:String str = new String(new StringBuffer("abc"));
2,反射方式:
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
String str = (String)constructor.newInstance(new StringBuffer("abc"));
五,Class.newInstance()方法:
1,示例:String obj = (String)Class.forName("java.lang.String").newInstance();
2,該方法內部先得到默認的構造方法,然後用該構造方法來創建實例對象。
3,該方法內部的具體事項代碼,用到了緩存機制來保存默認構造方法的示例對象。
構造函數反射的示例代碼:
package com.itheima.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 反射操作構造函數
* @author wuyong
*
*/
public class ConstrutorDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
Constructor constructor1 = String.class. getConstructor(StringBuffer .class);
String str2 = (String) constructor1.newInstance (/*"abc"*/new StringBuffer("abc"));
System.out .println(str2.charAt (2));
} catch (Exception e) {
e.printStackTrace();
}
}
}
成員變量的反射
Field類表示某個類中的一個成員變量。
語法:
類名 對象名 = new 類();
Field field = 對象名.getClass().getField(屬性名字符串);
此時field對象並不代表具體的值,只代表該類下的成員變量的一個對象,
要獲取到指定對象裏的成員變量的值,語法如下:
field.get(對象名);指明要取的成員變量的值屬於哪個對象裏的。
注:一,getField()和getDeclareField()方法的區別:
getField()方法只能獲取到訪問級別爲可見的屬性,不能獲取到不可見的屬性,若調用該方法
獲取不可見的屬性,會報出:notFoundFieldException的異常。
getDeclareField()方法可以獲取該類中聲明的所有屬性,包括private所修飾的。
二,使用Field 對象下的get(對象名)來獲取該對象下的屬性時,如果屬性的訪問修飾符爲不可見時,
則會報出IllegaAccessException()異常。若要獲得不可見的屬性的話,可以用以下的方式來強行
獲得,Field對象名.setAccessible(true);設置訪問級別爲可見。(稱爲暴力反射)
例子:將一個類中的所有String類型中的"b"全部換成"a";
注:字節碼比較時,要用"=="來比較,因爲是同一份字節碼。
成員變量反射的實例代碼:
package com.itheima.reflect;
import java.lang.reflect.Field;
/**
* 成員變量的反射
* @author wuyong
*
*/
public class FieldDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
ReflectPoint point = new ReflectPoint(3,5 );
Field fieldY = point .getClass().getField("y" );
//fieldY的值是多少?是5,錯!fieldY不是對象身上的變量,而是類上,要用它去取某個對象上對應的值
System.out .println("成員變量Y的值:" + fieldY.get (point));
Field fieldX = point .getClass().getDeclaredField("x" );
//由於X是不可見的,所以就要調setAccessible,並設爲true的方式。用暴力反射的方式來獲得。
fieldX.setAccessible (true);
System.out .println("成員變量X的值:" + fieldX.get (point));
//2)將任意一個對象中的所有String類型的成員變量所對應的字符串內容中的"b"改成"a"。
Field name = point.getClass().getField("name");
System.out.println("替換之前:" + name.get(point));
changeStringValue(point);
System.out .println("替換之後:" + name.get(point));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 定義一個將所有字符串的類成員變量中的”a“ 變成 ”b“的方法
* @param obj
* @throws Exception
*/
private static void changeStringValue(Object obj ) {
try {
Field[] fields = obj. getClass().getFields ();
for(Field field : fields ){
//if(field.getType().equals(String.class)){
if(field .getType() == String .class){
String oldValue = ( String)field .get( obj);
String newValue = oldValue.replace("b" , "a" );
field .set( obj, newValue );
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 被反射操作的類
* @author wuyong
*
*/
class ReflectPoint {
private int x;
public int y;
public String name = "a1b2c3d4c3b2a1";
public ReflectPoint(int x,int y){
this.x = x;
this.y = y;
}
public static void main(String[] args){
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
成員方法的反射
Method代表某一個類中的一個成員方法
語法:
Class cls = TestUtil1.class;
TestUtil1 tu1 = (TestUtil1)cls.newInstance();
Method method = cls.getDeclaredMethod("show", null);//指定獲取方法名爲“show”,並且沒有參數的方法
method.invoke(tu1, null);//在對象tu1中執行無參的方法
Method method2 = cls.getDeclaredMethod("show2", null);
method2.invoke(null, null);//當對象參數爲null時,表示這是一個靜態方法
調用方法的方式:
一,使用對象名.方法名。調用;
二,使用反射來獲得對應的方法對象,來調用invoke(Object obj,Object...args)執行方法;
其中obj是指那一個對象上的方法,args是表示該方法的參數列表
注:專家模式:哪個類擁有該方法需要的私有數據,那這個方法就屬於哪個類。
用反射調用某個類的main方法
語法:
String className = args[0];//獲取第一個參數,首先要設置RunConfigurations中的Arguments爲要執行的類的完整類名。
Class cls2 = Class.forName(className);//通過活動到的完整類名,獲得該類的字節碼
Method main = cls2.getMethod("main", new String[]{}.getClass());//通過字節碼,獲得該類的main方法
/*
* 因爲爲了兼容JDK1.4以下的版本,在傳入參數的時候,會對參數進行裝包;執行時,會對參數進行拆包,如果
* 單純的傳入一個數組的話,則在拆包的時候將每一個元素都拆成一個參數,此時會報出:IllegalArgumentException異常
* 解決方式有以下2種。
*/
//使用一個Object數組,再把傳入的數組參數當成Object數組的一個元素在傳入。
//main.invoke(null, new Object[]{new String[]{"abc","東方","123"}});
//將傳入的參數強轉成Object對象,
main.invoke(null, (Object)new String[]{"abc","東方","123"});
成員方法反射的示例代碼:
package com.itheima.reflect;
import java.lang.reflect.Method;
/**
* 類成員方法的反射
* @author wuyong
*
*/
public class MethodDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
//執行普通方法
String str1 = "asdewfs21";
//str1.charAt(1)
Method methodCharAt = String .class. getMethod("charAt" , int .class);
System.out .println("字符串的第二個字符是:" + methodCharAt.invoke (str1, 1 ));
System.out .println("字符串的第二個字符是:" + methodCharAt.invoke (str1, new Object[]{2 }));
//執行Person的main方法,及靜態方法的演示
// Person.main(new String[]{"111","222","333cc"});
//靜態方法由於不用對象就可以調用,所以對象那個參數是null。
//用反射的方式調用ReflectPoint的main方法,
//注:運行前要設置當前主函數的參數步驟:Run Configureations ---> Arguments --->要調用的類的全名稱如:com.itheima.reflect.ReflectPoint
String startingClassName = args [0];
Method mainMethod = Class .forName(startingClassName).getMethod ("main", String[].class);
//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
mainMethod.invoke (null, (Object )new String []{"111","222" ,"333"}); //數組,jdk會將數組拆成多個參數,從而出現參數不對異常。
}catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 被反射操作的類
* @author wuyong
*
*/
class ReflectPoint {
private int x;
public int y;
public String name = "a1b2c3d4c3b2a1";
public ReflectPoint(int x,int y){
this.x = x;
this.y = y;
}
public static void main(String[] args){
for (int i = 0; i < args.length; i++) {
System.out.println(args[i] + "aa");
}
}
}
數組的反射,及數組與Object
數組的反射
1,具有相同位數(例如:一維對一維)和元素類型的數組屬於同一個類型,即具有相同的Class實例對象
2,代表數字的Class實例對象的getSuperClass()方法返回的父類爲Object類對應的Class
3,基本類型的一維數組可以被當做Object類型使用,不能當作Object[]類型使用;
非基本類型的一維數組,既可以當作Object類型使用,又可以當作Object[]類型使用。
4,Arrays.asList()方法處理int[]和String[]的差異
當處理的是一維的基本數據類型的數組時,其保存的是一維數組的完整名稱,即內存中的地址如:[I @a6d7f3。但二維的基本數據類型數組是可以遍歷的。
當處理的是引用類型的數組,則會將每一個元素都保存到集合中。
5,Array工具類用於完成對數組的反射操作。
6,通過反射獲取元素的步驟:
1,獲取數組的字節碼:對象.getClass()
2,判斷是否是數組:字節碼對象.isArray()
3,獲取數組的長度:getlength(Object obj)
4,遍歷數組元素
5,通過元素.getClass().getName()方法獲取元素對應的類型
數組反射的示例代碼:
package com.itheima.reflect;
import java.lang.reflect.Array;
import java.util.Arrays;
/**
* 數組的反射,及數組與Object對象
* @author wuyong
*
*/
public class ArrayDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int [] a1 = new int []{1, 2,3 };
int [] a2 = new int [4];
int[][] a3 = new int [2][ 3];
String [] a4 = new String[]{"a" ,"b","c"};
System.out .println("a1 = a2" + (a1.getClass () == a2.getClass ())); //true , 相同的類型和相同的維數有相同字節碼對象
//JDK1.5之後,編譯錯誤,因爲出現了泛型是。類型不同,沒有可比性
// System.out .println(a1.getClass () == a4.getClass ());//false
//JDK1.5之後,編譯錯誤維數不同,沒有可比性
// System.out .println(a1.getClass () == a3.getClass ());//false
System.out .println("a1的名稱" + a1.getClass ().getName());//[I
System.out .println("a1的父類名稱" + a1.getClass ().getSuperclass().getName());//java.lang.Object
System.out .println("a4的父類名稱" + a4.getClass ().getSuperclass().getName());//java.lang.Object
Object aObj1 = a1 ;
Object aObj2 = a4 ;
//Object[] aObj3 = a1;//不行,因爲基本數據類型不是Object
Object[] aObj4 = a3; //二維數組,將一維數組作爲一個對象,
Object[] aObj5 = a4; //string本身就是一個對象
System.out .println("a1的toString()形式:" + a1);
System.out .println("a1的toString()形式:" + a4);
//基本類型的一維數組傳入asList方法後,會將數組當成一個對象元素存入集合中。
System.out .println("a1的保存集合後的形式:" + Arrays.asList (a1)); //[I@12344 asList(T...t) 作爲對象傳遞進去了。
//引用類型就可以遍歷
System.out .println(Arrays.asList (a4)); //[a,b,c] asList(Object[] obj) 兼容jdk1.4,作爲數組傳遞進去
// 數組的反射
//傳遞數組調用
printObject(a4 );
//傳遞對象調用
printObject("xyz" );
}
/**
* 數組的反射,如果是數組則遍歷,如果是當對象就輸出
* @param obj
*/
private static void printObject(Object obj ) {
Class clazz = obj. getClass();
if(clazz .isArray()){ //如果是數組,拆開數組,打印裏面的元素
int len = Array.getLength (obj);
for(int i=0;i
---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity開發</a>、<a href="http://www.itheima.com"target="blank">.Net培訓</a>、期待與您交流! ----------------------