Java基礎加強---day02
五、內省
JavaBean:JavaBean是一種特殊的Java類,主要用於傳遞數據信息,這種Java類中的方法主要用於訪問私有的字段,並且方法名符合某種命名規則。當某個java類中的一些方法符合某種命名規則,那麼就可以把它當做JavaBean來使用。
如果要在兩個模塊之間傳遞多個信息,可以將這些信息封裝到一個JavaBean中,這種JavaBean的實例對象通常稱爲值對象(Value Object)。這些信息在類中用私有字段來存儲,如果要讀取或者設置這些字段的值,那麼需要通過一些相應的方法來訪問。
去掉前綴(set、get)後,剩下的部分就是屬性名,如果剩餘部分的第二個字母是小寫的,則把剩餘部分的首字母改爲小寫的;如果第二個字母是大寫的,第一個字母保留大寫。
總之:一個JavaBean類的屬性名可以根據方法名推斷出來,並且我們根本看不到java類內部的成員變量。
由於JavaBean廣泛的應用性,Eclipse有專門的快捷鍵產生,即getter和setter。
JavaBean的好處:
1、在JavaEE開發中,經常要使用到JavaBean,很多環境要求按照JavaBean,所以JavaBean很重要!
2、JDK中提供了對JavaBean進行操作的一些API,所以操作相對簡單方便,這一套API就稱爲內省。
內省:對應的英文單詞爲:IntroSpector,有內部視察的意思,主要用於對JavaBean的操作。java提供的API爲:PropertyDescriptor。但PropertyDescriptor類對JavaBean的操作過於複雜,也可以通過導入BeanUtils工具包對JavaBean進行內省操作。
萬事萬物都應該都應該用一個東西來描述,BeanInfo是專門用於描述JavaBean的一個類,也可以通過遍歷BeanInfo中所有屬性的方式對JavaBean進行描述,IntroSpector.getBeanInfo(<?>class),獲取BeanInfo對象。但這種方法操作較爲麻煩,可以作爲了解。
導入方式:選中工程,右鍵-->buildPath-->Configure buildPath 然後導入相應的工具包,即可。
產生getter和setter的方式:在類中的空白處,右鍵-->Source-->Generate Getters and Setters,然後選中相應字段,即可。
抽取方法的方式:選中要抽取成爲方法的代碼,右鍵-->refactor-->Extract Method,即可。
下面代碼體現:
1、內省的演示:
package itheima.day08;
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;
import org.apache.commons.beanutils.BeanUtils;
import com.sun.xml.internal.fastinfoset.sax.Properties;
import itheima.day07.ReflectPoint;
public class IntroSpectorTest {
public static void main(String[] args) throws Exception {
// 提供設置、獲取某個字段的類 就是JavaBean
ReflectPoint pt1 = new ReflectPoint(3, 5);
// 獲取讀取一個字段的方法
String propertyName = "x";
Object returnValue = getProperty(pt1, propertyName);
System.out.println(returnValue);//3
// 設置一個字段的值
Object value =7;
setProperty(pt1,propertyName,value);
// 使用BeanUtils工具包
System.out.println(BeanUtils.getProperty(pt1, "x"));
// BeanUtils工具包的方法更加簡便
System.out.println(pt1.getX());//7
// BeanUtils.setProperty(pt1, "birthday.time", "111");//級聯操作
// System.out.println(BeanUtils.getProperty(pt1, "birthday.time"));
}
private static void setProperty(ReflectPoint pt1, String propertyName,
Object value) throws Exception {
// PropertyDescriptor(String propertyName, Class<?> beanClass)
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass());
// 獲取set方法
Method methodSetX = pd2.getWriteMethod();
// 方法關聯具體的對象
methodSetX.invoke(pt1,value);
}
private static Object getProperty(Object pt1,String propertyName) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
// 操作JavaBean的API,內省
// PropertyDescriptor(String propertyName, Class<?> beanClass)
PropertyDescriptor pd = new PropertyDescriptor(propertyName, pt1.getClass());
// 獲取應用於讀取字段屬性值的方法
Method methodGetX = pd.getReadMethod();
// 關聯具體的對象
Object returnValue = methodGetX.invoke(pt1);
// 這是早期的做法
// BeanInfo類專門用於描述JavaBean
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
// 獲取JavaBean中所有的屬性值
PropertyDescriptor[] pds =beanInfo.getPropertyDescriptors();
Object retvalue =null;
for(PropertyDescriptor pd1:pds){
// 尋找我們想要的字段名
if(pd1.getName().equals(propertyName)){
Method methodGetx1 = pd.getReadMethod();
retvalue= methodGetx1.invoke(pt1);
}
}
return returnValue;
}
}
六、註解
註解:註解相當於一種標記,在程序中加了註解就等於爲程序打上了某種標記;是註解是JDK1.5的一個重要新特性。
JDK中自帶有三個註解:
1、@SupressWarning:取消編譯器提出的警告,只在源文件時期中有效。
2、@Deprecated:提示元素是過時的,直到運行期都有效。
3、@Override:表示覆蓋父類中的方法,只在源文件時期有效,只能用在方法上。
註解相當於程序中要調用的一個類,要在源程序中應用某個註解,得先有這個註解類;好比要調用某個類,必須先開發好這個類。註解的應用結構如下圖所示:
@Retention,元註解,用在註解中的註解,有三個取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;分別對應:java源文件、class文件、內存中的字節碼。
@Target:指示註釋類型所適用的程序元素的種類。如果註釋類型聲明中不存在 Target 元註釋,則聲明的類型可以用在任一程序元素上。如果存在這樣的元註釋,則編譯器強制實施指定的使用限制。可以是Filed(字段)、Method(方法)、Type(類型,包括類、接口、註解等)。
註解的屬性:註解本身就是給給源程序打上某種標記,則註解的屬性就是標記中的標記,即更具體的標記。
下面代碼演示:
註解類:
package itheima.day08;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import itheima.day07.EnumTest;
//源註解,指明該註解類的生命週期
@Retention(RetentionPolicy.RUNTIME)
//源註解,指明註解類可以用在什麼類型的數據上,即使用目標
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
// 註解的屬性
String color() default "bule";
String value();
// 註解的屬性有默認值時,應用時可以不必指定
int[] arrayAttr() default {3,2,1};
// 帶枚舉的註解
EnumTest.TrafficLamp lam() default EnumTest.TrafficLamp.RED;
}
應用註解的類:
package itheima.day08;
//使用"註解類"的類
//在使用註解類時,必須給未初始化的屬性賦值
@ItcastAnnotation(color ="red", value = "abc")
public class AnnotationTest {
// 取消警告
@SuppressWarnings("deprecated")
// 只有value一個屬性待賦值時,可以省略屬性名
@ItcastAnnotation("xyz")
public static void main(String[] args) {
// 過時的方法
System.runFinalizersOnExit(true);
// MyEclipse提示該方法過時
AnnotationTest.sayHello();
// 對使用註解類的類進行反射
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){
// 通過反射的形式獲取到註解類
ItcastAnnotation annotation =
(ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
// @itheima.day08.ItcastAnnotation(color=red, lam=RED, arrayAttr=[3, 2, 1], value=abc)
System.out.println(annotation);
// 獲取到註解類之後,可以獲取註解類中的屬性字段
System.out.println(annotation.color());
System.out.println(annotation.value());
System.out.println(annotation.arrayAttr().length);
System.out.println(annotation.lam().nextLamp().name());
}
}
// 給該方法打上過時的標記,使用的是JDK提供的註解類
@Deprecated
public static void sayHello(){
System.out.println("hello itheima");
}
}
七、泛型
泛型:泛型是提供給javac編譯器使用的,可以限定集合中的輸入類型,讓編譯器擋住源程序中的非法輸入,編譯器編譯帶類型說明的集合時會去除掉“類型”信息,使程序運行效率不受影響,對於參數化的泛型類型,getClass()方法的返回值和原始類型完全一樣。由於編譯生成的字節碼會去掉泛型的類型信息,只要能跳過編譯器,就可以往某個泛型集合中加入其它類型的數據,例如,用反射得到集合,再調用其add方法即可。
泛型是JDK1.5的一個新特性,沒有泛型時,集合中通過Object提供擴展性,但什麼類型的對象都可以往同一個集合中放,這就容易引起操作集合中元素時發生一些異常。指定了泛型後,集合中只能放進特定的類型元素,提高了代碼的安全性。
泛型的特定術語:
ArrayList<E>:泛型類型;E稱爲類型變量或者類型參數;
ArrayList<Integer>稱爲參數化的類型;<>稱爲typeof;ArrayList稱爲原始類型。
參數化類型可以引用一個原始類型的對象,編譯報告警告;如:
Collection<String>c = new Vector();//兼容JDK1.4以前編寫的程序;
原始類型可以引用一個參數化類型的對象,編譯報告警告;
Collectionc = new Vector<String>();//原來的方法接受一個集合參數,新的類型也要能傳進去。
上限:? extends E。
下限:? super E。
關於泛型,在集合那一章中有詳細的介紹,在此不再贅述!
下面代碼演示:
package itheima.day08;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
public class GenericTest {
public static void main(String[] args) throws Exception {
// 在泛型出現之前,用Object提供擴展性
// 面向接口的編程
Collection collection1 = new ArrayList();
// 使用Object提供擴展性,弊端很明顯:什麼東西都可以往集合中放
collection1.add(1);
collection1.add(1.2);
collection1.add("abc");
System.out.println(collection1.size());
// JDK1.5版本以後,用泛型限定存入集合的類型
Collection<String> collection2 = new ArrayList<String>();
collection2.add("abcd");
// 編譯通不過,把安全問題扼殺在編譯時期
// collection2.add(1.2);
System.out.println(collection2.size());
// 可以使用反射技術透過編譯器
Constructor<String> constructor1 =
String.class.getConstructor(StringBuffer.class);
String str2 = constructor1.newInstance(new StringBuffer("abc"));
System.out.println(str2);
ArrayList<Integer> collection3 = new ArrayList<Integer>();
// 同一份字節碼,說明字節碼與泛型無關,泛型只是給編譯器看門的一個道具,僅此而已
System.out.println(collection2.getClass()==collection3.getClass());//true
// 用反射透過編譯器
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
System.out.println(collection3.get(0));
}
}