一、註解(Annotation) 1、什麼是註解? 從JDK5開始,Java增加了Annotation(註解),Annotation是代碼里的特殊標記,這些標記可以在編譯、類載入、運行時被讀取,並執行相應的處理。 2、Annotation與註釋的區別: (1)Annotation不是程式本身,可以對 ...
一、註解(Annotation)
1、什麼是註解?
從JDK5開始,Java增加了Annotation(註解),Annotation是代碼里的特殊標記,這些標記可以在編譯、類載入、運行時被讀取,並執行相應的處理。
2、Annotation與註釋的區別:
(1)Annotation不是程式本身,可以對程式進行解釋,此處可以理解為註釋。但是Annotation可以被其他程式(比如編譯器)讀取,併進行處理。
(2)註解與註釋最大的區別就是註解存在被處理的流程,即註解是會被程式處理的。
3、註解的格式:
(1)以 “@註釋名” 的形式在代碼中存在。
(2)註解可以附加在程式元素( 包、類、構造器、方法、成員變數、參數、局域變數 )上面,為其添加額外的輔助信息,可以通過反射機制訪問這些數據。
(3)Annotation不能運行,其只有成員變數,沒有方法。Annotation與public、final等修飾符地位類似,屬於程式元素的一部分,但不能作為程式元素使用。
4、常見註解:
(1)@Override
定義在java.lang.Override中,此註釋只用於修飾方法,表示重寫一個父類的方法。
【舉例:】 @Override public String toString() { return "Hello"; }
(2)@Deprecated
定義在java.land.Deprecated中,此註釋可用於修飾方法、屬性、類,表示該方法、類、屬性不推薦使用(廢棄)。在方法、類、屬性上會有一條刪除線(形如toString())。
【舉例:】 @Deprecated public String toString() { return "TimerTaskDemo []"; }
(3)@SuppressWarnings
定義在java.lang.SuppressWarnings中,用來阻止編譯時的警告信息。其使用時需要設置參數。
【參數為:】 deprecation,使用了過時的類或方法的警告。 unchecked,執行了未檢查的轉換時的異常,比如集合未指定泛型。 fallthrough,當在switch語句發生case穿透時的警告。 path,當類路徑、源文件路徑不存在時的警告。 serial,可序列化類缺少serialVersionUID時的警告。 finally,任何finally不能完成時的警告。 all,以上所有警告。 【格式:】 @SuppressWarnings("all") 或者 @SuppressWarnings(value = { "serial", "unchecked" })
5、元註解:
(1)元註解的作用就是負責註解其他的註解。Java5.0定義了4個標準的meta-annotation類型,它們被用來提供對其它 annotation類型作說明。存在於java.lang.annotation中。
(2)元註解分類:
@Target
@Retention
@Documented
@Inherited
(3)@Target元註解:
用於描述註解的使用範圍。Annotation可被用於 packages、types(類、介面、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變數、枚舉值)、方法參數和本地變數(如迴圈變數、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。
【格式:】 public @interface Target { ElementType[] value(); } 【參數:ElementType】 CONSTRUCTOR:用於描述構造器 FIELD:用於描述成員變數 LOCAL_VARIABLE:用於描述局部變數 METHOD:用於描述方法 PACKAGE:用於描述包 PARAMETER:用於描述參數 TYPE:用於描述類、介面(包括註解類型) 或enum聲明 【舉例:】 @Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE})
(4)@Retention元註解:
用於描述註解的聲明周期。某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取(請註意並不影響class的執行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。
【格式:】 public @interface Retention { RetentionPolicy value(); } 【參數:RetentionPoicy】 SOURCE:在源文件中有效(即源文件保留) CLASS:在class文件中有效(即class保留) RUNTIME:在運行時有效(即運行時保留,可以通過反射機制讀取) 【舉例:】 @Retention(RetentionPolicy.SOURCE)
(5)@Documented元註解:
用於描述其它類型的annotation應該被作為被標註的程式成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記註解,沒有成員。
(6)@Inherited元註解:
@Inherited 元註解是一個標記註解,@Inherited闡述了某個被標註的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。
註意:
@Inherited annotation類型是被標註過的class的子類所繼承。類並不從它所實現的介面繼承annotation,方法並不從它所重載的方法繼承annotation。
當@Inherited annotation類型標註的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation類型被髮現,或者到達類繼承結構的頂層。
6、自定義註解:
(1)自定義註解時,需要使用@interface用來聲明一個註解,其會自動繼承java.lang.annotation.Annotation介面。
【格式:】 public @interface 註解名 {定義體} 【或者:】 public @interface 註解名 { 類型 value() default 預設值; //這裡是參數,不是抽象方法。 } 其中定義體實質是聲明瞭一個配置參數(註:此處不是抽象方法)。 1、方法名指的是參數名。 2、返回值類型指的是參數的類型(只能為基本類型、Class、String、enum、Annotation類型、以及以上所有類型的數組)。 3、可以通過default來聲明參數的預設值。 4、如果只有一個參數,那麼參數名(方法名)一般為value。 5、只能使用public, default兩個許可權修飾符。
(2)方法:
判斷類或者方法是否有註解 boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 獲得註解對象 <A extends Annotation> A getAnnotation(Class<A> annotationClass) //獲取指定註解 Annotation[] getAnnotations() //獲取當前元素上的所有註解
【舉例:】 package com.test; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Table { String value(); } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface FieldDemo { String columnName() default ""; String type() default ""; int length() default 10; } @Table("student") class Student { @FieldDemo(columnName = "id", length = 10, type = "int") private int id; @FieldDemo(columnName = "name", length = 20, type = "varchar") private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } public class AnnotationDemo { public static void main(String[] args) { try { // 獲取Student類的信息 Class classDemo = Class.forName("com.test.Student"); // Class<Student> classDemo = (Class<Student>)Class.forName("com.test.Student"); System.out.println(classDemo); // 輸出class com.test.Student // 獲取當前元素上的所有註解,此時獲取的是@Table Annotation[] annotations = classDemo.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } // 輸出@com.test.Table(value=student) // 直接獲取指定的某註解 Table table = (Table) classDemo.getAnnotation(Table.class); System.out.println(table.value()); // 輸出student // 獲取類的屬性的註解 Field field = classDemo.getDeclaredField("name"); // 獲取指定註解 FieldDemo fieldDemo = field.getAnnotation(FieldDemo.class); // 輸出name varchar 20 System.out.println(fieldDemo.columnName() + " " + fieldDemo.type() + " " + fieldDemo.length()); } catch (Exception e) { e.printStackTrace(); } } }
二、反射機制
1、什麼是反射?
JAVA反射機制指的是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;即這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。
JAVA中與反射相關的類,放在java.lang.reflect包中。
2、Class類
Class對象包含了一個類的完整的結構信息。通過Class對象,可以對類進行操作,即為反射。
(1)規則:
Class類擁有泛型作為定義。
Class 類的實例表示正在運行的 Java 應用程式中的類和介面。
Class類沒有public構造方法。
Class對象是在載入類時由Java 虛擬機以及通過調用類載入器中的defineClass 方法自動構造的。
一個類只有一個Class對象。
(2)內置class實例(class對象):
byte.class, short.class, int.class, long.class, char.class, float.class, double.class, boolean.class, void.class. 註: int.class != Integer.class 。 int.class == Integer.Type 。
(3)對於數組類型class實例:
每個數組屬於被映射為 Class 對象的一個類,所有具有相同元素類型和維數的數組都共用該Class 對象。
int[] a = new int[100]; int[] b = new int[10]; long[] c = new long[10]; int[][] d = new int[10][2]; System.out.println(a.getClass());//輸出class [I System.out.println(b.getClass());//輸出class [I System.out.println(c.getClass());//輸出class [J System.out.println(d.getClass());//輸出class [[I System.out.println(a.getClass() == b.getClass());//輸出true
(4)獲取Class實例的方法:
【方法1:】
根據傳入的參數動態裝載一個類,並且做類的初始化。
Class.forName()方法
【方法2:】
獲得對象運行時所指的真正對象(多態的場合返回子類的類名)。
Class.getClass() 方法
【方法3:】
JVM將使用類A的類裝載器,將類A裝入記憶體(前提是:類A還沒有裝入記憶體),不對類A做類的初始化工作.返回類A的Class的對象。
A.class屬性
(5)通過Class實例創建對象:
Class.newInstance()方法 。調用預設構造函數,獲得一個實例 Class.newInstance方法與new的區別 newInstance: 弱類型。低效率。只能調用無參構造。 new:強類型。相對高效。能調用任何public構造。
(6)常用方法:
【獲得構造器:】 Constructor<T> getDeclaredConstructor(Class<?>...) 獲得指定構造方法 Constructor<?>[] getDeclaredConstructors() 獲得所有構造方法(聲明順序) Constructor<T> getConstructor(Class<?>...) 獲得許可權為public的指定構造方法 Constructor<?>[] getConstructors() 獲得許可權為public的所有構造方法 【獲得普通方法(成員方法):】 Method[] getDeclaredMethods() 獲得該類中定義的所有方法(不包含父類繼承) Method getDeclaredMethod(String name, Class<?>... parameterTypes) 根據該類中定義的指定方法(不包含父類繼承) Method[] getMethods() 獲得許可權為public的所有的方法 (包含父類繼承) Method getMethod(String name, Class<?>... parameterTypes) 獲得許可權為public的指定的方法 (包含父類繼承) 【獲得屬性(成員變數):】 Field[] getDeclaredFields() 獲得該類中定義的所有屬性(不包含繼承) Field getDeclaredField(String name)獲得該類中定義的指定屬性(不包含繼承) Field[] getFields() 獲得該類中所有public的屬性(包含繼承) Field getField (String name) 獲得該類中指定的public屬性(包含繼承) 【獲得內部類:】 Class<?>[] getDeclaredClasses() 獲得所有內部類 (不包含繼承) Class<?>[] getClasses() 獲得所有許可權為public的內部類(包含繼承) 【其他:】 Package getPackage() 獲得Package對象 String getName() 獲得類的全稱,即包名+類名 String getSimpleName() 獲得類的簡稱,即類名 Class<? super T> getSuperclass() 獲得繼承的類 Class<?>[] getInterfaces() 獲得實現的介面
(7)獲得構造器後,可以進行的操作
(8)獲得成員方法後,可以進行的操作
(9)獲得成員變數後,可以進行的操作
package com.test; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; class Teacher { private String name; private int age; public Teacher() { } public Teacher(String name, int age) { this.name = name; this.age = age; } 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; } } public class ReflectionDemo { public static void main(String[] args) { try { // 載入Teacher.class對象 Class<Teacher> teacherClass = (Class<Teacher>) Class.forName("com.test.Teacher"); // 獲取無參構造器,若Teacher類沒有無參構造方法,則會報錯 Teacher teacher = teacherClass.newInstance(); System.out.println(teacher + ", " + teacher.getName() + ", " + teacher.getAge()); // 獲取有參構造器 Constructor<Teacher> constructor = teacherClass.getDeclaredConstructor(String.class, int.class); Teacher teacher2 = constructor.newInstance("tom", 20); System.out.println(teacher2 + ", " + teacher2.getName() + ", " + teacher2.getAge()); // 獲取成員方法 Teacher teacher3 = teacherClass.newInstance(); Method method = teacherClass.getDeclaredMethod("setAge", int.class); method.invoke(teacher3, 30); System.out.println(teacher3.getAge()); Method method2 = teacherClass.getDeclaredMethod("getAge"); System.out.println(method2.invoke(teacher3)); // 獲取成員變數 Teacher teacher4 = teacherClass.newInstance(); Field field = teacherClass.getDeclaredField("age"); field.setAccessible(true);// 忽略安全檢查,可以獲取private類型的數據,破壞封裝性。 System.out.println(field.get(teacher4)); } catch (Exception e) { e.printStackTrace(); } } } 【結果:】 com.test.Teacher@2a139a55, null, 0 com.test.Teacher@15db9742, tom, 20 30 30 0
3、反射機制性能問題
setAccessible,是啟用和禁用安全檢查的開關,其值為true時,表示禁用Java語言訪問的安全性檢查,為false時,表示啟用安全性檢查,將其值設為true,可以提高反射的效率。
未完待續。。。。