注解與反射
1.0、什么是注解
-
Annotation是從JDK5.0開始引入的新技術(shù)
-
Annotation的作用:
-
不是程序本身,可以對(duì)程序作出解釋(這一點(diǎn)和注釋(comment)沒什么區(qū)別)
-
可以被其他程序(比如:編譯器等)讀取
-
-
Annotation的格式:
-
注解是以“@注釋名”在代碼中存在的,還可以添加一些參數(shù)值,例如:
SuppressWarnings(value="unchecked")
-
-
Annotation在哪里使用?
-
可以附加在package,class,method,field等上面,相當(dāng)于給他們添加了額外的輔助信息,我們可以通過反射機(jī)制編程實(shí)現(xiàn)對(duì)這些數(shù)據(jù)的訪問
-
1.1、內(nèi)置注解
-
@override:定義在`java.lang.Override中,此注釋只適用于修飾方法,表示一個(gè)方法聲明打算重寫超類中的另一個(gè)方法聲明 -
@Deprecated:定義在`java.lang.Deprecated中,此注釋可以用于修飾方法,屬性,類,表示不鼓勵(lì)程序員使用這樣的元素,通常是因?yàn)樗芪kU(xiǎn)或者存在更好的選擇 -
@SuppressWarnings:定義在java.lang.SuppressWarnings中,用來抑制編譯時(shí)的警告信息-
(與前兩個(gè)注釋有所不同,你需要添加一個(gè)參數(shù)才能正確使用,這些參數(shù)都是定義好了的,我們選擇使用就好了)
-
@SuppressWarnings("all") -
@SuppressWarnings("unchecked") -
@SuppressWarnings(value={"unchecked","deprecation"}) -
等等.....
-
-
1.2、元注解
-
元注解的作用就是負(fù)責(zé)注解其它注解,Java定義了4個(gè)標(biāo)準(zhǔn)的meta-annotation類型,他們被用來提供其它annotation類型作說明
-
這些類型和它們所支持的類在
java.lang.annotation包中可找到(@Target,@Retention,@Documented,@Inherited)-
@Target:用于描述注解的使用范圍(即:被描述的注解可以用在什么地方)
-
@Retention:表示需要在什么級(jí)別保存該注釋信息,用于描述注釋的生命周期
-
(SOUREC < CLASS <RUNTIME)
-
-
@Document:說明該注釋將被包含在
javadoc中 -
-
//測(cè)試元注解
1.3、自定義注解
-
使用
@interface自定義注解時(shí),自動(dòng)繼承了java.lang.annotation.Annotation接口 -
分析:
-
@interface用來聲明一個(gè)注解,格式:public @interface 注解名 {定義內(nèi)容} -
其中的每一個(gè)方法實(shí)際上是聲明了一個(gè)配置參數(shù)
-
方法的名稱就是參數(shù)的名稱
-
返回值類型就是參數(shù)的類型(返回值只能是基本類型,Class,String,enum)
-
通過
default來聲明參數(shù)的默認(rèn)值 -
如果只有一個(gè)參數(shù)成員,一般參數(shù)名為value(此時(shí)賦值時(shí)可以省略參數(shù)名)
-
注解元素必須要有值,我們定義注解元素時(shí),經(jīng)常使用空字符串,0作為默認(rèn)值
-
//自定義注解
public class Test01 {
?
2、反射機(jī)制
2.1、Java反射機(jī)制概述
-
靜態(tài) VS 動(dòng)態(tài)語言
-
動(dòng)態(tài)語言:是一類在運(yùn)行時(shí)可以改變其結(jié)構(gòu)的語言:例如新的函數(shù)、對(duì)象、甚至代碼可以被引進(jìn),已有的函數(shù)可以被刪除或是其它結(jié)構(gòu)上的變化。通俗點(diǎn)說就是在運(yùn)行時(shí)代碼可以根據(jù)某些條件改變自身結(jié)構(gòu)(主要?jiǎng)討B(tài)語言:Object-C、C#、JavaScript、PHP、Python等)
-
靜態(tài)語言:與動(dòng)態(tài)語言相對(duì)應(yīng)的,運(yùn)行時(shí)結(jié)構(gòu)不可變的語言就是靜態(tài)語言。如Java、C、C++。
Java不是動(dòng)態(tài)語言,但Java可以稱之為“準(zhǔn)動(dòng)態(tài)語言”。即Java有一定的動(dòng)態(tài)性,我們可以利用反射機(jī)制獲得類似動(dòng)態(tài)語言的特性。Java的動(dòng)態(tài)性讓編程的時(shí)候更加靈活
-
-
Java Reflection
-
Reflection(反射)是Java被視為動(dòng)態(tài)語言的關(guān)鍵,反射機(jī)制允許程序在執(zhí)行期借助于Reflection API取得任何類的內(nèi)部信息,并直接操作任意對(duì)象的內(nèi)部屬性及方法
Class c = Class.forName("java.lang.String") -
加載完類之后,在堆內(nèi)存的方法區(qū)域中就產(chǎn)生了一個(gè)Class類型的對(duì)象(一個(gè)類只有一個(gè)Class對(duì)象),這個(gè)對(duì)象就包含了完整的類的結(jié)構(gòu)信息。我們可以通過這個(gè)對(duì)象看到類的結(jié)構(gòu)。這個(gè)對(duì)象就像一面鏡子,透過這個(gè)鏡子看到類的結(jié)構(gòu),所以,我們形象的稱之為:反射
-
-
Java反射機(jī)制提供的功能
-
在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類
-
在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象
-
在運(yùn)行時(shí)判斷一個(gè)類所具有的成員變量和方法
-
在運(yùn)行時(shí)獲取泛型信息
-
在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的成員變量和方法
-
在運(yùn)行時(shí)處理注解
-
生成動(dòng)態(tài)代理
-
-
Java反射優(yōu)點(diǎn)和缺點(diǎn)
-
優(yōu)點(diǎn):可以實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建對(duì)象和編譯,體現(xiàn)出很大靈活性
-
缺點(diǎn):對(duì)性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且滿足我們的要求。這類操作總是慢于直接執(zhí)行相同的操作
-
2.2、理解Class類并獲取Class實(shí)例
-
Class類
-
對(duì)象照鏡子后可以得到的信息:某個(gè)類的屬性、方法和構(gòu)造器、某個(gè)類到底實(shí)現(xiàn)類哪些接口。對(duì)于沒個(gè)類而言,JRE都為其保留一個(gè)不變的Class類型的對(duì)象。一個(gè)Class對(duì)象包含了特定某個(gè)結(jié)構(gòu)(class/interface/enum/annotation/primitive type/void/[])的有關(guān)信息
-
Class本身也是一個(gè)類
-
Class對(duì)象只能由系統(tǒng)建立對(duì)象
-
一個(gè)加載的類在JVM中只會(huì)有一個(gè)Class實(shí)例
-
一個(gè)Class對(duì)象對(duì)應(yīng)的是一個(gè)加載到JVM中的一個(gè).class文件
-
每個(gè)類的實(shí)例都會(huì)記得自己是由哪個(gè)Class實(shí)例所生成
-
通過Class可以完整的得到一個(gè)類中的所有被加載的結(jié)構(gòu)
-
Class類是Reflection的根源,針對(duì)任何你想動(dòng)態(tài)加載、運(yùn)行的類,唯有先獲得相應(yīng)的Class對(duì)象
-
-
獲取Class類的實(shí)例
-
若已知具體的類,通過類的class屬性獲取,該方法最為安全可靠,程序性最高
Class clazz = Person.class; -
已知某個(gè)類的實(shí)例,調(diào)用該實(shí)例的
getClass()方法獲取Class對(duì)象Class clazz = person.getClass(); -
已知一個(gè)類的全類名,且該類在類路徑下,可通過Class類的靜態(tài)方法
forName()獲取,可能拋出ClassNotFoundExceptionClass clazz = Class.forName("demo01.Student"); -
內(nèi)置基本數(shù)據(jù)類型可以直接使用 類名.Type
-
還可以利用ClassLoader
-
-
Class類的常用方法
-
| 方法名 | 功能說明 |
|---|---|
static ClassforName(String name) |
返回指定類名name的Class對(duì)象 |
Object newInstance() |
調(diào)用缺省構(gòu)造函數(shù),返回Class對(duì)象的一個(gè)實(shí)例 |
getName() |
返回此Class對(duì)象所表示的實(shí)體(類,接口,數(shù)組類或void)的名稱 |
Class getSuperClass() |
返回當(dāng)前Class對(duì)象的父類的Class對(duì)象 |
Class[] getinterdfaces() |
獲取當(dāng)前Class對(duì)象的接口 |
ClassLoader getClassLoader() |
返回該類的類加載器 |
Constructor[] getConstructors() |
返回一個(gè)包含某些Constructor對(duì)象的數(shù)組 |
Method getMothed(String name,Class.. T) |
返回一個(gè)Method對(duì)象,此對(duì)象的形參類型為paramType |
Filed[] getDeclaredFields() |
返回Field對(duì)象的一個(gè)數(shù)組 |
//測(cè)試Class類的創(chuàng)建方式有哪些
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("這個(gè)人是:"+person.name);
?
//方式一:通過對(duì)象獲得
Class c1 = person.getClass();
System.out.println(c1.hashCode());
?
//方式二:forname獲得
Class c2 = Class.forName("com.xie.reflection.Student");
System.out.println(c2.hashCode());
?
//方式三:通過類名.class獲得
Class c3 = Student.class;
System.out.println(c3.hashCode());
?
//方式四:基本內(nèi)置類型的包裝類都有一個(gè)Type屬性
Class c4 = Integer.TYPE;
System.out.println(c4.hashCode());
?
//獲得父類類型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}
?
class Person{
String name;
?
public Person() {
}
?
public Person(String name) {
this.name = name;
}
?
-
哪些類型可以有Class對(duì)象?(Alt 可以同時(shí)選中多行)
-
class:外部類,成員(成員內(nèi)部類,靜態(tài)內(nèi)部類),局部?jī)?nèi)部類,匿名內(nèi)部類
-
interface :接口
-
[] :數(shù)組
-
enum :枚舉
-
annotation:注解@interface
-
primitive type :基本數(shù)據(jù)類型
-
void
-
//所有類型的Class
public class Test03 {
public static void main(String[] args) {
Class c1 = Object.class;//類
Class c2 = Comparable.class;//接口
Class c3 = String[].class;//一維數(shù)組
Class c4 = int[][].class;//二維數(shù)組
Class c5 = Override.class;//注解
Class c6 = ElementType.class;//枚舉
Class c7 = Integer.class;//基本數(shù)據(jù)類型
Class c8 = void.class;//void
Class c9 = Class.class;//Class
?
System.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);
}
}
2.3、類的加載與ClassLoader
-
類的加載與ClassLoader的理解
-
加載:將class文件字節(jié)碼內(nèi)容加載到內(nèi)存中,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),然后生成一個(gè)代表這個(gè)類的
java.lang.Class對(duì)象 -
鏈接:將Java類的二進(jìn)制代碼合并到JVM的運(yùn)行狀態(tài)之中的過程
-
驗(yàn)證:確保加載的類信息符合JVM規(guī)范,沒有安全方面的問題
-
準(zhǔn)備:正式為類變量(static)分配內(nèi)存并設(shè)置初始值的階段,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配
-
-
初始化:
-
執(zhí)行類構(gòu)造器
<clinit>()方法的過程。類構(gòu)造器<clinit>()方法是由編譯器自動(dòng)收集類中所有類變量的賦值動(dòng)作和靜態(tài)代碼塊中的語句合并產(chǎn)生的。(類構(gòu)造器是構(gòu)造類信息的,不是構(gòu)造該類對(duì)象的構(gòu)造器) -
當(dāng)初始化一個(gè)類的時(shí)候,如果發(fā)現(xiàn)其父類還沒有進(jìn)行初始化,則需要先觸發(fā)其父類的初始化
-
虛擬機(jī)會(huì)保證一個(gè)類的
<clinit>()方法在多線程環(huán)境中被正確的加鎖和同步
-
-
-
Java內(nèi)存分析
-
堆:
-
存放new的對(duì)象和數(shù)組
-
可以被所有的線程共享,不會(huì)存放別的對(duì)象引用
-
-
棧:
-
存放基本變量類型(會(huì)包含這個(gè)基本類型的具體數(shù)值)
-
引用對(duì)象的變量(會(huì)存放這個(gè)引用在堆里面的具體地址)
-
-
方法區(qū):
-
可以被所有的線程共享
-
包含了所有的class和static變量
-
-
-
什么時(shí)候會(huì)發(fā)生類初始化
-
類的主動(dòng)引用(一定會(huì)發(fā)生類的初始化)
-
當(dāng)虛擬機(jī)啟動(dòng),先初始化main方法所在的類
-
new一個(gè)類的對(duì)象
-
調(diào)用類的靜態(tài)成員(除了final常量)和靜態(tài)方法
-
使用
java.lang.reflect包的方法對(duì)類進(jìn)行反射調(diào)用 -
當(dāng)初始化一個(gè)類,如果其父類沒有被初始化,則先會(huì)初始化它的父類
-
-
類的被動(dòng)引用(不會(huì)發(fā)生類的初始化)
-
當(dāng)訪問一個(gè)靜態(tài)域時(shí),只有真正聲明這個(gè)域的類才會(huì)被初始化。如:當(dāng)通過子類引用父類的靜態(tài)變量,不會(huì)導(dǎo)致子類的初始化
-
通過數(shù)組定義類的引用,不會(huì)觸發(fā)類的初始化
-
引用常量(final)不會(huì)觸發(fā)類的初始化(常量在鏈接階段就存入調(diào)用類的常量池中了)
-
-
-
類加載器的作用
-
類加載器的作用:將class文件字節(jié)碼內(nèi)容加載到內(nèi)存中,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)運(yùn)行時(shí)的數(shù)據(jù)結(jié)構(gòu),然后在堆中生成一個(gè)代表這個(gè)類的
java.lang.Class對(duì)象,作為方法區(qū)中類數(shù)據(jù)的訪問入口 -
類緩存:標(biāo)準(zhǔn)的JavaSE類加載器可以按要求查找類,但一旦某個(gè)類加載到類加載器中,它將維持加載(緩存)一段時(shí)間。不過JVM垃圾回收機(jī)制可以回收這些Class對(duì)象
-
-
JVM規(guī)范定義了如下類型的類加載器
-
引導(dǎo)類加載器:用C++編寫的,是JVM自帶的類加載器,負(fù)責(zé)Java平核心類庫(rt)。該加載器無法直接獲取
-
擴(kuò)展類加載器:負(fù)責(zé)
jre/lib/ext目錄下的jar包或-D java.ext.dirs指定目錄下的jar包裝入工作庫 -
系統(tǒng)類加載器:負(fù)責(zé)java -classpath 或
-D java.class.path所指的目錄下的類與jar包裝入工作,是最常用的加載器
-
public class Test04 {
public static void main(String[] args) throws ClassNotFoundException {
//獲取系統(tǒng)類的加載器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//獲取系統(tǒng)類的加載器的父類加載器-->擴(kuò)展類加載器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//獲取擴(kuò)展類加載器的父類加載器-->根加載器(C/C++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//測(cè)試當(dāng)前類是哪個(gè)加載器加載的
ClassLoader classLoader = Class.forName("com.xie.reflection.Test04").getClassLoader();
System.out.println(classLoader);
//測(cè)試JDK內(nèi)置的類的誰加載的
ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader1);
//如何獲得系統(tǒng)加載器可以加載的路徑
System.out.println(System.getProperty("java.class.path"));
/*
D:\Environment\java\jdk1.8\jre\lib\charsets.jar;
D:\Environment\java\jdk1.8\jre\lib\deploy.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\access-bridge-64.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\cldrdata.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\dnsns.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\jaccess.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\jfxrt.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\localedata.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\nashorn.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\sunec.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\sunjce_provider.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\sunmscapi.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\sunpkcs11.jar;
D:\Environment\java\jdk1.8\jre\lib\ext\zipfs.jar;
D:\Environment\java\jdk1.8\jre\lib\javaws.jar;
D:\Environment\java\jdk1.8\jre\lib\jce.jar;
D:\Environment\java\jdk1.8\jre\lib\jfr.jar;
D:\Environment\java\jdk1.8\jre\lib\jfxswt.jar;
D:\Environment\java\jdk1.8\jre\lib\jsse.jar;
D:\Environment\java\jdk1.8\jre\lib\management-agent.jar;
D:\Environment\java\jdk1.8\jre\lib\plugin.jar;
D:\Environment\java\jdk1.8\jre\lib\resources.jar;
D:\Environment\java\jdk1.8\jre\lib\rt.jar;
D:\CODE\注解與反射\out\production\注解與反射;
D:\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar
*/
}
}
2.4、創(chuàng)建運(yùn)行時(shí)類的對(duì)象
-
通過反射獲取運(yùn)行時(shí)類的完整結(jié)構(gòu)
-
Field、Method、Constructor、Superclass、Interface、Annotation
-
//獲得類的信息
public class Test05 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.xie.reflection.User");
//獲得類的名字
System.out.println(c1.getName()); //獲得包名 + 類名
System.out.println(c1.getSimpleName()); //獲得類名
//獲得類的屬性
System.out.println("=====================");
Field[] fields = c1.getFields(); //只能找到public
fields = c1.getDeclaredFields(); //找到所有的屬性
for (Field field : fields) {
System.out.println(field);
}
//獲得指定屬性的值
Field name = c1.getDeclaredField("name");
System.out.println(name);
//獲得類的方法
System.out.println("===================");
Method[] methods = c1.getMethods(); //獲得本類及其父類的全部public方法
for (Method method : methods) {
System.out.println("no declared"+method);
}
Method[] declaredMethods = c1.getDeclaredMethods(); //獲得本類的所有方法
for (Method declaredMethod : declaredMethods) {
System.out.println("declared"+declaredMethod);
}
//獲得指定的方法
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
//獲得類的構(gòu)造器
System.out.println("==============");
Constructor[] constructors = c1.getConstructors();//public
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
Constructor[] constructors1 = c1.getDeclaredConstructors();//全部
for (Constructor constructor : constructors1) {
System.out.println(constructor);
}
//獲得指定構(gòu)造器
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println(constructor);
}
}
2.5、獲取運(yùn)行時(shí)類的完整結(jié)構(gòu)
-
有了Class對(duì)象,能做什么
-
創(chuàng)建類的對(duì)象:調(diào)用Class對(duì)象的
newInstance()方法-
類必須有一個(gè)無參數(shù)的構(gòu)造器
-
類的構(gòu)造器的訪問權(quán)限要足夠
-
-
步驟:
-
通過Class類的
getDeclaredConstructor(Class...parameterTypes)取得本類的指定形參類型的構(gòu)造器 -
向構(gòu)造器的形參中傳遞一個(gè)對(duì)象數(shù)組進(jìn)去,里面包含了構(gòu)造器所需的各個(gè)參數(shù)
-
通過
Constructor實(shí)例化對(duì)象
-
-
//動(dòng)態(tài)創(chuàng)建對(duì)象,通過反射
public class Test06 {
public static void main(String[] args) throws Exception {
//獲得Class對(duì)象
Class c1 = Class.forName("com.xie.reflection.User");
//構(gòu)造一個(gè)對(duì)象
//User user = (User) c1.newInstance();//本質(zhì)上是調(diào)用了類的無參構(gòu)造器
//System.out.println(user);
//通過構(gòu)造器創(chuàng)建對(duì)象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println(constructor.newInstance("xie", 001, 18));
//通過反射調(diào)用方法
User user = (User) c1.newInstance();
//通過反射獲取一個(gè)方法
Method setName = c1.getMethod("setName", String.class);
//invoke 激活
//(對(duì)象,"方法的值")
setName.invoke(user,"xmc");
System.out.println(user.getName());
//通過反射操作屬性
User user2 = (User) c1.newInstance();
Field name = c1.getField("name");
name.setAccessible(true);
name.set(user2,"xie2");
System.out.println(user2.getName());
}
}
2.6、通過反射獲取注釋信息
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.xie.reflection.Student2");
//通過反射獲得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//獲得注解的value的值
Tablexie tablexie = (Tablexie)c1.getAnnotation(Tablexie.class);
String value = tablexie.value();
System.out.println(value);
//獲得類的指定的注解
Field f = c1.getDeclaredField("name");
Fieldxie annotation = f.getAnnotation(Fieldxie.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@Tablexie("db_student")
class Student2{
@Fieldxie(columnName = "db_id",type = "int",length = 10)
private int id;
@Fieldxie(columnName = "db_age",type = "int",length = 10)
private int age;
@Fieldxie(columnName = "db_name",type = "varchar",length = 3)
private String name;
public Student2() {
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//類名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablexie{
String value();
}
//屬性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldxie{
String columnName();
String type();
int length();
}
posted on 2022-11-18 15:25 SNOWi 閱讀(165) 評(píng)論(0) 收藏 舉報(bào)
浙公網(wǎng)安備 33010602011771號(hào)