反射 ( reflect 反射不是JDK 1.5的新特性,是java1.2開始有的。)
透徹分析反射的基礎-Class
1 java 程序中的各個java類 屬於同一類事物,描述這類事物的java類名就是 Class。
2 Class類 代表java類,它的各個實例對象分別對應什麼呢?
^對應各個類在內存中的字節碼,例如,Person類的字節碼,ArrayList類的字節碼....
^一個類被類加載器加載到內存中,佔用一片存儲空間,這個空間裏面的內容就是類的字節碼,不 同的類的字節碼是不同的,所以它們在內存中的內容是不同的,這一個個空間分別用一個個的對 象來表示,這些對象具有了相同的類型Class.
3 如何得到各個字節碼對應的實例對象(Class類型)
^類名.class,例如 System.class
^對象。getClass(),例如new Daye().getClass
^Class.forName("類名"),例如,Class.forName("java.lang.Date");
面試題:Class.forName("")的作用是什麼?
答:返回字節碼。有兩種方式:
1)如果字節碼在java虛擬機中已經存在,則在內存中找到並返回該字節碼
2)如果字節碼在java虛擬機中不存在,則類加載器將其加載到內存後 返回該字節碼。
4 九個預定義的Class對象
八個基本類型 和void (int.class 、 void.class...)
參看Class.isPrimitive方法的幫助 (是不是基本數據類型)
Int.class==Integer.TYPE
5 數組類型的Class實例對象
Class.isArray()
總之只要在源程序中出現的類型,都有各自的Class實例對象,例如:int[],void...
反射 : 就是把java類中的各種成分映射成相應的java類
Constructor類 代表某個類中的一個構造方法
得到某個類的構造方法:
Constructor [] constructor=
Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//獲得方法時要用到類型
創建實例對象:
通常:String str=new String(new StringBuffer("abc"));
反射:String str=(String)constructor.newInstance(new StringBuffer("abc"));
//調用方法時 要用到與上面相同類型的實例對象
Class.newInstance()方法:
例子:String obj=(String)Class.forName("java.lang.String").newInstance();
該方法內部先得到默認的構造方法(無參的構造方法被緩存了),然後用該構造方法創建實例對象。
該方法用到了緩存機制來 保存默認的構造方法的實例對象。
Field 類 代表某個類中的一個成員變量
問題:得到的Field對象是對應到類上的成員變量,還是對應到對象上的成員變量?
類只有一個,而該類的實例對象有多個,所以Field對象關聯的是類上的成員變量。
所以字段fieldX代表X的定義,而不是具體的X變量。
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}}
public class ReflectTest{
public static void main(String[] args){
try {
ReflectPoint flect1=new ReflectPoint(4,6);
Field fieldY=flect1.getClass().getField("y");
//fieldY的值是多少?5錯:fieldY是類的變量,不是對象的。
System.out.println(fieldY.get(flect1));
/*Field fieldX=flect1.getClass().getField("x");
* x是私有的外部類不能訪問,要想獲得X只能強制反射
*/
Field fieldX=flect1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
System.out.println(fieldX.get(flect1));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();} } }
作業:將任意一個對象中的所有String類型的成員變量所有對應的字符串內容中的“b”改爲“a”
import java.lang.reflect.Field;
public class ReflectTest{
ReflectPoint flect1=new ReflectPoint();
changeStringValue(flect1);
System,out.print(flect1);
public void changeStringValue(Object obj)throws Exception{
Field[] fields=obj.getClass().getFields();
for(Field field:fields){
if(field.getType==String.class){
String oldString=field.get(obj);
String newString=oldString.replace("b","a");
field.set(obj,newString);
}
}
}
}
public class ReflectPoint {
private int x;
public int y;
public String str1="boy";
public String str2="field";
public String str3="hello";
@Override
public String toString(){
return str1+":"+str2+":"+str3;
}
}
Method類 代表某個類中一個成員方法
得到類中某一個方法:
Method methodCharAt=Class.forName("java.lang.String").getMethod("charAt",int.class);
調用方法:
通常:System.out.print(str.charAt(1));
反射:System.out.print(methodCharAt.invoke(str,1));
如果傳遞給Method對象的invoke()方法的第一個參數爲null,說明該Method對象對應的是一個靜態方法
(因爲靜態方法調用時 對象還沒有產生)System.out.print(methodCharAt.invoke(null,1));
jdk1.4和 jdk1.5的區別:
jdk1.5: public Object invoke(Object obj,Object ... args)
jdk1.4: public Object invoke(Object obj,Object[] args)
按jdk1.4的語法需要將一個數組作爲參數傳遞給invoke方法時,數組中的每個元素分別對應被調用方法的一個參數,所有 調用charAt方法的代碼也可以用jdk1.4改寫methodCharAt(str,new Object[]{1});
對接收數組參數的成員方法進行反射
用反射的方式執行某個類中的main方法:
目標:寫一個程序,這個程序能夠根據用戶提供的類名,去執行該類的main()。那麼我們爲什麼要用反 射的方式調用呢?
問題:啓動java程序的main方法的參數時一個字符串數組,通過反射調用main方法時,如何爲invoke 方法傳遞參數呢?按jdk1.5,整個數組是一個參數;按jdk1.4,數組中的每個元素對應一個參數 jdk1.5肯定要兼容jdk1.4的語法,把數組打散成若干個單獨的參數。所以給main方法傳遞參數時 不能使用代碼。
解決辦法:
1 mainMethod.invoke(null,new Object[]{new String[]{"xxx"}})
將數組作爲另一個數組的元素。
2 mainMethod.invoke(null,(object)new String[]{"xxx"});
編譯器會作特殊處理,編譯時不把參數當做數組看待。
目標程序:(運行這段代碼時 我們在運行對話框裏 給類傳遞參數(要運行main方法類的完整名稱)這樣主 函數才能找到類名進行反射)
import java.lang.reflect.Method;
public class MainMethodRunTest {
public static void main(String[] args) {
String mainStaticMethod = args[0];
try {
Method mainMethod=
Class.forName(mainStaticMethod).getMethod("main",String[].class);
mainMethod.invoke(null,new Object[]{new String[]{"12","43","34"}});
//mainMethod.invoke(null, (Object)new String[]{"12","43","34"});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();}}}
class MainMethodTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
for(String arg:args){
System.out.println(arg);
} } }
數組與Object的關係及其反射類型
1 具有相同維數和元素類型的數組屬於同一個類型,即具有相同的Class實例對象
2 代表數組的Class實例對象的getSuperClass()方法返回的父類爲Object類對應的Class
3 基本類型的一位數組可以被當做Object類型使用,不能當作Object[]類型使用;非基本類型的一維數組,既可以當作Object類型也可當作Object[]類型使用。
4 Arrays.asList()方法處理int[]和String[]時的差異
jdk1.4中Arrays.asList(Object[] a) 它將String[]的元素轉化爲List對象,就可以打出了;而
int[] 屬於Object類型 不屬於Object[] 只能由jdk1.5處理。jdk1.5中,Arrays.asList(T ... a)
int[] 被當作一個Object 所以不能打出int[]的各個元素。
int [] a1=new int[]{1,2,8};
String [] a2=new String[]{"a","b","d"};
System.out.println(Arrays.asList(a1)); //結果[[I@C17164]
System.out.println(Arrays.asList(a2)); //結果[a,b,d]
5 Array工具類用於完成對數組的反射操作
例:打印任意對象
import java.util.Arrays;
public class AyyayReflectTest{
public static void main(String [] args){
int [] a1=new int[]{1,5,8};
String [] a2=new String[]{"v","fd","h"};
printObject(a1);
printObject(a2);
printObject("xyz");
}
private static void printObject(Object obj) {
// TODO Auto-generated method stub
Class clazz=obj.getClass();
if(clazz.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);
} } }
ArrayList、HashSet的比較及Hashcode分析
ArrayList按順序存儲元素,可以重複
HashSet存儲的元素不可以重複,當存儲元素時先比較如果存在該元素則不存放。
它們都可以實現Collection接口
**分析Hashcode:
如果要查找一個集合中是否有某個對象,通常我們會逐一取出每個元素與查找的對象比較,直到找到那個用equals方法比較相等的值,停止查找並返回肯定的信息,如果有一萬個元素並且不包含要查找的對象,那麼equals方法會被調用一萬次。有人發明了哈希算法,計算出每個元素的哈希值,並將集合分爲若干區域,這樣查找對象時 只要計算出它的哈希值所對應的區域 在對這個區域進行查找 ,大大的提高了程序的運行效率。當然Hashcode只對用哈希算法存儲的集合有價值,例如HashSet.
注意(內存泄露):當一個對象被存入HashSet集合以後,就不能修改這個對象中的那些參與計算哈希值 的字段了,否則,對象修改後的哈希值與最初存入的不同,這時即使在contains方法使用該對象當前引 用作爲參數去HashSet集合檢索對象,也將 返回 找不到對象的結果,這也會導致無法從HashSet集合中 單獨刪除當前對象,造成內存泄露。
反射的作用——>實現框架功能
框架與工具類的區別:工具類被用戶調用;框架則是調用用戶提供的類
框架要解決的核心問題:寫程序時無法知道要被調用的類名,所以在程序中無法直接new某個類的實例對象,而要用反射方式來做
綜合案例:採用配置文件夾反射的方式創建ArrayList和HashSet的實例對象。
工作間workspacethree 類名ReflectPoint.java和ReflectTest2.java 配置文件config.properties * 配置文件一定要用完整的目錄,這個目錄不是硬編碼,是運算出來的。
* 在javaWeb中 我們通過getRealPath();來獲得絕對路徑 將配置文件放在一個目錄下,之後讓用戶 * 配置這個目錄
* 另一個獲得資源文件的方式,用類加載器
ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast.day1/config.properties")
ReflectTest2.class.getResourceStream("config.peoperties")
內省-->瞭解JavaBean
IntroSpector(內省)-->JavaBean-->特殊的java類
JavaBean:是一種特殊的java類主要用於傳遞數據信息,這種java類中的方法主要用於訪問私有的字段, 且方法名符合某種命名規則
如果在兩個模塊之間傳遞多個信息,可以將這些信息封裝到一個JavaBean中,這種JavaBean的實例對象通常稱之爲值對象(Value Object簡稱VO),這些信息在類中用私有字段來存儲,稱爲類的屬性,
屬性是由方法來判斷的,去掉get或set後 就是JavaBean的屬性,如果第二個字母是小寫則第一個字母變成小寫getTime-->time、gettime-->time、getCPU-->CPU
JavaBean類的好處:
1 在javaEE開發中,經常要使用JavaBean,很多環境要求按JavaBean的方式操作。
2 JDK中提供了對JavaBean進行操作的一些API,這套API 稱爲內省,使用內省操作JavaBean比普通類 的方式更方便
寫一段代碼 讀取javaBean屬性,然後再設置javaBean屬性
代碼1: 用PropertyDescriptor類更簡單(workspace2/javaenhance/cn/itcast/day1/IntroSpectTest.java)
代碼2:採用遍歷BeanInfo的所有屬性方式查找和設置某個ReflectPoint對象的x屬性。在程序中把一個類當作javaBean來看,就是調用IntroSpector.getBeanInfo(),得到的BeanInfo對象封裝了把這個類當作JavaBean看的結果信息
**BeanUtils工具包
由於javaBean的屬性設置和獲取使用的非常多,有人開發出了Beanutils工具包。
將beanutils包的最大jar包commons-beanutils-1.8.3放入工程中(在工程裏新建一個文件夾lib),之 後build path;因爲beanutils包使用到了commons-logging.jar,所以將這個jar同樣的導入工程中。
(1)BeanUtils(獲得設置javabean屬性)
get()返回的屬性結果爲字符串,set可接受任意類型對象,通常爲字符串。
BeanUtils.setProperty(Object,propertyName,value)
BeanUtils.getProperty(object,peopertyName)
例:
BeanUtils.setProperty(rp, "x", "8");
BeanUtils.getProperty(rp,"x");
*操作符合屬性,支持屬性的級聯操作
BeanUtils.setProperty(rp,"birthday.time" ,"111");
System.out.println(BeanUtils.getProperty(rp, "birthday.time"));
*java7新特性:beanutils可以對MAP進行操作。提供了Map與javabean 的轉換
Map map={name:"lisi",age:17};
BeanUtils.setProperty(map,"name","wangsan");
(2)PropertyUtils(另一個獲得設置javabean屬性的簡單類 )
get()返回的屬性爲bean本身類型,set只接受bean屬性的本身類型。
PropertyUtils.setProperty(bean,propertyName ,value);
System.out.println(PropertyUtils.getProperty(bean, propertyName));
例如:
PropertyUtils.setProperty(rp, "x", 5);
System.out.println(PropertyUtils.getProperty(rp, "x"));