標籤:
一:註解
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[email protected]
java.lang.Override
表示一個方法聲明打算重寫超類中的另一個方法聲明。
如果方法利用此注釋類型進行註解但沒有重寫超類方法,則編譯器會產生一條錯誤訊息。
比如重寫的方法名不是父類中要重寫方法名,則系統會識別出來並報錯。
toString 寫成 tostring.
public class Person
{
@Override
public string toString ()
{
return null;
}
}
4[email protected]
不鼓勵程式員使用這樣的元素,通常是因為它很危險或存在更好的選擇。在使用不被贊成的程式元素或在不被贊成的代碼中執行重寫時,編譯器會發出警告。
@Deprecated //@Deprecated表示該方法過時
public static void sayHi()
{
System.out.println("hi,world");
}
5[email protected]
用來忽略某種警告,使用方式@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[email protected]
用來表示其他註解的生命週期.
@Retention(RetentionPolicy.SOURCE)
public @interface AnnotationInter {
}
8[email protected]
用來表示其他註解的使用範圍,枚舉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(Collectioncols) { 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(){}
- 只有參考型別才能作為泛型方法的實際參數,swap(new int[3],3,5);語句會報告編譯錯誤。
- 普通方法、構造方法和靜態方法中都可以使用泛型。
- 也可以用類型變數表示異常,稱為參數化的異常,可以用於方法的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
-
所有已知子介面:
-
GenericArrayType, ParameterizedType, TypeVariable<D>, WildcardType
-
所有已知實作類別:
-
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虛擬機器可以安裝多個類載入器,系統預設三個主要的類載入器,每個類載入器負責載入不同位置的類
- Bootstrap,ExtClassLoader,AppClassLoader。
- 有的類載入器也是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()方法來指定某個類載入器去載入某個類。
java高新技術中《九》