?
前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。
一、內省是什么、實現方式:
內省(Introspector)是Java語言對Bean類屬性、事件的一種缺省處理方法。 例如類A中有屬性name,那我們可以通過getName,setName來得到其值或者設置新的值。 通過getName/setName來訪問name屬性,這就是默認的規則。 Java中提供了一套API用來訪問某個屬性的getter/setter方法,通過這些API可以使你不需要了解這個規則, 這些API存放于包java.beans中。
Java語言對Bean類屬性、事件的一種缺省處理方法。例如類A中有屬性name,那我們可以通過getName,setName來得到其值或者設置新的值。通過getName/setName來訪問name屬性,這就是默認的規則。Java中提供了一套API用來訪問某個屬性的getter/setter方法,通過這些API可以使你不需要了解這個規則(但你最好還是要搞清楚),這些API存放于包java.beans中。一般的做法是通過類Introspector來獲取某個對象的BeanInfo信息, 然后通過BeanInfo來獲取屬性的描述器(PropertyDescriptor), 通過這個屬性描述器就可以獲取某個屬性對應的getter/setter方法, 然后我們就可以通過反射機制來調用這些方法。
一般的做法是通過類Introspector來獲取某個對象的BeanInfo信息,然后通過BeanInfo來獲取屬性的描述器(PropertyDescriptor),通過這個屬性描述器就可以獲取某個屬性對應的getter/setter方法,然后我們就可以通過反射機制來調用這些方法。
內省的解釋
內省在wiki上的解釋:
在計算機科學中,內省是指計算機程序在運行時(Run time)檢查對象(Object)類型的一種能力,通常也可以稱作運行時類型檢查。?
不應該將內省和反射混淆。相對于內省,反射更進一步,是指計算機程序在運行時(Run time)可以訪問、檢測和修改它本身狀態或行為的一種能力。
二、內省和反射區別:
區別:
反射式在運行狀態把Java類中的各種成分映射成相應的Java類,可以動態的獲取所有的屬性以及動態調用任意一個方法,強調的是運行狀態。?
內省機制是通過反射來實現的,BeanInfo用來暴露一個bean的屬性、方法和事件,以后我們就可以操縱該JavaBean的屬性
在Java內省中,用到的基本上就是上述幾個類。?
通過BeanInfo這個類就可以獲取到類中的方法和屬性,具體的可以參考JDK文檔
Web 開發框架 Struts 中的 FormBean 就是通過內省機制來將表單中的數據映射到類的屬性上,
因此要求 FormBean 的每個屬性要有 getter/setter 方法。
但也并不總是這樣,什么意思呢?就是說對一個 Bean 類來講,
我可以沒有屬性,但是只要有 getter/setter 方法中的其中一個,
那么 Java 的內省機制就會認為存在一個屬性,比如類中有方法 setMobile ,
那么就認為存在一個 mobile 的屬性。
將 Java 的反射以及內省應用到程序設計中去可以大大的提供程序的智能化和可擴展性。
有很多項目都是采取這兩種技術來實現其核心功能,例如我們前面提到的 Struts ,
還有用于處理 XML 文件的 Digester 項目,其實應該說幾乎所有的項目都或多或少的采用這兩種技術。
在實際應用過程中二者要相互結合方能發揮真正的智能化以及高度可擴展性。
三、內省代碼演示:
1).內省(Introspector)是Java?語言對Bean類屬性、事件的一種缺省處理方法。例如類 A 中有屬性 name, 那我們可以通過 getName,setName 來得到其值或者設置新的值。通過 getName/setName 來訪問 name 屬性,這就是默認的規則。?
?? Java 中提供了一套 API 用來訪問某個屬性的 getter/setter 方法,通過這些 API 可以使你不需要了解這個規則(但你最好還是要搞清楚),這些 API 存放于包 java.beans 中。
2).直接通過屬性的描述器java.beans.PropertyDescriptor類,來訪問屬性的getter/setter 方法;
相關代碼:
public?class?Point?{?????private?Integer?x;?????private?Integer?y;?????public?Point(Integer?x,?Integer?y)?{?????super();?????this.x?=?x;?????this.y?=?y;?????}?????public?Integer?getX()?{?????return?x;?????}????????public?void?setX(Integer?x)?{?????this.x?=?x;?????}?????public?Integer?getY()?{?????return?y;?????}??????public?void?setY(Integer?y)?{?????this.y?=?y;?????}?????}?????import?java.beans.PropertyDescriptor;?????import?java.lang.reflect.Method;?????public?class?Reflect?{?????public?static?void?main(String[]?args)?throws?Exception?{?????Point?point?=?new?Point(2,?5);?????String?proName?=?"x";?????getProperty(point,?proName);?????setProperty(point,?proName);?????}?????private?static?void?setProperty(Point?point,?String?proName)?throws?Exception?{?????PropertyDescriptor?proDescriptor?=?new?PropertyDescriptor(proName,?Point.class);?????Method?methodSetX?=?proDescriptor.getWriteMethod();?????methodSetX.invoke(point,?8);?????System.out.println(point.getX());//?8?????}?????private?static?void?getProperty(Point?point,?String?proName)?throws?Exception?{?????PropertyDescriptor?proDescriptor?=?new?PropertyDescriptor(proName,?Point.class);?????Method?methodGetX?=?proDescriptor.getReadMethod();?????Object?objx?=?methodGetX.invoke(point);?????System.out.println(objx);//?2?????}?????}??????
3).通過類 Introspector 來獲取某個對象的 BeanInfo 信息,然后通過 BeanInfo 來獲取屬性的描述器( PropertyDescriptor ),通過這個屬性描述器就可以獲取某個屬性對應的 getter/setter 方法,然后我們就可以通過反射機制來調用這些方法。
相關代碼:
把2中的getProperty()修改成如下形式:
private?static?void?getProperty(Point?point,?String?proName)?throws?Exception?{?????BeanInfo?beanInfo?=?Introspector.getBeanInfo(point.getClass());?????PropertyDescriptor[]?proDescriptors?=?beanInfo.getPropertyDescriptors();?????for(PropertyDescriptor?prop:?proDescriptors){?????if(prop.getName().equals(proName)){?????Method?methodGetx?=?prop.getReadMethod();?????System.out.println(methodGetx.invoke(point));//8?????break;?????}?????}?????}???
4).我們又通常把javabean的實例對象稱之為值對象 (Value Object),因為這些bean中通常只有一些信息字段和存儲方法,沒有功能性方法。一個JavaBean類可以不當JavaBean用,而當成普通類 用。JavaBean實際就是一種規范,當一個類滿足這個規范,這個類就能被其它特定的類調用。一個類被當作javaBean使用時,JavaBean的 屬性是根據方法名推斷出來的,它根本看不到java類內部的成員變量(javabean的成員變量通常都是私有private的)。
5).除了反射用到的類需要引入外,內省需要引入的類如下所示,它們都屬于java.beans包中的類,自己寫程序的時候也不能忘了引入相應的包或者類。
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
6).下面講解一些開源的工具類Beanutils,需要額外下載的,commons-beanutils.jar,要使用它還必須導入commons-logging.jar包,不然會出異常;
相關代碼一:
public?static?void?main(String[]?args)?throws?Exception?{?????Point?point?=?new?Point(2,?5);?????String?proName?=?"x";?????BeanUtils.setProperty(point,?proName,?"8");?????System.out.println(point.getX());//?8?????System.out.println(BeanUtils.getProperty(point,?proName));//?8?????System.out.println(BeanUtils.getProperty(point,?proName).getClass().getName());//?java.lang.String?????BeanUtils.setProperty(point,?proName,?8);?????System.out.println(BeanUtils.getProperty(point,?proName).getClass().getName());//?java.lang.String?????}?????//我們看到雖然屬性x的類型是Integer,但是我們設置的時候無論是Integer還是String,BeanUtils的內部都是當成String來處理的。??
相關代碼二:
BeanUtils支持javabean屬性的級聯操作;
public?static?void?main(String[]?args)?throws?Exception?{?????Point?point?=?new?Point(2,?5);//在point中加一個屬性?private?Date?birth?=?new?Date();并產生setter/getter方法?????String?proName?=?"birth";?????Date?date=?new?Date();?????date.setTime(10000);?????BeanUtils.setProperty(point,?proName,?date);?????System.out.println(BeanUtils.getProperty(point,?proName));?????BeanUtils.setProperty(point,?"birth.time",?10000);?????System.out.println(BeanUtils.getProperty(point,?"birth.time"));//10000?????
}?????
//之所以可以?BeanUtils.setProperty(point,?"birth.time",?10000);這樣寫,那是因為Date類中有getTime()和setTime()方法,即Date類中相當于有time這個屬性。???
相關代碼三:
BeanUtils和PropertyUtils對比:
public?static?void?main(String[]?args)?throws?Exception?{?????Point?point?=?new?Point(2,?5);?????String?proName?=?"x";?????BeanUtils.setProperty(point,?proName,?"8");?????System.out.println(BeanUtils.getProperty(point,?proName));//8?????System.out.println(BeanUtils.getProperty(point,?proName).getClass().getName());//java.lang.String?????//?PropertyUtils.setProperty(point,?proName,?"8");//exception:argument?type?mismatch?????PropertyUtils.setProperty(point,?proName,?8);?????System.out.println(PropertyUtils.getProperty(point,?proName));//8?????System.out.println(PropertyUtils.getProperty(point,?proName).getClass().getName());//java.lang.Integer?????
}?????
//BeanUtils它以字符串的形式對javabean進行轉換,而PropertyUtils是以原本的類型對javabean進行操作。如果類型不對,就會有argument?type?mismatch異常。??
6).理解了相應的原理,那些現成的工具用起來就會更舒服,如Beanutils與 PropertyUtils工具。這兩個工具設置屬性的時候一個主要區別是PropertyUtils.getPropety方法獲得的屬性值的類型為該 屬性本來的類型,而BeanUtils.getProperty則是將該屬性的值轉換成字符串后才返回
?