Java 注解與反射(超詳細!!!)
文章目錄
- Java 注解與反射(超詳細!!!)
- 1.注解
- 1.1內置注解
- 1.1.1 @SuppressWarnings注解用法
- 1.2 元注解
- 1.3自定義注解
- 2.反射
- 2.1 反射的用途
- 2.2 反射的使用方法
- 2.3 class類型的對象使用場景1:
- 2.4 class類型的對象使用場景2:
- 2.5 通過反射創造對象
- 2.6 使用反射機制獲取和調用類的構造方法,訪問私有構造方法并創建對象
- 2.7 通過反射,訪問并使用成員方法
1.注解
1.1內置注解
注解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及以后版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,注釋。
例如:
@override:定義在java.lang.override 中,此注釋只適用于修辭方法,表示一個方法聲明打算
重寫超類中的另一個方法聲明
@Deprecated:定義在java.lang.Deprecated中,此注釋可以用于修辭方法,屬性,類,表示不
鼓勵程序員使用這樣的元素,通常是因為它很危險或者存在更好的選擇
@SuppressWarnings:定義在java.lang.SuppressWarnings中,用來抑制編譯時的警告信息.與前兩個注釋有所不同,你需要添加一個參數才能正確使用,這些參數都是已經定義好了的,我們選擇性的使用就好了
例如
@SuppressWarnings("all")@SuppressWarnings("unchecked")@SuppressWarnings(value={unchecked", "deprecation")
、、、
接下來,我們通過一個案例,講解一下注解是如何使用的,該如何使用。
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.public class Main implements Sum {public static void main(String[] args) {Main m = new Main();m.sum();m.sum1();}@Deprecated // 不建議使用的注解public void sum1() {System.out.println("nihao11111");}@Overridepublic void sum() {System.out.println("nihao");}}
在此案例中,我們通過了@Deprecated
表明這個一個不建議使用的方法。但是,這僅僅是建議,我們仍然可以使用。
1.1.1 @SuppressWarnings注解用法
@SuppressWarnings
批注允許您選擇性地取消特定代碼段(即,類或方法)中的警告。其中的想法是當您看到警告時,您將調查它,如果您確定它不是問題,
就可以添加一個 @SuppressWarnings
批注,以使您不會再看到警告。雖然它聽起來似乎會屏蔽潛在的錯誤,但實際上它將提高代碼安全性,因為它將防止您對警告無動于衷 — 您看到的每一個警告都將值得注意。
例如下面這個例子,idea會爆一個警告,上述代碼編譯通過且可以運行,但每行前面的“感嘆號”就嚴重阻礙了我們判斷該行是否設置的斷點了。這時我們可以在方法前添加@SuppressWarnings(“unused”)
去除這些“感嘆號”。
Contents of collection 'items' are updated, but never queried
package 藍橋杯國賽刷題;import java.util.ArrayList;
import java.util.List;public class commentTest01 {public static void main(String[] args) {commentTest01 CommentTest01 = new commentTest01();CommentTest01.addItems("1234");}public void addItems(String item) {List items = new ArrayList();items.add(item);}
}
1.2 元注解
元注解的作用就是負責注解其他注解,Java定義了4個標準的meta-annotation類型,他們被用來提供對其他annotation類型作說明.
這些類型和它們所支持的類在java.lang.annotation包中可以找到分別是@Target
, @Retention
,@Documented
, @lnherited
@Target:用于描述注解的使用范圍(即:被描述的注解可以用在什么地方)
@Retention :表示需要在什么級別保存該注釋信息,用于描述注解的生命周期
@Document:說明該注解將被包含在javadoc中
@lnherited:說明子類可以繼承父類中的該注解
1.3自定義注解
我們根據先前的注解寫法,模仿自己寫一個注解
通過觀察創造注解包含以下元素@interface
,@Documented
,@Retention
,Target
使用 @interface
自定義注解時,自動繼承了java.lang.annotation.Annotation
接口
@ intertace用來聲明一個注解,格式:public @ intertace 注解名{定義內容}
其中的每一個方法實際上是聲明了一個參數,方法的名稱就是參數的名稱返回值類型就是參數的類型( 返回值只能是基本類型,Class, String, enum )。可以通過default來聲明參數的默認值。如果只有一個參數成員,一般參數名為value。注解元素必須要有值,我們定義注解元素時,經常使用空字符串,O作為默認值。
自定義注解樣例:
//自定義注解
public class test03 {//注解可以顯示賦值, 如果沒有默認值,我們就必須給注解賦值//注解參數的順序隨意@MyAnnotation2(age = 18, name = "jacky")public void test() {}@MyAnnotation3("jacky")public void test2() {}
}@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{//注解的參數: 參數類型 + 參數名 + ();String name() default "";int age() default 0;int id() default -1;String[] schools() default {"peking university"};
}@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{//如果注解參數為value賦值時可以直接寫值String value();
}
在這個例子我們,我們定義了兩個注解,分別是@MyAnnotation2
,@MyAnnotation3
,其參數不相同。
2.反射
反射(Reflection)是一種 Java 程序運行期間的動態技術,可以在運行時(runtime)檢査、修改其自身結構或行為。通過反射,程序可以訪問、檢測和修改它自己的類、對象、方法、屬性等成員
在連接數據庫中,反射的作用在于使用其特殊機制,直接調用目標類的構造方法。類如這段代碼中
Class.forName("com.mysql.jdbc.Driver");
通過反射的機制,將 com.mysql.jdbc.Driver
這個類加載到 JVM 中。觸發靜態初始化塊:MySQL 的驅動類在靜態初始化塊中會執行 DriverManager.registerDriver(new Driver())
,自動向 DriverManager
注冊自己,使得后續可以通過 DriverManager.getConnection()
建立數據庫連接。
2.1 反射的用途
- 動態加載類:程序可以在運行時動態地加載類庫中的類;
- 動態創建對象:反射可以基于類的信息,程序可以在運行時,動態創建對象實例;
- 調用方法:反射可以根據方法名稱,程序可以在運行時,動態地調用對象的方法(即使方法在編寫程序時還沒有定義)
- 訪問成員變量:反射可以根據成員變量名稱,程序可以在運行時,訪問和修改成員變量(反射可以訪問私有成員變量)
- 運行時類型信息:反射允許程序在運行時,查詢對象的類型信息,這對于編寫通用的代碼和庫非常有用;
? Spring 框架使用反射來自動裝配組件,實現依賴注入;
? MyBatis 框架使用反射來創建resultType 對象,封裝數據查詢結果;
2.2 反射的使用方法
反射的第一步是獲取 Class
對象。Class
對象表示某個類的元數據,可以通過以下幾種方式獲取:
//獲取Class類型信息
public class Text02 {public static void main(String[] args) throws ClassNotFoundException {//方式1:通過類名Class stringClass1 = String.class;//方式2:通過Class類的forName()方法Class stringClass2 = Class.forName("java.lang.String");//方式3:通過對象調用getClass()方法Class stringClass3 = "".getClass();System.out.println(stringClass1.hashCode());//1604839423System.out.println(stringClass2.hashCode());//1604839423System.out.println(stringClass3.hashCode());//1604839423}}
2.3 class類型的對象使用場景1:
//Class類型的對象使用場景1
public class Text03 {public static void main(String[] args) {String json= "{\"name\":\"長安荔枝\",\"favCount\":234}"; Document doc=JSON.parseObject(json,Document.class);System.out.println(doc.getName());System.out.println(doc.getFavCount());}}
使用 JSON.parseObject
方法將 JSON 字符串解析為 Document
類的對象。在解析過程中,JSON 字符串中的數據會自動映射到 Document
類的對應字段中。
2.4 class類型的對象使用場景2:
通過 Class
對象在運行時獲取一個類的相關信息,包括類名、包名、成員變量(字段)、成員方法等。
//Class類型的對象使用場景2
//獲取豐富的類型內容
public class Text04 {public static void main(String[] args) throws ClassNotFoundException {Class clz = Class.forName("java.util.HashMap");//獲取類名System.out.println("完全限定名:"+clz.getName());System.out.println("簡單的類名:"+clz.getSimpleName());//獲取包名System.out.println("package"+clz.getPackage().getName());System.out.println();//獲取成員變量Field[] fieldArray =clz.getDeclaredFields();System.out.println("成員變量(字段)");for(Field field:fieldArray) {System.out.println(field);}System.out.println();//獲取成員方法Method[] methodArray = clz.getDeclaredMethods();System.out.println("成員方法");for(Method method:methodArray) {System.out.println(method);} }
}
clz.getName()
返回類的完全限定名,包括包名,例如"java.util.HashMap"
。clz.getSimpleName()
返回類的簡單名稱,不包括包名,例如"HashMap"
。clz.getPackage().getName()
返回類所屬的包名,例如"java.util"
。clz.getDeclaredFields()
返回一個Field
數組,包含了類聲明的所有字段(包括私有字段)。clz.getDeclaredMethods()
返回一個Method
數組,包含了類聲明的所有方法(包括私有方法)。
2.5 通過反射創造對象
有兩種方式通過反射創造對象分別是:
方式一:通過 Class
對象直接調用 newInstance()
方法
方式二:通過獲取構造方法(Constructor
)來創建對象。
//通過反射的方式,創建對象
public class Text05 {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {Class clz = Class.forName("com.apesource.demo01.Document");//方式1:直接通過Class對象,調用newInstance()方法Object objx = clz.newInstance();//相當于在執行無參構造方法//方式2:通過構造器(構造方法)//無參構造方法Constructor constructor1 = clz.getDeclaredConstructor();//獲取無參構造方法System.out.println(constructor1);Object obj1 = constructor1.newInstance();//執行構造器(構造方法),創建對象//有參構造方法Constructor constructor2 = clz.getDeclaredConstructor(String.class);//獲取有參構造方法System.out.println(constructor2);Object obj2 = constructor2.newInstance("兩京十五日");Constructor constructor3 = clz.getDeclaredConstructor(int.class);//獲取有參構造方法System.out.println(constructor3);Object obj3 = constructor3.newInstance(34);Constructor constructor4 = clz.getDeclaredConstructor(String.class,int.class);//獲取有參構造方法System.out.println(constructor4);Object obj4 = constructor4.newInstance("風起隴西",64);System.out.println(objx);System.out.println(obj1);System.out.println(obj2);System.out.println(obj3);System.out.println(obj4);}
newInstance()
方法是Class
對象提供的一個方法,它調用類的無參構造方法來創建類的實例。- 注意:這個方法在 Java 9 以后已經被棄用,推薦使用
Constructor
對象來創建實例。 - 通過
getDeclaredConstructor()
方法獲取Document
類的無參構造方法,然后調用newInstance()
方法創建實例。 - 注意:如果類中沒有無參構造方法,調用
getDeclaredConstructor()
會拋出NoSuchMethodException
。 - 通過
getDeclaredConstructor(String.class)
獲取帶有一個String
參數的構造方法,并傳入"兩京十五日"
作為參數來創建對象。 - 通過
getDeclaredConstructor(int.class)
獲取帶有一個int
參數的構造方法,并傳入34
作為參數來創建對象。
2.6 使用反射機制獲取和調用類的構造方法,訪問私有構造方法并創建對象
public class Text06 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {Class clz = Class.forName("com.apesource.demo01.Document");//獲取一組構造器Constructor[] constructorArray1 = clz.getConstructors();//publicConstructor[] constructorArray2 = clz.getDeclaredConstructors();//public、private//獲取指定構造器Constructor constructor1 = clz.getConstructor();Constructor constructor2 = clz.getDeclaredConstructor(String.class);System.out.println(constructor1);System.out.println(constructor2);//調用私有構造器,必須設置它的訪問全限constructor2.setAccessible(true);//調用構造器,創建對象Object obj = constructor2.newInstance("長安三萬里");System.out.println(obj);}}
getConstructors()
方法返回一個包含所有公共(public
)構造方法的數組。如果類中沒有public
構造方法,則返回空數組。getDeclaredConstructors()
方法返回一個包含所有聲明的構造方法的數組(包括私有的、受保護的和默認訪問級別的構造方法)。getConstructor()
方法用于獲取類的無參構造方法(必須是public
的)。如果沒有無參構造方法或者不是public
,則拋出NoSuchMethodException
。getDeclaredConstructor(Class<?>... parameterTypes)
方法用于獲取指定參數類型的構造方法。這里通過傳入String.class
參數獲取一個帶有String
參數的構造方法。這個構造方法可以是任何訪問級別(public
、private
、protected
、默認)。setAccessible(true)
用于繞過 Java 訪問控制機制,使私有構造方法也可以被調用。如果不設置Accessible
為true
,那么調用私有構造方法時會拋出IllegalAccessException
。newInstance(Object... initargs)
方法使用指定的構造方法創建對象。這里調用了帶有String
參數的構造方法,并傳入"長安三萬里"
作為參數。
2.7 通過反射,訪問并使用成員方法
public class Text08 {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {//硬編碼的方式
// Document doc1 = new Document();
// doc1.setName("海底兩萬里");
// doc1.setFavCount(10025);//反射的方式Class clz = Class.forName("com.apesource.demo01.Document");//獲取類型信息Object doc1 = clz.newInstance();//創建對象//獲取指定名稱和參數類型的方法Method setNameMethod = clz.getMethod("setName", String.class);Method setFavCountMethod = clz.getMethod("setFavCount", int.class);//執行方法//doc1.setName("海底兩萬里");setNameMethod.invoke(doc1, "海底兩萬里");//doc1.setFavCount(10025);setFavCountMethod.invoke(doc1, 10025);System.out.println(doc1);}}
getMethod(String name, Class<?>... parameterTypes)
方法用于獲取類的某個public
方法。方法名稱和參數類型必須匹配才能成功獲取方法。invoke(Object obj, Object... args)
方法用于調用指定的實例方法。setNameMethod.invoke(doc1, "海底兩萬里");
等同于doc1.setName("海底兩萬里");
。setFavCountMethod.invoke(doc1, 10025);
等同于doc1.setFavCount(10025);
。
以上便是Java注解與反射的全部內容了,筆者編寫不易,懇請點贊收藏,歡迎評論區交流!!!