一文搞懂Java中的註解和反射
1、註解(Annotation)
1.1 什麼是註解(Annotation)
註解不是程序本身,可以在程序編譯、類加載和運行時被讀取,並執行相應的處理。註解的格式為"@註釋名(參數值)",可以附加在包、類、方法和字段上,通過反射機制實現實現註解的訪問。
1.2 內置註解
@Override:限定子類重寫方法
該註解表示覆蓋的是其父類的方法,當子類重寫父類方法時,確保子類確實重寫瞭父類的方法,避免出現低級錯誤
/** * 該註解標識覆蓋的是其父類的方法,當子類重寫父類方法時,確保子類確實重寫瞭父類的方法,避免出現低級錯誤 * @return */ @Override public String toString() { return super.toString(); }
@Deprecated:標記已過時
該註解表示某個屬性、方法或類等已過時(程序員不鼓勵使用的程序元素,通常是因為它是危險的,或者因為存在更好的替代方法),當其他程序使用已過時的屬性、方法或者類時,編譯器會給出警告(刪除線)。
/** * 該註解表示此方法已過時,存在危險,不推薦使用,其有代替方法,如果繼續使用會通過刪除線進行標識 */ @Deprecated public static void test() { System.out.println("標記已過時"); }
@SuppressWarnings(參數):抑制編譯器警告
該註解作用的類、方法和屬性會取消顯示編譯器警告,其參數主要是進行警告說明以及取消(unchecked)等。
@SuppressWarnings("取消此類的所有警告") public class BuiltAnnotation { @SuppressWarnings("取消此屬性的警告") private String username; @SuppressWarnings("取消此方法的警告") public static void main(String[] args) { // ... } }
1.3 元註解(meta-annotation)
元註解的作用就是負責註解其他註解,Java定義瞭4個標準的元註解類型,他們被用來提供對其他註解的作用范圍及類型進行說明,通過元註解可以自定義其他註解。
@Target:描述註解的使用范圍
例如@Target(ElementType.METHOD)表示作用在方法上,@Target(ElementType.TYPE)表示作用在類或接口上等
/** * @Target註解:描述註解的使用范圍 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /** * 類或接口:ElementType.TYPE; * 字段:ElementType.FIELD; * 方法:ElementType.METHOD; * 構造方法:ElementType.CONSTRUCTOR; * 方法參數:ElementType.PARAMETER; * ... */ ElementType[] value(); }
@Retention:表示需要在什麼級別保存該註解信息,用於描述註解的生命周期
通常自定義的註解都使用@Retention(RetentionPolicy.RUNTIME),也就是運行時期作用。
/** * @Retention:表示需要在什麼級別保存該註解信息,用於描述註解的生命周期 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * RetentionPolicy.SOURCE:僅編譯期,註解將被編譯器丟棄。 * RetentionPolicy.CLASS:僅class文件,註釋將由編譯器記錄在類文件中,但VM不需要在運行時保留,如果不指定則默認為class。 * RetentionPolicy.RUNTIME:運行期,註釋將由編譯器記錄在類文件中,並由VM在運行時保留,因此可以反射讀取。通常自定義的註解都是RUNTIME。 * 范圍:RUNTIME>CLASS>SOURCE */ RetentionPolicy value(); }
@Document:說明該註將被包含在javadoc中
@Iherited:定義子類是否可繼承父類定義的註解。
@Inherited僅針對 @Target(ElementType.TYPE) 類型的註解有用,並且隻能是 class 的繼承,對 interface 的繼承無效:
1.4 自定義註解
定義註解
/** * 1. 使用@interface定義註解; * 3. 通過元註解配置該註解,配置註解的使用范圍和生命周期等 * @author Loner */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Report{ /** * 2. 添加參數、默認值,如果沒有默認值,就必須給參數賦值,一般把最常用的參數定義為value(),推薦所有參數都盡量設置默認值 * 形式為:參數類型 參數名(); */ int type() default 0; String value() default "LonerMJ"; }
使用註解
@Report(type = 1, value = "test") public class CustomerAnnotation { @Report(type = 1, value = "test") public void testCustomerAnnotation() { System.out.println("測試自定義註解"); } }
2、反射(Reflection)
2.1 反射和反射機制
反射
Reflection(反射)是指程序在運行期可以拿到一個對象的所有信息。
反射機制
反射機制是指程序在運行時,通過Reflection API獲取任何類的內容信息,並能直接操作任何對象的內部屬性及方法。
2.2 Class類的獲取方式和常用方法
java.lang.Class類,實現反射的核心類,類加載完成之後,在堆內存的方法區中就會產生一個Class對象(一個類隻有一個Class對象),這個對象包含瞭類的完整結構信息,通過這個對象看到類的結構。
Class類的獲取方式
public class InstantiationClass { public static void main(String[] args) throws ClassNotFoundException { Teacher teacher = new Teacher("張三", "123456"); // 方式一:調用Class類的靜態方法forName(String className) Class<?> c1 = Class.forName("com.loner.mj.reflection.Teacher"); // 方式二:已知某個類的實例,調用該實例的getClass()方法,getClass是Object類中的方法。 Class<? extends Teacher> c2 = teacher.getClass(); // 方式三:已知具體類,通過類的class屬性獲取,該方法最安全可靠,程序性能最高 Class<Teacher> c3 = Teacher.class; // 方式四:通過基本內置類型的包裝類的TYPE屬性獲得CLass實例 Class<Integer> c4 = Integer.TYPE; // 方式五:通過當前子類的Class對象獲得父類的Class對象 Class<?> c5 = c1.getSuperclass(); } }
Class類的常用方法
方法名 | 方法功能 |
---|---|
static Class forName(String name) | 返回指定類名的Class對象 |
Obiect newInstance() | 調用無參構造函數,返回Class對象的一個實例 |
String getName() | 返回此Class對象所表示的實體(類,接口,數組類或void)的名稱 |
Class getSuperclass() | 返回當前Class對象的父類的Class對象 |
Class[] getinterfaces() | 返回當前Class對象的接口 |
ClassLoader getClassLoader() | 返回該類的類加載器 |
Method getDeclareMethod(String name, Class<?> … parameterTypes) | 獲取方法名和參數列表匹配的方法 |
Method[] getDeclareMethods() | 獲取所有非繼承的方法 |
Method[] getMethods() | 獲取所有非私有方法 |
Field getDeclareField(String name) | 獲取指定屬性 |
Field[] getDeclareFields() | 獲取所有屬性 |
Field[] getFields() | 獲取所有非私有屬性 |
Constructor getConstructor(Class<?>… parameterTypes | 獲取參數列表匹配的構造方法 |
Constructor getConstructors() | 獲取類的所有構造方法 |
A getAnnotation(Class<?> annotationClass) | 返回指定註解 |
Annotation[] getDeclaredAnnotations() | 返回所有註解 |
public class ReflectionMethods { public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException { Class<Worker> workerClass = Worker.class; /** * 類 */ System.out.println(workerClass.getName()); System.out.println(workerClass.getSimpleName()); System.out.println(workerClass.getSuperclass()); System.out.println(workerClass.getPackage()); Class<?>[] interfaces = workerClass.getInterfaces(); for (Class<?> i : interfaces) { System.out.println(i); } /** * 屬性 */ // 獲取所有的屬性 Field[] declaredFields = workerClass.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } // 獲取指定屬性 System.out.println(workerClass.getDeclaredField("username")); // 獲取所有公共屬性 Field[] fields = workerClass.getFields(); for (Field field : fields) { System.out.println(field); } /** * 構造方法 */ // 獲取所有構造方法 Constructor<?>[] declaredConstructors = workerClass.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } // 獲取指定的構造方法 System.out.println(workerClass.getDeclaredConstructor(String.class, String.class)); /** * 方法 */ // 獲取所有的方法 Method[] declaredMethods = workerClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); } // 獲取指定方法 System.out.println(workerClass.getDeclaredMethod("getUsername", null)); // 獲取所有功能方法 Method[] methods = workerClass.getMethods(); for (Method method : methods) { System.out.println(method); } } }
哪些類型具有Class對象
public class InstantiationClass { public static void main(String[] args) throws ClassNotFoundException { // 類(外部類,成員(成員內部類,靜態內部類),局部內部類,匿名內部類。) Class<Object> objectClass = Object.class; // 接口 Class<Comparable> comparableClass = Comparable.class; // 數組 Class<String[]> stringClass = String[].class; Class<int[][]> intClass = int[][].class; // 枚舉 Class<ElementType> elementTypeClass = ElementType.class; // 註解 Class<Override> overrideClass = Override.class; // 基本數據類型 Class<Integer> integerClass = Integer.class; // void Class<Void> voidClass = void.class; // Class Class<Class> classClass = Class.class; } }
2.3 反射的使用
反射操作對象
public class UseClass { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class<User> userClass = User.class; /** * 通過構造器實例化對象:不使用構造器,默認通過無參構造進行對象創建 */ Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class, String.class); User user = declaredConstructor.newInstance("張三", "123456"); System.out.println(user); /** * 調用方法並執行相關操作 */ Method setUsername = userClass.getDeclaredMethod("setUsername", String.class); // invoke(Object, 參數):激活,即執行相關操作為該對象 setUsername.invoke(user, "李四"); Method setPassword = userClass.getDeclaredMethod("setPassword", String.class); setPassword.invoke(user, "123456"); System.out.println(user); /** * 操作屬性:通過反射直接操作私有屬性會報錯,需要通過setAccessible(ture)關閉訪問安全檢查,此方法屬性、方法和構造都具有,會影響效率 */ Field username = userClass.getDeclaredField("username"); username.setAccessible(true); username.set(user, "用戶名"); System.out.println(user); } }
反射操作泛型
Java采用泛型擦除的機制來引入泛型,Java中的泛型僅僅是給編譯器javac使用的,確保數據的安全性和免去強制類型轉換問題。但是,一旦編譯完成,所有和泛型有關的類型全部擦除。
為瞭通過反射操作這些類型,Java新增瞭ParameterizedType,GenericArrayType,TypeVariable和WildcardType幾種類型來代表不能被歸一到Class類中的類型但是又和原始類型齊名的類型。
ParameterizedType:表示一種參數化類型,比如Collection
GenericArrayType:表示一種元素類型是參數化類型或者類型變量的數組類型
TypeVariable:是各種類型變量的公共父接口
WildcardType:代表一種通配符類型表達式
public class ClassOperateGenerics { public Map<String, String> list() { System.out.println("返回值是泛型"); return new HashMap<>(); } public void test(Map<String, User> map, List<Integer> list) { System.out.println("參數是泛型"); } public static void main(String[] args) throws NoSuchMethodException { /** * 獲取方法參數的泛型 */ Method method = ClassOperateGenerics.class.getMethod("test", Map.class, List.class); // 獲取所有方法參數的泛型 Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { // java.util.Map<java.lang.String, com.loner.mj.reflection.User> System.out.println(genericParameterType); if (genericParameterType instanceof ParameterizedType) { // 獲取所有泛型的真實參數 Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { // String, User, Integer System.out.println(actualTypeArgument); } } } /** * 獲取方法返回值的泛型 */ Method list = ClassOperateGenerics.class.getMethod("list", null); // 獲取方法返回值的泛型 Type genericReturnType = list.getGenericReturnType(); if (genericReturnType instanceof ParameterizedType) { // 獲取所有泛型的真實參數 Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } }
反射操作註解
public class ClassOperateAnnotation { public static void main(String[] args) throws NoSuchFieldException { Class<People> peopleClass = People.class; // 獲取類的所有註解 Annotation[] declaredAnnotations = peopleClass.getDeclaredAnnotations(); for (Annotation declaredAnnotation : declaredAnnotations) { System.out.println(declaredAnnotation); } // 獲取類的註解的值 Table declaredAnnotation = peopleClass.getDeclaredAnnotation(Table.class); System.out.println(declaredAnnotation.value()); // 獲取屬性的註解 Field name = peopleClass.getDeclaredField("name"); Fields annotation = name.getAnnotation(Fields.class); System.out.println(annotation.name()); } }
以上就是一文搞懂Java中的註解和反射的詳細內容,更多關於Java註解 反射的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Java元註解Retention代碼示例介紹
- java基礎之註解示例詳解
- 理解Java註解及Spring的@Autowired是如何實現的
- 一篇文章帶你入門java註解
- Java中的三種標準註解和四種元註解說明