(Java)注解和反射
文章目錄
- 注解和反射
- 一. 注解
- 1.1 元注解
- 1.2 內置注解
- 1.3 自定義注解
- 二. 反射
- 2.1 什么是反射
- 2.2 Class類
- 2.3 創建Class類的方式
- 2.4 所有類型的Class
- 2.5 類加載機制內存分析
- 2.6 類的初始化
- 2.7 類加載器
- 2.7.1 雙親委派機制
- 2.9 獲取運行時類的完整結構
- 2.10 通過反射動態的創建對象
- 2.11 反射操作泛型
- 2.12 反射操作注解(重點)
注解和反射
一. 注解
注解(Annotation)是Java提供的設置程序中元素的關聯信息和元數據(MetaData)的方法,它是一個接口,程序可以通過反射獲取指定程序中元素的注解對象,然后通過該注解對象獲取注解中的元數據信息。
1.1 元注解
import java.lang.annotation.*;//測試原注解 @MyAnnotation public class Test01 {public void test() {}}//定義一個注解 //Target 表示我們的注解可以用在哪些地方 @Target(value = {ElementType.METHOD,ElementType.TYPE})//Retention 表示我們的注解在什么地方有效 runtime>class>source @Retention(value = RetentionPolicy.RUNTIME)//Documented 表示是否將我們的注解生成在JAVAdoc中 @Documented//Inherited 子類可以繼承父類的注解 @Inherited @interface MyAnnotation{}1.2 內置注解
import java.util.ArrayList;//內置注解 public class Test03 {@Override //重寫的注解public String toString() {return "Test03{}";}@Deprecated //不推薦使用,存在更好的public static void test() {System.out.println("Deprecated");}@SuppressWarnings("all") //鎮壓警告public void test2() {ArrayList arrayList = new ArrayList();}public static void main(String[] args) {test();}}1.3 自定義注解
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //自定義注解 public class Demo01 {//注解可以顯示賦值,如果沒有默認值,我們就必須給注解賦值@MyAnnotation(name="伊澤瑞爾",schools = "戰爭學院")public void test1(){}//一個參數的注解可以直接寫值@MyAnnotation1("魔法貓咪")public void test2(){}//設置生效類型@Target({ElementType.TYPE,ElementType.METHOD})//設置作用范圍@Retention(RetentionPolicy.RUNTIME)@interface MyAnnotation{String name() default "";int age() default 0;int id() default -1;String[] schools() default {"",""};}//設置生效類型@Target({ElementType.TYPE,ElementType.METHOD})//設置作用范圍@Retention(RetentionPolicy.RUNTIME)@interface MyAnnotation1{//一個參數的注解一般用value作為參數String value();} }二. 反射
2.1 什么是反射
Java c c++是靜態語言,Java可以通過反射獲得類似動態語言的特性。(準動態語言)
動態語言是指在運行階段可以改變其結構
反射 優點:
- 可以動態創建對象和編譯,靈活
反射 缺點:
- 對象性能有影響,總是慢于直接執行相同的操作
2.2 Class類
- class本身也是一個類
- 只能由系統建立對象
- 一個加載的類在JVM中只會有一個Class實例
- 一個Class實例對應的是一個加載到JVM中的一個.class文件
- 每個類的實例都會記得自己是由哪個class實例生成的
- 通過Class可以完整的得到一個類所有被加載的結構
- Class類是Reflection反射的根源,針對任何想動態加載,運行的類,唯有先獲得相應的Class對象
2.3 創建Class類的方式
package com.company.demo11.reflection;public class Demo01 {public static void main(String[] args) throws ClassNotFoundException {Person person = new Student();System.out.println("這個人是:"+person.name);//1.通過對象獲得Class c1 = person.getClass();System.out.println(c1.hashCode());//2.forName獲得Class c2 = Class.forName("com.company.demo11.reflection.Student");System.out.println(c2.hashCode());//3.通過類名.class獲得Class c3=Student.class;System.out.println(c3.hashCode());//4.基本內置類型的包裝類都有一個Type屬性Class c4 = Integer.TYPE;System.out.println(c4);//獲取父類類型Class c5 = c1.getSuperclass();System.out.println(c5);} } class Person {String name;public Person() {}public Person(String name) {this.name = name;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +'}';} } class Student extends Person {public Student() {this.name = "學生";} } class Teacher extends Person {public Teacher() {this.name = "老師";} }2.4 所有類型的Class
package com.company.demo11.reflection;import java.lang.annotation.ElementType; //所有類型的Class public class Demo02 {public static void main(String[] args) {Class c1 = Object.class; //類Class c2 = Comparable.class; //接口Class c3 = String[].class; //一維數組Class c4 = int[][].class; //二維數組Class c5 = Override.class; //注解Class c6 = ElementType.class; //枚舉Class c7 = Integer.class; //基本數據類型Class c8 = void.class; //voidClass c9 = Class.class; //ClassSystem.out.println(c1);System.out.println(c2);System.out.println(c3);System.out.println(c4);System.out.println(c5);System.out.println(c6);System.out.println(c7);System.out.println(c8);System.out.println(c9);//只要元素類型與維度一樣,就是同一個Classint[] a = new int[10];int[] b = new int[100];System.out.println(a.getClass().hashCode());System.out.println(b.getClass().hashCode());}}2.5 類加載機制內存分析
JVM的類加載分為5個階段:加載,驗證,準備,解析,初始化。在類初始化完成后就可以使用該類的信息,在一個類不再被需要時可以從JVM中卸載。如圖所示:
代碼測試
內存分析如圖
2.6 類的初始化
主要通過執行類構造器的方法為類進行初始化。方法是在編譯階段由編譯器自動收集類中靜態語句塊和變量的賦值操作組成的。JVM規定,只有在父類的方法都執行成功后,子類中的方法才可以被執行。在一個類中既沒有靜態變量賦值操作也沒有靜態語句塊時,編譯器不會為該類生成方法。
在發生以下幾種情況時,JVM不會執行類的初始化流程。
- 常量在編譯時會將其常量值存入使用該常量的類的常量池中,該過程不需要調用常量所在的類,因此不會觸發該常量類的初始化。
- 在子類引用父類的靜態字段時,不會觸發子類的初始化,只會觸發父類的初始化。
- 定義對象數組,不會觸發該類的初始化,只是開辟了一塊空間。
- 在使用類名獲取Class對象時不會觸發類的初始化。
- 在使用Class.forName加載指定的類時,可以通過initialize參數設置是否需要對類進行初始化。
- 在使用ClassLoader默認的loadClass方法加載類時不會觸發該類的初始化
2.7 類加載器
JVM提供了3種類加載器,分別是啟動類加載器,擴展類加載器和應用程序類加載器。如圖:
2.7.1 雙親委派機制
JVM通過雙親委派機制對類進行加載。
雙親委派機制是指一個類在收到類加載請求后不會嘗試自己加載這個類,而是把類加載請求向上委派給其父類去完成,其父類在接收到該類加載請求后又會將其委派給自己的父類,以此類推,這樣所有的的類加載請求都被向上委派到啟動類加載器中。
若父類加載器在接收到類加載請求后發現自己也無法加載該類(通常原因是該類的Class文件在父類的類加載器路徑中不存在),則父類會將該信息反饋給子類并向下委派子類加載器加載該類,直到類被成功加載,若找不到該類,則JVM會拋出ClassNotFound異常。
雙親委派類加載機制的類加載流程如圖:
2.9 獲取運行時類的完整結構
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method;public class Demo03 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {Class c1 = Class.forName("com.company.demo11.reflection.User");//獲取類的名字System.out.println(c1.getName());//com.company.demo11.reflection.UserSystem.out.println(c1.getSimpleName());//UserSystem.out.println("--------------------------------------");//獲得類的屬性Field[] fields = c1.getFields();//只能找到public屬性fields = c1.getDeclaredFields();//找到全部屬性for (Field field : fields) {System.out.println(field);/*private java.lang.String com.company.demo11.reflection.User.nameprivate int com.company.demo11.reflection.User.idprivate int com.company.demo11.reflection.User.age*/}System.out.println("--------------------------------------");//獲取指定屬性的值System.out.println(c1.getDeclaredField("name"));//private java.lang.String com.company.demo11.reflection.User.nameSystem.out.println("--------------------------------------");//獲取類的方法Method[] methods = c1.getMethods();//獲取本類及其父類的全部public方法for (Method method : methods) {System.out.println(method);}System.out.println("--------------------------------------");Method[] declaredMethods = c1.getDeclaredMethods();//獲取本類的所有方法for (Method declaredMethod : declaredMethods) {System.out.println(declaredMethod);}System.out.println("--------------------------------------");//獲得指定的方法Method getName = c1.getMethod("getName", null);Method setName = c1.getMethod("setName", String.class);System.out.println(getName);//public java.lang.String com.company.demo11.reflection.User.getName()System.out.println(setName);//public void com.company.demo11.reflection.User.setName(java.lang.String)System.out.println("--------------------------------------");//獲取構造器Constructor[] constructors = c1.getConstructors();//獲取public的構造器for (Constructor constructor : constructors) {System.out.println("public構造器:"+constructor);/*public com.company.demo11.reflection.User(java.lang.String,int,int)public com.company.demo11.reflection.User()*/}Constructor[] declaredConstructors = c1.getDeclaredConstructors();//獲取全部的構造器for (Constructor declaredConstructor : declaredConstructors) {System.out.println("全部構造器:"+declaredConstructor);/*public com.company.demo11.reflection.User(java.lang.String,int,int)public com.company.demo11.reflection.User()*/}//獲取指定的構造器Constructor constructor = c1.getConstructor(String.class, int.class,int.class);System.out.println("指定構造器"+constructor);//public com.company.demo11.reflection.User(java.lang.String,int,int)} }2.10 通過反射動態的創建對象
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;public class Demo04 {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {//獲得Class對象Class c1 = Class.forName("com.company.demo11.reflection.User");//創建一個對象,此操作必須要有無參構造方法User user1 = (User) c1.newInstance();System.out.println(user1);//調用無參構造方法 User{name='null', id=0, age=0}//通過構造器創建對象Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);User user2 = (User) constructor.newInstance("周星馳", 007, 18);System.out.println(user2);//User{name='周星馳', id=7, age=18}//通過反射調用普通方法User user3 = (User) c1.newInstance();//通過反射獲得一個方法Method setName = c1.getDeclaredMethod("setName", String.class);//invoke 激活 (對象,方法的值)setName.invoke(user3,"伊澤瑞爾");System.out.println(user3.getName());//伊澤瑞爾//通過反射操作屬性User user4 = (User) c1.newInstance();Field name = c1.getDeclaredField("name");//不能直接操作私有屬性,關閉安全檢測.name.setAccessible(true);name.set(user4,"魔法貓咪");System.out.println(user4.getName());//魔法貓咪} }2.11 反射操作泛型
/通過反射獲取泛型 public class Test11 {public void test01(Map<String,User> map, List<User> list) {System.out.println("test01");}public Map<String,User> test02() {System.out.println("test02");return null;}public static void main(String[] args) throws NoSuchMethodException {/*#java.util.Map<java.lang.String, com.lcy.reflection.User>class java.lang.Stringclass com.lcy.reflection.User#java.util.List<com.lcy.reflection.User>class com.lcy.reflection.User*///加載的方法和參數Method method = Test11.class.getMethod("test01", Map.class, List.class);//獲得泛型的參數類型Type[] genericParameterTypes = method.getGenericParameterTypes();for (Type type :genericParameterTypes) { //打印泛型System.out.println("#"+type);if (type instanceof ParameterizedType) { //想知道里面的參數類型//強轉獲得真實的泛型參數信息Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();for (Type temp :typeArguments) {System.out.println(temp);}}}/*class java.lang.Stringclass com.lcy.reflection.User*/method = Test11.class.getMethod("test02",null);//獲得返回值類型Type genericReturnType = method.getGenericReturnType();if (genericReturnType instanceof ParameterizedType) {Type[] types = ((ParameterizedType) genericReturnType).getActualTypeArguments();for (Type temp :types) {System.out.println(temp);}}}}2.12 反射操作注解(重點)
import java.lang.annotation.*; import java.lang.reflect.Field;public class Demo05 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {//獲取Class對象Class c1 = Class.forName("com.company.demo11.reflection.Student1");//通過反射獲取注解Annotation[] annotations = c1.getAnnotations();for (Annotation annotation : annotations) {System.out.println(annotation);//找到了外面的注解@com.company.demo11.reflection.Table(value=db_stu)}//獲取注解value的值Table annotation = (Table) c1.getAnnotation(Table.class);System.out.println(annotation.value());//db_stu//獲取類指定的注解Field f = c1.getDeclaredField("name");FieldZh annotation1 = f.getAnnotation(FieldZh.class);System.out.println(annotation1.columName());System.out.println(annotation1.type());System.out.println(annotation1.length());}} @Table("db_stu") class Student1{@FieldZh(columName = "id",type = "int",length = 10)int id;@FieldZh(columName = "name",type = "varchar",length = 10)String name;@FieldZh(columName = "age",type = "int",length = 3)int age;@Overridepublic String toString() {return "Student1{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}public Student1() {}public Student1(int id, String name, int age) {this.id = id;this.name = name;this.age = age;}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 int getAge() {return age;}public void setAge(int age) {this.age = age;} } //創建表的注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Table{String value(); } //創建屬性的注解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface FieldZh{String columName();String type();int length();}總結
以上是生活随笔為你收集整理的(Java)注解和反射的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (JavaWeb)Filter过滤器
- 下一篇: (JavaWeb)IDEA中JDBC的使