1. 程式人生 > >黑馬程式設計師-----基礎加強-反射

黑馬程式設計師-----基礎加強-反射

------<a href="http://www.itheima.com" target="blank">Java培訓、Android培訓、iOS培訓、.Net培訓</a>、期待與您交流! -------

1.反射的概念和用途總述

        反射首先是一種動態的思想,不再是硬編碼。就是說在使用過程中,外部傳入一個類,通過對這個類進行反射,再去按照反射定義者的意圖使用這個類。傳說中目標類和客戶類的關係發生改變。

        做個比喻,飯店新請一個廚師,負責人就問他會做什麼菜啊、調味品用什麼量啊等等,等負責人瞭解之後就讓廚師去做菜了,但是為了適應當地人口味,負責人還可以在下命令之前告訴廚師,少發花椒多放辣等。廚師的主要做菜功能不變,但是負責人可以要求他做一些功能上的調整。

反射的作用

反射的作用總結起來就一個:倒轉了目標類和客戶類的依賴關係。 以前我們設計程式,客戶類要麼依賴於目標類,要麼依賴於目標類的介面。因為目標類是作為工具提供給客戶類使用的,根據 java 基本語法規則,要使用某個類,必須知道該類提供的介面。有了反射之後,我們就可以方便是使用反射來實現框架,解除框架對於我們寫的類——目標類,的依賴關係。反射的概念和實現原理
Reflection 是Java被視為動態(或準動態)語言的一個關鍵性質。反射就是把 JVM 通過符號引用動態解析 java 類的位元組碼的能力對映成為各種 Java 類的成分類的機制,通過這個機制,java 把 JVM 動態解析符號引用的功能封裝為各種 API 類公開給我們使用,這個機制允許我們可以於執行時載入、探知、使用,編譯期間完全未知的classes
程式在執行時通過 Reflection APIs 取得任何一個 class 的內部資訊,包括其modifiers(諸如public, static 等等)、superclass(例如Object)、實現之interfaces(例如Serializable),也包括 fields 和 methods 的所有資訊,並於運行時改變該類的物件的 fields 內容或呼叫該類或者該類物件的 methods。這種動態獲取類的資訊以及動態呼叫物件的方法的功能就是Java 語言的反射(Reflection)機制。

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啊。難道有血緣關係?私生子?八卦之火熊熊燃燒。不過,做學問要嚴肅!好吧,嚴肅點

Classclass的區別

1classJava中的類用於描述一類事物的共性,該類事物有什麼屬性,沒有什麼屬性,至於這個屬性的值是什麼,則由此類的例項物件確定,,不同的例項物件有不同的屬性值。

2Class:指的是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