一文搞懂Java中的注解和反射
1、注解(Annotation)
1.1 什么是注解(Annotation)
注解不是程序本身,可以在程序編譯、類(lèi)加載和運(yùn)行時(shí)被讀取,并執(zhí)行相應(yīng)的處理。注解的格式為"@注釋名(參數(shù)值)",可以附加在包、類(lèi)、方法和字段上,通過(guò)反射機(jī)制實(shí)現(xiàn)實(shí)現(xiàn)注解的訪問(wèn)。
1.2 內(nèi)置注解
@Override:限定子類(lèi)重寫(xiě)方法
該注解表示覆蓋的是其父類(lèi)的方法,當(dāng)子類(lèi)重寫(xiě)父類(lèi)方法時(shí),確保子類(lèi)確實(shí)重寫(xiě)了父類(lèi)的方法,避免出現(xiàn)低級(jí)錯(cuò)誤
/** * 該注解標(biāo)識(shí)覆蓋的是其父類(lèi)的方法,當(dāng)子類(lèi)重寫(xiě)父類(lèi)方法時(shí),確保子類(lèi)確實(shí)重寫(xiě)了父類(lèi)的方法,避免出現(xiàn)低級(jí)錯(cuò)誤 * @return */ @Override public String toString() { return super.toString(); }
@Deprecated:標(biāo)記已過(guò)時(shí)
該注解表示某個(gè)屬性、方法或類(lèi)等已過(guò)時(shí)(程序員不鼓勵(lì)使用的程序元素,通常是因?yàn)樗俏kU(xiǎn)的,或者因?yàn)榇嬖诟玫奶娲椒ǎ?,?dāng)其他程序使用已過(guò)時(shí)的屬性、方法或者類(lèi)時(shí),編譯器會(huì)給出警告(刪除線)。
/** * 該注解表示此方法已過(guò)時(shí),存在危險(xiǎn),不推薦使用,其有代替方法,如果繼續(xù)使用會(huì)通過(guò)刪除線進(jìn)行標(biāo)識(shí) */ @Deprecated public static void test() { System.out.println("標(biāo)記已過(guò)時(shí)"); }
@SuppressWarnings(參數(shù)):抑制編譯器警告
該注解作用的類(lèi)、方法和屬性會(huì)取消顯示編譯器警告,其參數(shù)主要是進(jìn)行警告說(shuō)明以及取消(unchecked)等。
@SuppressWarnings("取消此類(lèi)的所有警告") public class BuiltAnnotation { @SuppressWarnings("取消此屬性的警告") private String username; @SuppressWarnings("取消此方法的警告") public static void main(String[] args) { // ... } }
1.3 元注解(meta-annotation)
元注解的作用就是負(fù)責(zé)注解其他注解,Java定義了4個(gè)標(biāo)準(zhǔn)的元注解類(lèi)型,他們被用來(lái)提供對(duì)其他注解的作用范圍及類(lèi)型進(jìn)行說(shuō)明,通過(guò)元注解可以自定義其他注解。
@Target:描述注解的使用范圍
例如@Target(ElementType.METHOD)表示作用在方法上,@Target(ElementType.TYPE)表示作用在類(lèi)或接口上等
/** * @Target注解:描述注解的使用范圍 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /** * 類(lèi)或接口:ElementType.TYPE; * 字段:ElementType.FIELD; * 方法:ElementType.METHOD; * 構(gòu)造方法:ElementType.CONSTRUCTOR; * 方法參數(shù):ElementType.PARAMETER; * ... */ ElementType[] value(); }
@Retention:表示需要在什么級(jí)別保存該注解信息,用于描述注解的生命周期
通常自定義的注解都使用@Retention(RetentionPolicy.RUNTIME),也就是運(yùn)行時(shí)期作用。
/** * @Retention:表示需要在什么級(jí)別保存該注解信息,用于描述注解的生命周期 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * RetentionPolicy.SOURCE:僅編譯期,注解將被編譯器丟棄。 * RetentionPolicy.CLASS:僅class文件,注釋將由編譯器記錄在類(lèi)文件中,但VM不需要在運(yùn)行時(shí)保留,如果不指定則默認(rèn)為class。 * RetentionPolicy.RUNTIME:運(yùn)行期,注釋將由編譯器記錄在類(lèi)文件中,并由VM在運(yùn)行時(shí)保留,因此可以反射讀取。通常自定義的注解都是RUNTIME。 * 范圍:RUNTIME>CLASS>SOURCE */ RetentionPolicy value(); }
@Document:說(shuō)明該注將被包含在javadoc中
@Iherited:定義子類(lèi)是否可繼承父類(lèi)定義的注解。
@Inherited僅針對(duì) @Target(ElementType.TYPE) 類(lèi)型的注解有用,并且只能是 class 的繼承,對(duì) interface 的繼承無(wú)效:
1.4 自定義注解
定義注解
/** * 1. 使用@interface定義注解; * 3. 通過(guò)元注解配置該注解,配置注解的使用范圍和生命周期等 * @author Loner */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Report{ /** * 2. 添加參數(shù)、默認(rèn)值,如果沒(méi)有默認(rèn)值,就必須給參數(shù)賦值,一般把最常用的參數(shù)定義為value(),推薦所有參數(shù)都盡量設(shè)置默認(rèn)值 * 形式為:參數(shù)類(lèi)型 參數(shù)名(); */ 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("測(cè)試自定義注解"); } }
2、反射(Reflection)
2.1 反射和反射機(jī)制
反射
Reflection(反射)是指程序在運(yùn)行期可以拿到一個(gè)對(duì)象的所有信息。
反射機(jī)制
反射機(jī)制是指程序在運(yùn)行時(shí),通過(guò)Reflection API獲取任何類(lèi)的內(nèi)容信息,并能直接操作任何對(duì)象的內(nèi)部屬性及方法。
2.2 Class類(lèi)的獲取方式和常用方法
java.lang.Class類(lèi),實(shí)現(xiàn)反射的核心類(lèi),類(lèi)加載完成之后,在堆內(nèi)存的方法區(qū)中就會(huì)產(chǎn)生一個(gè)Class對(duì)象(一個(gè)類(lèi)只有一個(gè)Class對(duì)象),這個(gè)對(duì)象包含了類(lèi)的完整結(jié)構(gòu)信息,通過(guò)這個(gè)對(duì)象看到類(lèi)的結(jié)構(gòu)。
Class類(lèi)的獲取方式
public class InstantiationClass { public static void main(String[] args) throws ClassNotFoundException { Teacher teacher = new Teacher("張三", "123456"); // 方式一:調(diào)用Class類(lèi)的靜態(tài)方法forName(String className) Class<?> c1 = Class.forName("com.loner.mj.reflection.Teacher"); // 方式二:已知某個(gè)類(lèi)的實(shí)例,調(diào)用該實(shí)例的getClass()方法,getClass是Object類(lèi)中的方法。 Class<? extends Teacher> c2 = teacher.getClass(); // 方式三:已知具體類(lèi),通過(guò)類(lèi)的class屬性獲取,該方法最安全可靠,程序性能最高 Class<Teacher> c3 = Teacher.class; // 方式四:通過(guò)基本內(nèi)置類(lèi)型的包裝類(lèi)的TYPE屬性獲得CLass實(shí)例 Class<Integer> c4 = Integer.TYPE; // 方式五:通過(guò)當(dāng)前子類(lèi)的Class對(duì)象獲得父類(lèi)的Class對(duì)象 Class<?> c5 = c1.getSuperclass(); } }
Class類(lèi)的常用方法
方法名 | 方法功能 |
---|---|
static Class forName(String name) | 返回指定類(lèi)名的Class對(duì)象 |
Obiect newInstance() | 調(diào)用無(wú)參構(gòu)造函數(shù),返回Class對(duì)象的一個(gè)實(shí)例 |
String getName() | 返回此Class對(duì)象所表示的實(shí)體(類(lèi),接口,數(shù)組類(lèi)或void)的名稱 |
Class getSuperclass() | 返回當(dāng)前Class對(duì)象的父類(lèi)的Class對(duì)象 |
Class[] getinterfaces() | 返回當(dāng)前Class對(duì)象的接口 |
ClassLoader getClassLoader() | 返回該類(lèi)的類(lèi)加載器 |
Method getDeclareMethod(String name, Class<?> ... parameterTypes) | 獲取方法名和參數(shù)列表匹配的方法 |
Method[] getDeclareMethods() | 獲取所有非繼承的方法 |
Method[] getMethods() | 獲取所有非私有方法 |
Field getDeclareField(String name) | 獲取指定屬性 |
Field[] getDeclareFields() | 獲取所有屬性 |
Field[] getFields() | 獲取所有非私有屬性 |
Constructor getConstructor(Class<?>... parameterTypes | 獲取參數(shù)列表匹配的構(gòu)造方法 |
Constructor getConstructors() | 獲取類(lèi)的所有構(gòu)造方法 |
A getAnnotation(Class<?> annotationClass) | 返回指定注解 |
Annotation[] getDeclaredAnnotations() | 返回所有注解 |
public class ReflectionMethods { public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException { Class<Worker> workerClass = Worker.class; /** * 類(lèi) */ 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); } /** * 構(gòu)造方法 */ // 獲取所有構(gòu)造方法 Constructor<?>[] declaredConstructors = workerClass.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } // 獲取指定的構(gòu)造方法 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); } } }
哪些類(lèi)型具有Class對(duì)象
public class InstantiationClass { public static void main(String[] args) throws ClassNotFoundException { // 類(lèi)(外部類(lèi),成員(成員內(nèi)部類(lèi),靜態(tài)內(nèi)部類(lèi)),局部?jī)?nèi)部類(lèi),匿名內(nèi)部類(lèi)。) Class<Object> objectClass = Object.class; // 接口 Class<Comparable> comparableClass = Comparable.class; // 數(shù)組 Class<String[]> stringClass = String[].class; Class<int[][]> intClass = int[][].class; // 枚舉 Class<ElementType> elementTypeClass = ElementType.class; // 注解 Class<Override> overrideClass = Override.class; // 基本數(shù)據(jù)類(lèi)型 Class<Integer> integerClass = Integer.class; // void Class<Void> voidClass = void.class; // Class Class<Class> classClass = Class.class; } }
2.3 反射的使用
反射操作對(duì)象
public class UseClass { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class<User> userClass = User.class; /** * 通過(guò)構(gòu)造器實(shí)例化對(duì)象:不使用構(gòu)造器,默認(rèn)通過(guò)無(wú)參構(gòu)造進(jìn)行對(duì)象創(chuàng)建 */ Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class, String.class); User user = declaredConstructor.newInstance("張三", "123456"); System.out.println(user); /** * 調(diào)用方法并執(zhí)行相關(guān)操作 */ Method setUsername = userClass.getDeclaredMethod("setUsername", String.class); // invoke(Object, 參數(shù)):激活,即執(zhí)行相關(guān)操作為該對(duì)象 setUsername.invoke(user, "李四"); Method setPassword = userClass.getDeclaredMethod("setPassword", String.class); setPassword.invoke(user, "123456"); System.out.println(user); /** * 操作屬性:通過(guò)反射直接操作私有屬性會(huì)報(bào)錯(cuò),需要通過(guò)setAccessible(ture)關(guān)閉訪問(wèn)安全檢查,此方法屬性、方法和構(gòu)造都具有,會(huì)影響效率 */ Field username = userClass.getDeclaredField("username"); username.setAccessible(true); username.set(user, "用戶名"); System.out.println(user); } }
反射操作泛型
Java采用泛型擦除的機(jī)制來(lái)引入泛型,Java中的泛型僅僅是給編譯器javac使用的,確保數(shù)據(jù)的安全性和免去強(qiáng)制類(lèi)型轉(zhuǎn)換問(wèn)題。但是,一旦編譯完成,所有和泛型有關(guān)的類(lèi)型全部擦除。
為了通過(guò)反射操作這些類(lèi)型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType幾種類(lèi)型來(lái)代表不能被歸一到Class類(lèi)中的類(lèi)型但是又和原始類(lèi)型齊名的類(lèi)型。
ParameterizedType:表示一種參數(shù)化類(lèi)型,比如Collection
GenericArrayType:表示一種元素類(lèi)型是參數(shù)化類(lèi)型或者類(lèi)型變量的數(shù)組類(lèi)型
TypeVariable:是各種類(lèi)型變量的公共父接口
WildcardType:代表一種通配符類(lèi)型表達(dá)式
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("參數(shù)是泛型"); } public static void main(String[] args) throws NoSuchMethodException { /** * 獲取方法參數(shù)的泛型 */ Method method = ClassOperateGenerics.class.getMethod("test", Map.class, List.class); // 獲取所有方法參數(shù)的泛型 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) { // 獲取所有泛型的真實(shí)參數(shù) 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) { // 獲取所有泛型的真實(shí)參數(shù) 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; // 獲取類(lèi)的所有注解 Annotation[] declaredAnnotations = peopleClass.getDeclaredAnnotations(); for (Annotation declaredAnnotation : declaredAnnotations) { System.out.println(declaredAnnotation); } // 獲取類(lèi)的注解的值 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中的注解和反射的詳細(xì)內(nèi)容,更多關(guān)于Java注解 反射的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot logback調(diào)整mybatis日志級(jí)別無(wú)效的解決
這篇文章主要介紹了springboot logback調(diào)整mybatis日志級(jí)別無(wú)效的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Mybatis3中方法返回生成的主鍵:XML,@SelectKey,@Options詳解
這篇文章主要介紹了Mybatis3中方法返回生成的主鍵:XML,@SelectKey,@Options,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01SpringBoot創(chuàng)建線程池的六種方式小結(jié)
本文主要介紹了SpringBoot創(chuàng)建線程池的六種方式小結(jié),包括自定義線程池,固定長(zhǎng)度線程池,單一線程池,共享線程池,定時(shí)線程池,SpringBoot中注入異步線程池,感興趣的可以了解一下2023-11-11SpringBoot整合log4j日志與HashMap的底層原理解析
這篇文章主要介紹了SpringBoot整合log4j日志與HashMap的底層原理,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01spring cloud gateway中redis一直打印重連日志問(wèn)題及解決
這篇文章主要介紹了spring cloud gateway中redis一直打印重連日志問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05kafka消費(fèi)者kafka-console-consumer接收不到數(shù)據(jù)的解決
這篇文章主要介紹了kafka消費(fèi)者kafka-console-consumer接收不到數(shù)據(jù)的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03一個(gè)MIDP俄羅斯方塊游戲的設(shè)計(jì)和實(shí)現(xiàn)
一個(gè)MIDP俄羅斯方塊游戲的設(shè)計(jì)和實(shí)現(xiàn)...2006-12-12