1. 程式人生 > >JavaSE基礎學習筆記-提高篇-Java反射概要

JavaSE基礎學習筆記-提高篇-Java反射概要

Class物件

Java中的各個類也是一類事物,按照面向物件的思想,可以對這些類進行描述和封裝,JavaClass類來代表class類。

9個預定義的Class物件:8個基本資料型別 + void

獲取類實力物件的三種常用方法:

①類名.class  例: Class s = String.class;

②物件名.getClass() 例:  Integer  in =  78; Class inc = in.getClass();

③Class的靜態方法forName(String className)

  例:Class dou = Class.forName(“java.lang.Double”);

【基本資料型別的class物件與和它對應的包裝器型別的class物件不相等,但是以下情況是相等的:int.class == Integer.TYPE

  陣列的class物件: 例class inArr = int[].class;   

構造方法的反射

構造方法對應的類是Constructor

Class物件建立一個物件:

①獲得類的Class物件

②回去該類的一個公開的建構函式物件(Constructor

③呼叫ConstructornewInstance方法建立物件

例如:用反射建立一個帶有自定義建構函式的自定義類

import java.lang.reflect.*;
public class ReflectTest {
 
/**
 * @param args
 */
public static void main(String[] args) throws Exception{
 
//獲取類的Class物件
Class<ReflectClass> cl = ReflectClass.class;
//獲取該Class物件的對應引數的建構函式物件
Constructor<ReflectClass> cos = cl.getConstructor(String.class, int.class);
//呼叫構造器物件的newInstance方法建立一個ReflectClass物件
ReflectClass ref = cos.newInstance("blackhose", 23);
System.out.println(ref);
}
 
}


結果:[email protected]

【檢視JDK文件發現,對應newInstance方法在Class類中有所提供,在Constructor類中也有提供,那麼兩者有什麼區別呢?

區別在於:Class中的newInstance是當呼叫無參建構函式時預設呼叫的,實際底層也是呼叫了一次Constructor的newInstance方法,只不過沒有往Constructor中的newInstance傳遞任何引數。實際上Class的newInstance可以對物件進行快取,當第一次呼叫類的無參建構函式時,Class底層會呼叫Constructor的newInstance,當第二層呼叫時,Class就把自己緩衝的用無參建構函式建立的類物件直接用newInstance反給了呼叫者。如果使用者選擇用有參建構函式建立物件,那麼沒有快取,Class的newInstance會一直間接呼叫Constructor的newInstance方法。】

成員變數的反射

成員變數在反射中對應的類是Field

成員變數的反射是基於類的Class物件的,

①首先,獲得對應類的Class物件

②然後從Class物件中獲取其中的欄位物件

  Field getField(String name);   返回一個指定名稱的Field

  Field[] getFields()            返回類中所有的Field物件

③獲取指定物件中指定欄位是值,用Field中的getObject)方法獲取指定物件中對於的值。

getDeclaredFields() 獲取 ,然後用setAccessible(true)設定該欄位為可操作的,才能獲取到對應的值

欄位反射的具體操作例項如下:

import java.lang.reflect.*;
public class ReflectTest {
 
/**
 * @param args
 */
public static void main(String[] args) throws Exception{
        /**********************構造器反射******************/
//獲取類的Class物件
Class<ReflectClass> cl = ReflectClass.class;
//獲取該Class物件的對應引數的建構函式物件
Constructor<ReflectClass> cos = cl.getConstructor(String.class, int.class);
//呼叫構造器物件的newInstance方法建立一個ReflectClass物件
ReflectClass ref = cos.newInstance("blackhose", 23);
System.out.println(ref);
/*************欄位反射 *********/
//因為str為私有的,用getField取不到這個Field物件
Field fStr = cl.getDeclaredField("str");
//設定這個私有屬性的值是可操作的,如果不進行設定,雖然拿到了這個Field物件,但是裡面的資訊是拿不到的
fStr.setAccessible(true);
Field fN = cl.getField("n");
//獲取兩個欄位是值
String s = (String)fStr.get(ref);
Integer n = (Integer)fN.get(ref);
System.out.println(s + ", " + n);
//替換指定物件中的指定欄位的值
Field[] fields = cl.getFields();
//遍歷,找到欄位型別是String的
for(Field f : fields){
if(f.getType() == String.class){
//取得物件中對應欄位的值
System.out.println("....");
String oldVal = (String)f.get(ref);
//將獲取到的值進行替換操作
String newVal = oldVal.replace('a', 'b');
System.out.println(newVal);
//吧新值設定到cl物件中
f.set(ref, newVal);
}
}
//列印更改後的結果
System.out.println(cl.getField("str").get(ref));
}
 
}


成員方法的反射

每個類中都有方法,在Java中方法這類事物用Method類來描述

通過反射呼叫方法可以把方法大概分為兩類:一類是靜態方法,不基於物件,基於類,另一類是非靜態方法,是基於物件進行呼叫的。

1.對非靜態方法的反射呼叫

  ①獲取類的Class物件

  ②獲取Class物件的Method物件

     例:Method  cs = String.class.getMethod("charAt"int.class);

  ③用Method物件呼叫指定名稱的方法並向方法傳遞引數

     例: cs.invoke(str, 2);

2.呼叫靜態方法

   ①②同上

   ③在呼叫Method物件的invoke方法時,具體的物件用null代替就可以

     了,因為靜態方法不基於任何物件

 3.引數中有陣列的成員方法的反射呼叫

當用反射呼叫接收的引數中存在陣列的方法時,情況就有所不同了,JDK1.5出現了可變引數列表和泛型,為了向前相容JDK1.4及更早的版本,編譯器做了一些小動作。

①當陣列為基本資料型別時,可以直接傳遞

②但是,當陣列為引用資料型別的陣列時,在傳遞引數時編譯器moore你接收Object型別的陣列,如果傳入的不是Object型別的陣列編譯器就會對其程序拆解,吧當中的每一個元素都當成一個Object陣列,這樣,就出現了型別引數不匹配的異常報告。

為了解決這個問題,在傳入引數時,必須要對被傳入的陣列進行Object保護,具體的說就是把咪表陣列強制轉換成Object型別,這樣就告訴編譯器傳入的意見是一個Object陣列了,不要進行拆解了,這樣就“騙”過了編譯器而把正確的值傳遞過去了。

寫法有兩種:  (ObjectString[]{}  或  new Object(new String[]{})    *String 可以換成任意引用型別

陣列的反射

Java中用Array對陣列型別進行描述

陣列是引用資料型別,那麼Java中怎麼判斷陣列的Class物件是否相同呢?

Java規定:只要資料型別形同並且維度相同,那麼這兩個陣列所對應的class字 

          節碼就是同一份。

由於JDK1.5引入了泛型和可變引數,JDK1.4及以前的版本中的很多方法中都是通過接收Oject陣列來實現多個引數的傳遞的,所以在這裡就必須弄清楚陣列和Object之間的關係。

①對於基本資料型別陣列來說,每一個數組物件的引用都是一個Object

  例如:int [] arr = new int[];

       Object obj = arr;

②對應基本資料型別的二維及二維以上的陣列,第一維數相當於一個Object陣列,第二維數相當於陣列中的內容:

  例如:    int [][] arr = new int [][];

            Object[] obj = arr;

③對應引用資料型別的陣列可以直看成是一個Object陣列

  例如:String [] str = new String[];

       Object[] obj = str;

對於陣列的反射,Java中用Array類來進行描述,陣列的反射是基於物件的。

具體的應用例項如下:

/********************方法反射*******************/
  Class<ReflectClass> cc1 = ReflectClass.class;  //獲取Class物件
  //獲取指定構造器物件
  Constructor<ReflectClass> cons = cc1.getConstructor(int.class,String.class);
  //建立物件
  ReflectClass rcs = cons.newInstance(13,"fuck");
  //根據方法名和引數型別獲取方法物件
  Method mth = cc1.getMethod("func", double.class);
 //用方法物件呼叫指定類物件的該方法
  mth.invoke(rcs, 3.34);
  
  
  /*****************對接收陣列引數的成員方法的反射呼叫************************/
          //呼叫Test類中的show方法
  Class<Test>   ctest = Test.class;
  Method mthtest = ctest.getMethod("show", long[].class);
  //呼叫靜態方法
  mthtest.invoke(null, new long[]{1l,2l,3l,4l});
  
  /*
   值得注意的是,在用反射方法呼叫接收陣列型別引數的方法時,會出現兩種情況
   
   1.當接收的引數為基本資料型別陣列時,在傳遞引數的時候直接傳遞進行一個該基本資料型別的陣列就可以了,而且當中
                的值也能正常輸出
   2.但是,當接收的引數為引用資料型別的陣列時,情況就放生了變化,1.5版本以後出現可變引數列表後,編譯器為了向前相容,
                在用invoke呼叫引數列表中有引用資料型別陣列使,預設接收Object型別,如果傳入的不是Object型別,編譯器會對其
     進行拆解,把裡面的每一個元素都當成一個Object型別的陣列,這時就會出現引數型別不匹配的異常報告,
     
     為了解決這個問題,在傳入引數時,必須對要傳入的陣列進行Object保護,就是 把目標陣列強制轉換成Object,這樣就是告訴編譯器
     傳入的已經是一個Object陣列了,不要進行拆解,這樣就騙過編譯器而把正確的值傳入進去,
     
     寫法有兩種:1.  (Object)String[]{}
      
         2.  new Object[]{String[]{}}
   * */
  
  
  
  /********************陣列的反射************************/
  
  //陣列與Object的關係。
  int [] arr1 = new int[]{1,2,4,5,4};
  int [] arr2 =new int[4];
  int [][] arr3 = new int[][]{{1,2,3},{4,5,6},{7,8,9}};
  Object []obj = arr3;
  String[] str = {"dsfa","dfa","asdf"};
  Object[] obj1 = str;
  String[][]  str1 = {{"asdf","sdaf"},{"sdaf"},{"sfdasfda"}};
  Object[][]  str2 = {{"asdf","sdaf"},{"sdaf"},{"sfdasfda"}};
  System.out.println(arr1.getClass() == arr2.getClass());
  //比較不了 ,編譯器在編譯期間進行檢查
  //System.out.println(arr2.getClass() == arr3.getClass());
  //比較不了 ,編譯器在編譯期間進行檢查
  // System.out.println(arr1.getClass() == str.getClass());
  //System.out.println(arr3.getClass() == str.getClass());
  System.out.println(arr1.getClass().getSuperclass());
  System.out.println(arr3.getClass().getSuperclass());
  System.out.println(str.getClass().getSuperclass());
  
  System.out.println(Arrays.asList(arr1));
  System.out.println(Arrays.asList(str));
  System.out.println(Arrays.asList(arr3));
  System.out.println(Arrays.asList(str1));
  System.out.println(Arrays.asList(str2));
  
  
  //實現陣列的反射操作
  printArrays(arr1);
  
}
//實現陣列的反射操作
public static void printArrays(Object obj){
Class c = obj.getClass();
//判斷c是不是一個數組                                     /**Array 是Java反射中提供的用於動態建立和訪問陣列的方法*/
if(c.isArray()){
//獲取陣列的長度
int len = Array.getLength(c);
//根據陣列的長度對其進行迴圈遍歷
for(int i = 0;i < len;i ++){
//獲取陣列的具體元素
System.out.println(Array.get(c, i));
}
}
}
 
}

相關推薦

JavaSE基礎學習筆記-提高-Java反射概要

Class物件 Java中的各個類也是一類事物,按照面向物件的思想,可以對這些類進行描述和封裝,Java用Class類來代表class類。 【9個預定義的Class物件:8個基本資料型別 + void】 獲取類實力物件的三種常用方法: ①類名.class  例: Class

JavaSE基礎學習筆記及案例(三)反射

1.反射 1.1反射概述 1.java反射機制是在執行狀態時,對於任意一個類,都可以知道這個類的所有屬性和方法; 動態獲取資訊以及動態呼叫物件的方法稱為java反射機制; 2.反射的三種方式 (1)Object類的getClass()方法,判斷兩個物件是否是同一個位元組碼檔案; (2)靜

Java基礎學習筆記二十三 Java核心語法之反射

負責 目錄 boolean tostring 筆記 str 編譯 三種 進制 類加載器 類的加載 當程序要使用某個類時,如果該類還未被加載到內存中,則系統會通過加載,鏈接,初始化三步來實現對這個類進行初始化。 加載就是指將class文件讀入內存,並為之創建一個Clas

JavaSE基礎學習筆記及案例(二)多執行緒(下)與簡單工廠模式的瞭解

1.多執行緒(下) 1.1單例設計模式:保證類在記憶體中只存在一個物件 ************餓漢式與懶漢式的區別【面試題】 餓漢式單例模式:以空間換時間 懶漢式單例模式:以時間換空間(不推薦使用,僅在面試中用到) 3.多執行緒訪問時:餓漢式不會建立多個物件;而懶漢式

JavaSE基礎學習筆記及案例(一)IO流與多執行緒(上)

IO流 1. IO流知識點 IO流(字元輸入流FileReader) 位元組輸入流 FileInputStream IO流(字元輸出流FileWriter) 位元組輸出流 FileOutputStream 字元緩衝區輸入流( BufferedReader) 位元組緩衝區輸入流Bu

java學習筆記(中級)—java實現高質量圖片壓縮

使用java幾十行程式碼實現一個高質量圖片壓縮程式,再也不用去自己找網路的壓縮程式啦!而且很多網上的工具還有水印或者其他的限制,自己動手寫一個簡單的應用,是再合適不過了。 一、實現原理 1、宣告兩個字串變數,分別是要壓縮圖片的路徑和壓縮後圖片的存放路徑 private String brfore_image_

Java基礎學習筆記Java基礎語法之接口和多態

java cas 發現 過程 類類型 結果 覆寫 實例 new 接口 接口概念 接口是功能的集合,同樣可看做是一種數據類型,是比抽象類更為抽象的”類”。接口只描述所應該具備的方法,並沒有具體實現,具體的實現由接口的實現類(相當於接口的子類)來完成

Java基礎學習筆記二十二 網絡編程

數據丟失 交互圖 主動 總結 交互 servers -- 處理 關閉 絡通信協議 通過計算機網絡可以使多臺計算機實現連接,位於同一個網絡中的計算機在進行連接和通信時需要遵守一定的規則,這就好比在道路中行駛的汽車一定要遵守交通規則一樣。在計算機網絡中,這些連接和通信的規則被

Java基礎學習筆記十六 集合框架(二)

first 哈希 cat etag 基於 col 容器 處的 新元素 List List接口的特點: 它是一個元素存取有序的集合。例如,存元素的順序是11、22、33。那麽集合中,元素的存儲就是按照11、22、33的順序完成的。 它是一個帶有索引的集合,通過索引就

Java基礎學習筆記二十四 MySQL安裝圖解

password data 默認 count 重新 doc documents tran xp系統 、MYSQL的安裝 1、打開下載的mysql安裝文件mysql-5.5.27-win32.zip,雙擊解壓縮,運行“setup.exe”。

Java基礎學習筆記二十七 DBUtils和連接池

ride 基本 代碼塊 ear 不同 一行 ria 靜態方法 ... DBUtils 如果只使用JDBC進行開發,我們會發現冗余代碼過多,為了簡化JDBC開發,本案例我們講采用apache commons組件一個成員:DBUtils。DBUtils就是JDBC的簡化開發工

Java基礎學習筆記

body -1 ride java基礎學習 功能 根據 title 過濾 ret File 的高級獲取功能 String[] list() 返回一個字符串數組,這些字符串指定此抽象路徑名表示的目錄中的文件和目錄 示例

Java語言基礎學習筆記(七)

day tez lec mdk abd err .com mar mdm 烈7A茨諳9m繁5暗MChttp://www.zcool.com.cn/collection/ZMTg3NzE1Njg=.html 3馗iC蓖17握WM啦http://www.zcool.com.cn

文章帶你入門Linux——馬哥Linux基礎學習筆記

更改密碼 自帶 ctime 詳細信息 內嵌 桌面環境 地址定界 格式符 p s 1.課程體系: 中級: 初級:系統基礎 中級:系統管理、服務安全及服務管理、Shell腳本; 高級: MySQL數據庫; cache & stor

Java基礎學習筆記——數學函數、字符和字符串

表示 偶數 相等 sdi 開始 增強 con sca isl 4.2 常用數學函數 Math類中方法分為三類:三角函數、指數函數方法和服務方法。服務方法包括取整、求最小值、求最大值、求絕對值和隨機方法。除了這些方法之外,Math類還提供了兩個很有用的double型常量,

Java基礎學習筆記(四)

animal 自定義類型 轉型 通過 PC pri 法則 lse super() 21.構造方法(續):分類: 隱式構造方法:如果在一個類中,沒有手動編寫構造方法,則系統會提供一個默認的無參的構造方法 顯式構造方法:如果在一個類中,手動編寫構造方法,則系統不會提供默認的

Java語言基礎——學習筆記

練習題 pub 使用 取數據 以及 九九 思想 問題 進行 04.01_Java語言基礎(循環結構概述和for語句的格式及其使用) A:循環結構的分類 for,while,do...while B:循環結構for語句的格式: for(初始化表達式;條件表達式;循環

java基礎學習筆記(13)

1:正則表示式(理解) (1)就是符合一定規則的字串 (2)常見規則 A:字元 x 字元 x。舉例:‘a’表示字元a \ 反斜線字元。 \n 新行(換行)符 (’\u000A’) \r 回車符 (’\u000D’) B:字元類 [abc] a、b 或 c(簡單類) [^abc] 任何字元

rust學習筆記中級2–結構體與基礎型別成員函式的實現(霜之小刀)

rust學習筆記中級篇2–結構體與基礎型別成員函式的實現(霜之小刀) 歡迎轉載和引用 若有問題請聯絡請聯絡 Email : [email protected] QQ:2279557541 結構體的成員函式的實現 先看個最簡單的示例。

java學習筆記第二天---JAVA 語言基礎

JAVA 語言基礎 1,關鍵字 小寫 電腦語言裡事先定義的 2,識別符號; 由26個字母大小寫,數字,符號_  $組成 規則 1  數字不能開頭          2不可以用關鍵字    &