1. 程式人生 > >黑馬程式設計師——反射——Class、Constructor、Field、Method及簡單框架原理

黑馬程式設計師——反射——Class、Constructor、Field、Method及簡單框架原理

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">------</span><a target=_blank href="http://www.itheima.com" target="blank" style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">Java培訓、Android培訓、iOS培訓、.Net培訓</a><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">、期待與您交流! -------</span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">反射的基石:Class</span>

java程式中的各個java類屬於同一類事務,描述這類事務的java類名就是Class。

Class常用方法:

isPrimitive()//是否基本型別位元組碼檔案

isArray()//是否陣列型別的Class位元組碼檔案

//類的基本組成:Field、Method、Contructor、package

Method getMethod();

總之,只要要源程式中出現的型別,都有各自的Class例項物件,比如int[],void

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

注:包裝類.TYPE ;得到的是包裝類所包裝的基本資料型別的class檔案

         陣列也是一種型別,使用isArray();來判斷


Java反射機制:

         反射就是把Java類中的各種成分對映成相應的java類。

         Java執行狀態,對任意的類(class檔案),都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能呼叫它的任意一個屬性和方法;這種動態的獲取資訊和呼叫物件的方法的功能稱為java的反射機制。

Ps

         框架就是對外提供一些介面(功能擴張的標準),由實現類按照這個介面的標準去實現,框架內部如果需要操作這些實現類的物件完成某些操作,那麼就需要把這些實現類的全名(包名+類名)寫在某個配置檔案中,框架程式碼只需要讀取這個配置檔案,就可以獲取這個實現類的位元組碼檔案,然後利用反射技術建立這個實現類的物件並呼叫相應的方法完成一些操作。

         用於描述位元組碼檔案的類就是Class類,建立物件,可以提出位元組碼檔案中的內容,如欄位/建構函式/一般函式。該類就可以獲取位元組碼檔案中的所有內容,那麼反射就是依靠該類完成的。想要對一個類檔案進行解刨,只要獲取該類的位元組碼檔案物件就可以了。

獲取位元組碼檔案物件的3種方式(注:3種方式所得到的位元組碼都是同一個位元組碼物件):

1.      類名.class

2.      物件.getClass();

3.      Class.forName(“類名”);

測試類,用於後續的被反射的類:

package com.leaf.bean;

public class Person {
	private int age;
	private String name;

	public Person() {
	}

	public Person(String name, int age) {
		this.age = age;
		this.name = name;
		System.out.println(“paramConstructor run”);
	}

	public void personMethod() {
		System.out.print("personMethod run");
	}

	public void show() {
		System.out.println("name:" + name + "….age:" + age);
	}

	private void privateMethod() {
		System.out.println("privateMethod run");
	}

	public static void staticMethod() {
		System.out.println("staticMethod run");
	}
}

示例:獲取Class物件
package com.leaf.test;

import com.leaf.bean.*;


//下面開始反射的演示:
public class ReflectDemo{
	public static void main(String[] args)throws ClassNotFoundException{
		getClassFile_1();
		getClassFile_2();
		getClassFile_3();
	}
	/*
		方式1:Object類中的getClass()方法。
	用這種方式:優點,所有的類都實現了這個辦法
				缺點:必須要明確具體的類,並建立物件。
	*/
	public static void getClassFile_1(){
		Person p = new Person("lisi",15);
		Class clazz = p.getClass();

		Person p1 = new Person("lisi",15);
		Class clazz1 = p1.getClass();

		System.out.println(clazz == clazz1);//result:true
		System.out.println("...................");
		}
	/*
		方式2:任何資料型別都具備一個靜態屬性.class,用來獲取其對應的Class物件
		相對於第一種辦法來說簡單一些,但是還是要明確用到類中的靜態成員。
		還是不夠擴充套件。
	*/
	public static void getClassFile_2(){
		Class clazz = Person.class;
		Class clazz1 = Person.class;

		System.out.println(clazz == clazz1);//result:true
		System.out.println("...................");
	}
	/*	方法3:
		通過給定的類的字串名稱獲取
		擴充套件性最好
		可以用Class類中的方法完成
		該方法就是forName();
		只要知道類名就可以了,最方便。
	*/
	public static void getClassFile_3()throws ClassNotFoundException{
		//把類的字串名稱寫到配置檔案中去,然後讀出來
		String className = "com.leaf.bean.Person";//"包名+類名"
		Class clazz = Class.forName(className);

		System.out.println(clazz);//result:class com.leaf.bean.person
	}
}

result:

根據結果分析:三種方法得到的是同一份位元組碼檔案。

示例:

獲取Class中的建構函式

package com.leaf.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectDemo1 {
	public static void main(String[] args) {
		try {
			createNewObject_1();
			createNewObject_2();
		} catch (ClassNotFoundException | InstantiationException
				| IllegalAccessException | ClassCastException
				| NoSuchMethodException | InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void createNewObject_1() throws ClassNotFoundException,
			InstantiationException, IllegalAccessException {
		// 早期:new的時候,先根據被new的類的名稱尋找到該類的位元組碼檔案,並載入進記憶體。
		// 並建立該位元組碼檔案物件,並接著建立該位元組檔案的對應的Person物件。
		// Person p = new Person();

		// 現在:
		String name = "com.leaf.bean.Person";
		// 找尋該檔案類檔案,並加在進記憶體,併產生Class物件。
		Class clazz = Class.forName(name);
		// 如何產生該類的物件??
		Object obj = clazz.newInstance();// 呼叫Person的空參建構函式

	}

	public static void createNewObject_2() throws ClassCastException,
			InstantiationException, NoSuchMethodException,
			IllegalAccessException, InvocationTargetException,
			ClassNotFoundException {
		// Person p = new Person("xiaoqiang",39);
		/*
		 * 當獲取指定名稱對應類中的所體現的物件時,而該物件初始化不使用空引數建構函式該怎麼辦呢?既然是通過指定的建構函式進行物件的初始化,
		 * 所有應該先獲取到該建構函式,通過位元組碼檔案物件即可完成。方法:getConstructor(paramterTypes);
		 */
		String name = "com.leaf.bean.Person";
		// 找尋該名稱類檔案,並載入進記憶體,併產生Class物件;
		Class clazz = Class.forName(name);
		// 獲取到了指定的建構函式物件
		Constructor constructor = clazz.getConstructor(String.class,int.class);
		// 通過該建構函式物件的newInstance方法進行物件的初始化
		Object obj = constructor.newInstance("xiaoming",38 );
	}
}
result:



示例:

獲取位元組碼檔案中的欄位

package com.leaf.test;

import java.lang.reflect.Field;

public class ReflectDemo2 {
	public static void main(String[] args) throws Exception {
		getFieldDemo();

	}

	public static void getFieldDemo() throws Exception {
		Class clazz = Class.forName("com.leaf.bean.Person");

		// gerField只能獲取所有可訪問公共欄位,private獲取不到
		Field fieldAge = clazz.getDeclaredField("age");
		// 對私有欄位的訪問取消許可權檢查,暴力訪問
		fieldAge.setAccessible(true);
		Object obj = clazz.newInstance();
		// 為物件的屬性值賦值
		fieldAge.set(obj, 89);
		// 獲取某物件的某屬性值
		Object o = fieldAge.get(obj);
		System.out.println(fieldAge);
	}
}

result:


示例:

獲取Class檔案中的類的方法:

package com.leaf.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectDemo3 {

	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("com.leaf.bean.Person");
		getMethod_1(clz);
		getMethod_2(clz);
		getMethod_3(clz);
		
	}
	/*
	 * 獲取指定Class檔案中的所有的公共函式和所有函式的方法
	 */
	public static void getMethod_1(Class clz)throws Exception{
		//獲取所有的公共方法
		Method[] methods = clz.getMethods(); 
		//獲取所有的方法。包括私有的
		methods = clz.getDeclaredMethods();
		
		for(Method method :methods){
			
			System.out.println(method);
		}
		
	}
	/*
	 * 獲取class檔案中的指定的無參方法
	 */
	public static void getMethod_2(Class clz)throws Exception{
		Method method = clz.getMethod("personMethod",null);
		Object obj = clz.newInstance();
		Constructor con = clz.getConstructor(String.class,int.class);
		obj = con.newInstance("小明",25);
		method.invoke(obj, null);
	}
	/*
	 * 獲取class檔案中指定的有參的方法
	 */
	public static void getMethod_3(Class clz)throws Exception{
		Method method = clz.getMethod("paramMethod", String.class);
		Constructor con = clz.getConstructor(String.class,int.class);
		Object obj = con.newInstance("小明",25);
		method.invoke(obj, "我是引數");
		
	}
	/*
	 * 注:私有的獲取方法和私有欄位的獲取方法相同。
	 */
}

result:

反射的應用:框架

    框架與框架要解決的核心問題:

                我做房子賣給使用者,由使用者自己安裝門窗和空調,我做的房子就是框架,使用者需要使用我的框架,把門窗插入進我提供的框架中,框架和工具類有區別,工具類被使用者的類呼叫,而框架則是呼叫使用者提供的類。

    那什麼是要解決的核心問題??

              我再寫框架時,你這個使用者可能還在上小學,還不會寫程式,我寫的框架程式怎樣能呼叫到你以後寫的類呢?因為在寫程式時,無法知道要被呼叫的類名,所以在程式中,無法直接new某個類的例項物件了,而是要通過反射的方式來實現。

      綜合案例:

             框架程式怎樣能呼叫到你以後寫的類(門窗)呢? 因為在寫才程式時無法知道要被呼叫的類名,所以,在程式中無法直接new 某個類的例項物件了,而要用反射方式來.

示例:

簡單框架:通過反射來呼叫配置檔案中的類,並呼叫其中的方法!

配置檔案:config.properties

className=java.util.ArrayList

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;

public class ReflectTest2 {
	public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		//使用流把配置檔案載入進記憶體
		/*
		 * 配置檔案的存放目錄的選擇:
		 * 1.使用絕對路徑,在配置檔案中寫上檔案要存放的位置或使用api得到專案存放在硬碟上的絕對路徑,在拼湊出配置檔案的目錄。
		 * 2.類載入器。但是隻能只讀,不能修改。配置檔案都是放在ClassPath目錄下。(在Eclipse中直接放在源程式資料夾下,會自動載入進ClassPath)
		 * 	ReflectTest2.class.getClassLoader().getResoourceAsStream("com/leaf/ReflectDemo/config.properties");
		 * 	ReflectTest2.class.getResoourceAsStream("config.properties");相對路徑載入
		 * 	ReflectTest2.class.getResoourceAsStream("/com/leaf/ReflectDemo/config.properties");絕對路徑載入
		 
		 * */
		InputStream is = new FileInputStream("config.properties");
		
		//使用Properties來解讀配置檔案
		Properties pro = new Properties();
		pro.load(is);
		is.close();
		//讀取的類名可以通過反射來呼叫,這樣就可以在不知道類名的情況下,先設計出執行的框架。
		String className = pro.getProperty("className");
		Collection col = (Collection)Class.forName(className).newInstance();
				
//		Collection col = new HashSet();
		ReflectPoint rp1 = new ReflectPoint(3, 3);
		ReflectPoint rp2 = new ReflectPoint(5, 5);
		ReflectPoint rp3 = new ReflectPoint(3, 3);
		ReflectPoint rp4 = new ReflectPoint(4, 3);
		
		col.add(rp1);
		col.add(rp2);
		col.add(rp3);
		col.add(rp4);
		col.add(rp1);
//		rp4.x = 7;
//		col.remove(rp4);
		System.out.println(col.size());
		System.out.println(col);
		
	}
	
}
用來進行測試的類
package com.leaf.ReflectDemo;

public class ReflectPoint {
	int x;
	int y;
	public ReflectPoint(int x,int y){
		this.x = x;
		this.y = y;
	}
	@Override
	public int hashCode() {
		return x*39+y+41;
	}
	@Override
	public boolean equals(Object obj) {
		if(this == obj){
			return true;
		}
		if(!(obj instanceof ReflectPoint))
			try {
				throw new Exception("型別異常");
			} catch (Exception e) {
				e.printStackTrace();
			}
		ReflectPoint rp = (ReflectPoint)obj;
		int temp = rp.x - this.x;
		return temp==0?(this.y - rp.y)==0:temp==0;
	}
	@Override
	public String toString() {
		return "RP:"+x+":"+y;
	}
}
result:


反射生效,我們能修改配置檔案中的類,來呼叫其他集合中的類為我們使用,反射是不是很神奇?


相關推薦

黑馬程式設計師——反射——ClassConstructorFieldMethod簡單框架原理

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">------</span><a target=_bla

黑馬程式設計師——常用API詳解之ObjectScannerString

------Java培訓、Android培訓、iOS培訓、.Net培訓、期待與您交流! ------- A:Object類 Object類是所有類的根類,其所有的方法為所有類所共有,所以很多類的基本功能都是依賴於Object實現的。 如: 無參構造方法(Object只有無參

黑馬程式設計師 反射學習筆記

----------android培訓、java培訓、java學習型技術部落格、期待與您交流!---------- 反射:  “反射就是把Java類中的各種成分對映成相應的java類”,而在此之前,首先是必須獲得類的Class物件,再呼叫Class的相關方法,獲取例項類中

黑馬程式設計師_java高新技術(1)列舉反射內省

------- android培訓、java培訓、期待與您交流! ---------- JDK 5.0 靜態匯入,自動裝箱拆箱,增強for迴圈, 可變引數,列舉,泛型,元資料 day01***Eclipse/列舉 *Workspace與project     會切換工作間與

黑馬程式設計師——Java集合框架(一)之迭代器Collection層次結構等

-----------android培訓、java培訓、java學習型技術部落格、期待與您交流!------------ 集合框架概述 一、什麼是集合框架   1.什麼是集合?   集合是指把具有相同性質的一類東西匯聚成一個整體,簡單說就是指儲存資料的一個容器。集

黑馬程式設計師——Java IO流(二)之流操作規律總結File類Properties類序列流等

-----------android培訓、java培訓、java學習型技術部落格、期待與您交流!------------ 六、流操作規律總結  1.明確源和目的:   源:    字元流:FileReader(純文字檔案)。    位元組流:FileInputStream(

黑馬程式設計師——Java面向物件(二)之封裝繼承多型介面等

-----------android培訓、java培訓、java學習型技術部落格、期待與您交流!------------ 五、面向物件的特徵  面向物件主要有三大特徵: 1.特徵一 —— 封裝  1)定義:是指隱藏物件的屬性和實現細節,僅對外提供公共訪問方式。 2)好處:

黑馬程式設計師——Java之String類基本資料型別物件包裝類等

    例如:parseInt("0", 10) 返回 0。        parseInt("473", 10) 返回 473。        parseInt("-0", 10) 返回 0。        parseInt("-FF", 16) 返回 -255。        parseInt("1100

黑馬程式設計師——Java面向物件(一)之匿名物件程式碼塊static關鍵字等

   a)子類只繼承父類的預設(預設)建構函式,即無形參建構函式。如果父類沒有預設建構函式,那子類不能從父類繼承預設建構函式。    b)子類從父類處繼承來的父類預設建構函式,不能成為子類的預設建構函式。    c)在建立物件時,先呼叫父類預設建構函式對物件進行初始化,然後呼叫子類自身自己定義的建構函

黑馬程式設計師——Java集合框架(三)之Map集合Collections與Arrays工具類

-----------android培訓、java培訓、java學習型技術部落格、期待與您交流!------------ Map集合 一、概述 Map集合儲存的元素是鍵值對,即將鍵和值一對一對往裡存,而且要保證鍵的唯一性。  問題思考:   1.如何保證鍵的唯一性?   

黑馬程式設計師——Java面向物件(三)之內部類異常包等

-----------android培訓、java培訓、java學習型技術部落格、期待與您交流!------------ 六、物件的其他重要內容 1.單例設計模式  1)什麼是單例設計模式?   單例設計模式是指能夠確保一個類只有一個例項,並且能夠自行向整個系統提供這個例項

黑馬程式設計師——Java IO流(一)之IO流概述字元流位元組流等

-----------android培訓、java培訓、java學習型技術部落格、期待與您交流!------------ IO流 一、概述 1.IO流是用來處理裝置之間的資料傳輸。  2.Java對資料的操作時通過流的方式。  3.Java用於操作流的物件都在IO包中。  

黑馬程式設計師:Java基礎——集合框架之體系概述共性方法與迭代器

------- Java EE培訓、java培訓、期待與您交流! ---------- 1.體系概述     1.1 為什麼出現集合類?         面嚮物件語言對事物的體現都是以物件的形式,所

黑馬程式設計師——Java基礎---IO流(字元流位元組流轉換流流操作規律)

三、字元編碼 字元編碼通過轉換流來完成。在兩個物件進行構造的時候,可以加入字符集(即編碼表),可傳入編碼表的有: 轉換流:InuputStreamReader和OutputStreamWriter。列印流:PrintStream和PrintWriter,只有輸出流。轉換流的編碼應用 可以將字元以指定編

黑馬程式設計師--OC基礎--類方法物件方法和self的使用

1、類方法 (1)類方法就是一個方法,它的使用不需要使用者去建立一個類的例項進行呼叫而可以直接通過類名對方法進行呼叫。 宣告一個類方法的格式: +(返回值型別) 方法名:(引數1型別)引數1名稱 方法名:(引數2型別)引數2名稱; 例:

黑馬程式設計師_HTMLJavaScripDOM

一、      HTML 1.超文字標記語言不是一種程式語言,是一種描述性的標記語言,用於描述超文字中內容的顯示方式(副檔名為.html或.htm) 基本格式:     <html>        <head><title></title></head

黑馬程式設計師—Objective-C學習—封裝繼承多型

三大特性:成員變數的封裝、繼承、多型 一、封裝 寫成員變數時,不要寫@public,應使用set方法。成員變數儘量不要用@public,不讓外界直接訪問應提供一個方法給外界設定和訪問成員變數的值。即set方法和getf方法。 1、set方法 a、作用:提供一個方法給外界

8--黑馬程式設計師---技術總結之抽象類內部類

、期待與您交流! ---------------------- 一.抽象類       1.抽象類的概念       在面向物件的概念中,所有的物件都是通過類來描繪的,但是反過來,並不是所有的類都是用來描繪物件的,如果一個類中沒有 包含足夠的資訊來描繪一個具體的物

黑馬程式設計師——java基礎拾遺之多執行緒(二) 執行緒同步執行緒通訊

執行緒安全的概念:當多個執行緒同時執行一段程式碼時,如果結果和單執行緒執行時一致,而且其他變數也和預期的一致,說明是這段程式碼是執行緒安全的。但是,多執行緒執行的過程中會出現單執行緒時候不會出現的問題,大多出現在多個執行緒同時操作全域性變數或者靜態變數的時候。當出現這種

黑馬程式設計師--SQL Server 基本語法基礎知識

-------------------------------------------- ASP.Net+Android+IO開發、.Net培訓、期待與您交流! -----------------------------------