反射:框架設計的靈魂
框架:半成品軟件,可以在框架的基礎上進行軟件開發,簡化編碼。
反射:將類的各個組成部分封裝爲其他對象,這就是反射機制。
好處:
1.可以在程序運行過程中,操作這些對象。
2.可以解耦,提高程序的可拓展性。
java代碼在計算機中的三個階段:
在學習反射之前,先讓我們回顧下創建對象的四種方式:
創建對象的四種方式
1.使用new創建對象
2.使用反射機制創建對象
3.使用clone創建對象
4.使用序列化機制
我們接下來就要學習如何通過反射
獲取class對象的方式:
1.Class.forName(“全類名”):將字節碼文件加載進內存,返回Class對象
2.類名.class:通過類名的屬性class獲取
3.對象.getClass(); getClass()方法在Object類中定義着。
/**
* @author Bmo
* @date 2019/8/15 16:35
*/
public class GetClassDemo {
public static void main(String[] args) throws ClassNotFoundException {
//1.Class.forName("全類名")
Class cls1 = Class.forName("demo01.entity.Student");
System.out.println(cls1);
//2.類名.class
Class cls2 = Student.class;
System.out.println(cls2);
//3.對象.getClass()
Student s = new Student();
Class cls3 = s.getClass();
System.out.println(cls3);
// == 比較三個對象
//true
System.out.println(cls1 == cls2);
//true
System.out.println(cls1 == cls3);
//結論:同一個字節碼文件(*.class)在一次程序運行過程中,只會被加載一次,不論通過哪一種方式獲取的Class對象都是同一個。
}
}
通過比較我們獲取的三個Class對象我們得出結論:
同一個字節碼文件(*.class)在一次程序運行過程中,只會被加載一次,不論通過哪一種方式獲取的Class對象都是同一個。
Class對象功能:
獲取功能:
1.獲取成員變量們:
Field[] getFields()
Field getField(String name)
Field getDeclaredField(String name)
Field[] getDeclaredFields()
2.獲取構造方法們:
Constructor<?>[] getConstructors()
Constructor getConstructor(Class<?>… parameterTypes)
Constructor getDeclaredConstructor(Class<?>… parameterTypes)
Constructor<?>[] getDeclaredConstructors()
3.獲取成員方法們:
Method getMethod(String name, Class<?>… parameterTypes)
Method[] getMethods()
Method getDeclaredMethod(String name, Class<?>… parameterTypes)
Method[] getDeclaredMethods()
4.獲取類名:
String getName()
一個熟悉以上知識點的小Demo:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author Bmo
* @date 2019/8/15 11:15
*/
public class Test {
public static void main(String[] args) {
String classPath = "demo02.Student";
printProperty(classPath);
}
public static Object createInstance(String classPath) {
try {
Class<?> clazz = Class.forName(classPath);
return clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
public static void printProperty(String classPath) {
try {
Class<?> clazz = Class.forName(classPath);
// 獲取這個字節碼所有的public字段(含父類) 目前沒有 不會打印
Field[] fields = clazz.getFields();
//獲取當前clazz的所有字段
Field[] myfields = clazz.getDeclaredFields();
for (Field field : myfields) {
System.out.println(field.getName());
}
//帶declared是不含父類的
//獲取所有public方法(含父類)
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
//動態獲取當前類的類名
System.out.println(clazz.getName());
Constructor<?>[] constructors = clazz.getConstructors();
System.out.println(constructors.length);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Field : 成員變量
操作:
- 設置值 void set(Object obj, Object value)
- 獲取值 get(Object obj)
- 忽略訪問權限修飾符的安全檢查 setAccessible(true):暴力反射
Constructor:構造方法
創建對象:
T newInstance(Object... initargs)
如果使用空參數構造方法創建對象,操作可以簡化:Class對象的newInstance方法
Method : 方法對象
執行方法:
Object invoke(Object obj, Object... args)
獲取方法名稱:
String getName:獲取方法名
案例時間:
學習完相關的方法以後,是不是迫不及待躍躍欲試想要寫一個案例來驗證下自己的學習成果了呢?別急,這裏正好有一個。
案例需求:
寫一個"框架",可以幫我們創建任意類的對象,並且執行其中任意的方法。
需求分析:
實現:
- 1.配置文件
- 2.反射
步驟:
- 1.將需要創建的對象全類名和需要執行的方法定義在配置文件中
- 2.在程序中加載讀取配置文件
- 3.使用反射技術來加載類文件進內存
- 4.創建對象
- 5.執行方法
Student.java
package reflect;
/**
* @author Bmo
* @date 2019/8/15 17:15
*/
public class Student {
public void sleep() {
System.out.println("sleep...");
}
public void eat() {
System.out.println("eat....");
}
public void eat(String food) {
System.out.println("eat...." + food);
}
}
pro.properties
className=reflect.Student
methodName=sleep
ReflectTest
package reflect;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @author Bmo
* @date 2019/8/15 17:17
* 框架類
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
//可以創建任意類的對象,可以執行任意方法
//1.加載配置文件
//1.1創建Propertie對象
Properties pro = new Properties();
//1.2加載配置文件,轉換爲一個集合
//1.2.1獲取class目錄下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);
//2.獲取配置文件中定義的數據
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加載該類進內存
Class cls = Class.forName(className);
//4.創建對象
Object obj = cls.newInstance();
//5.獲取方法對象
Method method = cls.getMethod(methodName);
//6.執行方法
method.invoke(obj);
}
}