Java中的反射機(jī)制基本運(yùn)用詳解
Java中的反射機(jī)制基本運(yùn)用
看完反射可以了解一下注解
注解annotation://www.dhdzp.com/article/221276.htm
1、什么是反射(reflect)
反射是java的動(dòng)態(tài)機(jī)制,它允許將對(duì)象的實(shí)例化,方案的調(diào)用,屬性的操作等從編碼期確定轉(zhuǎn)移到程序運(yùn)行期確定。
反射能大大提高代碼的靈活度。但同時(shí)也帶來(lái)了更多的系統(tǒng)開銷和較慢的運(yùn)行效率,因此程序不能過度依賴反射。
2、反射機(jī)制提供的功能
- 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類
- 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象(實(shí)例化)
- 在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法
- 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法——?jiǎng)討B(tài)代理
3、反射->獲取類對(duì)象
在開始之前創(chuàng)建一個(gè)實(shí)體類,用于測(cè)試反射機(jī)制
package reflect_text;
/**
* 用于測(cè)試反射機(jī)制
*
* @author Akio
* @Create 2021/8/14 10:37
*/
public class Person {
private String name = "劉瑜澄";//設(shè)置初始值
private int age = 22;
public Person() {//無(wú)參構(gòu)造
}
public Person(String name, int age) {//有參構(gòu)造
this.name = name;
this.age = age;
}
public void sayHello() {//無(wú)參方法
System.out.println(name + ":使用sayHello方法");
}
public void sayGoodBye() {//無(wú)參方法
System.out.println(name + ":使用sayGoodBye方法");
}
public void say(String info) {//有參方法
System.out.println(name + ":" + info);
}
public void say(String info, int sum) {//有參方法(重載say方法)
for (int i = 0; i < sum; i++) {
System.out.println(name + ":" + info);
}
}
private void privateMethod() {//私有方法
System.out.println(name + ":這是一個(gè)私有方法");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
以上這個(gè)用于測(cè)試反射的實(shí)體類創(chuàng)建成功后,先學(xué)習(xí)反射中是如何獲取類對(duì)象的。
反射的第一步就是要獲取操作類的類對(duì)象,即一個(gè)Class的實(shí)例,JVM中每個(gè)被加載的類有且只有一個(gè)類對(duì)象與之對(duì)應(yīng),獲取到這個(gè)類對(duì)象后我們就可以通過這個(gè)類對(duì)象來(lái)了解該類的一切信息(類名、有哪些方法、屬性等等) 以便在程序運(yùn)行期間通過反射機(jī)制進(jìn)行相關(guān)操作
這里介紹三種獲取類對(duì)象的方式:
(包名.)類名.class
Class personClass = reflect_text.Person.class; Class intClass = int.class;
這種方式最直接,但是由于是靠硬編碼形式寫死(編譯期間已經(jīng)確定),因此不夠靈活。但是需要注意,基本類型(int\double等)只能通過這種方式獲取類對(duì)象
Class.forName(String className)
Class personClass = Class.forName("reflect_text.Person");
Class stringClass = Class.forName("java.lang.String");
這種方式較為常用,遵循運(yùn)行期綁定。
類加載器ClassLoader
Class stringClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.String");
Class personClass = ClassLoader.getSystemClassLoader().loadClass("reflect_text.Person");
在這一節(jié)中介紹幾個(gè)簡(jiǎn)單的方法:
getName() 獲取類的完全限定名:包名.類名 getSimpleName() 僅僅獲取類名 getMethods() 返回一個(gè)Method數(shù)組,獲取class所表示的類的所有公開方法(包含從超類中繼承的方法)
例子:
Scanner scanner = new Scanner(System.in);
Class cls = Class.forName(scanner.nextLine());//運(yùn)行期綁定
String name = cls.getName();//獲取類的完全限定名:包名.類名
System.out.println("完全限定名 = " + name);
name = cls.getSimpleName();//僅獲取類名
System.out.println("僅類名 = " + name);
Method[] methods = cls.getMethods();
for (Method m : methods) {
System.out.print(m.getName()+"\t");
}
4、反射->利用無(wú)參構(gòu)造實(shí)例化對(duì)象
Class類中提供了一個(gè)方法newInstance()來(lái)實(shí)例化,該方法要求此類必須具有無(wú)參構(gòu)造器,它是通過無(wú)參構(gòu)造器實(shí)例化對(duì)象的。
Person person = new Person();
//1獲取要實(shí)例化的類的類對(duì)象
Scanner scanner = new Scanner(System.in);
System.out.println("請(qǐng)輸入類名:");
Class cls = Class.forName(scanner.nextLine());
/*
通過Class提供的方法newInstance()來(lái)實(shí)例化
該方法要求此類必須具有無(wú)參構(gòu)造器,它是通過無(wú)參構(gòu)造器實(shí)例化對(duì)象的
*/
Object obj = cls.newInstance();
System.out.println("obj = " + obj);

5、反射->利用有參構(gòu)造實(shí)例化對(duì)象
getConstructor()
//獲取無(wú)參構(gòu)造器,可以利用無(wú)參構(gòu)造器實(shí)例化對(duì)象,但這個(gè)方法對(duì)于使用無(wú)參構(gòu)造器實(shí)例化對(duì)象可有可無(wú)
getConstructor(類對(duì)象)
//通過類對(duì)象獲取特定的構(gòu)造器,該參數(shù)列表是根據(jù)特定構(gòu)造器的參數(shù)列表類型來(lái)決定的,如
getConstructor(String.class, int.class)
即為調(diào)用Person類中兩個(gè)參數(shù)的有參構(gòu)造器
public Person(String name, int age) {//有參構(gòu)造
this.name = name;
this.age = age;
}
舉例
//加載類對(duì)象
Class cls = Class.forName("reflect.Person");
//通過類對(duì)象獲取特定的構(gòu)造器
Constructor c = cls.getConstructor(String.class, int.class);
Object o = c.newInstance("流年", 21);//實(shí)例化
System.out.println(o);
結(jié)果可知初始值已經(jīng)被修改

6、反射->調(diào)用無(wú)參方法
getMethod(String MethodName) 獲取類對(duì)象的MethodName方法,返回值類型為Method invoke(Object object) 執(zhí)行object對(duì)象的某方法
舉例
//一般調(diào)用方法的做法-------------------
Person p = new Person();//實(shí)例化對(duì)象
p.sayHello();//調(diào)用該對(duì)象方法
//反射機(jī)制調(diào)用方法-----------------------
//1、實(shí)例化對(duì)象
Class cls = Class.forName("reflect_text.Person");
Object o = cls.newInstance();
//2、調(diào)用o的sayHello方案
//2.1通過Class獲取Person的sayHello方法
Method method = cls.getMethod("sayHello");
//2.2調(diào)用o的該方法
method.invoke(o);//等效于一般方法中的o.sayHello()
可見兩種操作均能達(dá)到一樣的效果

7、反射->調(diào)用有參方法
getMethod(String MethodName, 類對(duì)象) 獲取類對(duì)象的MethodName有參方法,并傳入對(duì)應(yīng)參數(shù)類型的類對(duì)象,返回值類型為Method
舉例
//一般調(diào)用有參方法------------------------
Person p = new Person();
p.say("七夕快樂~");
p.say("七夕快樂~",3);
//反射機(jī)制調(diào)用有參方法---------------------
Class cls = Class.forName("reflect_text.Person");
Object o = cls.newInstance();
//調(diào)用say(String info)方法
Method m1 = cls.getMethod("say", String.class);
m1.invoke(o, "春節(jié)快樂~");
//調(diào)用say(String info, int sum)
Method m2 = cls.getMethod("say", String.class, int.class);
m2.invoke(o,"春節(jié)快樂~",3);
通過結(jié)果可以看到,效果都是一樣的

8、反射->訪問私有方法
注意:反射訪問私有的方法,但是會(huì)破壞類的封裝性
getDeclaredMethod(String MethodName) 可以僅獲取此類定義的所有方法,包含私有方法 setAccessible(boolean flag) 開啟私有方法的訪問權(quán)限
舉例
//正常情況下,在本類中不可以訪問外部的私有方法
//但在反射機(jī)制中可行
Class cls = Class.forName("reflect_text.Person");
Object o = cls.newInstance();
Method method = cls.getDeclaredMethod("privateMethod");
method.setAccessible(true);//打開訪問權(quán)限
method.invoke(o);

9、反射->類加載路徑
加載資源時(shí)常用相對(duì)路徑,之前學(xué)習(xí)的相對(duì)路徑./由于運(yùn)行環(huán)境不同,位置并非固定,因此實(shí)際開發(fā)中使用較少。
接下來(lái)介紹,在開發(fā)中常用的類加載路徑
常用的路徑通常為類的加載路徑,有兩個(gè):
1:類對(duì)象.getResource()與當(dāng)前類所處同一目錄
2:類加載器.getResource()類加載路徑,類的package定義中根包位置。
例如:有一個(gè)類:
package reflect_text;
public class WebServer{
……
}
在WebServer類中,當(dāng)我們使用上述兩種方式獲取路徑時(shí)他們的對(duì)應(yīng)位置為:
WebServer.class.getResource()
當(dāng)前WebServer所在的目錄(編譯后的class文件所在目錄)
WebServer.class.getClassLoader().getResource()
則是在WebServer的包的最上級(jí),即com包的上一級(jí)
package reflect_text;
public class ReflectDemo {
File dir = new File(ReflectDemo.class.getResource(".").toURI());
System.out.println("dir = " + dir);
//dir = D:\ClassCode\JavaSE_API\out\production\JavaSE_API\reflect
File dir2 = new File(ReflectDemo.class.getClassLoader().getResource(".").toURI());
System.out.println("dir2 = " + dir2);
//dir2 = D:\ClassCode\JavaSE_API\out\production\JavaSE_API
}

總結(jié)
本片文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
mybatis?like模糊查詢特殊字符報(bào)錯(cuò)轉(zhuǎn)義處理方式
這篇文章主要介紹了mybatis?like模糊查詢特殊字符報(bào)錯(cuò)轉(zhuǎn)義處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
Spring MVC--攔截器實(shí)現(xiàn)和用戶登陸例子
本文主要介紹了Spring MVC--攔截器實(shí)現(xiàn)和用戶登陸例子,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-03-03
MyBatis圖文并茂講解注解開發(fā)一對(duì)多查詢
這篇文章主要介紹了SpringBoot中Mybatis注解一對(duì)多查詢的實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
java多線程Thread的實(shí)現(xiàn)方法代碼詳解
這篇文章主要介紹了java多線程Thread的實(shí)現(xiàn)方法代碼詳解,涉及start(),run(),stop(),interrupt(),isInterrupted(),join()和join(long millis)等方法的介紹,具有一定借鑒價(jià)值,需要的朋友可以了解下。2017-11-11
項(xiàng)目管理利器-Maven(Windows安裝)圖文教程
下面小編就為大家?guī)?lái)一篇項(xiàng)目管理利器-Maven(Windows安裝)圖文教程。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2017-06-06
Java批量插入數(shù)據(jù)的代碼實(shí)現(xiàn)
日常工作或者學(xué)習(xí)中,可能會(huì)遇到批量插入數(shù)據(jù)的需求,一般情況下數(shù)據(jù)量少的時(shí)候,我們會(huì)直接調(diào)用批量接口插入數(shù)據(jù)即可,當(dāng)數(shù)據(jù)量特別大時(shí),我們就會(huì)用到分批插入數(shù)據(jù),所以本文給大家介紹了Java批量插入數(shù)據(jù)的代碼實(shí)現(xiàn),需要的朋友可以參考下2024-01-01
Java工程使用ffmpeg進(jìn)行音視頻格式轉(zhuǎn)換的實(shí)現(xiàn)
FFmpeg是一套可以用來(lái)記錄、轉(zhuǎn)換數(shù)字音頻、視頻,并能將其轉(zhuǎn)化為流的開源計(jì)算機(jī)程序,本文主要介紹了Java工程使用ffmpeg進(jìn)行音視頻格式轉(zhuǎn)換的實(shí)現(xiàn)2024-02-02

