天天看點

8千字幹貨教程|java反射精講

java反射機制精講

目錄

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

作者簡介:全棧學習筆記,一個正在努力的人

反射機制的概念:

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

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

java反射機制提供的功能:

在運作時判斷任意一個對象所屬的類;

在運作時構造任意一個類的對象;

在運作時判斷任意一個類所具有的成員變量和方法;

在運作時調用任意一個對象的方法;

生成動态代理。

這其實也涉及到了語言的動态與靜态,java語言本身不算是動态語言,但是他有一個非常突出的動态機制,就是我們所說的反射機制。

什麼是動态語言呢?就是說,程式在運作的時候,(注意是運作的時候,不是編譯的時候)允許改變程式結構或者變量類型。反之靜态就是沒有這些特點了。

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();           

原文位址

https://www.cnblogs.com/swzx-1213/p/12597159.html