Reflection(反射)

Reflection


  • 什么是反射

在運行區(qū)間,動態(tài)地去獲取類中的信息(類的信息鼻弧,方法信息,構(gòu)造器信息,字段信息)

  • 反射中常用的API
//Class:表示所有的類信息
// 獲取對應(yīng)類的Class實例
static Class<?> forName(String class className); // 獲取對應(yīng)類的Class實例
T newInstance(); // 創(chuàng)建對應(yīng)類的對象(該類中必須有一個公共無參數(shù)的構(gòu)造器)
String getName(); //獲取類的權(quán)限定名
String getSimpleName(); //獲取類的簡單名稱
Constructor<?>[] getConstructors(); // 獲取當前類中所有的公共構(gòu)造器
COnstructor<T> getConstructor(Class<?> ...parameterTypes); // 獲取當前類中指定的構(gòu)造方法
Method[] getMethods(); // 獲取當前類中所有的公共方法(包括從父類中繼承過來的方法)
Method getMethod(String name, Class<?> ...parameterTypes); // 獲取指定的類方法
Method[] getDeclaredMethods(); //獲取當前類中所有方法青自,和訪問權(quán)限無關(guān)
Method getDeclaredMethod(String name, Class<?> ...parameterTypes);
// 獲取指定的類方法,和訪問權(quán)限無關(guān)

Object invoke(Object obj, Object ...args); //調(diào)用指定的方法
//參數(shù):obj,該方法所屬的對象驱证,如果是靜態(tài)方法則傳入null;args,調(diào)用方法所需的實際參數(shù)

Class類和Class的實例


  • Class類:

用于描述一切接口延窜。枚舉是一種注解是一種接口抹锄。

為了明確區(qū)分出Class實例表示的是誰的字節(jié)碼逆瑞,Class提供了泛型

Class<Date> clzl = Date.class; //clzl表示的是Date的字節(jié)碼
Class<String> clz2 = String.class; // clz2表示的是String的字節(jié)碼
  • Class實例:

JVM中一份字節(jié)碼

  • 獲取到Class實例的三種方式
    • 類型.class(就是一份字節(jié)碼)
    • Class.forName(String className);根據(jù)一個類的全限定名來構(gòu)建Class對象
    • 每一個對象都有getClass()的方法

反射很強大荠藤,但是非常消耗性能,主要是為了做工具和框架是用的获高。

// 同一個類在JVM中只有一份字節(jié)碼;
//第一種方式:數(shù)據(jù)類型.class
Class<User> clz1 = User.class;
// 第二種方式:Class.forName(String className);
Class<?> clz2 = Class.forName("cn.itsource.User");
// 第三種方式:對象.getClass();得到對象的真實類型
User u = new User();
Class clz3 = u.getClass();
//clz1 == clz2 == clz3; 因為表示都是JVM中共同的一份字節(jié)碼(User.class)
  • 八大基本數(shù)據(jù)類型和關(guān)鍵字VoidClass實例

在八大基本數(shù)據(jù)類型的包裝類和Void類中都有一個常量:TYPE

所有數(shù)據(jù)類型都有class屬性哈肖,表示對應(yīng)的Class實例

  Integer.Type-->int.class
  Void.Type-->void.class
  
  Integer.Type == int.class; // true
  Integer.Type == Integer.class; // false
  • 數(shù)據(jù)的Class實例
    • 特點

所有具有相同元素類型和維數(shù)的數(shù)組才共享同一份字節(jié)碼對象(Class對象)

 // 表示數(shù)組的Class實例:
String[] sArr = {"A", "B", "C"};
Class clz = String[].class; // 此時clz表示就是一個String類的一位數(shù)組類型;
  • 示例代碼
public class ArrayClassInstanceDemo {
       String[] arr1 = {};
       String[] arr2 = {"A", "B"};
       Class clz1 = String[].class
       Class clz2 = arr2.getClass();
       Class clz3 = getClass();
       System.out.println(cl2 == c3);
       String[][] arr = {}
       System.out.println(clz1 == String[][].class); //false
       int[] iArr = {};
       System.out.prinln(iArr.getClass == clz1); // false
}
  • 獲取類中的構(gòu)造器

    • 常用方法
// Constructor<T>類:表示父類中構(gòu)造器的類型,Constructor的實例就是某一個類中的某一個構(gòu)造器
// 獲取某一個類中的所有構(gòu)造器:
// 1.明確操作的是哪一份字節(jié)碼對象念秧。
// 2.獲取構(gòu)造器淤井。

// Class類獲取構(gòu)造器方法:
// Constructor類:表示類中構(gòu)造器的類型,Constructor的實例就是某一個類中中的某一個構(gòu)造器

public Constructor<?>[] getConstructors(); // 該方法只能獲取當前Class所表示的類的public修飾的構(gòu)造器
public Constructor<?>[] getDeclaredConstructors(); // 獲取當前Class所表示類的所有構(gòu)造器摊趾,和訪問權(quán)限無關(guān)
public Constructor<T> getConstructor(Class<?>... parameterTypes); // 獲取當前Class所表示類中指定的一個public的構(gòu)造器
// 參數(shù):parameterTypes 表示:構(gòu)造器參數(shù)的Class類型
// 例如:
public User(String username);
Constructor c = clz.getConstructor(String.class);
public Constructor<T> getDeclaredConstructor(Class<?>.. parameterTypes); // 獲取當前Class所表示類中指定的一個構(gòu)造器
  • 實例代碼
public class User {
private String name;
private Integer age;
private User() {}
public User(String name) {}
  public static void main(String[] args) {
    Class<User> clz = User.class;
    Constructor<User> conn = clz.getConstructor(in.class);
    System.out.println(conn);
    // 由于在User類中沒有帶有int類型參數(shù)的構(gòu)造器币狠,所以會拋出異常
    // NoSuchMethodException
  }
}
  • 創(chuàng)建對象
    • 方式一
// 在反射中,Constructor最主要的作用就是調(diào)用構(gòu)造器砾层,創(chuàng)建構(gòu)造對象
// 常用方法
public T newInstance(Object... initargs); // 如果調(diào)用帶參數(shù)的構(gòu)造器漩绵,只能使用該方法
// 參數(shù):initargs:表示調(diào)用構(gòu)造器的實際參數(shù)
// 返回:返回創(chuàng)建的實例,T表示Class所表示的類的類型
// 如果:一個雷的構(gòu)造器可以直接訪問梢为,同時沒有參數(shù)渐行,那么可以直接使用Class類中的newInstance方法創(chuàng)建對象.
public Object newInstance(); // 相當于new 類名();
// 注意不能調(diào)用私有的構(gòu)造器(可以先將對象的)
* 方式二
// 調(diào)用Class類中的newInstance()方法來創(chuàng)建
Class clz = User.class;
User user = clz.newInstance();
  • APIAccessibleObject的介紹

    AccessibleObject 類是File``MethodConstructor對象的基類。在使用Field铸董、Method或者Constructor對來來設(shè)置或者獲取訪問字段祟印、調(diào)用方法或者創(chuàng)建和初始化類的實例的時候,會執(zhí)行訪問檢查粟害。

    在ACcessibleObject中提供了一個方法setAccessible(boolean flag)方法來設(shè)置是否忽略底層的訪問檢查flag:true表示忽略訪問檢查蕴忆,false表示要檢查(缺省值)

獲取類中的方法


  • 使用反射獲取某一個勒種的方法:
    • 找到獲取方法所在類的字節(jié)碼對象
    • 摘到需要被獲取的方法
  • Class類中常用方法:
// 獲取包括自身和繼承過來的所有public方法
public Method[] getMethods();

// 獲取自身的所有方法(不包括繼承,和訪問權(quán)限無關(guān))
public Method[] getDeclaredMethods();

// 表示調(diào)用指定的一個公共方法(包括繼承的)
// 參數(shù):methodName(被調(diào)用方法的名字);parameterTypes(被調(diào)用方法的參數(shù)類型,如:String.class)
public Method getMethod(String methodName, Class<?>... parameterTypes);

// 調(diào)用指定的一個本類中的方法(不包括繼承的)
// 參數(shù):同上
public Method getDeclaredMethod(String name, Class<?>... parameterTypes);
  • 示例代碼
// 獲取所有方法
private static void getAllMethod() {
  Class clz = User.class;
  Method[] ms = clz.getMethods(); // 獲取所有方法(注意:包括繼承的所有公共方法)
  for (Method m : ms) {
    System.out.println(m);
  }
  ms = clz.getDeclaredMethos();// 獲取所有的方法(注意:不包括繼承的)
}
for (Method m : ms) {
  System.out.println(m);
}

pubic void testMethods() {
  // 獲取 public void sayHi();
  Class clz = User.class;
  // 只有通過方法簽名才能找到唯一的方法
  // 方法簽名 = 方法簽名 + 參數(shù)列表(參數(shù)類型悲幅,參數(shù)個數(shù)套鹅,參數(shù)順序)
  Method m = clz.getMethod("sayHi", String.class);
  System.out.println(m);
  
  // 調(diào)用private void sayGoodBye(String name, int age);
  m = clz.getDeclaredMethod("sayGoodBye", String.class, int.class);
  System.out.println(m);
}

使用反射調(diào)用方法


  • 使用反射調(diào)用方法:
    • 找到被調(diào)用方法所在的字節(jié)碼
    • 獲取到被調(diào)用的方法對象
    • 調(diào)用該方法

調(diào)用方法

// 調(diào)用當前Method所表示的方法
// 參數(shù):obj(被調(diào)用方法的對象--一般都是這個類的一個對象);args(傳遞的參數(shù))
public Object invoke(Object obj,Object.. args);
  • 示例代碼
public static void main(String[] args) {
  // 1.獲取User類的Class實例
  Class<User> clz = User.class;
  // 2.獲取User類中指定的方法:public String doWork3(String name, Integer.class);
  Method method3 = clz.getMethod("doWork3", String.class, Integer.class);
  // 3.調(diào)用對象方法,這里需要傳入被調(diào)用方法所屬的對象汰具,和方法所需要的實際參數(shù)
  User u = clz.newInstance(); // 保證在User類中有公共無參數(shù)的構(gòu)造器
  // 用變量ret來接收該方法的返回值
  String ret = (String) method3.invoke(u, "neld", 18);
  
  // 獲取User類的一個指定的私有方法:private void doWork2(String name)
  Method method2 = clz.getMethod("doWork2", String.class);
  // 將方法設(shè)置為可訪問的
  method2.setAccessible(true);
  // 調(diào)用方法
  method2.invoke(u, "neld");
}

使用放射調(diào)用靜態(tài)方法


  • 方法
public Object invoke(Object obj, Object.. args);
// 如果底層方法是靜態(tài)的卓鹿,那么可以忽略指定的obj參數(shù)。將obj參數(shù)設(shè)置為null即可留荔。

使用反射調(diào)用可變參數(shù)


  • 方法

對于數(shù)組的引用類型吟孙,底層會自動解包 ,為了解決該問題聚蝶,我們使用Object的一個一維數(shù)組把實際參數(shù)包裝起來杰妓。

無論是基本數(shù)據(jù)類型還是引用數(shù)據(jù)類型,或者是可變參數(shù)類型碘勉,方正就是一切實際參數(shù)都包裝在 new Object[] {} 中巷挥,就沒有自動解包的問題了

  • 示例代碼
public static main(String[] args) {
  // 使用反射調(diào)用public static int show(int... args)
  Class clz = VarArgsMethodInvokeDemo.class; // 獲取本類的字節(jié)碼
  Method m = clz.getMethod("show1", int[].class);
  //m.invoke(null, 1, 2, 3, 4, 5) // 會報錯
  m.invoke(null, new int[]{1, 2, 3, 4, 5}); // 通過
  m.invoke(null, new Object[]{new int[] {1, 2, 3, 4, 5}}); // 通過
  m = clz.getMethod("show2", String[].class);
  // m.invoke(null, "A", "B", "C"); // 不通過
  // m.invoke(null, new String[] {"A", "B", "C"})// 不通過
  // 對于數(shù)組類型的引用型參數(shù),底層會自動解包验靡,為了解決該問題倍宾,我們使用Object的一維數(shù)組把實際參數(shù)包裝起來
  m.invoke(null, new Object[]{new String[]{"A", "B", "C"}});
}

// 可變參數(shù)底層就是一個數(shù)組
// 基本類型
public static void show1(int... args) {
  System.out.println(Arrays.toString(args));
}
// 引用類型
public static void show2(String.. . args) {
  System.out.println(Arrays.toString(args));
}

使用反射獲取字段


  • 思路

    • 找到字段所在類的字節(jié)碼
    • 獲取字段
  • 常用方法

// 獲取當前Class所表示類中的所有public的字段雏节,包括繼承的字段
public Field[] getFileds();

// 獲取類中指定的public字段,包括繼承的字段
public Field getField(String fieldName);

// 獲取當前類中的所有字段高职,不包括繼承的字段
public Field[] getDeclaredFileds();

// 獲取當前Class所表示類中該fieldName名字的字段矾屯,不包括繼承的字段
public Field[] getDeclaredFileds();

// 獲取當前Class中的字段,指定的字段初厚,不包括繼承的字段
public Field getDeclaredFiled(String name);

單例設(shè)計模式


  • 概念

在項目中件蚕,某個類有且只有一個實例,一般的把工具類做成單例的

  • 步驟
    • 把當前類的構(gòu)造器私有化
    • 在當前類中實現(xiàn)創(chuàng)建好一個私有的靜態(tài)對象
    • 向外暴露一個公共的靜態(tài)的方法來返回該對象
  • 寫法
    • 餓漢式
    • 懶加載式
    • 枚舉
    • 使用緩存機制來實現(xiàn)單例效果
    • 在Spring中(對象工廠),創(chuàng)建的對象默認就是單例的

餓漢式

public class ArrayTool {
  private ArrayTool(){}
  private static ArrayTool instance = new ArrayTool();
  // 獲取類的一個實例
  public static ArrayTool getInstance() {
    return instance;
  }
  // 工具方法
  public void sort(int arr) {
    System.out.println("數(shù)組排序...");
  }
}

懶加載(可能有線程安全問題产禾,所以要使用同步方法)

public class ArrayTool {
  private static ArrayTool instance = null;
  
  public static ArrayTool getInstace() {
    if (instance == null) {
      synchronized (ArrayTool.class) {
        if (instance == null) {
          instance = new ArrayTool();
        }
      }
    }
  }
}

public void sort(int[] arr) {
  System.out.println("數(shù)組排序...");
}

枚舉

public enum ArrayTool {
  INSTANCE; // public static final ArrayTool INSTANCE;
  // 工具方法
  private ArrayTool(){}
  public void sort(int[] arr) {
    System.out.println("數(shù)組排序...");
  }
}

Eclipse項目下classpath文件分析


source folder目錄下的文件會編譯到output(默認bin)目錄中

  • 加載資源文件
    • 使用相對路徑

相對于CLASSPATH的根路徑(output, 輸出目錄) 排作,需要獲取ClassLoader對象

// 獲取ClassLoader對象的兩種方式
// 方式一:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
// 方式二:
ClassLoader loader2 = 當前類名.class.getClassloader();

// 在調(diào)用即可獲取到output目錄下的資源文件流
public InputStream getResourceAsStream(String fileName);

注意


在使用newInstace()的時候,需要被實例化對象的類亚情,具有無參的公共的構(gòu)造方法妄痪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市楞件,隨后出現(xiàn)的幾起案子衫生,更是在濱河造成了極大的恐慌,老刑警劉巖土浸,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罪针,死亡現(xiàn)場離奇詭異,居然都是意外死亡黄伊,警方通過查閱死者的電腦和手機泪酱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來还最,“玉大人墓阀,你說我怎么就攤上這事⊥厍幔” “怎么了斯撮?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扶叉。 經(jīng)常有香客問我勿锅,道長,這世上最難降的妖魔是什么辜梳? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任粱甫,我火速辦了婚禮泳叠,結(jié)果婚禮上作瞄,老公的妹妹穿的比我還像新娘。我一直安慰自己危纫,他們只是感情好宗挥,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布乌庶。 她就那樣靜靜地躺著,像睡著了一般契耿。 火紅的嫁衣襯著肌膚如雪瞒大。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天搪桂,我揣著相機與錄音透敌,去河邊找鬼。 笑死踢械,一個胖子當著我的面吹牛酗电,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播内列,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼撵术,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了话瞧?” 一聲冷哼從身側(cè)響起嫩与,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎交排,沒想到半個月后划滋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡埃篓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年古毛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片都许。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡稻薇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胶征,到底是詐尸還是另有隱情塞椎,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布睛低,位于F島的核電站案狠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钱雷。R本人自食惡果不足惜骂铁,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望罩抗。 院中可真熱鬧拉庵,春花似錦、人聲如沸套蒂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至烁挟,卻和暖如春婴洼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背撼嗓。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工柬采, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人且警。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓警没,卻偏偏與公主長得像,于是被迫代替她去往敵國和親振湾。 傳聞我的和親對象是個殘疾皇子杀迹,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容

  • Java反射機制可以讓我們在編譯期(Compile Time)之外的運行期(Runtime)獲得任何一個類的字節(jié)碼...
    badcyc閱讀 574評論 0 0
  • Java反射機制可以讓我們在編譯期(Compile Time)之外的運行期(Runtime)獲得任何一個類的字節(jié)碼...
    總是擦破皮閱讀 32,589評論 6 28
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)押搪,斷路器树酪,智...
    卡卡羅2017閱讀 134,711評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法大州,內(nèi)部類的語法续语,繼承相關(guān)的語法,異常的語法厦画,線程的語...
    子非魚_t_閱讀 31,664評論 18 399
  • 初秋的空洞 是某種美學(xué)上的留白 又像是似是而非的困境 緊鎖那些甜美的雨夜 房頂?shù)脑铝?在雨天的晚上碎成 九百九十幾...
    租了五顆星閱讀 581評論 6 12