8千字java反射乾貨|java反射精講

2021-02-13 武哥聊編程

java反射機制精講

目錄
1. 反射機制的概念
2. 反射的基礎Class類
3. 反射的用法
4. 反射的應用示例

反射機制的概念:

在運行狀態中,對於任意一個類,都能夠獲取到這個類的所有屬性和方法,對於任意一個對象,都能夠調用它的任意一個方法和屬性(包括私有的方法和屬性),這種動態獲取的信息以及動態調用對象的方法的功能就稱為java語言的反射機制。反射被視為動態語言的關鍵。簡單來說反射就是java的各種成分映射成對應的java類。

通俗點講,通過反射,該類對我們來說是完全透明的,想要獲取任何東西都可以。包括構造方法,屬性,方法。

java反射機制提供的功能

在運行時判斷任意一個對象所屬的類;
在運行時構造任意一個類的對象;
在運行時判斷任意一個類所具有的成員變量和方法;
在運行時調用任意一個對象的方法;
生成動態代理。

這其實也涉及到了語言的動態與靜態,java語言本身不算是動態語言,但是他有一個非常突出的動態機制,就是我們所說的反射機制。
什麼是動態語言呢?就是說,程序在運行的時候,(注意是運行的時候,不是編譯的時候)允許改變程序結構或者變量類型。反之靜態就是沒有這些特點了。

反射的基礎Class類

Class類是反射實現的基礎,所以想要學會反射,必須先掌握Class類的一些基本的概念。
類是什麼?類是Class類的實例對象,所以說Class類是所有類的類。

要想解剖一個類,必須先獲取到該類的字節碼文件對象。而解剖使用的就是Class 類中的方法,所以先要獲取每一個字節碼文件對應的Class類型的對象。

Class類沒有公共的構造方法,Class對象是在類加載的時候由Java虛擬機以及通過調用類加載器中的 defineClass 方法自動構造的,因此不能顯式地聲明一個Class對象。這裡又涉及到一個東西,類的加載

簡要的說明一下:

類加載器:當程序需要用某個類時,如果該類還沒有被加載到內存中,則,系統會通過加載,連接,初始化 這三步來對類進行初始化

加載:就是指將class文件讀入內存(編譯之後的文件是.class文件),並為之創建一個Class對象
任何類被使用時,系統都會建立一個Class對象,第一次的時候會,第二次則會判斷這個類是否存在。

連接:驗證是否有正確的內部結構,並和其他類協調一致
準備為類的靜態成員分配內存,並設置默認初始化值
並做一個解析:將類的二進位數據中的字符引用替換為直接引用。

上面說到Class對象是不能直接創建的,但是我們可以通過其他方式得到Class類的,目前有三種方式可以得到我們想要的Class類,得到Class類之後就能正常的使用反射了。

獲取Class的三種方式(獲取一個類的字節碼對象):

第一種:使用對象獲取,使用對象的getClass獲取

Person person = new Person();
Class clazz = person.getClass();

第二種:使用靜態屬性class

Class clazz = Person.class

第三種:使用Class類的靜態方法forName(字符串的類名)
注;類名要寫全包名

Class clazz = Calss.forName("…….");

好了,重點來了,反射怎麼玩才有趣!

反射的用法

上面說了通過反射可以得到任意一個類的什麼什麼,下面來看看是不是真的。

第一步要幹啥?當然是通過之前的哪三種方法來得到這個可以為所欲為的Class類。有三種方法,我們先都做個示例吧!
上代碼

//獲取Class第一種方法
Student student = new Student();
Class clazz = student.getClass();
//獲取Class第二種方法
Class clazzTwo = Student.class;
//獲取Class第三種方法
Class clazzThree = Class.forName("demo.qzxxbj.entity.Student");

System.out.println("第一個"+clazz+"\n第二個"+clazzTwo+"\n第三個"+clazzThree);

結果

第一個class demo.qzxxbj.entity.Student
第二個class demo.qzxxbj.entity.Student
第三個class demo.qzxxbj.entity.Student

可以看到三種方法得到的Class對象是一樣的,沒有區別。
第三種方法是會有一個找不到類的異常拋出的。
其中Student就是一個簡單的類,可以是任何類。

通過Class獲取任意一個類的屬性
Student類的代碼

package demo.qzxxbj.entity;

/**
* @author 微信公眾號:全棧學習筆記
* @date 2020/3/29
* @description
*/

public class Student {
private String name;
private Integer age;
private String sex;
public int number;

public int getNumber() {
return number;
}

public void setNumber(int number) {
this.number = number;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

}

以下獲取Class的方法都採用第二種,比較簡潔

//獲取Class第二種方法
Class clazzTwo = Student.class;

//獲取該類指定屬性名的public成員變量,包括父類的
Field field = clazzTwo.getField("number");

//field public int demo.qzxxbj.entity.Student.number
System.out.println("該類指定屬性名的public成員變量,包括父類的"+field);

//獲取該類指定名稱聲明的變量,即不包括父類的
Field deField = clazzTwo.getDeclaredField("name");

// deField private java.lang.String demo.qzxxbj.entity.Student.name
System.out.println("該類所有聲明的變量,即不包括父類的"+deField);

//獲取該類所有的public聲明的成員變量
Field fields[] = clazzTwo.getFields();

System.out.println("public聲明的變量:");
//public int demo.qzxxbj.entity.Student.number
for (Field field1:fields){
System.out.println(field1);
}

//獲取該對象的所有成員變量
Field deFields[] = clazzTwo.getDeclaredFields();
System.out.println("該對象的所有成員變量");
//private java.lang.String demo.qzxxbj.entity.Student.name
//private java.lang.Integer demo.qzxxbj.entity.Student.age
//private java.lang.String demo.qzxxbj.entity.Student.sex
//public int demo.qzxxbj.entity.Student.number
for (Field field1:deFields){
System.out.println(field1);
}

記住getFields(),getField(String name),getDeclaredFields(),getDeclaredField(String name)的區別,你就能好好掌握這個知識點!

通過Class獲取任意成員方法

還是看代碼吧!

獲取成員方法Method

//獲取Class第二種方法
Class clazzTwo = Student.class;
//根據方法名以及參數類型獲取,只能獲取public聲明的方法,包括父類的
Method method = clazzTwo.getMethod("setAge",Integer.class);

//public java.lang.Integer demo.qzxxbj.entity.Student.getAge()
System.out.println(method);

//根據方法名以及參數名稱獲取該類聲明的所有的屬性方法,不包括父類的
Method deMethod = clazzTwo.getDeclaredMethod("setAge", Integer.class);

System.out.println(deMethod);

//獲取該對象聲明的所有的public方法,包括父類的
Method methods[] = clazzTwo.getMethods();

//獲取該對象聲明的所有的方法,但是不包含父類的方法
Method deMethods[] = clazzTwo.getDeclaredMethods();

    一個Method方法列印出來是什麼呢?上面代碼中也包含了

public void demo.qzxxbj.entity.Student.setAge(java.lang.Integer)

    和之前講的Field是不是很相似。
既然說到了方法,那麼就肯定涉及到了方法調用,我們得到了這些方法,又該怎麼調用這個類裡面的方法呢?使用invoke函數,Method這個類裡面包含了一個invoke函數,英語好的就知道了,這個invoke的中文意思就是「調用」。
怎麼用呢?

//獲取Class第二種方法
Class clazzTwo = Student.class;
//根據方法名以及參數類型獲取,只能獲取public聲明的方法,包括父類的
Method method = clazzTwo.getMethod("setAge",Integer.class);

//public java.lang.Integer demo.qzxxbj.entity.Student.getAge()
System.out.println(method);

//利用Class創建一個對象的實例
Student student = (Student) clazzTwo.newInstance();

//函數調用
Object value = method.invoke(student,20);
//null
System.out.println(value);

    以上的代碼,你可能會看不懂,我來講一下,首先,我們獲取一個類的Class,然後我們通過這個Class獲取該類的一個setAge方法,獲取到這個方法後繼續調用這個方法,調用方法是不是應該調用一個實例對象裡面的方法?所以我們需要先實例化一個對象,通過什麼方法呢,通過class裡面的newInstance(),創建一個實例,這種方法需要該實例化的類具有一個無參構造方法。還有其他方法也能創建一個實例,後面我們會說。創建出一個實例對象之後,我們就能開始調用方法了。
    通過invoke對方法進行調用,invoke的第一個參數就是一個實例化對象,不然我去哪找這個方法。第二個參數,或者第三個,等等,後面的所有參數都是我調用的該方法所具有的參數,按照順序填進去就OK了。然後這個函數返回的是一個Object對象,你都能想到,我調用一個方法是不是要讓他做一些事,做了這些事需要返回一個東西,不知道這個東西是啥,就用Object獲取嘛。由於我們調用的這個方法不需要返回值,所以就是null了。很簡單是不是。學到了記得給我點個關注哦!精彩美文第一時間推送到你的手中。

通過Class獲取構造方法

這個被我放到了最後來學習,畢竟我覺得用的比例比較少。一起來學習一下怎麼用Class獲取構造方法,並調用他。

public Student(String name, int id) {
this.name = name;
this.id = id;
}

這裡我們在Student類裡面添加了一個構造方法。

然後我們來獲取這個構造方法。

//獲取Class第二種方法
Class clazzTwo = Student.class;

//獲取無參構造方法,public聲明的,包括父類,加上參數時就是獲取特定的構造方法
Constructor constructor = clazzTwo.getConstructor();

//public demo.qzxxbj.entity.Student()
System.out.println(constructor);

//獲取該類所有的public聲明的構造方法
Constructor constructors[] = clazzTwo.getConstructors();

//獲取指定參數的構造方法
Constructor deConstructor = clazzTwo.getDeclaredConstructor(String.class,Integer.class);

//獲取所有的該類的構造方法,不包括父類的
Constructor deConstructors[] =clazzTwo.getDeclaredConstructors();

    上面代碼應該很容易看懂吧,我就不細說了。這裡說一下如何使用得到的構造方法,構造方法顧名思義就是來實例化對象的,上面我們也有說到怎麼通過Class實例化一個對象,現在我們來通過構造方方法實例化一個對象

Student student = (Student) deConstructor.newInstance("全棧學習筆記",21);

//21
System.out.println(student.getAge());

    現在知道了吧,我們差不都將反射的功能講完了,就差一個反射的動態代理,這個比較重要,會專門出一篇博客,碼字不易。希望點個關注。微信公眾號:全棧學習筆記,精彩美文每天為你推送。

    最後我根據我自己以前的經驗寫了一個java反射的sql語句拼接,相當於是一個反射的應用吧。

反射的應用示例

    通過反射動態的生成SQL語句,是不是也有點牛逼的感覺?
    直接上代碼吧,我只發一個SQL語句,感興趣的可以私信我找我拿完整的代碼!

public String insert(Object object) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {`
//insert into student(id,name,sex) values (1,"全棧學習筆記","男")
StringBuilder sql = new StringBuilder();
Class clazz = object.getClass();
sql.append("insert into ");

sql.append(clazz.getSimpleName()+"(");
Field[] fields = clazz.getDeclaredFields();
for(Field field:fields){
sql.append(field.getName()+",");
}
sql.deleteCharAt(sql.length()-1);
sql.append(")");
sql.append(" values (");
for(Field field:fields){
field.setAccessible(true);
Object value = field.get(object);

String fieldName = field.getName();
String str1 = fieldName.substring(0,1).toUpperCase();
String str2 = fieldName.substring(1,fieldName.length());
String strValue = str1.concat(str2);
//String strValue = fieldName.substring(0,1).toUpperCase().concat(fieldName.substring(1,fieldName.length()));
Method method = clazz.getMethod("get"+strValue,null);

Object value1 = method.invoke(object,null);

// if(value1.getClass().equals(String.class))
// if(field.getType().equals(String.class))
if(value1 instanceof String){
sql.append("\"").append(value1).append("\"").append(",");
}else {
sql.append(value1).append(",");
}
}
sql.deleteCharAt(sql.length()-1);
sql.append(")");
System.out.println(sql.toString());
return sql.toString();
}

    本期的講解就到這裡,後面應該也會出一期關於註解的文章,如果你發現文章中有錯誤的地方,歡迎指出來哦!如果你覺得你能學到不少知識,請點個關注哦!歡迎轉發!讓更多的朋友學到!

分享一份面試寶典《Java面試突擊52講》,覆蓋了JVM、鎖、高並發、反射、微服務、Zookeeper、資料庫、消息隊列、分布式系統等等。掃描下方二維碼,關注Java開發寶典,並回復「Java」即可領取!武哥分享無任何套路~

點讚是最大的支持 

相關焦點

  • Java 基礎與提高幹貨系列—Java 反射機制
    正文Java反射機制定義Java反射機制是指在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。用一句話總結就是反射可以實現在運行時可以知道任意一個類的屬性和方法。
  • Java基礎教程:java反射機制教程
    一、反射概念 在正式講解反射之前,為了很好的去理解它我們先從一個案例說起。請看下面的代碼: 這時候java語言在設計的時候為我們提供了一個機制,就是反射機制,他能夠很方便的去解決我們的問題。
  • Java反射,泛型在Json中的運用
    最近項目中遇到了Json數據自動獲取的功能,不然令人想起java的反射,已經很長時間沒複習java了正好一塊連java的這一塊內容一起過一遍。java中的反射無疑就相當於java開發者的春天,在眾多的框架中也能看到它的身影,可以在運行時檢查類,接口、變量和方法等信息,可以實例化調用方法以及設置變量值等。本文主要以代碼的形式直接將反射,泛型的運用展現出來。java中的反射首先新建一個基礎類Author。
  • Java 反射,這篇寫的很透徹!
    Java技術棧www.javastack.cn關注閱讀更多優質文章一、反射機制是什麼?二、反射的具體使用三、反射的優缺點很多時候我們會遇到別人問一個問題:你給我講一下反射,到底是什麼東西?怎麼實現的?我們能用反射來做什麼?它有什麼優缺點?
  • Java反射機制深入詳解
    一.概念反射就是把Java的各種成分映射成相應的Java類。Class類的構造方法是private,由JVM創建。反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。例如它允許一個java的類獲取他所有的成員變量和方法並且顯示出來。
  • Java基礎之反射篇
    Java基礎之反射篇「Hello,大家好!我是老醜。今天給大家帶來的知識點是反射」1. 反射是什麼?反射(Reflection),它允許在運行中的Java程序獲取自身的信息,並且可以操作類或者對象的內部屬性。2. 反射可以做什麼?3.
  • 阿里P8教你Java註解與反射
    5.3 Java反射相關的主要APIjava.lang.Class:代表一個類java.lang.reflect.Method:代表類的方法java.lang.reflect.Field:代表類的成員變量java.lang.reflect.Constructor:代表類的構造器…5.4 Class類通過Class對象可以得知某個類的屬性,方法,構造器,註解,以及實現了哪些接口等等信息
  • java如何通過反射操作欄位
    java方法還可以這樣調用》、《我照樣要訪問》這三篇文章,描述了通過java反射創建對象以及調用方法,有興趣的朋友可以翻閱一下。今天我再來寫寫怎麼通過反射操作欄位吧。老規矩,先上我們要操作的類的代碼。這個demo類比較簡單,一個非靜態的欄位name,一個靜態的欄位staticName,都賦值了初始值。
  • Java代碼審計基礎之反射
    反射方法簡介:反射方法和上文的反射構造方法差不多,如果是私有的話也是要設置強行調用,並且獲取方法的函數為 getDeclaredxxxx吾有一類:我們可以看到主要調用了 Runtime.getRuntime().exec那麼我們要如何通過反射的方式進行調用呢?反射調用 Runtime.getRuntime().exec第一種方式,通過強行反射私有構造方法,用 Runtime 實例化進行反射這裡有一個小坑,Runtime的構造函數是私有的:
  • Java反射是什麼?看這篇絕對會了!
    反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。例如它允許一個java的類獲取他所有的成員變量和方法並且顯示出來。Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中獲得函數定義相關的信息。
  • 面試官:Java 反射是什麼?我回答不上來!
    一.概念反射就是把Java的各種成分映射成相應的Java類。Class類的構造方法是private,由JVM創建。反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。
  • Java面試高頻考點:反射機制使用大全
    作為一個Java開發工程師,在面試的過程中,反射機制也是經常會被問到的一個問題。例如Spring的IOC實現機制,其底層都是依賴於java的反射機制,因此,這是一個非常重要的知識點。對於初學java的同學來說,掌握其使用方法很有必要。
  • 反射——Java高級開發必須懂得
    如果是類形式的,名稱為類的全稱,例如:c1的類類型名稱為int,c2的類類型名稱為java.lang.String。getSimpleName():不包含包名的類的名稱。描述:創建一個工具類名稱為ClassUtil,並且有一個靜態方法,參數為Object類型參數,首先獲取該對象的類類型,這裡使用第二種獲取方式,傳遞的是哪個對象,Class對象就是該對象類類型(這個功能是由native聲明的一個方法實現的,java中jni就是做本地方法的,該方法是由java來聲明,用C語言來實現),萬事萬物皆對象,方法同樣是對象
  • java基礎之fianl,反射,異常,特性
    { System.out.println("普通語句塊");}public InitialOrderTest() { System.out.println("構造函數");}反射反射可以提供運行時的類信息,並且這個類可以在運行時才加載進來,甚至在編譯時期該類的 .class 不存在也可以加載進來。
  • 「JAVA」萬字長篇詳述字節碼對象與反射機制完成動態編程
    Java 反射在Java的開發環境中,運行java文件需要使用:java xx.java 命令,運行java命令後,便會啟動JVM,將字節碼文件加載到JVM中,然後開始運行;當運行java命令時,該命令將會啟動一個JVM
  • Java反射機制,速度提高1000倍
    原文:Java Reflection, 1000x Faster作者:aka Nicolas Laurent譯者:Teixeira10譯者註:在本文中,作者例舉了幾個版本的代碼,利用java發射機制,逐步提高代碼運行速度,同時將Github上的代碼進行展示。
  • Java核心技術精講PDF掃描版下載
    《Java核心技術精講》內容包括Java簡介、Java基礎語法、面向對象、異常的捕獲及處理、包及訪問控制權限、Java新特性、多線程、常用類庫、JavaIO操作、網絡編程、類集框架、Java資料庫編程、DAO設計模式、Eclipse開發工具。
  • 深入理解Java反射
    總的來說大概兩個原因:一個是覺得太基礎講出來比較 low 沒有人會關注,另一個是講一些很牛的新技術又怕出錯;然而每一項技術在自己的應用中都會有你自己獨特的視角,也許這一點正是別人關心的。先看下 java.lang.reflect 包下的幾個主要類的關係圖,當然動態代理的工具類也在該包下。
  • 學Java反射,看這篇就夠了 | 原力計劃
    我們來一句話定義反射:反射就是把 Java 類中的各種成分映射成一個個的 Java 對象。不理解這句話什麼意思?沒關係,在我百度了幾分鐘後,找到三種解釋:解釋一:一個類有:成員變量、方法、構造方法、包等等信息,利用反射技術可以對一個類進行解剖,把各個組成部分映射成一個個對象。
  • Java語言的反射機制
    Java 反射是Java語言的一個很重要的特徵,它使得Java具有了「動態性」。Java提供了一套機制來動態獲取類的信息以及動態調用對象的方法的功能,這套機制就叫——反射反射機制是如今很多流行框架的實現基礎,其中包括SSH(Struts、Spring、Hibernate)和SSM(SpringMVC、Spring、MyBatis)等。