反射、列舉類和內省操作
反射
作用:程式可以動態獲取其本身定義的類、方法、欄位等
應用領域:多數作為程式框架使用,常見為程式碼+配置檔案結構等
相關類包
-
- java.lang.reflect.Constructor<T>
- java.lang.reflect.Field
- java.lang.reflect.Method
1.獲取類方法
常用的獲取類物件的三種方法(常用Class.forName()來獲取到類物件)
核心方法
定義Person類
package org.nihaoi.fan; public class Person { //Person類的全地址名為 org.nihaoi.fan.Person //.... }
測試程式
@org.junit.Test public void test1() throws Exception{ //1. 使用Class類的靜態方法forName獲取類物件 引數為:類的全地址名 Class clazz = Class.forName("org.nihaoi.fan.Person"); //2. 通過物件獲取 Person p = new Person(); Class clazz0 = p.getClass(); //3. 通過給定類名獲取 Class clazz1 = Person.class; System.out.println(clazz.getName()); System.out.println(clazz0.getName()); System.out.println(clazz1.getName()); }
輸出結果
org.nihaoi.fan.Person
org.nihaoi.fan.Person
org.nihaoi.fan.Person
依據上述例子, 在配置檔案中只需要給出對應類的全地址名,就可以通過發射機制獲取到這個類。
這樣設計會增強程式的適用性
2.獲取類中的構造器
現在不僅要獲取類物件,同時還要用獲取到的Person類來例項化Person類的物件,此時如果Person中沒有重寫構造器,那隻需要呼叫類物件的newInstance()方法即可。如果Person本身提供了多個構造器,那就需要先獲取其構造器。
核心方法
對於public構造器 使用getConstructor即可
對於private構造器 使用getDeclaredConstructor獲取
改寫Person類
public class Person {
public String secret ="handsome";
public Person(){
System.out.println("person constructer null");
}
public Person(String name,int age){
System.out.println("person constructer name:"+name+",age:"+age);
}
private Person(String name,String des){
System.out.println("person private constructer :"+name+" is "+des);
}
}
測試程式@org.junit.Test
public void test1() throws Exception{
//獲取類物件
Class clazz = Class.forName("org.nihaoi.fan.Person");
//獲取構造器(無參構造器)
Constructor c0 = clazz.getConstructor();
Object o0 = c0.newInstance(); //使用構造器的newInstance方法例項化物件
//獲取構造器(兩個引數)
Constructor c1 = clazz.getConstructor(String.class,int.class);
Object o1 = c1.newInstance("fan",21);
//獲取pirvate構造器
Constructor c2 = clazz.getDeclaredConstructor(String.class,String.class);
c2.setAccessible(true); //做許可權強改
Object o2 = c2.newInstance("fan","man");
}
測試結果
person constructer null
person constructer name:fan,age:21
person private constructer :fan is man
3.獲取到類的方法
對於public方法使用getMethod即可
對於private方法需要使用getDeclaredMethod方法,使用需要修改access屬性
修改Person類
public class Person {
public String secret ="handsome";
public void eat(){
System.out.println("person eat");
}
public String eat(String what){
System.out.println("person eat:"+what);
return "I what double:"+what+".";
}
private void drink(String what){
System.out.println("person private method drink:"+what);
}
public static void drink(){
System.out.println("person static method drink:");
}
}
測試程式
@org.junit.Test
public void test1() throws Exception{
//獲取類物件
Class clazz = Class.forName("org.nihaoi.fan.Person");
//獲取無參方法
Method method0 = clazz.getMethod("eat", null);
method0.invoke(clazz.newInstance(), null);
//獲取帶參方法
Method method1 = clazz.getMethod("eat", String.class);
method1.invoke(clazz.newInstance(), "apple");
//獲取private帶參方法
Method method2 = clazz.getDeclaredMethod("drink", String.class);
method2.setAccessible(true);
method2.invoke(clazz.newInstance(), "water");
//獲取static帶參方法
Method method3 = clazz.getMethod("drink", null);
method3.invoke(clazz, null);
}
輸出結果person eat
person eat:apple
person private method drink:water
person static method drink:
4.獲取欄位
核心方法
Person類
public class Person {
public String secret ="handsome";
private String name ="fan";
public static int age = 21;
}
測試程式
@org.junit.Test
public void test1() throws Exception{
//獲取類物件
Class clazz = Class.forName("org.nihaoi.fan.Person");
//獲取public屬性值
Field field = clazz.getField("secret");
System.out.println(field.get(clazz.newInstance()));
System.out.println(field.getType());
//獲取private屬性值
Field field0 = clazz.getDeclaredField("name");
field0.setAccessible(true);
field.getType();
System.out.println(field0.get(clazz.newInstance()));
System.out.println(field0.getType());
//獲取static屬性值
Field field1 = clazz.getField("age");
System.out.println(field1.get(null));
System.out.println(field1.getType());
}
測試結果
handsome
class java.lang.String
fan
class java.lang.String
21
int
列舉類
使用範圍:物件在一定範圍內固定,例如一年的季節,一年的月份,考試成績的等次等
如何定義:使用關鍵字enum替換class關鍵字。
因為列舉類的例項物件是有固定個數的,所以其構造器必定不能由外部呼叫,故應設定為private屬性,一般編譯器會強制處理。
程式demo
定義一個季節類
public enum Season {
Spring,Summer,Autumn,Winter;
}
系統預設呼叫private屬性的構造器,其中Spring、Summer等共表示四個例項物件,用逗號隔開
注意在列舉類中,例項物件必須寫在第一行
新增屬性
如果我們需要對每個物件新增對應的屬性值,可以重寫構造器,併為物件屬性封裝set和get方法
public enum Season {
Spring("warm"),Summer("hot",2),Autumn("cool"),Winter("cold"); //顯式的例項化物件
private String mFeeling;
private int mArray;
//構造器 編譯器會自動處理為private屬性
Season(String feeling){
this.mFeeling = feeling;
}
Season(String feeling,int array){
this.mArray = array;
}
//物件的公有方法
public String getFeeling(){
return this.mFeeling;
}
public int getArray(){
return this.mArray;
}
public void setArray(int array){
this.mArray = array;
}
}
呼叫
System.out.println("Summer array:"+Season.Summer.getArray());
System.out.println("Spring Feeling:"+Season.Spring.getFeeling());
Season.Winter.setArray(20);
System.out.println("Winter setArray:"+Season.Winter.getArray());
結果
Summer array:2
Spring Feeling:warm
Winter setArray:20
含有抽象方法
上述例子拓展,如果需要物件必須有物件的描述方法,且實現方法可能有所不同,可以為該類新增一個抽象方法,讓每個物件必須去重寫。注意例項化的形式。
例如
public enum Season {
//顯實的例項化物件
Spring("warm"){
@Override
public void getNormalDes() {
System.out.println("春日洋洋");
}
},
Summer("hot"){
@Override
public void getNormalDes() {
System.out.println("夏日炎炎");
}
},
Autumn("cool"){
@Override
public void getNormalDes() {
System.out.println("秋風澀澀");
}
},
Winter("cold"){
@Override
public void getNormalDes() {
System.out.println("冬天霧霾大");
}
};
private String mFeeling;
//構造器 編譯器會自動處理為private屬性
Season(String feeling){
this.mFeeling = feeling;
}
//物件的公有方法
public String getFeeling(){
return this.mFeeling;
}
public abstract void getNormalDes();
}
呼叫
Season.Spring.getNormalDes();
結果
春日洋洋
enum類的方法
繼承樹:
Q:enum類可否被繼承,可以實現介面嗎?
A:自定義的Enum類編譯器會自動新增final關鍵字,故無法被子類繼承。可以實現介面的。
幾個Enum類中常用的方法
name()方法 返回列舉物件在enum類中宣告的名稱
ordinal()方法 返回列舉物件在enum類中初始化的序列。從上到下排序(從0開始)
還有使用values()返回Enum類的所有物件, 使用valueOf判斷獲取到的字串是否在Enum類中已宣告等。
內省操作JAVABean
類的屬性可以使用反射技術類獲取。但是反射屬性的操作還是有些複雜,在原類中如果對屬性封裝成JavaBean類,即可使用內省包來獲取屬性。
內省核心方法
getBeanInfo 用於獲取一個類的所有Bean資訊。
使用getPropertyDescriptorsBeanInfo可以從beaninfo中獲取到單個屬性值。
也可以直接例項化一個bean屬性
demo
JavaBean類-Person類
public class Person {
private String secret ="handsome";
private String name ="fan";
private int age = 21;
public String getAdress() { //Person並無此屬性,提供此get方法,在獲取BeanInfo是可以獲取到該Bean屬性的
return "hefei";
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
測試程式
@org.junit.Test
public void test1() throws Exception{
//獲取類物件
Class clazz = Class.forName("org.nihaoi.fan.Person");
//使用getBeanInfo獲取到所有JavaBean屬性
BeanInfo beanInfo = Introspector.getBeanInfo(clazz,Object.class);
PropertyDescriptor[] pDescriptors = beanInfo.getPropertyDescriptors();//獲取到每個屬性的描述
for (PropertyDescriptor p : pDescriptors) {
System.out.print("type:"+p.getPropertyType()); //依次獲取型別
System.out.println(" ---> name:"+p.getName()); //獲取屬性名稱
}
}
如果直接例項化一個bean屬性
測試程式
@org.junit.Test
public void test1() throws Exception{
//獲取類物件
Class clazz = Class.forName("org.nihaoi.fan.Person");
PropertyDescriptor pd = new PropertyDescriptor("age", clazz);
System.out.println("type:"+pd.getPropertyType()+",name:"+pd.getName());
}
測試結果
type:class java.lang.String ---> name:adress
type:int ---> name:age
type:class java.lang.String ---> name:name
type:class java.lang.String ---> name:secret
直接例項化bean屬性
type:int,name:age
根據輸出結構可以注意到,只要JavaBean類中封裝對了對應的get方法,JavaInfo中就有獲取到對於的屬性值,無論JavaBean類中是否有該屬性。
使用BeanUtils
jar包
下載地址 http://download.csdn.net/detail/u011974639/9666579
需要同時匯入 common包和logging包
核心方法
Person類
public class Person {
private String secret ="handsome";
private String name ="fan";
private int age = 21;
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
測試程式
@org.junit.Test
public void test1() throws Exception{
//獲取類物件
Class clazz = Class.forName("org.nihaoi.fan.Person");
//例項化一個物件
Object newInstance = clazz.newInstance();
BeanUtils.setProperty(newInstance, "age", "11"); //使用setProperty方法設定屬性
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true);
System.out.println(ageField.get(newInstance)); //檢視屬性是否改變
BeanUtils.setProperty(newInstance, "secret", "delphi"); //設定secret屬性
Field secretField = clazz.getDeclaredField("secret");
secretField.setAccessible(true);
System.out.println(secretField.get(newInstance));
}
輸出
11
delphi
在上面的程式,我們使用 BeanUtils.setProperty(newInstance, "age", "11"); 對一個int型變數傳入的是一個字串資料,BeanUtils內容為我們做了資料轉換。
但是BeanUtils預設支援8種基本資料型別,如果需要設定其他型別,需要先設定好轉換器。
例如對於一個Date資料做Bean操作
使用ConvertUtils.register()註冊一個轉換器
該轉換器需要一個介面例項,查閱BeanUtil文件,有大量的給定轉換器,需要的時候可以檢視。
demo
Person類中有一個Date屬性
public class Person {
private Date birthday;
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
測試程式
@org.junit.Test
public void test1() throws Exception{
//獲取類物件
Class clazz = Class.forName("org.nihaoi.fan.Person");
//例項化一個物件
Object newInstance = clazz.newInstance();
//給定的是String資料 需要轉換為Date儲存,故需要配置一個轉換類(可以用給定的DateLocaleConverter, DateTimeConverter)
ConvertUtils.register(new Converter() {
@Override
public Object convert(Class type, Object value) {
if (value==null) {
return null;
}
if (!(value instanceof String)) { //給定引數不為String 丟擲異常
throw new ConversionException("only String Type.");
}
String svalue = (String) value;
if (svalue.trim().equals("")) {
return null;
}
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd");
try {
Date date = sd.parse(svalue);
return date;
} catch (ParseException e) {
throw new RuntimeException(e); //給定格式異常
}
}
}, clazz);
//給定String資料,轉換為Date資料
BeanUtils.setProperty(newInstance, "birthday", "1990-01-02");
System.out.println(BeanUtils.getProperty(newInstance, "birthday"));
}
測試結果
1990-01-02
在實際開發中,常常會把資料依照鍵值對形式儲存到map中,可以用BeanUtils.populate(bean,properites)來快速儲存。
核心方法