黑馬程式設計師——Java學習筆記之⑧——“Java新技術”

u010966408發表於2014-03-30


----------- android培訓java培訓、期待與您交流! ------------

 

1、ide : itegrity developmentenvironment 整合開發環境

perspectiveview : 透檢視

 

2、基本資料型別的自動裝箱和拆箱:為了方便基礎資料型別及其包裝類的使用。在某些情況下無須人工地進行轉換。

Integer  iObj = 3; //將一個基本資料型別int自動裝箱為一個Integer物件

System.out.println (iObj + 12); //自動拆箱

 

2、享元模式(flyweight):就是有很多個小的物件,它們有很多屬性相同,把它們變成一個物件, 把那些不同的屬性變成方法的引數,稱之為外部狀態, 把那些相同的屬性稱之為這個物件的內部狀態。它使用共享物件,用來儘可能減少記憶體使用量以及分享資訊給儘可能多的相似物件。常見做法是把它們放在外部資料結構,當需要使用時再將它們傳遞給享元。具體示例,見“設計模式”總結日誌。

 

3、在建立Integer物件的時候,是存在常量池概念的。如果要封裝為Integer物件的int型值在-128~127之間,那麼JVM會先到Integer物件常量池中查詢,是否有相同值的Integer物件,如果常量池中已經存在具有相同值的Integer物件,那麼就將新建立的Integer物件的地址指向常量池中具有相同值的那個物件,也就是說它們會變成同一個物件,這就是享元模式。當然,如果要建立的Integer物件的值不在-128~127的範圍內,那麼就直接建立該Integer物件。

示例如下:

Integer i1 = 8;
Integer i2 = 8;
Integer i3 = 240;
Integer i4 = 240;
System.out.println(i1 == i2); //返回true
System.out.println(i3 == i4); //返回false


 

4、列舉(Enum):本身就是一個類。一般情況下是不能建立enum型別的例項的(構造方法可以有,但必須私有)。列舉型別相當於自己定義一個資料型別,而這個資料型別的名字是自己起的,型別裡面的內容也是自己定義的,當用到這個型別的變數時,只能輸入已經定義好的內容,這樣就可以防止其他程式設計師輸入不符合規定的值。

列舉是一種特殊的類,其中的每個元素都是該類的一個例項物件。列舉元素必須位於列舉體中的最開始部分,因為初始化動作最先執行的。

在列舉元素的後面跟上一對括號,就表示建立這個元素指向的實際物件的時候,使用哪個建構函式

注:如果列舉中只有一個元素,就可以作為單例的實現方式。

列舉類的定義示例如下:


 

5、操作列舉型別物件的常用方法:

valueOf();//得到列舉常量對應的值

toString();//得到當前列舉常量的名稱,可以被複寫,是得到的結果更易讀(與name()功能相同,優先使用toString() )

equals();//判斷兩個列舉常量是否相同

ordinal();//得到當前列舉常量的位置排行

getClass();//獲取物件所屬的類名

values();//返回一個儲存該列舉型別中物件的陣列

列舉型別物件的操作方法例項如下:



6、反射:就是把Java類中的各個成分對映成相應的Java類。(反射會導致程式效能嚴重下降)(反射的具體細節,見“反射”總結日記)

 

7、Class:各個類在記憶體中的位元組碼。(位元組碼:一個類被類載入器載入到記憶體中,佔用一片儲存空間,這個空間裡面的內容就是類的位元組碼。不同的類的位元組碼不同)

對位元組碼的比較要用 == ,判斷某個位元組碼是不是某種型別(類名.getType() == 型別名.class ),因為每種型別的位元組碼只有一份

 

8、Constructor類:代表位元組碼中的一個構造方法。

得到某個類的所有的構造方法:Constructor[]constructor =Class.forName("java.lang.String").getConstructor();

得到某一個構造方法:Constructorconstructor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);//這裡的StringBuffer.class是一個引數,如果要獲取某個建構函式,只需加入對應的引數列表即可。

通過newInstance();方法建立一個對應構造方法的例項。

 

9、Method類:代表位元組碼的一個方法

MethodmethodCharAt= String.class.getMethod("charAt", int.class);//getMehod引數形式為:(方法名,引數型別的位元組碼檔案)

Charch= methodCharAt.invoke(str1, 1);//invoke兩個引數:第一個引數是具體呼叫該方法的物件,第二個引數是執行該方法的具體引數(如果有多個引數,就將這些引數封裝為一個陣列Object[],注意當接收多個int型別的引數時,要將其封裝為Integer型別的陣列,否則會發生異常。如果有多個相同型別的引數,也可以使用可變引數)。如果str1這個引數為null,那麼就說明Method物件對應的是一個靜態方法。也就是說如果如果方法時靜態方法,那麼使用invoke時,其第一個引數為null,因為不需要該方法所屬類的例項物件存在,就能呼叫該方法。

 

10、Field類:代表位元組碼中的一個成員變數。得到的Field物件是對應到物件上的成員變數。

getField();//獲取位元組碼檔案中的公有成員變數,不包括私有變數

getDeclaredField();//獲取位元組碼檔案中的成員變數,包括私有變數

Field[]getFields();//獲取位元組碼檔案中所有的成員變數,不包括私有變數

setAccessible(true);//暴力反射,即獲取私有物件變數的反射後,給予許可權得以得到它的值

Field field.get(Object obj)//獲取物件中obj中field變數的值

 

11、得到位元組碼對應例項物件的方法:

①通過“類名.class”的形式。(例:System.class )

②通過.getClass(),這需要已經存在了一個類的物件,通過getClass方法獲取物件的位元組碼檔案。(例:newDate().getClass() )

③Class.forName("類名"),通過靜態方法去查詢(首先在虛擬機器中查詢,如果存在就載入,如果不存在,就上硬碟中查詢,並載入到虛擬機器快取中,並返回位元組碼檔案)和載入對應類的位元組碼。(例:Class.forName("java.util.Date") )

 

12、Class9個預定義的基本型別:boolean, byte, char, short, int, long, float, double, (外加void)。可以通過isPrimitive(); 來判斷是否為基本型別

可用:Boolean.TYPE,Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE,Long.TYPE, Float.TYPE,Double.TYPE, Void.TYPE 檢視

總之,只要是在源程式中出現的型別,都有各自的Class例項物件,例如: int[] , void

 

13、 陣列的反射:具有相同的元素型別和緯度的陣列,其反射後的位元組碼檔案是同一個。示例如下:

int[] a1 =newint[3];
int[] a2 = new int[4];

System.out.println(a1==a2); //false,因為a1和a2是兩個不同的物件
System.out.println(a1.getClass() == a2.getClass()); //ture,因為其元素型別都為int,緯度都為一維。

 

14、asList( Object[] args);將陣列轉換為list集合

int[] a5 =newint[]{1,2,3};

Integer[] a6 =newInteger[]{1,2,3};
String[] a7 = new String[]{"a","b","c"};
System.out.println(a5);//[I@c971d55
System.out.println(a6);//[Ljava.lang.String;@1506bde8

//(jdk1.4中)asList(Object[]args),而String為Object,但int不是
System.out.println(Arrays.asList(a5));//[[I@c971d55]這裡如果將a5中的元素封裝為Integer型別的話,就會列印出a5集合中的元素,因為Integer是Object

System.out.println(Arrays.asList(a6));//[1,2,3]

System.out.println(Arrays.asList(a7));//[a,b,c]

 

15、陣列反射的應用思路:將一個陣列轉化為物件obj-->用物件獲取位元組碼檔案,判斷是否為陣列Class-->是,使用Arrays工具類的Array.getLength(obj)獲取長度,for迴圈一個個輸出陣列元素Array.get(obj,index); -->否,直接輸出obj。

 

16、記憶體洩露:一般情況下,如果物件中複寫了HashCode,則在物件pt存入HashSet集合後,如果修改了該物件的內容,這時其HashCode已經改變,pt已儲存到了別的地方,如果執行刪除pt的操作會失敗。這就是記憶體洩露,即某個物件不用了,但是它一直在佔用記憶體空間。

 

17、AOP(AspectOriented program,簡稱AOP)即為面向方面的程式設計。

系統中存在交叉業務,一個交叉業務就是要切入到系統中的一個方面。交叉業務的程式設計問題即為面向方面的程式設計(Aspect Oriented program,簡稱AOP),AOP的目標就是要使交叉業務模組化。可以採用將切面程式碼移動到原始方法的周圍,這與直接在方法中編寫切面程式碼的執行效果是一樣的。使用代理技術正好可以解決這種問題,代理是實現AOP功能的核心和關鍵技術。

 

18、JavaBean是一種特殊的Java類,主要用於傳遞資料資訊,這種java類中的方法主要用於訪問私有的欄位,且方法名稱符合某種命名規則。

如果要在兩個模組之間傳遞多個資訊,可以將這些資訊封裝到一個JavaBean中,這種JavaBean的例項物件通常稱之為值物件(Value Object,簡稱VO)。這些資訊在類中用私有欄位來儲存,如果讀取或設定這些欄位的值,則需要通過一些相應的方法來訪問。JavaBean的屬性是根據其中的setter和getter方法來確定的,而不是根據其中的成員變數。如果方法名為setId,至於把它存在哪個變數上,不用管。如果方法名為getId,至於從哪個變數上取,不用管。去掉set或者get字首後,剩餘部分就是屬性名,如果剩餘部分的第二個字母是小寫的,則把剩餘部分的首字母改成小的。總之,一個類被當做JavaBean使用時,JavaBean的屬性是根據方法名推斷出來的,它根本看不到java類內部的成員變數。

 

19、JavaBean的好處:

①在Java EE開發中,經常要使用到JavaBean。很多環境就要求按JavaBean方式進行操作。

②JDK中提供了對JavaBean進行操作的一些API,這套API就稱為內省(introSpector)。如果要自己通過getX方法來訪問私有的x,有一定難度。用內省這套API操作JavaBean比用普通類的方式更方便。

 

20、使用beanutils工具包操作JavaBean:(需要自行下載,並匯入工具包:org.apache.commons.beanutils)

   ①BeanUtils.setProperty(pt,String propertyName , StringpropertyValue);    //將pt物件的propertyName的設定為propertyValue

   ②BeanUtils.getProperty( pt ,String propertyName );    //獲取pt物件的propertyName屬性的值,但是BeanUtils會自動進行型別轉換。例如:

   ReflectPoint pt = new ReflectPoint(3,5);    //這裡pt是一個JavaBean類(ReflectPoint)的物件

   System.out.println(BeanUtils.getProperty(pt,"x").getClass().getName());列印結果為java.lang.String

 

   ③PropertyUtils.setProperty(pt,String propertyName, propertyValue);   

   ④PropertyUtils.getProperty(pt , String propertyName );//獲取pt物件的propertyName屬性的值,這裡PropertyUtils會自動匹配屬性型別,且不會進行型別轉換。例如:

   System.out.println(PropertyUtils.getProperty(pt,"x").getClass().getName());列印結果為java.lang.Integer

上述兩個工具包的操作例項如下:


 

21、PropertyDescriptor類:表示JavaBean類通過儲存器匯出一個屬性。主要方法:

   getPropertyType(),獲得屬性的Class物件。

    getReadMethod(),獲得用於讀取屬性值的方法;getWriteMethod(),獲得用於寫入屬性值的方法。

    hashCode(),獲取物件的雜湊值。

    setReadMethod(Method readMethod),設定用於讀取屬性值的方法;setWriteMethod(MethodwriteMethod),設定用於寫入屬性值的方法;

   簡單的內省操作如下所示:

   ReflectPoint pt = new ReflectPoint(3,5);

   String propertyName = "x";

   PropertyDescriptor pd =newPropertyDescriptor(propertyName,pt.getClass());//PropertyDescriptor 描述 Java Bean 通過一對儲存器方法匯出的一個屬性

    Method methodGetX =pd.getReadMethod();

    Object retVal = methodGetX.invoke(pt1,null);

 

   比較複雜的內省操作例項:

   BeanInfo beanInfo =Introspector.getBeanInfo(pt1.getClass());   //在 Java Bean 上進行內省,瞭解其所有屬性、公開的方法和事件。
    PropertyDescriptor[] pds =beanInfo.getPropertyDescriptors();  //獲得屬性的描述
    Object retVal = null;
    for(PropertyDescriptor pd : pds)    //採用遍歷BeanInfo的方法,來查詢、設定類的屬性。
    if(pd.getName().equals(propertyName))   
    {
        Method methodGetX=pd.getReadMethod();//如果找打了以x為引數的方法,就可得到它的get方法
        retVal = methodGetX.invoke(pt1);
        break;
    }

 

22、註解:相當於一種標記,在程式中加入了註解就等於打上了某種標記。沒加,等於沒有某種標記。(jdk1.5的新特性:列舉、註解)標記可以新增在 包,類,欄位,方法、方法的引數以及區域性變數上。

   以後javac編譯器,開發工具和其他程式可以用反射來了解自己的類即各種元素上有無何種標記。有什麼標記就幹相應的事。

 

23、常用的註解介紹:

   @SuppressWarnings("deprecation")    //這個註解是為了不讓系統提示已過時的方法等。(deprecated  過時的)

   @Override   //標註為複寫了父類中的方法。

   @Deprecated //註釋此方法已經過時了。對於新編寫程式的人來說,是不推薦使用的意思。對老程式來說,是這個方法還可以使用,還有效的意思。

 

   @Override 示例:

   @Override   //這裡如果有複寫標識:@Override,說明是要複寫父類中的equals方法,如果引數不對,就不能算是複寫
    public boolean equals(Object obj)
    {
        ..........
        ............
    }
 注:在HashSet集合中,在定義物件的hashCode方法和equals方法時如果沒有複寫equals方法或者在嘗試複寫時,引數列表不對,這就不能算是複寫,從而在比較時,程式會呼叫父類中的equals方法。從而使得自己定義的equals方法沒有任何意義。這是新手常犯的錯誤。實際上如果引數列表不同的話,就只能叫做過載,而不是複寫。

 

24、元註解:即“註解的註解”。它的生命週期為:Java原始檔—>class檔案—>記憶體中的位元組碼

   @Retention(RetentionPolicy.RUNTIME) //這裡是指將AnnotationDemo註解保留到執行期間

   在反射測試的問題中,在註解前面加上這三種註解之一:(@SuppressWarnings、@Deprecated、@Override 都對應著SOURCE階段)

   ①RetentionPolicy.SOURCE--->java原始檔

   ②RetentionPolicy.CLASS--->class檔案(預設的)

   ③RetentionPolicy.RUNTIME--->記憶體中的位元組碼檔案

 

25、註解的定義示例(定義了高階屬性):

   @Target({ElementType.METHOD,ElementType.TYPE})//在定義註解時,加上該註解的意思,是指正在定義的註解既可用於方法上,又可用於類上(此外,Target的屬性值還有ElementType.FIELD,表示可用於)
    public @interface AnnotationDemo {
    String color() default"black";    //設定註解的屬性color的預設值“black”
    String value();    //沒有設定註解屬性的預設值
    int[] arrayAttr() default {1,2,3};     //陣列屬性
    Enum.TrafficLamp lamp() defaultEnum.TrafficLamp.RED;     //列舉屬性
    MetaAnnotation annotationAttr()default@MetaAnnotation("zby");    //註解屬性
}

 

26、註解的屬性:當一個註解中有多個屬性時,如果有一個屬性有預設值,則使用該註解的時候,可使用預設值,從而不定義該屬性。如果該註解中只有一個沒有預設值得屬性,則如果其他屬性都使用預設值,而只定義該屬性時,也可以不寫屬性名和=,只寫屬性值即可

 

27、註解的反射呼叫:

   //AnnotationDemo為已經定義過的一個註解

   //AnnotationTest為已經定義好的一個類,其中用到了註解AnnotationDemo

   AnnotationDemo annotation =(AnnotationDemo)(AnnotationTest.class.getAnnotation(AnnotationDemo.class));

  System.out.println(annotation);  

  System.out.println(annotation.color());//列印出AnnotationDemo的屬性

 

28、泛型< >在通過編譯器編譯後,會自動去掉,以便不影響程式執行效率。通過以下例子加以說明:

ArrayList<String>collection = new ArrayList<String>();

ArrayList<Integer>collection2 = new ArrayList<Integer>();

   System.out.println(collection.getClass()== collection2.getClass());//結果為“true”,這說明編譯完後的位元組碼檔案中沒有泛型了,泛型只是提供給編譯器使用的,這叫做去型別化。

這時可以想到,可以通過反射往集合中加入其它型別的元素,因為反射能跳過編譯器的編譯。

collection2.getClass().getMethod("add",Object.class).invoke(collection2, "abc");

 

29、泛型:是java 1.5中定義的一種新的資料型別,是一種引數化的類,也叫“類中類”,它是物件導向的擴充套件。通過泛型可以定義型別安全的資料型別,它的最顯著應用就是建立集合類,可以約束集合類內的元素型別,java中比較常用的是Map<Key,Value>和Collection<T> 。

     泛型的原理:泛型是提供給java編譯器使用的可以限定集合中的輸入型別,讓編譯器擋住源程式中的非法輸入。

 

30、泛型的優點:

       ①使用泛型的效能高,不需要裝箱和拆箱的操作

       ②型別安全。泛型集合對它所儲存的物件做了型別的約束,在沒有跳過編譯器之前它是不允許非泛型集合所儲存的資料型別新增到泛型集合中去的。

 

31、泛型的特點:

       ①引數化型別與原始型別相容

       Collection<String> c=newVector(); 引數化型別與原始型別的物件,編譯器報告警告

       Collection c=newVector<String>();  //原始型別可以引用一個引數化型別的物件,編譯器報告警告

       ②引數化型別不考慮型別引數的繼承性

       Vector<Object> v=newVector<String>();      //錯誤,引數型別不能繼承

       ③在建立陣列例項時,陣列的元素不能使用引數化的型別

       Vector<Integer>vectorlist[]=new Vector<Integer>[10];//錯誤,陣列的元素不能使用引數化型別

       ④泛型中的 ? 萬用字元

       使用?萬用字元可以引用其它各種引數化的型別,使用?萬用字元定義的變數主要做引用 ,可以呼叫與引數化型別無關的方法,不能呼叫與引數化有關的方法。

      publci voidtestCollection(Collection<?> c)

        {

           c.add("element");     //這樣編譯器會報錯,新增元素與引數化型別有關

           System.out.print(c.size());            //size方法與引數型別無關,不會報錯

        }

       ⑤泛型中?萬用字元的限定(限定萬用字元包括自己)

       Vector<? extends Number>x = new Vector<Integer>(); //限定萬用字元的上界

       注:定義泛型的時候可以定義多個上限,例如:<T extends Senalizable & cloneable>

       Vector<? super Integer>y = new Vector<Number>();     //限定萬用字元的下界

 

32、泛型的定義,示例如下:

       public static <T> voidswapArr(T[] a, int i, int j)           //定義一個交換陣列中兩個元素位置的泛型方法
       {
              T temp = a[i];
              a[i] = a[j];
              a[j] = temp;
       }

    // swapArr(new int[]{1,2,3,4,5,6},3,5);     //錯誤,因為只有引用型別才能作為泛型方法的實際引數
    // swapArr(newInteger[]{1,2,3,4,5,6},3,5)    //正確,因為這裡將int型陣列中的元素封裝成為了Integer物件

 

33、泛型的使用與萬用字元使用的比較

    例子1:

    //列印任意型別的集合(用萬用字元的方法)
    public static voidprintCollection(Collection<?> collection){ 

         System.out.println(collection.size());
         for(Object obj : collection)

    {
         System.out.println(obj); 
    }} 

 

    例子2:

    //列印任意型別的集合(用自定泛型的方法)
    public static <T> voidprintCollection_2(Collection<T> collection){  
    System.out.println(collection.size());
    for(Object obj : collection)

    {
    System.out.println(obj);
    }}

 

    這種情況下,萬用字元的方法比泛型的方法更有效。假如函式中還要進行新增操作,即:collection.add(T t),這時,泛型方法就比萬用字元方法更有效。

    一般情況下,當一個型別變數用來表達兩個引數之間或者引數和返回值之間的關係時,即同一個型別變數在方法簽名的兩處被使用,或者型別變數在方法體程式碼中也被使用,而不是僅在簽名的時候使用,才需要使用泛型方法。如果類中只有一個方法需要使用泛型,要使用類級別的泛型,而不是方法級別的泛型

 

34、Dao:data access objet 資料連線物件  

 

35、泛型類中不能定義靜態方法,當然,如果類中某個方法跟泛型類中的泛型不同的話,可以為靜態。

 

36、類載入器(ClassLoader):本身也是個Java類,被其他類載入器載入,而位於源頭的類載入器是BootStrap。BootStrap類載入器不需要被別的類載入器載入,因為它是巢狀在java虛擬機器的核心中的,隨著虛擬機器的啟動而載入。

    通過getClassLoader(); 可以得到一個類的類載入器。由於類載入器也是一個java類。因此,接著可以用getClass().getName()來得到該載入器的名稱。示例如下;

    String classLoader =System.class.getClassLoader().getClass().getName() ; //語句執行後,classLoader的內容為null,這說明,System類的類載入器是BootStrap。

    當然,當獲得一個類載入器後,還可以用getParent(); 獲得此類載入器的父類載入器。

 

37、Java虛擬機器中可以安裝多個類載入器,系統預設有三個主要載入器,負責載入特定位置的類:(其結構體系如下)

 

38、類載入器的委託機制:每個類載入器載入類時,要先委託給上級類載入器。

 

39、JVM可以在執行期動態生成出類的位元組碼,這種動態生成的類往往被用作代理類,即動態代理類。

 

40、JVM生成的動態類必須實現一個或者多個介面,所以JVM生成的動態類只能用作具有相同介面的目標類的代理。

 

41、CGLIB庫可以動態生成一個類的子類,一個類的子類也可以用作該類的代理,所以,如果要為一個沒有實現介面的類生成動態代理類,那麼可以使用CGLIB庫。

 

42、應用AOP時,系統功能程式碼可插入的位置,例項如下:


   

43、獲取一個類的動態代理例項的應用示例如下:




   

 

----------- android培訓java培訓、期待與您交流! ------------

   

  


相關文章