一個需求,引出反射 | 完整的反射使用流程:
在不修改源碼的情況下,來控制程序,也符合設計模式中的opc原則(開閉原則:不修改源碼,擴容功能)
1、創建配置文件:re.properties
classfullpath=com.reflection.Cat
method=hi
2、創建Cat類
package com.reflection;public class Cat {public String name = "招財貓";public void hi(){System.out.println("hi!"+ name);}public void cry(){System.out.println(name + "喵喵叫!");}
}
3、創建調用類:quection,并回顧傳統調用的方法,以及反射機制解決的完整流程
package com.reflection;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;public class quection {public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {//問題:根據配置文件 re.properties 中的指定信息,創建Cat類 并 調用hi方法;//傳統方法Cat cat = new Cat();cat.hi(); // 如果想調用cry方法,只能修改代碼;System.out.println("===========================================");//反射//1. 使用Properties類 獲取配置文件中的內容Properties properties = new Properties();properties.load(new FileInputStream("src\\main\\resources\\re.properties"));String classfullpath = properties.getProperty("classfullpath").toString();String method = properties.getProperty("method").toString();System.out.println("classfullpath:" + classfullpath);System.out.println("method:" + method);//2. 使用反射機制解決Class aClass = Class.forName(classfullpath); //加載類,返回Class類型的對象aClassObject o = aClass.newInstance(); //通過aClass得到 com.reflection.Cat 類的對象實例System.out.println("o的運行類型:" + o.getClass());//通過 aClass 得到 com.reflection.Cat 類的 methodName為“hi”的方法對象//即:在反射中,可把方法視為對象(萬物接對象)Method method1 = aClass.getMethod(method);System.out.println("===========================================");//通過 方法對象 來實現 調用方法; 也就是 通過 method1對象 來調用 Cat類中的方法;method1.invoke(o);//傳統方法:對象.方法(); 反射:方法.invoke(對象);}
}
在 傳統方法調用 和 反射機制 中,若改為調用Cat類中的cry方法:
傳統方法需要修改代碼,而反射機制只需要修改配置文件即可,將re.properties配置文件中的,method=hi 改為 method=cry
反射機制:
反射機制允許程序在執行期借助于ReflectionAPI取得任何類的內部信息(比如成員變量,構造器,成員方法等),并能操作對象的屬性及方法。反射在設計模式和框架底層都會用到。
加載完類后,在堆中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象包含了類的完整結構信息。通過這個對象得到類的結構。這個Class對象就像一面鏡子,透過這個鏡子看到類的結構,所以,形象的稱之為:反射。
Java反射機制原理示意圖:
Java反射機制可以完成:
1、在運行時判斷任意一個對象所屬的類
2、在運行時構造任意一個類的對象
3、在運行時得到任意一個類所具有的成員變量和方法
4、在運行時調用任意一個對象的成員變量和方法
5、生成動態代理
反射相關的主要類:這些類在java.lang.reflection包中
1、java.lang.Class:代表一個類,Class對象表示某個類加載后在堆中的對象
2、java.lang.reflect.Method:代表類的方法,Method對象表示某個類的方法
3、java.lang.reflect.Field:代表類的成員變量,Field對象表示某個類的成員變量
4、java.lang.reflect.Constructor:代表類的構造方法,Constructor對象表示構造器
反射相關的主要類的代碼應用:
package com.reflection;public class Cat {private String name = "招財貓";public int age = 0;public Cat() {}public Cat(String name) {this.name = name;}public void hi(){System.out.println("hi!"+ name);}public void cry(){System.out.println(name + "喵喵叫!");}
}
package com.reflection;import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;public class reflection01 {public static void main(String[] args) throws Exception {//反射//1. 使用Properties類 獲取配置文件中的內容Properties properties = new Properties();properties.load(new FileInputStream("src\\main\\resources\\re.properties"));String classfullpath = properties.getProperty("classfullpath").toString();String method = properties.getProperty("method").toString();//2. 使用反射機制解決Class aClass = Class.forName(classfullpath); //加載類,返回Class類型的對象aClassObject o = aClass.newInstance(); //通過aClass得到 com.reflection.Cat 類的對象實例System.out.println("o的運行類型:" + o.getClass());//通過 aClass 得到 com.reflection.Cat 類的 methodName為“hi”的方法對象//即:在反射中,可把方法視為對象(萬物接對象)Method method1 = aClass.getMethod(method);System.out.println("===========================================");//通過 方法對象 來實現 調用方法; 也就是 通過 method1對象 來調用 Cat類中的方法;method1.invoke(o);//傳統方法:對象.方法(); 反射:方法.invoke(對象);//java.lang.reflect.Field:代表類的成員變量,Field對象表示某個類的成員變量//得到name字段;//getField不能得到私有的屬性Field name = aClass.getField("age");System.out.println(name.get(o));//傳統寫法:對象.成員變量 反射:成員變量對象.get(對象)//java.lang.reflect.Constructor:代表類的構造方法,Constructor對象表示構造器Constructor constructor = aClass.getConstructor();//()中可以指定構造器參數類型,返回無參構造器System.out.println(constructor);//Cat()Constructor constructor1 = aClass.getConstructor(String.class);//這里傳入的String.class 就是String類的Class對象System.out.println(constructor1);//Cat(String name)}
}
反射的優點和缺點:
優點: 可以動態的創建和使用對象(也是框架底層核心),使用靈活,沒有反射機制,框架技術就失去底層支撐。
缺點: 使用反射基本是解釋執行,對執行速度有影響。
package com.reflection;import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;public class reflection02 {public static void main(String[] args) throws Exception {m1();m2();m3();}//傳統方法調用hipublic static void m1() {Cat cat = new Cat();long start = System.currentTimeMillis();for (int i = 0; i < 900000000; i++) {cat.hi();}long end = System.currentTimeMillis();System.out.println("m1() 耗時:" + (end - start));}//反射機制調用方法hipublic static void m2() throws Exception {Class aClass = Class.forName("com.reflection.Cat"); //加載類,返回Class類型的對象aClassObject o = aClass.newInstance(); //通過aClass得到 com.reflection.Cat 類的對象實例Method method1 = aClass.getMethod("hi");//通過 aClass 得到 com.reflection.Cat 類的 methodName為“hi”的方法對象long start = System.currentTimeMillis();for (int i = 0; i < 900000000; i++) {method1.invoke(o);//反射調用方法}long end = System.currentTimeMillis();System.out.println("m2() 耗時:" + (end - start));}//反射機制優化調用方法hipublic static void m3() throws Exception {Class aClass = Class.forName("com.reflection.Cat"); //加載類,返回Class類型的對象aClassObject o = aClass.newInstance(); //通過aClass得到 com.reflection.Cat 類的對象實例Method method1 = aClass.getMethod("hi");//通過 aClass 得到 com.reflection.Cat 類的 methodName為“hi”的方法對象method1.setAccessible(true);long start = System.currentTimeMillis();for (int i = 0; i < 900000000; i++) {method1.invoke(o);//反射調用方法}long end = System.currentTimeMillis();System.out.println("m3() 耗時:" + (end - start));}
}=========執行結果============
m1() 耗時:4
m2() 耗時:1187
m3() 耗時:775
反射的簡單優化——關閉訪問檢查:
1、Method 和 Field、Constructor對象都有setAccessible()方法
2、setAccessible作用是啟動和禁用訪問安全檢查的開關
3、參數值為true表示,反射的對象在使用時取消訪問檢查,提高反射的效率。參數值為false,則表示反射的對象執行訪問檢查。