黑馬程式設計師-----基礎加強-反射
------<a href="http://www.itheima.com" target="blank">Java培訓、Android培訓、iOS培訓、.Net培訓</a>、期待與您交流! -------
1.反射的概念和用途總述
反射首先是一種動態的思想,不再是硬編碼。就是說在使用過程中,外部傳入一個類,通過對這個類進行反射,再去按照反射定義者的意圖使用這個類。傳說中目標類和客戶類的關係發生改變。
做個比喻,飯店新請一個廚師,負責人就問他會做什麼菜啊、調味品用什麼量啊等等,等負責人瞭解之後就讓廚師去做菜了,但是為了適應當地人口味,負責人還可以在下命令之前告訴廚師,少發花椒多放辣等。廚師的主要做菜功能不變,但是負責人可以要求他做一些功能上的調整。
反射的作用
反射的作用總結起來就一個:倒轉了目標類和客戶類的依賴關係。 以前我們設計程式,客戶類要麼依賴於目標類,要麼依賴於目標類的介面。因為目標類是作為工具提供給客戶類使用的,根據 java 基本語法規則,要使用某個類,必須知道該類提供的介面。有了反射之後,我們就可以方便是使用反射來實現框架,解除框架對於我們寫的類——目標類,的依賴關係。反射的概念和實現原理Reflection 是Java被視為動態(或準動態)語言的一個關鍵性質。反射就是把 JVM 通過符號引用動態解析 java 類的位元組碼的能力對映成為各種 Java 類的成分類的機制,通過這個機制,java 把 JVM 動態解析符號引用的功能封裝為各種 API 類公開給我們使用,這個機制允許我們可以於執行時載入、探知、使用,編譯期間完全未知的classes
2.Java類反射中所必須的類:
Java的類反射所需要的類並不多,它們分別是:Class、Field、Constructor、Method、Object,下面我將對這些類做一個簡單的說明。
Class類:類的例項表示正在執行的 Java 應用程式中的類和介面。列舉是一種類,註釋是一種介面。每個陣列屬於被對映為 Class 物件的一個類,所有具有相同元素型別和維數的陣列都共享該 Class 物件。
Field類:提供有關類或介面的屬性的資訊,以及對它的動態訪問許可權。反射的欄位可能是一個類(靜態)屬性或例項屬性,簡單的理解可以把它看成一個封裝反射類的屬性的類。
Constructor類:提供關於類的單個構造方法的資訊以及對它的訪問許可權。這個類和Field類不同,Field類封裝了反射類的屬性,而Constructor類則封裝了反射類的構造方法。
Method類:提供關於類或介面上單獨某個方法的資訊。所反映的方法可能是類方法或例項方法(包括抽象方法)。 這個類不難理解,它是用來封裝反射類方法的一個類。
Object類:每個類都使用 Object 作為超類。所有物件(包括陣列)都實現這個類的方法。
3.詳述各個反射類及程式碼示例其用法
1、Class類
被稱為反射的基石的Class類究竟何德何能獲得這一殊榮呢?靜聽分解:
首先,這傢伙長得像極了class,我們一直使用的class啊。難道有血緣關係?私生子?八卦之火熊熊燃燒。不過,做學問要嚴肅!好吧,嚴肅點
Class和class的區別
1)class:Java中的類用於描述一類事物的共性,該類事物有什麼屬性,沒有什麼屬性,至於這個屬性的值是什麼,則由此類的例項物件確定,,不同的例項物件有不同的屬性值。
2)Class:指的是Java程式中的各個Java類是屬於同一類事物,都是Java程式的類,這些類稱為Class。例如人對應的是Person類,Java類對應的就是Class。
Class獲取物件的方法(借鑑toShareBeauty同學的圖)
Class功能函式程式碼示例
package cn.itcast.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
/**
* @class: ReflectionClassDemo
* @package: cn.itcast.reflect
* @description: TODO
* @author: vivianZhao
* @date: 2013-7-20 上午10:55:13
* @version: 1.0
*/
public class ReflectionClassDemo {
public static void main(String args[]) throws Exception {
ReflectionClassDemo ref = new ReflectionClassDemo();
ref.getConstructor();
}
public void getConstructor() throws Exception {
Class<?> c = Class.forName("java.lang.Long");
Class<?> cs[] = { java.lang.String.class };
System.out.println("\n-------------------------------\n");
Constructor<?> cst1 = c.getConstructor(cs);
System.out.println("1、通過引數獲取指定Class物件的構造方法:");
System.out.println(cst1.toString());
Constructor cst2 = c.getDeclaredConstructor(cs);
System.out.println("2、通過引數獲取指定Class物件所表示的類或介面的構造方法:");
System.out.println(cst2.toString());
Constructor cst3 = c.getEnclosingConstructor();
System.out.println("3、獲取本地或匿名類Constructor 物件,它表示基礎類的立即封閉構造方法。");
if (cst3 != null)
System.out.println(cst3.toString());
else
System.out.println("-- 沒有獲取到任何構造方法!");
Constructor[] csts = c.getConstructors();
System.out.println("4、獲取指定Class物件的所有構造方法:");
for (int i = 0; i < csts.length; i++) {
System.out.println(csts[i].toString());
}
System.out.println("\n-------------------------------\n");
Type types1[] = c.getGenericInterfaces();
System.out.println("1、返回直接實現的介面:");
for (int i = 0; i < types1.length; i++) {
System.out.println(types1[i].toString());
}
Type type1 = c.getGenericSuperclass();
System.out.println("2、返回直接超類:");
System.out.println(type1.toString());
Class[] cis = c.getClasses();
System.out.println("3、返回 Class 中使用的所有的類和所有的介面:");
for (int i = 0; i < cis.length; i++) {
System.out.println(cis[i].toString());
}
Class cs1[] = c.getInterfaces();
System.out.println("4、實現的介面");
for (int i = 0; i < cs1.length; i++) {
System.out.println(cs1[i].toString());
}
System.out.println("\n-------------------------------\n");
Field fs1[] = c.getFields();
System.out.println("1、類或介面的所有可訪問公共欄位:");
for (int i = 0; i < fs1.length; i++) {
System.out.println(fs1[i].toString());
}
Field f1 = c.getField("MIN_VALUE");
System.out.println("2、類或介面的指定已宣告指定公共成員欄位:");
System.out.println(f1.toString());
Field fs2[] = c.getDeclaredFields();
System.out.println("3、類或介面所宣告的所有欄位:");
for (int i = 0; i < fs2.length; i++) {
System.out.println(fs2[i].toString());
}
Field f2 = c.getDeclaredField("serialVersionUID");
System.out.println("4、類或介面的指定已宣告指定欄位:");
System.out.println(f2.toString());
System.out.println("\n-------------------------------\n");
Method m1[] = c.getMethods();
System.out.println("1、返回類所有的公共成員方法:");
System.out.println(m1.length);
for (int i = 0; i < m1.length; i++) {
System.out.println(m1[i].toString());
}
Method m3[] = c.getDeclaredMethods();
System.out.println("2、返回類自己定義所有的成員方法:");
System.out.println(m3.length);
for (int i = 0; i < m3.length; i++) {
System.out.println(m3[i].toString());
}
Method m2 = c.getMethod("longValue", new Class[] {});
System.out.println("3、返回指定公共成員方法:");
System.out.println(m2.toString());
}
}
2、構造方法的反射應用_Constructor 類
package cn.itcast.day1;
import java.lang.reflect.Constructor;
public class ConstructorDemo {
public static void main(String[] args) throws Exception{
//得到某個類所有的構造方法
Constructor<?>[] constructors = Class.forName("java.lang.String").getConstructors();
//取得指定類的構造方法
Class classType = Class.forName("java.lang.String");
Constructor constructor = classType.getDeclaredConstructor(StringBuffer.class);
/*建立例項物件*/
//通常方式:
String str = new String(new StringBuffer("abc"));
//反射方式
String str1 = (String)constructor.newInstance(new StringBuffer("abc"));
//Class.NewInstance()方法
String str2 = (String)Class.forName("java.lang.String").newInstance();
/*該方法內部先得到預設的構造方法,然後用該構造方法建立例項物件。該方法的內部用到了快取機制來儲存預設構造方法的例項物件*/
//獲得構造方法並建立例項物件
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
/*getConstructor()中用到是不定長度引數,1.4版本之前,則是通過傳入陣列來調節引數型別和陣列不確定的情況*/
String str3 = (String)constructor1.newInstance(new StringBuffer("aaa"));
}
}
3、成員變數的反射_Field 類
//成員變數的反射
ReflectPoint pt1 = new ReflectPoint(3, 6);
//成員變數時共有的可以正常反射
Field filedY = pt1.getClass().getField("y");
System.out.println(filedY.get(pt1));
//如果成員變數是私有的要強行反射getDeclaredField
Field fieldX = pt1.getClass().getDeclaredField("x");
//暴力反射修改欄位的訪問屬性的方法方法 setAccessible(true); 這是繼承自 java.lang.reflect.AccessibleObject 的方法
fieldX.setAccessible(true);
//獲取
System.out.println(fieldX.get(pt1));
練習:將任意一個物件中的所有String型別的成員變數所對應的字串內容中的"b"改成"a"
<span style="font-size:14px;">import java.lang.reflect.Field;
public class Reflectest {
public static void main(String[] args) throws Exception {
ReflectPoint pt1=new ReflectPoint();
changeStringValue(pt1);
System.out.println(pt1);
}
private static void changeStringValue(Object obj) throws Exception{
Field[] fields=obj.getClass().getFields();//獲取所有的成員變數
//遍歷成員變數
for(Field field:fields){
//比較位元組碼用==
if(field.getType()==String.class){
String oldValue=(String)field.get(obj);//獲取obj的String型別的成員變數
String newValue=oldValue.replace('b', 'a');//將b換成a
field.set(obj, newValue);//將此 Field表示的欄位設定為指定的新值
}
}
}
}
class ReflectPoint {
public String str1="ball";
public String str2="basketball";
public String str3="itcast";
//重寫toString方法
public String toString(){
return str1+" "+str2+" "+str3+" ";
}
} </span>
4.成員方法的反射_Method類
得到類中的某一個方法:
例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
呼叫方法:
通常方式:System.out.println(str.charAt(1));
反射方式: charAt.invoke(str, 1);
如果傳遞給 Method 物件的 invoke() 方法的第一個引數為 null,說明該 Method 物件對應的是一個靜態方法。
jdk1.4和jdk1.5的invoke方法的區別:
jdk1.5:public Object invoke(Object obj,Object... args)
jdk1.4:public Object invoke(Object obj,Object[] args),按 jdk1.4的語法,需要將一個數組作為引數傳遞給 invoke 方法,陣列中的每個元素分別對應被呼叫方法中的一個引數,所以,呼叫 charAt 方法的程式碼也可以用 jdk1.4 改寫為 charAt.invoke(“str”, new Object[]{1}) 形式。
package cn.itcast.day1;
import java.lang.reflect.Method;
public class MethoDemo {
public static void main(String[] args) throws Exception {
Sring className = args[0];
Method method = Class.forName(className).getMethod("main", String[].class);
method.invoke(null, (Object)new String[]{"aaa","bbb","ccc"});
/*
* 如果按照一般寫法,傳遞引數應該是這樣:
* method.invoke(null, new String[]{"aaa","bbb","ccc"});
* 但是由於jvm自動拆包,會將String陣列當作三個引數傳入,這個main方法中只接受一個String[]不符,編譯器會報錯,所以有兩種解決方案。
* 其一:像上述程式中所寫的那樣,在前面加上強制型別轉換,告訴編譯器這是一個整體,不要拆包
* 其二:可以這樣寫——method.invoke(null, new Object[]{new String[]{"aaa","bbb","ccc"}});
* 定義一個Object型別陣列,並將String[]整體作為一個元素放入陣列中,編譯器拆包後得到的便是一個String[]型別引數。
*/
}
}
class Test{
public static void main(String[] args){
for (String str : args){
System.out.println(str);
}
}
}
5.陣列與Object類的關係及其反射型別
<span style="font-size:14px;"><strong>/**
* 需求:演示陣列 和 Object 的關係
*
* 思路:
* 1.獲取陣列的 Class 物件,比較是否相等
* 2.列印陣列的 Class 物件的名字
* 3.陣列 和 Object 型別之間的型別轉換
*
* 步驟:
*
* 總結:
* 1.java 裡面,相同元素型別和相同維度數的陣列是同一個型別的陣列,對應同一個 Class 物件
* 2.陣列型別的簽名是" [ + 元素型別名簽名 ",如果是多維陣列,也是符合前面的規則,結果就成了幾維陣列會有幾
* 個" [ "符號
* 3.陣列型別可以向上轉型為 Object 型別
* 4.java 語言中沒有多維陣列,其實都是一維陣列,所謂多維陣列,是陣列中的元素還是陣列,只有最後一層是一個非
* 陣列型別
*/
package cn.itcast.reflect;
public class ArrayAndObject {
public static void main(String[] args) {
// TODO Auto-generated method stub
int [] a1 = new int[4];
int [] a2 = new int[5];
int [][] a3 = new int[2][3];
String [] a4 = new String[3];
// 返回 true,說明同類型同維度的陣列是同一個 Class 物件
System.out.println(a1.getClass() == a2.getClass());
// 不可比較,說明同類型不同維度的陣列不是同一個 Class 物件
//System.out.println(a1.getClass() == a3.getClass());
// 不可比較,說明不同型別同維度的陣列不是同一個 Class 物件
//System.out.println(a1.getClass() == a4.getClass());
// 陣列型別的名稱是 [ + 型別名簽名,如果是多維陣列,幾維陣列用幾個 [
System.out.println(a1.getClass().getName());
System.out.println(a3.getClass().getName());
System.out.println(a4.getClass().getName());
// 陣列型別的父型別都是 Object 型別
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a3.getClass().getSuperclass().getName());
// 陣列型別的父類都是 Object 型別,所以陣列型別可以上轉為 Object 類
Object aObject1 = a1;
Object aObject2 = a4;
// 陣列中的元素有兩種型別,一種是基本型別,一種是引用型別
//Object[] aObjects3 = a1;
// 陣列型別的型別匹配需要匹配兩個地方,第一個是否是陣列,第二個陣列中的元素型別的匹配
// Object [] aObject4 定義了一個 ,一維陣列,其中陣列中的元素是 Object 型別
// a3 是定義了一個一維陣列A,陣列中的元素是 一維陣列B,一維陣列B中的元素是 int 型別,一維陣列B可以
// 向上轉型為 Object 型別,所以可認為 Java 語言中沒有多維陣列,其實都是一維陣列,所謂多維陣列,是
// 陣列中的元素還是陣列,只有最後一層是一個非陣列型別
Object[] aObject4 = a3;
Object[] aObject5 = a4;
}
}</strong></span>
Arrays.asList()方法處理 int[] 和 String[] 時的差異<strong> int [] a11 = new int[]{1, 2, 3};
String [] a12 = new String[]{"a", "b","c"};
System.out.println(Arrays.asList(a11));
// 這說明陣列中的元素向上轉型的時候不會進行自動裝箱拆箱
// 自動裝箱拆箱只會在運算子表示式中進行
//System.out.println(Arrays.asList((Integer [])a11));
System.out.println(Arrays.asList(a12));</strong>
列印結果為: [[[email protected]]
[a, b, c]
這是因為 Arrays 類的 asList 方法在 jdk1.5 和 jdk1.4 中不同, jdk1.5 :static <T> List<T> asList(T... a) jdk1.4:public static List asList(Object[] a) jdk1.5 為了相容 jdk1.5 而首先按照 jdk1.4 的型別執行,所以把 int[] 當做一個 Object 物件,把 String[] 當做一個 Object[] 物件。
總結:反射部分的知識目前所瞭解,用途不是很廣,也比較難以理解,但是我可以感覺到,反射的作用很大(作用大和用途不廣不矛盾),當需要的時候,會非常省時省力,也非常有效率,非常有必要學透徹。
相關推薦
黑馬程式設計師-----基礎加強-反射
------<a href="http://www.itheima.com" target="blank">Java培訓、Android培訓、iOS培訓、.Net培訓</a>、期待與您交流! ------- 1.反射的概念和用途總述
黑馬程式設計師 基礎加強 註解
什麼是註解的屬性 一個註解相當於一個胸牌,如果你胸前貼了胸牌,就是傳智播客的學生,否則就不是。如果想區分出是傳智播客哪個班的學生,這時候可以為胸牌再增加一個屬性來進行區分。加了屬性的標記效果為:@MyAnnotation(color=”read”) 定義基本型別的屬性和應用屬性: 在註解類中增加String
黑馬程式設計師基礎加強---註解
註解(未來的程式設計基於註解) ----------- android培訓、java培訓、java學習型技術部落格、期待與您交流! ------------ 1.概述 概念:註解(也稱為元資料):為我們在程式碼中新增資訊提供了一種形式化的方法,使我們可以在稍後的某個
黑馬程式設計師-----基礎加強-動態代理
------<a href="http://www.itheima.com" target="blank">Java培訓、Android培訓、iOS培訓、.Net培訓</a>、期待與您交流! ------- 代理類的作用與原理 1、生活中
黑馬程式設計師基礎-----基礎加強
靜態匯入: 靜態匯入是JDK1.5的新特性,比如Math類中的靜態方法,需要類名呼叫,也就是Math.max(3,6); 但是我們用靜態匯入的方法可以不用類名呼叫,直接寫方法掉用。寫法如下: 在包名的下邊輸入:import static java.lang.Math.ma
黑馬程式設計師——基礎知識--IO流
import java.io.*; public class Test { public static void main(String[] args) { try { String content = "Thank you for your fish
黑馬程式設計師——java的反射機制
------- android培訓、java培訓、期待與您交流! ---------- 前言:通過觀看畢向東老師的java基礎視訊,查漏補缺,將一些自己掌握的還不牢固的知識寫出來,希望和大家交流分享。 一、反射技術是java在執行的時候獲取類的基本資訊。 反射技術大大提高了
黑馬程式設計師-基礎部分(1
--------------------android培訓、java培訓、期待與您交流! -------------------- j2ee,j2se,j2me (中2的意思是版本2.0),5.0之後更名為JAVAEE,JAVASE,JAVAME, java的版本1.0/
《黑馬程式設計師》 使用反射獲取位元組碼檔案中的方法
public class ReflectMethod { /** * 方法的反射 * @param args */ public static void main(String[] args) throws Exception { String str
黑馬程式設計師----oc加強筆記----記憶體管理
引用計數器: 每個OC物件都有自己的引用計數器,是一個整數表示物件被引用的次數,即現在有多少東西在使用這個物件。物件剛被建立時,預設計數器值為1,當計數器的值變為0時,則物件銷燬。 2)對引用計數器的操作
黑馬程式設計師——基礎學習---感言
------<a href="http://www.itheima.com" target="blank">Java培訓、Android培訓、iOS培訓、.Net培訓</a>、期待與您交流! ------- 在自學java基礎的時候,就有好幾個問題搞
黑馬程式設計師----基礎學習第十二天
------- android培訓、java培訓、期待與您交流! ----------
黑馬程式設計師 Java高新技術--反射和內省
1.框架與框架要解決的核心問題:比如:我做房子賣給使用者住,由使用者自己安裝門窗和空調,我做的房子就是框架,使用者需要使用我的框架,把門窗插入進我提供的框架中。框架和工具類有區別,工具類被使用者的類呼叫,而框架則是呼叫使用者提供的類。2.框架要解決的核心問題:我在寫框架(房子)時,你這個使用者可能還在上小學,
黑馬程式設計師 java高新技術 反射
---------- android培訓、java培訓、期待與您交流! ---------- 一、Class類 Class是Java程式中各個Java類的總稱;它是反射的基石,通過Class類來使用反射。 物件的建立和使用: 建立例項物件:
黑馬程式設計師--java高新技術----反射
---------------------- ASP.Net+Android+IOS開發、.Net培訓、期待與您交流! ---------------------- 什麼是反射? 反射就是把一個類中的各種元素對映成一個類。 得到一個類中的元素都是從這個
黑馬程式設計師-java基礎加強-反射的深入講解
-------------------------ASP.Net+Unity開發、.Net培訓、期待與您交流!-------------------------- 透徹分析反射的基礎_Class類 Class類1、定義java程式中的各個java類也屬於同一類事物,而描述這
黑馬程式設計師---java基礎加強 反射的深入理解
=================第5單元:反射的深入講解=============== 17.透徹分析反射的基礎_Class類 反射的基礎: Class也就是每個java源程式通過編譯後生成的檔案載入進入記憶體的那個位元組碼檔案, 獲取到該位元組碼,就可以獲取
黑馬程式設計師----Java基礎之反射
------- <a href="http://www.itheima.com" target="blank">android培訓</a>、<a href="http://www.itheima.com" target="blank">java培訓</a&g
黑馬程式設計師_java基礎加強學習筆記之註解
------- <a href="http://www.itheima.com" target="blank">android培訓</a>、<a href="http://www.itheima.com" target="blank">j
《黑馬程式設計師》基礎加強---eclipse加強
Ctrl+1 快速修復(最經典的快捷鍵,就不用多說了)Ctrl+D: 刪除當前行 Ctrl+Alt+↓ 複製當前行到下一行(複製增加)Ctrl+Alt+↑ 複製當前行到上一行(複製增加)Alt+↓ 當前行和下面一行互動位置(特別實用,可以省去先剪下,再貼上了)Alt+↑ 當前行和上面一行互動位置(同上)Alt