一、反射
1. 反射機制
反射機制的相關類除了一個java.lang.Class,其余都在java.lang.reflect包下,
反射機制用于讀取class位元組碼檔案,需要注意,JVM加載位元組碼到記憶體中時都只會保存一份,多次讀取class檔案時不用擔心也會加載多次,
反射機制相關的常用類:
- java.lang.Class:代表整個類的位元組碼,表示一個型別,
- java.lang.reflect.Method:代表位元組碼中的方法位元組碼,表示一個方法,
- java.lang.reflect.Constructor:代表位元組碼中的構造方法位元組碼,表示一個構造方法,
- java.lang.reflect.Field:代表位元組碼中的屬性位元組碼,表示一個屬性,
2. 反射類位元組碼Class(類/型別)
獲取類的位元組碼(java.lang.Class類)有三種方式:
- 第一種方式:通過Class類的靜態方法forName,例如
Class c1 = Class.forName("java.lang.String");就表示獲取到了String這個類的class位元組碼,注意,這是Class類下的一個靜態方法,引數需要是完整的包名,另外,Class.forName方法的使用會導致類的加載,也就是說如果希望只是執行一個類的靜態代碼塊,并不執行其他的代碼,就可以使用這個方法來進行類的加載,此時,自然就會去執行對應的靜態代碼了, - 第二種方式:通過Object類的getClass方法,例如
String s = "abc"; Class c2 = s.getClass();,即通過任何類物件的getClass方法就可以拿到對應了類位元組碼了,并且因為JVM只會在記憶體中加載一份相同類的位元組碼,所以這個例子的c2和第一種方式的c1使用雙等號判斷回傳結果是true, - 第三種方式:通過類的class屬性,例如
Class c3 = String.class;,java中任何型別都有class屬性,
Class中常用的方法:
- String getName():回傳類的完整類名(包含包路徑),
- String getsimpleName():回傳類的簡類名(類定義名稱),
- Field[] getFields():獲取Class中所有public型別的Field物件(屬性),
- Field[] getDeclaredFields():獲取Class中所有的Field物件(屬性),
- Field getDeclaredField(String name):獲取指定名稱的Field物件(屬性),
- Method[] getDeclaredMethods():獲取所有的Method物件(方法),
- Method getDeclaredMethod(String name, Class<?>... parameterTypes):根據方法名稱和引數型別串列獲取Method物件(方法),例如“userClass.getDeclaredMethod("login", String.class, int.class);”,
- Constructor<?>[] getDeclaredConstructors():獲取所有的Constructor物件(構造方法),
- Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):獲取指定引數型別串列的Constructor物件(構造方法),例如“userClass.getDeclaredConstructor(String.class, int.class);”,
- Class<? super T> getSuperclass():獲取類的父類,
- Class<?>[] getInterfaces():獲取所有類實作的介面,
3. 反射屬性位元組碼Field(欄位/屬性)
獲取Field需要先獲取到對應的類位元組碼Class,然后才能從類中獲取到對應的Field(java.lang.reflect.Field),
Field常用方法:
- Class<?> getType():回傳屬性的資料型別,
- String getName():回傳屬性的名稱,
- int getModifiers():回傳修飾符串列的代號,此代號可以使用java.lang.reflect.Modifier的靜態方法toString方法傳入代號獲取到具體的修飾符名稱串列,
- void set(Object obj, Object value):給指定物件的Field物件賦予值value,
- Object get(Object obj):獲取指定物件的Field值(屬性值),
- void setAccessible(boolean flag):設定為true時表示打破封裝,如果不呼叫這個方法設定為true的話就沒辦法獲取物件的私有屬性了,呼叫這個方法之后就可以獲取到所有的屬性了,包括私有屬性,
4. 反射方法位元組碼Method(方法)
獲取Method需要先獲取到對應的類位元組碼Class,然后才能從類中獲取到對應的Method(java.lang.reflect.Method),
Method中的常用方法:
- Class<?> getReturnType():獲取回傳值的資料型別,
- Class<?>[] getParameterTypes():獲取方法的引數型別串列,
- int getModifiers():回傳修飾符串列的代號,此代號可以使用java.lang.reflect.Modifier的靜態方法toString方法傳入代號獲取到具體的修飾符名稱串列,
- Object invoke(Object obj, Object... args):呼叫指定物件的方法,
5. 反射構造方法Constructor(構造方法)
獲取Constructor需要先獲取到對應的類位元組碼Class,然后才能從類中獲取到對應的Method(java.lang.reflect.Constructor),
Constructor中的常用方法:
- int getModifiers():回傳修飾符串列的代號,此代號可以使用java.lang.reflect.Modifier的靜態方法toString方法傳入代號獲取到具體的修飾符名稱串列,
- T newInstance(Object... initargs):使用newInstance方法創建一個實體物件,
二、注解
1. 定義注解(Annotation)
注解,或者稱之為注釋,也是一種參考資料型別,編譯之后也會生成class檔案,具體用法見示例:
[修飾符串列] @interface 注解型別名{ // 屬性定義 }
注解定義示例:
// 無屬性的注解定義 public @interface MyAnnotation{ // 這里面什么都不寫,表示沒有屬性 } // 有屬性的注解定義 public @interface MyAnnotation2{ // 定義一個沒有默認值的屬性,在使用這個注解的時候就必須給這個屬性傳值 // 注意,注解的屬性定義是有小括號的,但它不是方法,就只是屬性 String name(); // 使用default給屬性指定默認值,有默認值的屬性在使用時就可以不用給這個屬性傳值了 int id() default 2333; } // 屬性只有一個,且為value時,使用時可以不用指定屬性名稱 public @interface MyAnnotation3{ String value(); }
注解使用示例:
// 使用:直接在類、方法、屬性、形參、注解等上面使用形如”@注解型別名“的格式即可, // 注解的使用其實就像修飾符一樣在定義的前面加上就可以,但是通常的使用習慣是在定義上一行進行添加 public class AnnotationTest { public static void main(String[] args) { } // 相當于:@MyAnnotation private int id; @MyAnnotation private int id; // 注解只有一個屬性,且屬性名為value時,可以不用指定屬性名 @MyAnnotation3("hello") public AnnotationTest() { } // 定義了沒有默認值的屬性的注解,就必須給這個屬性傳值,有默認值的屬性可以傳,也可以不傳 @MyAnnotation2(name = "zhangsan") public static void func() { @MyAnnotation2(name = "lisi", id = 666) int i = 10; } // 相當于:@MyAnnotation public void func2(@MyAnnotation String name) @MyAnnotation public void func2(@MyAnnotation String name) { System.out.println(name); } }
注解屬性型別:定義注解的屬性時,屬性的型別可以是byte、short、int、long、float、double、boolean、char、String、Class、列舉型別,以及這幾種型別的陣列形式,不能是其他的型別,有一個小技巧,屬性如果是陣列,并且使用時傳入的陣列元素只有一個的話,定義陣列的大括號是可以不寫的,
2. Java內置注解
內置注解在java.lang包下,常用的有:
- @Override:這個注解只能注解方法,并且只是給編譯器在編譯階段做參考用的,和運行階段的代碼沒有關系,編譯器在編譯時會檢查這個方法是否是重寫的父類方法,如果不是則會報錯,
- @Deprecated:表示被標注的類、方法等元素已經過時了,不建議使用,在IDEA中,被標注的方法等會出現一條橫線,提示你這個方法已過時,
3. 元注解
用來標注“注解型別”的注解,即注解的注解,稱之為元注解,在java.lang.annotation包下,常用的元注解有:
- @Target(ANNOTATION_TYPE):用來指定被標注的注解可以出現在哪些位置上,引數為列舉型別ElementType的陣列,具體有哪些列舉值可以參考幫助檔案,如“@Target(ElementType.METHOD)”表示被標注的注解只能出現在方法上,
- @Retention(RUNTIME):用來指定被標注的注解最終保存在哪里,引數是一個列舉型別RetentionPolicy,有三個列舉值,“@Retention(RetentionPolicy.SOURCE)”表示被標注的注解保存在java源檔案中,并不會出現在編譯之后的class檔案中,“@Retention(RetentionPolicy.CLASS)”表示被標注的注解保存在class檔案中,“@Retention(RetentionPolicy.RUNTIME)”表示被標注的注解保存在class檔案中,并且可以被反射機制讀取出來,
4. 反射注解
以類的注解為例,首先獲取到類的位元組碼物件后,使用Class物件的方法進行反射,常用的方法有:
- boolean isAnnotationPresent(MyAnnotation.class):判斷一個類是否有指定的注解,這里需要傳入一個指定注解的類位元組碼物件,
- getAnnotation(MyAnnotation.class):獲取類的指定注解物件,這里需要傳入一個指定注解的類位元組碼物件,
屬性獲取:通過反射拿到注解物件之后,就可以通過呼叫方法(其實是屬性)的形式獲取屬性值,因為注解定義屬性時,本身就自帶小括號,所以看起來就是在呼叫方法了,如“String name = annotationObj.name();”
注:方法、屬性等的注解獲取也是和上面的方法一樣,而且呼叫的方法等大多也都是一樣的,
5. 注解的作用
注解通常是通過反射機制去檢查被注解的類、方法等是否滿足要求,比如@Override就是檢查被注解的方法是否是重寫父類的方法,如果不是就會編譯報錯,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/157163.html
標籤:Java
下一篇:Python練習題3.8字串逆序
