前不久學習了反射機制,來總結下。java
在此以前,回顧下java程序的編譯運行過程,分爲三個階段:源碼(.java文件)進過編譯生成字節碼文件(.class文件),而後jvm加載字節碼文件執行程序(runtime)。bash
前兩個步驟(編譯階段)是在硬盤上完成的,後一個步驟(運行階段)是在內存中完成的,而中間這個銜接就是:jvm經過類加載器----ClassLoader把硬盤中的class文件加載到內存中生成一個Class類的對象,這樣就能夠使用這個類中的成員變量和方法。一個類默認只會被加載一次,因此這個類對應的Class對象有且僅有一個。框架
1983年Smith首次提出反射這個概念,主要指程序能夠訪問、檢測和修改他自己狀態或行爲的一種能力。jvm
java反射機制是在運行狀態中中對類進行解剖並操做類中的構造方法,成員方法,成員屬性(主要用於框架中),這種動態獲取信息以及動態調用對象的方法的功能稱爲java語言的反射機制。ide
瞭解了反射機制的概念,那麼可見要想利用java反射機制作一些事,那麼就要利用Class對象,因此說Class對象是反射的前提。學習
那麼,怎麼獲取Class對象?ui
類名.classthis
對象名.getClassspa
Class.forName("全限定名(包名 + 類名)");code
補充:Class對象分兩種
1.普通Class對象:基於 引用類型
2.預約義(在jvm中的)Class對象:基於 基本類型 和 void
先準備一個類:
package com.test.demo;
public class Student {
public String name;
private int age;
public Student() {
}
private Student(String name, int age) {
this.name = name;
this.age = age;
}
public void show(String msg){
System.out.println("show方法 = " + msg);
}
private void speak(String msg,int number){
System.out.println("speak方法 = " + msg +":"+ number );
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
複製代碼
再次以前,咱們能夠經過公共的空參構造new一個Student,可是沒法new私有的滿參構造。
Student student = new Student();
複製代碼
如今來反射構造構造器(反射的形式建立實例)
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//獲取Class對象
Class<?> clazz = Student.class;
/* 根據參數類型獲取相應的構造器 參數類型是形參類型 */
Constructor<?> constructor = clazz.getConstructor();
/* 建立實例 參數類型是實參類型(形參一一對應) */
Object obj = constructor.newInstance();
System.out.println("obj = " + obj);
}
複製代碼
這樣獲取到的Student對象和new出來的空參構造器new出來的對象效果同樣的(實際業務開發並無意義)。
前者經過new建立出來對象的方式相比用反射建立的對象更被動,前者 是被new出來的,而用反射,是本身建立本身(對象),構造方法反客爲主。
還有一種方式,就是直接經過Class對象建立構造器:
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
//獲取Class對象
Class<?> clazz = Student.class;
/* 默認調用空參構造建立一個實例 jdk9中已過期 */
Object obj = clazz.newInstance();
System.out.println("obj = " + obj);
}
複製代碼
在Student類中 ,還有一個私有的構造器,正常方式下是不能經過私有構造器建立對象的。,可是反射能夠作到:
public static void main(String[] args)
throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
//獲取Class對象
Class<?> clazz = Student.class;
/*
獲取構造
由於權限是私有,但getConstructor()只能獲取public修飾的方法
getDeclaredConstructor():獲取聲明的方法。只要聲明的就能夠
*/
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
System.out.println("滿參私有構造 :" + constructor);
/*
私有構造,newInstance會產生非法訪問異常:java.lang.IllegalAccessException
因此要改變權限setAccessible() -->暴力反射
*/
constructor.setAccessible(true);
Object obj = constructor.newInstance("小明",20);
System.out.println("obj = " + obj);
}
複製代碼
以上就是利用反射來建立一個對象(反射構造器)。
接下來看看Student對象內兩個方法的反射
咱們以前(外部)使用方法,都是都是經過對象調用(非私有)方法,若是是靜態方法就是類直接調用。
那麼,使用反射調用(非私有)方法,該怎麼作?
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
//獲取Class對象
Student student = new Student();
Class<? extends Student> clazz = student.getClass();
/* getMethod():獲取Class對象裏的方法 參數一:方法名 參數二:參數列表類型 */
Method show = clazz.getMethod("show", String.class);
/* 調用show方法須要對象和參數 invoke()方法:調用的意思 參數一:調用此方法的對象 參數二:調用此方法須要傳入的實參 */
show.invoke(student, "hello public show");
}
複製代碼
反射能夠理解爲語言語法上的倒裝句:
咱們平時寫代碼都是我(對象)去調用方法,這裏就是:
new Student().show("對象調用方法");
而在 show.invoke(student, "hello public show"); 中,
show方法考慮的是誰來調用我,而後Student對象說,我來調用你(student做爲參數)。
擴展:若是公共的show方法加上static關鍵字,會影響方法調用嗎?
提示:靜態與對象無關.
答:加上static關鍵字,普通代碼即便不new對象也能夠調用,這個你們都知道,那麼,在show.invoke(student, "hello public show"); 中參數1 寫 null 也是不影響的,由於,show方法來自於 Student的Class對象。
接下來看看私有方法的反射如何實現?
ps: 反射通道的API都頗有規律,可讀性很強
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
//獲取Class對象
Student student = new Student();
Class<? extends Student> clazz = student.getClass();
/* getDeclaredMethod():獲取Class對象裏的聲明過的方法(包括) 參數一:方法名 參數二:參數列表類型 */
Method speak = clazz.getDeclaredMethod("speak", String.class, int.class);
//私有方法,暴力反射
speak.setAccessible(true);
/* 調用show方法須要對象和參數 invoke()方法:調用的意思 參數一:調用此方法的對象 參數二:調用此方法須要傳入的實參 */
speak.invoke(student, "hello private speak",2018);
}
複製代碼
在Student實體中有一個共有屬性一個私有屬性,咱們能夠經過對象來設置共有屬性的值,那麼經過反射如何實現全部屬性的賦值?
先來看看共有屬性name的賦值
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
//獲取Class對象,參數全限定名
Class<?> clazz = Class.forName("com.test.demo.Student");
/* getField():經過屬性名獲取屬性 */
Field name = clazz.getField("name");
//獲取對象
Object obj = clazz.newInstance();
/* 設置一個值 參數一:哪一個對象的屬性值 參數二:參數 */
name.set(obj,"張三");
System.out.println(obj);
}
複製代碼
根據前面說的API,反射屬性不難理解。
私有屬性的反射也不難實現
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
//獲取Class對象,參數全限定名
Class<?> clazz = Class.forName("com.test.demo.Student");
/* getDeclaredField():經過屬性名獲取(全部權限)屬性 */
Field age = clazz.getDeclaredField("age");
//暴力反射
age.setAccessible(true);
//建立對象
Object obj = clazz.newInstance();
/* 設置一個值 參數一:哪一個對象的屬性值 參數二:參數 */
age.set(obj,20);
System.out.println(obj);
}
複製代碼
使用java的反射機制,通常須要遵循三步:
那麼反射到底有什麼用?
反射最主要仍是運用在框架中,瞭解反射(不止反射)才更好的瞭解一些框架的原理。