黑馬程序員 java高新技術--java5的枚舉、反射的深入講解

                                                                    -------android培訓java培訓java學習型技術博客、期待與您交流! ----------


                                                                     知識點三   java5的枚舉

一、概述:

jdk1.5增加的新特性,本來是去掉C中的枚舉的,jdk1.5後又把很多從c語言中去掉的東西找了回來。
枚舉就是一個類,每一個枚舉元素都是一個對象。

1)爲什麼要有枚舉
問題:要定義星期幾或性別的變量,該怎麼定義?假設用1-7分別表示星期一到星期日,但有人可能會寫成int weekday = 0;或即使使用常量方式也無法阻止意外。
枚舉就是要讓某個類型的變量的取值只能爲若干個固定值中的一個,否則,編譯器就會報錯。枚舉可以讓編譯器在編譯時就可以控制源程序中填寫的非法值,普通變量的方式在開發階段無法實現這一目標。
2)用普通類如何實現枚舉功能,定義一個Weekday的類來模擬枚舉功能。 
私有的構造方法
每個元素分別用一個公有的靜態成員變量表示
可以有若干公有方法或抽象方法。採用抽象方法定義nextDay就將大量的if.else語句轉移成了一個個獨立的類。
3)枚舉的基本應用
舉例:定義一個Weekday的枚舉。
擴展:枚舉類的values,valueOf,name,toString,ordinal等方法(記住,講課時要先於自定義方法前介紹,講課更流暢)
總結:枚舉是一種特殊的類,其中的每個元素都是該類的一個實例對象,例如可以調用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。

分析:如果想在一個類中編寫完各/*英文,簡寫,中文 
Monday, Mon 星期一 
Tuesday, Tue 星期二 
Wednesday, Wed 星期三 
Thursday, Thu 星期四 
Friday, Fri 星期五 
Saturday, Sat 星期六 
Sunday, Sun 星期日*/ 
用鼠標移動,按下,彈起等常量的值無法記憶來說明常量的好處。用下拉列表框來顯示星期幾,就引出了values()方法,用服務器接收的是”MON”字符串,要變成WeekDay.MON對象,就引出了valueOf()方法。
爲了大家更好地理解枚舉,先用普通類來模仿一下枚舉,這可以參照java.awt.Color類。
枚舉類的實例對象個數是有限的,就是那些成員,可以在枚舉類的構造方法中加入監控語句,看到這幾個實例對象被創建出來的過程。
如果想在一個類中編寫完各個枚舉類和測試調用類,那麼可以將枚舉類定義成調用類的內部類。
個枚舉類和測試調用類,那麼可以將枚舉類定義成調用類的內部類。

示例:/*

 注意 
 */
package cn.itcast.day1;

//枚舉類
public abstract class WeekDay1 {
    //把構造方法定義爲私有的
	private WeekDay1(){}
	
	//final static這兩個關鍵詞哪個放在前面都可以
	//加上{}表示,我們必須用這個抽象類的子類來創建實例對象,這個子類沒有名字,稱爲匿名類
	//這個子類的具體代碼見這對大括號
	//把nextDay()方法分別由每個子類去實現。
	public final static WeekDay1 SUN=new WeekDay1(){

		@Override
		public WeekDay1 nextDay() {
			// TODO Auto-generated method stub
			return MON;
		}
		
		
	};
	public final static WeekDay1 MON=new WeekDay1(){

		@Override
		public WeekDay1 nextDay() {
			// TODO Auto-generated method stub
			return SUN;
		}};
	
	//讓每個元素自己寫自己的nextDay()方法
	public abstract WeekDay1 nextDay();
	
	//ctrl+shift+/註釋代碼
	/*public WeekDay nextDay()
	{
		if(this == SUN)
		{
			return MON;
		}else
		{
			return SUN;
		}
		
	}*/
	//打印每個枚舉的值
	public String toString()
	{   
		//這裏應該用if-else判斷,這是簡寫的方法
		//return this==SUN?"SUN":"MON";
		
		if(this==SUN)
		{
			return "SUN";
		}else{
			return "MON";
		}
	}
}


package cn.itcast.day1;

import java.util.Date;

//Enumeration在集合中是迭代器
//內部類可以用四個訪問修飾符修飾,外部類只能是默認不寫或者是public修飾的
//因爲內部類和成員方法和成員變量是平級的
public class EnumTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//定義類型WeekDay,那麼以後用這個類型定義的值就是這個類型規定範圍的值,不是這些值得話,編譯器就不能編過。
		WeekDay1 weekDay = WeekDay1.MON;
		System.out.println(weekDay.nextDay());
		//FRI現在是一個對象,枚舉就相當於一個類,枚舉當中的一個元素就相當這個類的一個實例對象
		WeekDay weekday2=WeekDay.FRI;
		//枚舉自動的幫我們實現了toString()方法
		System.out.println(weekday2);
		//打印出自己的名字
		System.out.println(weekday2.name());
		//ordinal()自己的排行
		System.out.println(weekday2.ordinal());
		//getClass()的到自己的類
		System.out.println(weekday2.getClass());
		//將傳來的參數變成WeekDay的實例對象,把一個串變成對應的枚舉元素。
		System.out.println(WeekDay.valueOf("SUN").toString());
		//我們不能直接遍歷枚舉類,把枚舉中的元素裝入數組中,在對枚舉中的數組進行遍歷
		System.out.println(WeekDay.values().length);
	}
    //定義枚舉所使用的關鍵字是enum
	//只要用到了類,它的靜態變量都能執行
	public enum WeekDay{
		//元素列表一定要放在最前面
		//在枚舉元素的後面跟上一對大括號()就表示調用帶參數的構造方法
		SUN(1),MON(),TUE,WED,THI,FRI,SAT;
		//定義構造方法
		private WeekDay(){System.out.println("first");}
		private WeekDay(int day){System.out.println("second");
		
		//這樣就表示New Date類的一個子類,{}直接可以寫之子類的代碼
		//子類的構造方法又是默認調用父類無參數的構造方法,當然可以調用父類有參的構造方法
		new Date(300){};
	}
	
	//實現帶有抽象方法的枚舉(枚舉就是一個類,枚舉的成員就是這個類的實例對象)
	public  enum TrafficLamp
	{
		//定義三個子類,必須完成父類的 抽象方法
		RED(30){

			@Override
			public TrafficLamp nextLamp() {
				// TODO Auto-generated method stub
				return GREEN;
			}
			
		},
		GREEN(45){

			@Override
			public TrafficLamp nextLamp() {
				// TODO Auto-generated method stub
				return YELLOW;
			}
			
		},
		YELLOW(5){

			@Override
			public TrafficLamp nextLamp() {
				// TODO Auto-generated method stub
				return RED;
			}
			
		};
		//定義一個抽象方法,nextLamp下一個燈
		public abstract TrafficLamp nextLamp();
		private int time;
		private TrafficLamp(int time)
		{
			//把傳進來的值賦值給我的成員變量
			this.time=time;
		}
	}
	}
}
二、枚舉的高級應用:

1.枚舉就相當於一個類,其中也可以定義構造方法、成員變量、普通方法和抽象方法。
2.枚舉元素必須位於枚舉體中的最開始部分,枚舉元素列表的後要有分號與其他成員分隔。把枚舉中的成員方法或變量等放在枚舉元素的前面,編譯器報告錯誤。
3.帶構造方法的枚舉
構造方法必須定義成私有的
如果有多個構造方法,該如何選擇哪個構造方法?
枚舉元素MON和MON()的效果一樣,都是調用默認的構造方法。
4.帶方法的枚舉
定義枚舉TrafficLamp
實現普通的next方法
實現抽象的next方法:每個元素分別是由枚舉類的子類來生成的實例對象,這些子類採用類似內部類的方式進行定義。
增加上表示時間的構造方法
5.枚舉只有一個成員時,就可以作爲一種單例的實現方式。

補充:

寫帶有抽象方法的枚舉步驟和技巧:
        1. enum TrafficLamp{RED,GREEN,YELLOW}
        2. enum TrafficLamp{RED,GREEN,YELLOW;public abstract next();}
        3.enum TrafficLamp{RED{},GREEN{},YELLOW{};public abstract next();}
        4.填充各個{}中的next方法

三、枚舉中涉及的幾個知識點

1、構造器:

1)構造器只是在構造枚舉值的時候被調用。

2)構造器只有私有private,絕不允許有public構造器。這樣可以保證外部代碼無法重新構造枚舉類的實例,這樣是合情合理的,因爲枚舉值是public static final的常量,但是枚舉類的方法和數據域是可以被外部訪問的。

3)構造器可以有多個,調用哪個即初始化相應的值。

2、所有的枚舉類都繼承了Enum方法,方法如下:

1)toString() ---> 返回枚舉量的名稱

2)ordina() ---> 返回枚舉值在枚舉類中的順序,按定義的順序排

3)getClass() ---> 獲取對應的類名

靜態方法:

1)valueOf(String e) ---> 轉爲類中對應的對象,即將字符串轉爲對象

2)values() ---> 獲取數組,將元素放入一個數組中。

四、注意:

1、元素列表即所有的元素項目,必須放在整個類的最前面。

2、元素列表後要有分號

3、枚舉類中的方法爲private

4、在元素後跟上(),就可指定要使用的構造方法,含參數。

5、枚舉類中的每個元素都是它的一個子類對象,當枚舉類中存在abstract方法,須在子類後加{},並在其中實現此方法。

五、總結:

1、匿名內部類比較常用

2、類的方法返回的類型可以是類的類型

3、類中可定義靜態常量,常量的結果就是自己這個類型的實例對象

4、如果枚舉只有一個成員,就可以作爲一種單利的實現方式,對象只有一個。


                                            知識點四  反射的深入講解
一、反射的基礎_Class類
1)對比提問: Person類代表人,它的實例對象就是張三,李四這樣一個個具體的人, Java程序中的各個Java類屬於同一類事物,描述這類事物的Java類名就是Class。對比提問:衆多的人用一個什麼類表示?衆多的Java類用一個什麼類表示?
人->Person
Java類->Class
 2)Class類代表Java類,它的各個實例對象又分別對應什麼呢?
對應各個類在內存中的字節碼,例如,Person類的字節碼,ArrayList類的字節碼,等等。
一個類被類加載器加載到內存中,佔用一片存儲空間,這個空間裏面的內容就是類的字節碼,不同的類的字節碼是不同的,所以它們在內存中的內容是不同的,這一個個的空間可分別用一個個的對象來表示,這些對象顯然具有相同的類型,這個類型是什麼呢?
注意:

①、Class是Java程序中各個Java類的總稱;它是反射的基石,通過Class類來使用反射。

②、Class和class的區別

class:Java中的類用於描述一類事物的共性,該類事物有什麼屬性,沒有什麼屬性,至於這個屬性的值是什麼,則由此類的實例對象確定,不同的實例對象有不同的屬性值。

Class:指的是Java程序中的各個Java類是屬於同一類事物,都是Java程序的類,這些類稱爲Class。例如人對應的是Person類,Java類對應的就是Class。

③、屬性:類名,類的訪問屬性,類所屬包名,字段名稱列表,方法名稱列表等。
3)如何得到各個字節碼對應的實例對象( Class類型)
類名.class,例如,System.class
對象.getClass(),例如,new Date().getClass()
Class.forName("類名"),例如,Class.forName("java.util.Date");

當獲取類名的時候,是不知道此類的名稱的,forName(字符串參數)方法中傳入字符串型的變量作爲對外訪問的入口,即傳入什麼類名就獲得什麼類名,從而得知相應的類名。

注:

forName()是靜態方法,是反射中使用的一種方式獲取字節碼的實例對象。

每個類的字節碼對象只有唯一的一個,如任何字符串對象,對應唯一的String.clas字節碼。

注意:

對象的創建和使用:

①、創建實例對象:不可用new Class()的方式,因爲Class沒有這樣的構造方法。而是將字節碼對象賦值給Class變量。如Class c1 =Person.class。

如Person類,它的字節碼:首先要將Person的java文件編譯爲class文件放於硬盤上,即爲二進制代碼,再將這些代碼加載到內存中,接着用它創建一個個對象。就是把類的字節碼加載進內存中,再用此字節碼創建一個個對象。當有如Person、Math、Date等等的類,那麼這些字節碼就是分別的一個Class對象。即Class c2 =Date.class;。

②、獲得類的字節碼對象:如Class.forName(”java.lang.String”)即獲得String.class。得到這個字節碼對象有兩種情況:

此類已經加載進內存:若要得到此類字節碼,不需要再加載。

此類還未加載進內存:類加載器加載此類後,將字節碼緩存起來,forName()方法返回加載進來的字節碼。

4)九個預定義Class實例對象:
參看Class.isPrimitive方法的幫助
①包括八種基本類型(byte、short、int、long、float、double、char、boolean)的字節碼對象和一種返回值爲void類型的void.class
②Integer.TYPE是Integer類的一個常量,它代表此包裝類型包裝的基本類型的字節碼,所以和int.class是相等的。
基本數據類型的字節碼都可以用與之對應的包裝類中的TYPE常量表示
數組類型的Class實例對象,可以用Class.isArray()方法判斷是否爲數組類型的
Int.class == Integer.TYPE
5)數組類型的Class實例對象
Class.isArray()
總之,只要是在源程序中出現的類型,都有各自的Class實例對象,例如,int[].class、void.class等。

6)方法:

1、static Class forName(String className)

 ---> 返回與給定字符串名的類或接口的相關聯的Class對象。

2、Class getClass()

---> 返回的是Object運行時的類,即返回Class對象即字節碼對象

3、Constructor getConstructor()

---> 返回Constructor對象,它反映此Class對象所表示的類的指定公共構造方法。

4、Field getField(String name)

---> 返回一個Field對象,它表示此Class對象所代表的類或接口的指定公共成員字段。

5、Field[] getFields()

---> 返回包含某些Field對象的數組,表示所代表類中的成員字段。

6、Method getMethod(String name,Class… parameterTypes)

---> 返回一個Method對象,它表示的是此Class對象所代表的類的指定公共成員方法。

7、Method[] getMehtods()

返回一個包含某些Method對象的數組,是所代表的的類中的公共成員方法。

8、String getName()

---> 以String形式返回此Class對象所表示的實體名稱。

9、String  getSuperclass()

---> 返回此Class所表示的類的超類的名稱

10、boolean isArray()

---> 判定此Class對象是否表示一個數組

11、boolean isPrimitive()

---> 判斷指定的Class對象是否是一個基本類型。

12、T newInstance()

---> 創建此Class對象所表示的類的一個新實例。

示例:

public static void fuction()throws Exception{
		String str1 = "abc";
		Class cls1 = str1.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
		System.out.println(cls1 == cls2);//true
		System.out.println(cls1 == cls3);//true
		
		//判斷是否爲基本類型:isPrimitive()
		System.out.println(cls1.isPrimitive());//false
		System.out.println(int.class == Integer.class);//false
		//Integer.TYPE代表包裝類對應的基本數據類型的字節碼
		System.out.println(int.class == Integer.TYPE);//true
		System.out.println(int[].class.isPrimitive());//false
		//判斷是否爲數組類型的
		System.out.println(int[].class.isArray());//true
}

二、深入理解反射的概念

1.反射就是把Java類中的各種成分映射成相應的java類。

2.一個Java類中用一個Class類的對象來表示,一個類中的組成部分:成員變量,方法,構造方法,包等等信息也用一個個的Java類來表示,就像汽車是一個類,汽車中的發動機,變速箱等等也是一個個的類。表示java類的Class類顯然要提供一系列的方法,來獲得其中的變量,方法,構造方法,修飾符,包等信息,這些信息就是用相應類的實例對象來表示,它們是Field、Method、Contructor、Package等等。 
3.一個類中的每個成員都可以用相應的反射API類的一個實例對象來表示,通過調用Class類的方法可以得到這些實例對象後,得到這些實例對象後有什麼用呢?怎麼用呢?這正是學習和應用反射的要點。

三、反射中的各種類:
1.Constructor類
1)Constructor類代表某個類中的一個構造方法
2)得到某個類所有的構造方法:
例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
3)得到某一個構造方法:
例子:     
Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);①
//獲得方法時要用到類型
4)創建實例對象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));②
//調用獲得的方法時要用到上面相同類型的實例對象

注意:上面的兩個地方①②都要用到StringBuffer,這必須是一致的。

第①個是指定要帶StringBuffer參數類型的構造方法,即所需使用的是含StringBuffer類型的構造方法。

第②個是用這個構造方法創建對象,要傳入的參數類型是StringBuffer。

5)Class.newInstance()方法:
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
該方法內部先得到默認的無參數的構造方法,然後用該構造方法創建實例對象。
該方法內部的具體代碼是怎樣寫的呢?用到了緩存機制來保存默認構造方法的實例對象。

示例:
//new String(new StringBuffer("abc"));  
Constructor constructor1 =   
String.class.getConstructor(StringBuffer.class);  
String str2 =(String)constructor1.newInstance(new StringBuffer("abc"));
System.out.println(str2);
//Class.newInstrance創建不帶參數的構造方法  
String str3 =   (String)Class.forName("java.lang.String").newInstance();  
System.out.println("str3:"+str3);

  2.Field
1)Field類代表某個類中的一個成員變量的反射
示例:
public class ReflectPoint {
//定義兩個變量
private int x;
public int y;
        public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
        @Override
public String toString(){
return str1 + ":" + str2 + ":" + str3;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
public class FieldTest(){  
ReflectPoint pt1 = new ReflectPoint(3,5);  
    //fieldX和fieldY並不是對象身上的變量,而是類上的  
    //要用它去取某個對象上的對應的值,傳入什麼對象,就取相應對象的值。  
    Field fieldY = pt1.getClass().getField("y");  
    System.out.println(fieldY.get(pt1));  
    //獲取私有的成員變量  
Field fieldX = pt1.getClass().getDeclaredField("x");  
    fieldX.setAccessible(true);  
    System.out.println(fieldX.get(pt1));  
}  
2)演示用eclipse自動生成Java類的構造方法
右鍵Source(alt+shift+S)-->Generate Constructor using Fileds;
3)問題:得到的Field對象是對應到類上面的成員變量,還是對應到對象上的成員變量?類只有一個,而該類的實例對象有多個,如果是與對象關聯,哪關聯的是哪個對象呢?所以字段fieldX 代表的是x的定義,而不是具體的x變量。
示例代碼:
ReflectPoint point = new ReflectPoint(1,7);
Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");
Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);
System.out.println(x.get(point));
4)獲取成員變量:

1)獲取公有的成員變量:

getField(String name)和get(變量)

2)獲取私有的成員變量:暴力反射

getDeclared(String name)

setAccessible(boolean b),將b設爲true即可

get(變量)

舉例:

//替換字符   

private static void changeStringValue(Object obj) throws Exception {  
    Field[] fields = obj.getClass().getFields();  
    for(Field field : fields){  
        //此處需要用==比較,因爲是同一份字節碼對象  
        if(field.getType() == String.class){  
            String oldValue = (String)field.get(obj);  
            String newValue = oldValue.replace('b','a');  
            field.set(obj, newValue);  
        }  
    }  
}  
5)作業:將任意一個對象中的所有String類型的成員變量所對應的字符串內容中的"b"改成"a"。
參考答案如下:
class Xxx
{
String name="abc";
String email="abd";
int x = 5;
}
func(Object obj)
{
Field [] fields = obj.getClass().getDeclaredFields();
for(Field field : fields)
{
if(field.getType()==java.lang.String.class)
{
field.setAccesible(true);
String original = (String)field.get(obj);
field.set(obj,original.replaceAll("b","a");
}
}
}
6)思考與總結:
一個問題,我把自己的變量定義成private,就是不想讓人家訪問,可是,現在人家用暴力反射還是能夠訪問我,這說不通啊,能不能讓人家用暴力反射也訪問不了我。首先,private主要是給javac編譯器看的,希望在寫程序的時候,在源代碼中不要訪問我,是幫組程序員實現高內聚、低耦合的一種策略。你這個程序員不領情,非要去訪問,那我攔不住你,由你去吧。同樣的道理,泛型集合在編譯時可以幫助我們限定元素的內容,這是人家提供的好處,而你非不想要這個好處,怎麼辦?繞過編譯器,就可以往集合中存入另外類型了。:
3.Method類
1)Method類代表某個類中的一個成員方法
2)得到類中的某一個方法:
例子:    Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
3)調用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1)); 
如果傳遞給Method對象的invoke()方法的第一個參數爲null,這有着什麼樣的意義呢?說明該Method對象對應的是一個靜態方法!
4)jdk1.4和jdk1.5的invoke方法的區別:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的語法,需要將一個數組作爲參數傳遞給invoke方法時,數組中的每個元素分別對應被調用方法中的一個參數,所以,調用charAt方法的代碼也可以用Jdk1.4改寫爲 charAt.invoke(“str”, new Object[]{1})形式。
5)

專家模式:誰調用這個數據,就是誰在調用它的專家。

如人關門:

調用者:是門調用管的動作,對象是門,因爲門知道如何執行關的動作,通過門軸之類的細節實現。

指揮者:是人在指揮門做關的動作,只是給門發出了關的信號,讓門執行。

總結:變量使用方法,是方法本身知道如何實現執行的過程,也就是“方法對象”調用方法,才執行了方法的每個細節的。

6)用反射方式執行某個類中的main方法

原理:

首先要明確爲何要用反射:在寫源程序時,並不知道使用者傳入的類名是什麼,但是雖然傳入的類名不知道,而知道的是這個類中的方法有main這個方法,所以可以通過反射的方式,通過使用者傳入的類名(可定義字符串型變量作爲傳入類名的入口,通過這個變量代表類名),內部通過傳入的類名獲取其main方法,然後執行相應的內容。

1)目標:
寫一個程序,這個程序能夠根據用戶提供的類名,去執行該類中的main方法。用普通方式調完後,大家要明白爲什麼要用反射方式去調啊?
2)問題:
啓動Java程序的main方法的參數是一個字符串數組,即public static void main(String[] args),通過反射方式來調用這個main方法時,如何爲invoke方法傳遞參數呢?按jdk1.5的語法,整個數組是一個參數,而按jdk1.4的語法,數組中的每個元素對應一個參數,當把一個字符串數組作爲參數傳遞給invoke方法時,javac會到底按照哪種語法進行處理呢?jdk1.5肯定要兼容jdk1.4的語法,會按jdk1.4的語法進行處理,即把數組打散成爲若干個單獨的參數。所以,在給main方法傳遞參數時,不能使用代碼mainMethod.invoke(null,new String[]{“xxx”}),javac只把它當作jdk1.4的語法進行理解,而不把它當作jdk1.5的語法解釋,因此會出現參數類型不對的問題。
3)解決辦法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"}); ,編譯器會作特殊處理,編譯時不把參數當作數組看待,也就不會數組打散成若干個參數了。

示例:

//Method類演示  
private static void methodTest(String [] args) throws Exception {  
    String str1 = "abc";  
    //一般方法:  
    System.out.println(str1.charAt(1));  
    //反射方法 :  
    Method methodCharAt =  
        Class.forName("java.lang.String").getMethod("charAt",int.class);  
    System.out.println(methodCharAt.invoke(str1,1));  
      
    //用反射方式執行某個main方法  
    //一般方式:  
    Test.main(new String[]{"111","222","333"});  
    System.out.println("-------");  
      
    //反射方式:  
    String startingClassName = args[0];  
    Method methodMain =  
        Class.forName(startingClassName).getMethod("main",String[].class);  
        //方案一:強制轉換爲超類Object,不用拆包  
        methodMain.invoke(null,(Object)new String[]{"111","222","333"});  
        //方案二:將數組打包,編譯器拆包後就是一個String[]類型的整體  
        methodMain.invoke(null,new Object[]{new String[]{"111","222","333"}});  
    }  
//定義一個測試類  
class Test{  
    public static void main(String [] args){  
        for(String arg : args){  
            System.out.println(arg);  
        }  
    }  
}  

四、數組的反射

1、數組字節碼的名字:有[和數組對應類型的縮寫,如int[]數組的名稱爲:[I

2、基本數據類型的一維數組不能轉換爲Object數組,如:

int[] a = new int[3];Object[] obj= a;這樣是不成立的。

3、如何得到某個數組中的某個元素的類型:

例:int a = newint[3];Object[] obj= new Object[]{”ABC”,1};

無法得到某個數組的具體類型,只能得到其中某個元素的類型,如

Obj[0].getClass().getName()得到的是java.lang.String

若通過b.getClass().getName(),結果是:[Ljava.lang.Object;

4.具有相同維數和元素類型的數組屬於同一個類型,即具有相同的Class實例對象。
5.代表數組的Class實例對象的getSuperClass()方法返回的父類爲Object類對應的Class。
6.基本類型的一維數組可以被當作Object類型使用,不能當作Object[]類型使用;非基本類型的一維數組,既可以當做Object類型使用,又可以當做Object[]類型使用。
7.Arrays.asList()方法處理int[]和String[]時的差異。
Array工具類用於完成對數組的反射操作。

補充:

Object[] 與String[]沒有父子關係,Object與String有父子關係,所以new Object[]{“aaa”,”bb”}不能強制轉換成new String[]{“aaa”,”bb”};,Object x = “abc”能強制轉換成String x = “abc”。
main.invoke(null, (Object)(new Object[]{“aaa”,“xxx”}));不能調用public static void main(String [] args)
1.
看Class類幫助文檔開始部分的介紹。
String[] a1 = new String[8];
String[] a2 = new String[9];
String[][] a3 = new String[6][4];
().getName());
System.out.println(a3.getClass().getName());
System.out.println(a1.getClass() == a2.getClass());
System.out.println(a1.getClass() == a3.getClass());
2.
看getSuperClass方法的幫助
System.out.println(a1.getClass().getSuperclass().getName());

3.
int[] a = new int[3];
Object obj = a;
//Object[] obj1 = a //有錯!
Object[] obj3 = a1
Object obj4 = a3;
if(obj4 instanceof String[][]){
System.out.println(obj1.getClass().isArray());
}
**在這裏分析研究Arrays.asList()方法處理int[]和String[]時的差異,以及Arrays.deepToString()方法不能處理int[],但能處理String[]的原因。
4.
private static void printObject(Object obj) {
if(obj.getClass().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);
}
}

5.似乎沒有辦法直接得到,需要取出每個元素對象,然後再對各個對象進行判斷,因爲其中每個具體元素的類型都可以不同,例如Object[] x = new Object[]{“abc”,Integer.Max}。Object[] 與String[]沒有父子關係,Object與String有父子關係,所以new Object[]{“aaa”,”bb”}不能強制轉換成new String[]{“aaa”,”bb”};,Object x = “abc”能強制轉換成String x = “abc”。
main.invoke(null, (Object)(new Object[]{“aaa”,“xxx”}));不能調用public static void main(String [] args)

示例:

public static void arrayTest()throws Exception {  
    int[] a1 = new int[]{1,2,3};  
    int[] a2 = new int[4];  
    int[][] a3 = new int[2][3];  
    Integer[] ai = new Integer[3];  
    String[] a4 = new String[]{"a","b","c"};  
      
    System.out.println(a1.getClass() == a2.getClass());  
    System.out.println((Object)a1.getClass() == (Object)a3.getClass());  
          
    System.out.println(a3[0].getClass() == a1.getClass());  
    System.out.println(a3[0].getClass().getSuperclass().getName());  
    System.out.println(a1.getClass().equals(a3.getClass()));  
    System.out.println(a1.getClass().equals(a4.getClass()));  
    System.out.println(a1.getClass().getName());  
    System.out.println("----:" + a1.getClass());  
    System.out.println(a1.getClass().getSuperclass().getName());  
    System.out.println(a2.getClass().getSuperclass().getName());  
          
    Object obj1 = a1;  
    Object obj2 = a2;  
    //int基本數據類型不是Object的  
    //Object[] obj3 = a1;  
    Object[] obj4 = a3;  
    Object[] obj5 = a4;  
          
    System.out.println(a1);  
    System.out.println(a4);  
      
    System.out.println(Arrays.asList(a1));  
    System.out.println(Arrays.asList(a4));  
    System.out.println("-------");  
    int[] a = new int[3];  
    Object[] obj = new Object[]{"abc",new Integer(1)};  
    System.out.println(a.getClass().getName());  
    System.out.println(obj.getClass().getName());  
    System.out.println(obj[0].getClass().getName());  
}  

四、

ArrayList_HashSet的比較及Hashcode分析

覆寫hashCode()方法的意義:只有存入的是具有hashCode算法的集合的,覆寫hashCode()方法纔有價值。

1、哈希算法的由來:

若在一個集合中查找是否含有某個對象,通常是一個個的去比較,找到後還要進行equals的比較,對象特別多時,效率很低,通過哈希算法,將集合分爲若干個區域,每個對象算出一個哈希值,可將哈希值分組(一般模32爲一組),每組對應某個存儲區域,依一個對象的哈希碼即可確定此對象對應區域,從而減少每個對象的比較,只需在指定區域查找即可,從而提高從集合中查找元素的效率。

示意圖:


2、如果不存入是hashCode算法的集合中,那麼則不用複寫此方法。

3、只有類的實例對象要被採用哈希算法進行存入和檢索時,這個類才需要按要求複寫hashCode()方法,即使程序可能暫時不會用到當前類的hashCode()方法,但是爲提供一個hashCode()方法也不會有什麼不好,沒準以後什麼時候就會用到這個方法,所以通常要求hashCode()和equals()兩者一併被覆蓋。

4、提示:

1)若同類兩對象用equals()方法比較的結果相同時,他們的哈希碼也必須是相等的,但反過來就不成立了,如”BB”和”Aa”兩字符串用equals()比較式不相等的,但是他們的哈希值是相等的。

2)當一個對象被存儲進HashSet集合中,就不能再修改參與計算哈希值的字段,否則對象被修改後的哈希值與最初被存入的HashSet集合中的哈希值就不同了。在這種情況下,即使contains()方法是用對象的當前引用作爲參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也導致無法從HashSet集合中單獨刪除當前對象,從而造成內存泄露。

簡單說,之前存入的對象和修改後的對象,是具有不同的哈希值,被認爲是不同的兩個對象,這樣的對象不再使用,又不移除,而越來越多,就會導致內存泄露。

補充:

內存泄露:某些對象不再使用了,佔用着內存空間,並未被釋放,就會導致內存泄露;也就是說當程序不斷增加對象,修改對象,刪除對象,日積月累,內存就會用光了,就導致內存溢出。

3)對象在調用方法時,對象會先進行一次自身hashCode()方法的調用,再進行操作方法。

示例:

import java.util.ArrayList;  
import java.util.Collection;  
import java.util.HashSet;  
  
public class ReflectTest2 {  
    public static void main(String [] args){  
        Collection cons = new HashSet();  
        ReflectPoint pt1 = new ReflectPoint(3,3);  
        ReflectPoint pt2 = new ReflectPoint(5,5);  
        ReflectPoint pt3 = new ReflectPoint(3,3);  
  
        cons.add(pt1);  
        cons.add(pt2);  
        cons.add(pt3);  
        cons.add(pt1);  
        cons.remove(pt1);  
        System.out.println(cons.size());  
    }  
}  
  
public class ReflectPoint {  
    private int x;  
    public int y;  
    public String str1 = "ball";  
    public String str2 = "basketball";  
    public String str3 = "itcast";  
      
    public ReflectPoint(int x, int y) {  
        super();  
        this.x = x;  
        this.y = y;  
    }  
      
    public int hashCode() {  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + x;  
        result = prime * result + y;  
        //System.out.println("demo...");//測試  
        return result;  
    }  
  
    public boolean equals(Object obj) {  
        if (this == obj)  
            return true;  
        if (obj == null)  
            return false;  
        if (getClass() != obj.getClass())  
            return false;  
        ReflectPoint other = (ReflectPoint) obj;  
        if (x != other.x)  
            return false;  
        if (y != other.y)  
            return false;  
        return true;  
    }  
  
    public String toString(){  
        return str1+";" + str2 + ";" + str3;  
    }  
}  

五、

反射的作用->實現框架功能

1.

什麼是框架,例如,我們要寫程序掃描.java文件中的註解,要解決哪些問題:讀取每一樣,在每一箇中查找@,找到的@再去查詢一個列表,如果@後的內容出現在了列表中,就說明這是一個我能處理和想處理的註解,否則,就說明它不是一個註解或者說至少不是一個我感興趣和能處理的註解。接着就編寫處理這個註解的相關代碼。現在sun提供了一個apt框架,它會完成所有前期工作,只需要我們提供能夠處理的註解列表,以及處理這些註解的代碼。Apt框找到我們感興趣的註解後通知或調用我們的處理代碼去處理。

2.框架與框架要解決的核心問題
1)框架

我做房子賣給用戶住,由用戶自己安裝門窗和空調,我做的房子就是框架,用戶需要使用我的框架,把門窗插入進我提供的框架中。框架與工具類有區別,工具類被用戶的類調用,而框架則是調用用戶提供的類。

你做的門調用鎖,鎖是工具,你做的門被房子調用,房子是框架,房子和鎖都是別人提供的。
2)框架要解決的核心問題
我在寫框架(房子)時,你這個用戶可能還在上小學,還不會寫程序呢?我寫的框架程序怎樣能調用到你以後寫的類(門窗)呢?
因爲在寫才程序時無法知道要被調用的類名,所以,在程序中無法直接new 某個類的實例對象了,而要用反射方式來做。
3.綜合案例
先直接用new  語句創建ArrayList和HashSet的實例對象,演示用eclipse自動生成 ReflectPoint類的equals和hashcode方法,比較兩個集合的運行結果差異。
然後改爲採用配置文件加反射的方式創建ArrayList和HashSet的實例對象,比較觀察運行結果差異。
引入了elipse對資源文件的管理方式的講解。

4、簡單框架程序的步驟:

1)右擊項目名-->File-->命名,寫入鍵值對:className=java.util.ArrayList,等號右邊的可以自己定義集合的名稱,即用戶可以對此記事本修改成自己的類名。

2)代碼實現,加載此文件:

①將文件讀取到讀取流中,一定要用完整的路徑,可以使用getRealPath()方法獲取路徑名,再加上自己定義的文件夾名。

②用Properties類的load()方法將流加載經內存,即提取文件中的信息。

③關閉流:關閉的是讀取流,因爲流中的數據已經加載進內存。

3)通過getProperty()方法獲取類名屬性,將傳入的類名賦值給指定變量。

4)用反射的方式,創建對象newInstance()

5)進行相關的具體操作。

5.類加載器

1)簡述:類加載器是將.class的文件加載經內存,也可將普通文件中的信息加載進內存。

2)文件的加載問題:

①eclipse會將源程序中的所有.java文件加載成.class文件,以確保編譯,然後放到classPath指定的目錄中去。並且會將非.java文件原封不動的複製到.class指定的目錄中去。在真正編譯的時候,使用classPath目錄中的文件,即放置.class文件的目錄。

②寫完程序是要講配置文件放到.class文件目錄中一同打包,這些都是類加載器加載的,資源文件(配置文件)也同樣加載了配置文件。

③框架中的配置文件都要放到classPath指定的文件夾中,原因是它的內部就是用類加載器加載的文件。

3)資源文件的加載:是使用類加載器。

①由類加載器ClassLoader的一個對象加載經內存,即用getClassLoader()方法加載。若要加載普通文件,可用getResourseAsStream(String name)在classPath的文件中逐一查找要加載的文件。

②在.class身上也提供了方法來加載資源文件,其實它內部就是先調用了Loader方法,再加載的資源文件。

如:Reflect.class.getResourseAsStream(String name)

4)配置文件的路徑問題:

第一、用絕對路徑,通過getRealPath()方法運算出來具體的目錄,而不是內部編碼出來的。

一般先得到用戶自定義的總目錄,在加上自己內部的路徑。可以通過getRealPath()方法獲取文件路徑。對配置文件修改是需要要儲存到配置文件中,那麼就要得到它的絕對路徑纔行,因此,配置文件要放到程序的內部。

第二、name的路徑問題:

①如果配置文件和classPath目錄沒關係,就必須寫上絕對路徑,

②如果配置文件和classPath目錄有關係,即在classPath目錄中或在其子目錄中(一般是資源文件夾resource),那麼就得寫相對路徑,因爲它自己瞭解自己屬於哪個包,是相對於當前包而言的。

示例:

配置文件內容:

className=java.util.ArrayList

補充知識點:

程序中不處理異常,而是main方法聲明拋出異常,便於大家可以集中看主要的關鍵代碼。

Class類也提供getResourceAsStream方法的比喻:如果你每次都找我給你商店買可樂,那我還不如直接向你買可樂,即直接提供一個買可樂的方法給你。

程序示例:

import java.io.FileInputStream;  
import java.io.InputStream;  
import java.util.ArrayList;  
import java.util.Collection;  
import java.util.HashSet;  
import java.util.Properties;  
  
public class ReflectTest2 {  
    public static void main(String [] args)throws Exception{  
        //讀取系統文件到讀取流中  
        //方式一:  
        //InputStream ips = new FileInputStream("config.propert");  
        /*getRealPath()--得到完整的路徑//如:金山詞霸/內部 
         * 一定要用完整的路徑,但完整的路徑不是硬編碼出來的,而是運算出來的。*/  
        //方式二:  
        //InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/text1/config.propert");  
        //方式三:  
            //第一種:配置文件(資源文件)在當前包中  
        InputStream ips = ReflectTest2.class.getResourceAsStream("resourse/config.propert");  
            //第二種:配置文件(資源文件)不在當前包中,和此包沒太大關係  
        //InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/test2/resourse/config.properties");  
          
        //加載文件中的鍵值對  
        Properties props = new Properties();  
        props.load(ips);  
        //關閉資源,即ips調用的那個系統資源  
        //注意:關閉的是ips操作的流,加載進內存後,就不再需要流資源了,需要關閉  
        ips.close();  
        //定義變量,將文件中的類名賦值給變量  
        String className = props.getProperty("className");  
        //通過變量,創建給定類的對象  
        Collection cons =   
                (Collection)Class.forName(className).newInstance();  
          
        //將元素添加到集合中  
        /*Collection cons = new HashSet();*/  
        ReflectPoint pt1 = new ReflectPoint(3,3);  
        ReflectPoint pt2 = new ReflectPoint(5,5);  
        ReflectPoint pt3 = new ReflectPoint(3,3);  
        cons.add(pt1);  
        cons.add(pt2);  
        cons.add(pt3);  
        cons.add(pt1);  
        //移除元素  
        cons.remove(pt1);  
        System.out.println(cons.size());  
    }  
}  

六’、內省與JavaBean

1.JavaBean(存在於java.bean包中)

1)JavaBean是一種特殊的Java類,主要用於傳遞數據信息,這種java類中的方法主要用於訪問私有的字段,且方法名符合某種命名規則。

2)如果要在兩個模塊之間傳遞多個信息,可以將這些信息封裝到一個JavaBean中,這種JavaBean的實例對象通常稱之爲值對象(Value Object,簡稱VO)。這些信息在類中用私有字段來存儲,如果讀取或設置這些字段的值,則需要通過一些相應的方法來訪問,大家覺得這些方法的名稱叫什麼好呢?JavaBean的屬性是根據其中的setter和getter方法來確定的,而不是根據其中的成員變量。如果方法名爲setId,中文意思即爲設置id,至於你把它存到哪個變量上,用管嗎?如果方法名爲getId,中文意思即爲獲取id,至於你從哪個變量上取,用管嗎?去掉set前綴,剩餘部分就是屬性名,如果剩餘部分的第二個字母是小寫的,則把剩餘部分的首字母改成小的。

setId()的屬性名->id
isLast()的屬性名->last
setCPU的屬性名是什麼?->CPU
getUPS的屬性名是什麼?->UPS
3)總之,一個類被當作javaBean使用時,JavaBean的屬性是根據方法名推斷出來的,它根本看不到java類內部的成員變量。
4)一個符合JavaBean特點的類可以當作普通類一樣進行使用,但把它當JavaBean用肯定需要帶來一些額外的好處,我們纔會去了解和應用JavaBean!好處如下:
①在Java EE開發中,經常要使用到JavaBean。很多環境就要求按JavaBean方式進行操作,別人都這麼用和要求這麼做,那你就沒什麼挑選的餘地!
②JDK中提供了對JavaBean進行操作的一些API,這套API就稱爲內省。如果要你自己去通過getX方法來訪問私有的x,怎麼做,有一定難度吧?用內省這套api操作JavaBean比用普通類的方式更方便。

2.內省

概述:

1、IntroSpector:即內省,是對內部進行檢查,瞭解更多的底層細節。

2、內省的作用:主要針對JavaBean進行操作。

示例:

package cn.itcast.text1;  
  
import java.beans.BeanInfo;  
import java.beans.IntrospectionException;  
import java.beans.Introspector;  
import java.beans.PropertyDescriptor;  
import java.lang.reflect.InvocationTargetException;  
import java.lang.reflect.Method;  
  
public class IntroSpectorTest {  
  
    /** 
     * @param args 
     */  
    /* 
     * public static void main(String[] args) throws Exception { 
     
        // TODO Auto-generated method stub 
        ReflectPoint pt1 = new ReflectPoint(3,5); 
        String propertyName = "x"; 
        //"x"-->"X"-->"getX"-->MethodGetX--> 
        //內省的方式: 
        //屬性描述符:PropertyDescriptor 
        //get屬性信息 
        PropertyDescriptor pd = 
                new PropertyDescriptor(propertyName,pt1.getClass()); 
        Method methodGetX = pd.getReadMethod(); 
        Object retVal = methodGetX.invoke(pt1); 
        System.out.println(retVal); 
        //set屬性信息 
        Object value = 7; 
        PropertyDescriptor pd2 = 
                new PropertyDescriptor(propertyName,pt1.getClass()); 
        Method methodSetX = pd2.getWriteMethod(); 
        methodSetX.invoke(pt1,value); 
         
        System.out.println(pt1.getX()); 
     } 
     */  
    //上面的get或set代碼分別通過選中要重構的代碼,通過右擊選重構獲得get和set方法:  
    public static void main(String[] args) throws Exception {  
        // TODO Auto-generated method stub  
        ReflectPoint pt1 = new ReflectPoint(3,5);  
        String propertyName = "x";  
        //一般方式:"x"-->"X"-->"getX"-->MethodGetX-->  
        //內省方式:  
        //通過get和set方法獲取屬性值  
        Object retVal = getProperty(pt1, propertyName);  
        System.out.println(retVal);  
          
        Object value = 7;  
        setProperty(pt1, propertyName, value);  
        System.out.println(pt1.getX());  
    }  
      
        //設置屬性值的方法             //此處的類型爲Object,通用,下同  
    private static void setProperty(Object rf, String propertyName,  
            Object value) throws IntrospectionException,  
            IllegalAccessException, InvocationTargetException {  
        //創建屬性描述符對象,將屬性名稱和加載文件等信息寫入其中  
        PropertyDescriptor pd =  
                new PropertyDescriptor(propertyName,rf.getClass());  
        //通過反射的方法類Method,獲取屬性所對應的set方法  
        Method methodSetX = pd.getWriteMethod();  
        methodSetX.invoke(rf, value);  
    }  
    //獲取屬性值的方法  
    private static Object getProperty(Object rf, String propertyName)  
            throws IntrospectionException, IllegalAccessException,  
            InvocationTargetException {  
        //創建屬性描述符對象,獲取屬性所對應的名稱和加載文件等信息  
        PropertyDescriptor pd =  
                new PropertyDescriptor(propertyName,rf.getClass());  
        //通過反射的方法類Method,獲取屬性所對應的get方法  
        Method methodGetX = pd.getReadMethod();  
        Object retVal = methodGetX.invoke(rf);  
        return retVal;  
    }  
} 

3.

對JavaBean的複雜內省操作:

1)在IntroSpector類中有getBeanInfo(Class cls)的方法。

2)獲取Class對象的Bean信息,返回的是BeanInfo類型。

3)BeanInfo類中有getPropertyDescriptors()的方法,可獲取所有的BeanInfo的屬性信息,返回一個PropertyDescriptor[]。

4)在通過遍歷的形式,找出與自己想要的那個屬性信息。

示例:(改寫get方法)

…  
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());  
        PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();  
        Object value = null;  
        for(PropertyDescriptor pd : pds){  
            if(pd.getName().equals(propertyName)){  
                Method methodGetX = pd.getReadMethod();  
                value = methodGetX.invoke(pt1);  
                break;  
            }  
        }  
…  

這種方式要比上面的方法複雜些。

4.內省綜合案例

1)演示用eclipse自動生成 ReflectPoint類的setter和getter方法。
2)直接new一個PropertyDescriptor對象的方式來讓大家瞭解JavaBean API的價值,先用一段代碼讀取JavaBean的屬性,然後再用一段代碼設置JavaBean的屬性。
3)演示用eclipse將讀取屬性和設置屬性的流水帳代碼分別抽取成方法:
只要調用這個方法,並給這個方法傳遞了一個對象、屬性名和設置值,它就能完成屬性修改的功能。
得到BeanInfo最好採用“obj.getClass()”方式,而不要採用“類名.class”方式,這樣程序更通用。
4)採用遍歷BeanInfo的所有屬性方式來查找和設置某個RefectPoint對象的x屬性。在程序中把一個類當作JavaBean來看,就是調用IntroSpector.getBeanInfo方法, 得到的BeanInfo對象封裝了把這個類當作JavaBean看的結果信息。

5、BeanUtils工具包:

1.、BeanUtils等工具包都是由Apache開源組織提供的,爲了便於開發。

2、BeanUtils可以將8種基本數據類型進行自動的轉換,因此對於非基本數據類型,就需要註冊轉換器Converter,這就需要ConverUtils包

3.用eclipse如何加入jar包,先只是引入beanutils包,等程序運行出錯後再引入logging包。

4.去掉JavaBean(ReflectPoint)的public修飾符時,BeanUtils工具包訪問javabean屬性時出現的問題。


2、好處:

1)提供的set或get方法中,傳入的是字符串,返回的還是字符串,因爲在瀏覽器中,用戶輸入到文本框的都是以字符串的形式發送至服務器上的,所以操作的都是字符串。也就是說這個工具包的內部有自動將整數轉換爲字符串的操作。

2)支持屬性的級聯操作,即支持屬性鏈。如可以設置:人的腦袋上的眼鏡的眼珠的顏色。這種級聯屬性的屬性連如果自己用反射,那就很困難了,通過這個工具包就可以輕鬆調用。

3、可以和Map集合進行相互轉換:可將屬性信息通過鍵值對的形式作爲Map集合存儲(通過staticjava.util.Map describe(java.lang.Object bean)的方法),也可以將Map集合轉換爲JavaBean中的屬性信息(通過static voidpopulate(java.lang.Object bean, java.util.Map properties)的方法)。

4、示例:

1)設置和獲取屬性值:

import java.lang.reflect.InvocationTargetException;  
import java.text.ParseException;  
import java.text.SimpleDateFormat;  
import java.util.Date;  
import java.util.Map;  
import java.util.TreeMap;  
  
import org.apache.commons.beanutils.BeanUtils;  
import org.apache.commons.beanutils.ConversionException;  
import org.apache.commons.beanutils.ConvertUtils;  
import org.apache.commons.beanutils.Converter;  
import org.apache.commons.beanutils.PropertyUtils;  
import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;  
import org.junit.Test;  
  
public class BeanUtilDemo {  
  
    /** 
     * BeanUtils使用 
     */  
  
    @Test   
    public void test1() throws Exception{  
        //創建對象,設置屬性值  
        Person p = new Person();  
        BeanUtils.setProperty(p, "name", "zzz");  
        String name = BeanUtils.getProperty(p, "name");  
        System.out.println(name);  
    }  
      
    @Test   
    public void test2() throws Exception{  
        //創建對象,傳入屬性值  
        Person p = new Person();  
        String name = "wangwu";  
        String age = "23";  
        String hight = "173.5";  
        //設置屬性值  
        BeanUtils.setProperty(p, "name", name);  
        BeanUtils.setProperty(p, "age", age);  
        BeanUtils.setProperty(p, "hight", hight);  
        //獲取屬性值  
        System.out.println(BeanUtils.getProperty(p, "name"));  
        System.out.println(BeanUtils.getProperty(p, "age"));  
        System.out.println(BeanUtils.getProperty(p, "hight"));  
    }  
2)未註冊的屬性值的獲取和設置

//獲取未註冊的屬性,即非八種基本數據類型的引用類型  
//private Date birthday  
@Test   
public void test3() throws Exception{  
    Person p = new Person();  
    String name = "wangwu";  
    String age = "23";  
    String hight = "173.5";  
    String birthday = "1990-09-09";  
    ConvertUtils.register(new Converter() {  
        //註冊器Converter接口中方法的重寫  
        @Override  
        public Object convert(Class type, Object value) {  
            if(value == null)  
                return null;  
            if(!(value instanceof String))  
                throw new ConversionException("只支持String類型的轉換");  
            String str = (String) value;  
            if(value.equals(""))  
                return null;  
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
            try{  
                return sdf.parse(str);  
            }catch(ParseException e){  
                throw new RuntimeException(e);//異常鏈不能掉,這裏必須寫上e  
            }  
        }},  
        Date.class);  
      
    //測試  
    BeanUtils.setProperty(p, "name", name);  
    BeanUtils.setProperty(p, "age", age);  
    BeanUtils.setProperty(p, "hight", hight);  
    BeanUtils.setProperty(p, "birthday", birthday);  
    System.out.println(BeanUtils.getProperty(p, "name"));  
    System.out.println(BeanUtils.getProperty(p, "age"));  
    System.out.println(BeanUtils.getProperty(p, "hight"));  
    System.out.println(BeanUtils.getProperty(p, "birthday"));  
}  
  
//使用已經寫好的註冊器DateLocaleConverter  
@Test   
public void test4() throws Exception{  
    Person p = new Person();  
    String name = "wangwu";  
    String age = "23";  
    String hight = "173.5";  
    String birthday = "1990-09-09";  
    //將日期註冊到BeanUtils上  
    ConvertUtils.register(new DateLocaleConverter(), Date.class);//提供的註冊器不健壯,因爲傳入空字符串,就會報錯  
                                                                //所以,當沒有提供註冊器或需要加強註冊器的時候,可以自己寫  
    //測試  
    BeanUtils.setProperty(p, "name", name);  
    BeanUtils.setProperty(p, "age", age);  
    BeanUtils.setProperty(p, "hight", hight);  
    BeanUtils.setProperty(p, "birthday", birthday);  
    System.out.println(BeanUtils.getProperty(p, "name"));  
    System.out.println(BeanUtils.getProperty(p, "age"));  
    System.out.println(BeanUtils.getProperty(p, "hight"));  
    System.out.println(BeanUtils.getProperty(p, "birthday"));  
    Date date = p.getBirthday();  
    System.out.println(date.toLocaleString());  
}  

3)Map集合在BeanUtils中的應用:

//Map集合在BeanUtils中的應用  
@Test  
public void test5() throws Exception {  
    /* 
     * JDK 7.0新特性:  
     * Map map = {"name" : "zs", "age" : 22, "hight" : 176.5}; 
     */  
    //將數據存入集合  
    Map map = new TreeMap();  
    map.put("name", "zhangsan");  
    map.put("age", "20");  
    map.put("hight", "172.5");  
    map.put("birthday", "1999-10-02");  
      
    //註冊器  
    ConvertUtils.register(new DateLocaleConverter(), Date.class);  
    //獲取屬性  
    Person p = new Person();  
    BeanUtils.populate(p, map);  
      
    System.out.println(BeanUtils.getProperty(p, "name"));  
    System.out.println(BeanUtils.getProperty(p, "age"));  
    System.out.println(BeanUtils.getProperty(p, "hight"));  
    System.out.println(BeanUtils.getProperty(p, "birthday"));  
      
}  
  
//屬性鏈  
@Test  
public void test6() throws Exception {  
    Person p = new Person();  
    BeanUtils.setProperty(p, "birthday.time", "111212");  
    System.out.println(BeanUtils.getProperty(p, "birthday.time"));  
}  

4)

補充:

①BeanUtils是以字符串的形式進行操作的

②PropertyUtils是以傳入值本身的類型進行操作的。

//PropertyUtils可直接解析爲指定類型,而BeanUtils只能指定字符串的類型  
    @Test  
    public void test7() throws Exception {  
        Person p = new Person();  
        System.out.println("-----BeanUtiles-------");  
        BeanUtils.setProperty(p, "age", "22");//字符串形式  
        System.out.println(BeanUtils.getProperty(p, "age"));  
        System.out.println(BeanUtils.getProperty(p, "age").getClass().getName());  
          
        System.out.println("-----PropertyUtiles-------");  
        PropertyUtils.setProperty(p, "age", 22);//Integer形式  
        System.out.println(PropertyUtils.getProperty(p, "age"));  
        System.out.println(PropertyUtils.getProperty(p, "age").getClass().getName());  
    }  
注意:

①在前面內省例子的基礎上,用BeanUtils類先get原來設置好的屬性,再將其set爲一個新值。
get屬性時返回的結果爲字符串,set屬性時可以接受任意類型的對象,通常使用字符串。
②用PropertyUtils類先get原來設置好的屬性,再將其set爲一個新值。
get屬性時返回的結果爲該屬性本來的類型,set屬性時只接受該屬性本來的類型。

最新最全的的java學習視頻教程:http://pro.net.itcast.cn/View-22-1458.aspx                                                                                         -------android培訓java培訓java學習型技術博客、期待與您交流! ----------

詳細請查看:http://edu.csdn.net/heima



















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章