黑馬程序員-java-高新技術中《九》

                   ——Java培訓、Android培訓、iOS培訓、.Net培訓、期待與您交流! ——

一:註解

1.註解(Annotation)

    註解相當一個類或接口,每一個註解都是一個實例對象
    註解的使用形式:@interface即@註解類名

  定義註解類:

    @interface A
    {…}

    使用了“註解類”的類:

     @A
     class B{}

  對“使用了註解類的類”進行反射操作:

class C {
      B.class.isAnnotationPresent(A.class);
      A a=(A)B.class.getAnnotation(A.class); }

2.註解類的生命週期

   源文件(.java),字節碼文件(.class),內存中的字節碼(運行時期被加載到內存當中).

   可用枚舉類:RetentionPolicy下的常量表示

 RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME

3.@Override

   java.lang.Override

    表示一個方法聲明打算重寫超類中的另一個方法聲明。
    如果方法利用此註釋類型進行註解但沒有重寫超類方法,則編譯器會生成一條錯誤消息。
    比如重寫的方法名不是父類中要重寫方法名,則系統會識別出來並報錯。
    toString 寫成 tostring.

public class Person
{
   @Override
    public string toString ()
    {
       return null;
    }
}

4.@Deprecated

不鼓勵程序員使用這樣的元素,通常是因爲它很危險或存在更好的選擇。在使用不被贊成的程序元素或在不被贊成的代碼中執行重寫時,編譯器會發出警告。

@Deprecated  //@Deprecated表示該方法過時
     public static void sayHi()
    {
        System.out.println("hi,world");
    }

5.@SuppressWarnings

   用來忽略某種警告,使用方式@SuppressWarnings(value=

{TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})

  eg:@SuppressWarnings(value={"deprecation"});//忽略“deprecation”類型的警告

@SuppressWarnings(value={"deprecation"})
public class Demo
{
    public static void main (String []args)
    {      
           A  a=new A();
          a.ma();
    class  A
    {   
        @Deprecated
        public void ma()
        {
          System.out.println("ma方法");
        }
    }
}}

6.歸檔

   jdk提供的一種可以將多個文件或目錄合併/壓縮成單個文件(.jar)的歸檔工具。

   jar –cvf  mytest.jar  A.class  B.class ;//將這兩個class文件打包成mytest.jar文件
   打包命令,對於package xxx的xx.java文件打包

   javac  -d 打包後文件的路徑  打包的Java文件

   javac  -d  .  Person.java ;//Person.java文件下“package  hq.packag;”

7.@Retention

   用來表示其他註解的生命週期.

  @Retention(RetentionPolicy.SOURCE)
public @interface AnnotationInter {
 
}

8.@Target

   用來表示其他註解的使用範圍,枚舉ElementType下的不同常量規定了範圍

   TYPE(類,接口(包括註釋類型),枚舉),FIElD(字段),METHOD(方法),

   ANNOTATION_TYPE(註釋類型),CONSTRUCTOR(構造方法),PACKAGE(包)等。

@Target({ElementType.TYPE,ElementType.METHOD})
public @interface AnnotationInter {
 
}

二:泛型

  泛型是提供給javac編譯器使用的,可以限定集合中的輸入類型,讓編譯器擋住源程序中的非法輸入,編譯器編譯帶類型說明的集合時會去除掉“類型”信息,

  使程序運行效率不受影響,對於參數化的泛型類型,getClass()方法的返回值和原始類型完全一樣。由於編譯生成的字節碼會去掉泛型的類型信息

  只要能跳過編譯器,就可以往某個泛型集合中加入其它類型的數據,例如,用反射得到集合,再調用其add方法即可。

  總結:泛型只在編譯器時期有效,在運行時期無效,在運行時期,它指明的“類型”信息會去掉如:

1.泛型(只針對引用類型)使用規則

   ArrayList類定義和ArrayList類引用中涉及如下術語:整個稱爲ArrayList泛型類型

   ArrayList中的E稱爲類型變量或類型參數整個ArrayList稱爲參數化的類型

   ArrayList中的Integer稱爲類型參數的實例或實際類型參數

   ArrayList中的<>念着typeof

   ArrayList稱爲原始類型

   參數化類型與原始類型的兼容性:

   參數化類型可以引用一個原始類型的對象,編譯報告警告,

     例如, Collection<String> c = new Vector();//可以

   原始類型可以引用一個參數化類型的對象,編譯報告警告,例如,

      Collection c = new Vecto<String>r();//可以。

   第三種情況:

      Vector v1=new Vector<String> ();//符合最上面的情況

      Vector<Object> v=v1; //也可以,符合上面第二種情況

   參數化類型不考慮類型參數的繼承關係:

      Vector<String> v = new Vector<Object>();//錯誤

      Vector<Object> v=new Vector<String>();//錯誤

2.泛型中的?通配符

   問題:定義一個方法,該方法用於打印出任意參數化類型的集合中的所有數據,該方法如何定義呢?

   錯誤方式:

  public static void printCollection(Collection cols) { 

             for(Object obj:cols) { System.out.println(obj); } 

             /* cols.add("string");//沒錯 cols = new HashSet();//會報告錯誤!*/ }

   正確方式:

public static void printCollection(Collection<?> cols) {     

                for(Object obj:cols) { System.out.println(obj); }  

               //cols.add("string");//錯誤,因爲它不知自己未來匹配就一定是String    

               cols.size();//沒錯,此方法與類型參數沒有關係      

               cols = new HashSet<Date>();     }

   總結: 使用?通配符可以引用其他各種參數化的類型,?通配符定義的變量主要用作引用,可以調用與參數化無關的方法

            不能調用與參數化類型有關的方法。

3.泛型中的?通配符的擴展

    限定通配符的上邊界:

      正確:Vector<? extends Number> x = new Vector<Integer>();

      錯誤:Vector<? extends Number> x = new Vector<String>();

    限定通配符的下邊界:

        正確:Vector<? super Integer> x = new Vector<Number>();

        錯誤:Vector<? super Integer> x = new Vector<Byte>();

     <?>與<E>

          class<?> c=new class<E> ();//正確

          class<E> c=new class<?>();//錯誤,即?可以“=”任意參數,但是特定參數化類型“=”?錯誤

    總結:限定通配符總是包括自己。 ?只能用作引用,不能用它去給其他變量賦值

            Vector<? extends Number> y = new Vector<Integer>();

            Vector<Number> x = y;

            上面的代碼錯誤,原理與Vector<Object > x11 = new Vector<String>();相似,

            只能通過強制類型轉換方式來賦值。

4.定義泛型的方法

  •  Java中的泛型類型(或者泛型)類似於 C++ 中的模板。但是這種相似性僅限於表面,Java 語言中的泛型基本上完全是在編譯器中實現,

  •  用於編譯器執行類型檢查和類型推斷,然後生成普通的非泛型的字節碼,這種實現技術稱爲擦除(erasure)

  •  Java的泛型方法沒有C++模板函數功能強大,java中的如下代碼無法通過編譯: <T> T add(T x,T y) {   return (T) (x+y);      

  •    //return null;     } 用於放置泛型的類型參數的尖括號應出現在方法的其他所有修飾符之後和在方法的返回類型之前,也就是

  •   緊鄰返回值之前。按照慣例,類型參數通常用單個大寫字母表示。

  •  除了在應用泛型時可以使用extends限定符,在定義泛型時也可以使用extends限定符,例如,Class.getAnnotation()方法的定義。

  •  並且可以用&來指定多個邊界,如<V extends Serializable & cloneable> void method(){}

  1. 只有引用類型才能作爲泛型方法的實際參數,swap(new int[3],3,5);語句會報告編譯錯誤。

  2. 普通方法、構造方法和靜態方法中都可以使用泛型。

  3. 也可以用類型變量表示異常,稱爲參數化的異常,可以用於方法的throws列表中,但是不能用於catch子句中。

     在泛型中可以同時有多個類型參數,在定義它們的尖括號中用逗號分,

     例如: public static <K,V> V getValue(K key) { return map.get(key);}

//<T>必須聲明,這是對你使用“T”的解析,<T extends U&V>也可以,<T super U>不可以   

      public   <T> T method1(T x,T y) {  

         return y;  

      }    

     public  <T extends Number> void method2(T x)     {       

       }    

//多個參數的T    

   public static <K,V> V getValue(K key)     {  

        V map = null;        

        return  (V) ((ArrayList<Integer>) map).get((int) key);         

       }     

  //異常如何採用泛型   

     private static <T extends Exception> void sayHello() throws T     {  

            try{    

                    }catch(Exception e)

                       {      

                           throw (T)e;     

                        }     

      }

5.定義泛型的類

public class GenericDao<E> {   

      //多個方法使用的E類,放在該類上面聲明<E>    

        public void add(E e)     {         
          }   

     //靜態方法使用的E,必須用自己方法上聲明的<E>,不能使用類上面聲明的<E>    

         public static<E> void method(E e)     {     

          }   

         public void update (E e)     {        

        } }

注意: 在對泛型類型進行參數化時,類型參數的實例必須是引用類型,不能是基本類型。 當一個變量被聲明爲泛型時,

          只能被實例變量、方法和內部類調用,而不能被靜態變量和靜態方法調用。因爲靜態成員是被所有參數化的類

          所共享的,所以靜態成員不應該有類級別的類型參數

6.Type(接口)

Type 是 Java 編程語言中所有類型的公共高級接口。它們包括原始類型、參數化類型、數組類型、類型變量和基本類型。

    java.lang.reflect 接口 Type

  •      所有已知實現類:

  •  Class

7.反射,泛型的高級應用

public class GenericTest {

    public static void main(String[] args) throws Exception {     

   //如何獲取某個方法上聲明的特定泛型類型         

   //如:public void applyVector(Vector<Date> v);如何得知該Vector是Date,即如何獲取<T>裏的泛型類型       

   //解決:使用反射的Method類裏的getGenericParameterTypes()獲取該方法上的泛型類型   

      Method  applyMethod=GenericTest.class.getMethod("applyVector", Vector.class,Set.class);     

      Type [] tp= applyMethod.getGenericParameterTypes();         

      System.out.println(tp[0]);            

      ParameterizedType p=(ParameterizedType) tp[1];        

      System.out.println(p.getActualTypeArguments()[0]);   

     }   

    public void applyVector (Vector<Date> v,Set<HashSet> s)       {           
      }

}

 三:類加載器

1.類加載器

  •    類加載器是當程序運行時要使用某個類,則類加載器就加該類的字節碼加載到內存裏執行。

  •    Java虛擬機可以安裝多個類加載器,系統默認三個主要的類加載器,每個類加載器負責加載不同位置的類

  •        BootstrapExtClassLoaderAppClassLoader

  •    有的類加載器也是Java類,所以必須一個非Java類的加載器加載其他Java類的類加載器,這個就是Boostrap

  •    Java虛擬機中的所有類加載器採用具有父子關係的樹形結構進行組織。每個實例化類加載器對象時必須爲其指定

  •        一個父級的類加載器對象,或採用系統默認的類加載器作爲父級。

2.ClassLoader

   構造方法:

        ClassLoader();//使用方法 getSystemClassLoader() 返回的 ClassLoader 創建一個新的類加載器,將該加載器作爲父類加載器。

        ClassLoader(ClassLoader parent);//指定父類加載器,父類可能最終調用Bootstrap作爲最後的父級。

   成員方法:

        ClassLoader  getParent();//返回委託的父類加載器。

        static  ClassLoader  getSystemClassLoader();//返回系統的類加載器

        Class<?> loadClass(String name);//使用指定的二進制名稱來加載類。

        Class<?> findClass(String name);//使用指定的二進制名稱查找類.

        Class<?> defineClass(byte [] b,int off ,int len);//讀取byte數組轉成類實例

3.三大類加載器的關係及加載類的範圍

 

   BootStrap(啓動類加載器):

                 常用的Java類,如System,util下的集合類等等。

   ExtClassLoader(擴展類加載器):

                        遵循雙親委派模型,即兩種加載類的方式,一是loadClass方法,二是findClass方法。 它重寫了findClass方法

                        當父類的loadClass方法找不到類,且它也找不到時,就會使用findClass方法加載類,還不行就會報異常錯誤。

                        我們可以通過右鍵該類export,將我們自定義的類導出到該類加載器的文件夾下,

   AppClassLoader(應用程序類加載器):

                        沒有遵循雙親委派模型,即一種加載類的方式,即loadClass方法,它重寫了loadClass方法,

class NetworkClassLoader extends ClassLoader 
{
         String host;        
          int port;        
           public Class findClass(String name) { 
          byte[] b = loadClassData(name);    
          return defineClass(name, b, 0, b.length);
         }        
          private byte[] loadClassData(String name) {                      // load the class data from the connection    
                    . . .
         }
}

      遵循雙親委派模型的意義:它保證了相同全限定名的類是不會被重複加載到JVM中,即沒有重名的類加載上了。

      不遵循雙親委派模型的意義:有可能有大量相同名的類,被不同的自定義類加載器加載到JVM中,即有同名不同功能的類都加載上來了

4.類加載器的(雙親)委託機制

   類加載器加載類的時候,會委託父級加載器去找該類並加載該類,父類又委託給父類,直到祖宗加載該類

   祖宗沒加載到該類的話,會讓下一級找,直到最初委託的加載器(如果重寫了findClass方法,則使用該方法加載類)。

    還不行的話就會報異常ClassNotFoundException

  • 首先當前線程的類加載器(getContextClassLoader())去加載線程中的第一個類。

  • 如果類A中引用了類B,Java虛擬機將使用加載類A的類裝載器來加載類B。

  • 還可以直接調用ClassLoader.loadClass()方法來指定某個類加載器去加載某個類。

 


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