Java最強大的技術之一:反射
theme: condensed-night-purple
攜手創作,共同成長!這是我參與「掘金日新計劃 · 8 月更文挑戰」的第8天,點選檢視活動詳情
何為反射?
Java的 反射機制 是在執行狀態中,對於任意一個類,都能夠 知道這個類的所有屬性和方法 ;對於任意一個物件,都能夠 呼叫它的任意一個方法和屬性 ;這種 動態獲取的資訊以及動態呼叫物件的方法的功能 稱為 Java 語言的反射機制
。
簡而言之,只要你給我一個 .class
——類的名字,我就能通過反射獲取到類的屬性和方法。
反射是很多高階技術的基礎,Java 中的註解、動態代理,各種框架注入 Spring 、 MyBatis 等都用到了反射技術。
Class 類
既然反射能夠通過類的名字獲取到類的關鍵資訊,那麼我們就來聊聊 Class
類。
Class 類在 JDK 中的定義
java
public final class Class<T>
extends Object
implements Serializable, GenericDeclaration, Type, AnnotatedElement
類 Class 的例項表示執行中的 Java 應用程式中的類和介面。
列舉
是一種類,註釋
是一種介面。
每個 陣列
也屬於一個類,這個類反映為一個 類物件
,由具有相同元素型別和維數的所有陣列共享。
原始Java型別(布林型、位元組型、char型、short型、int型、long型、float型和double型)和關鍵字void也被表示為類物件。
類的載入過程
要想解剖一個類,必須先要獲取到該類的 位元組碼 檔案物件。
而解剖使用的就是 Class
類中的方法。所以先要獲取到每一個位元組碼檔案對應的Class型別的物件。
那麼一個class檔案(.java檔案編譯後)在我們的硬碟上,是怎麼被載入到記憶體中的呢?
class進入記憶體分三步走:
1. Loading
Loading
就是把一個class檔案裝到記憶體中,它本來是class檔案上一個個的二進位制,一個個位元組,通過Loading把它放到記憶體。
2. Linking
Linking
的過程又分為:
- verification:校驗裝到記憶體的class檔案是否符合class檔案的標準,加入裝進來的檔案不是“CA FE BA BE”這樣的,不符合class檔案標準,直接被拒。
-
preparation:把class檔案靜態變數賦預設值,不是賦初始值。比如
static int i = 8
,在該步驟只是把i賦了預設值0。 -
resolution:把class檔案常量池裡面用到的符號引用,把它轉成直接記憶體地址,可以訪問到的內容。
3. Initializing
Initializing
這一步就是將靜態變數賦初始值,比如上面的 static int i = 8
,在這一步才賦初始值8。
獲取 Class 類的三種方式
獲取 Class
類通常有以下三種方式:
- 物件.getClass()
通過物件的getClass()方法獲取Class類,說明物件已經建立好了,其實已經有Class類了。
- 類.class
這種方式獲取Class類,需要提前知道類的名稱,也就是專案中已經匯入了相應的包,依賴性強。
- Class.forName()
只需要傳入一個類的完全限定名即可。
推薦使用 Class.forName() 的方式獲取Class類。
反射常用到的API
- 獲取類的構造方法:
```java
public Constructor
public Constructor<?>[] getConstructors() throws SecurityException ```
- 獲取類的成員變數
```java public Field getField(String name) throws NoSuchFieldException, SecurityException
public Field[] getFields() throws SecurityException ```
- 獲取類的方法
```java public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException ```
For example
定義一個實體類:
```java public class User { private Integer id; private String userName; private String phoneNumber;
public User() {
}
public User(Integer id, String userName, String phoneNumber) {
this.id = id;
this.userName = userName;
this.phoneNumber = phoneNumber;
}
public void myDefine() {
System.out.println("xxx");
}
} ```
通過 反射
獲取 User 類的相關資訊:
java
public class CreateObjectTest {
public static void main(String[] args) throws ClassNotFoundException {
//物件.getClass()
// User user = new User();
// Class clazz = user.getClass();
// System.out.println(clazz.getPackage());
// System.out.println(clazz.getName());
// System.out.println(clazz.getCanonicalName());
// System.out.println(clazz.getSimpleName());
//類.class
// Class clazz = User.class;
//Class.forName
Class clazz = Class.forName("com.xblzer.tryout.bean.User");
System.out.println("getConstructors:");
Arrays.stream(clazz.getConstructors()).iterator().forEachRemaining(System.out::println);
System.out.println("getDeclaredFields:");
Arrays.stream(clazz.getDeclaredFields()).iterator().forEachRemaining(System.out::println);
System.out.println("getDeclaredMethods:");
Arrays.stream(clazz.getDeclaredMethods()).iterator().forEachRemaining(System.out::println);
}
}
執行結果:
反射在 Spring 中的應用舉例
反射在眾多框架中都有普遍的應用。比如 Spring IOC
容器幫我們例項化眾多的bean,下面我們簡單模擬一下 反射
在其中起到的作用。
此處使用的案例接這篇:【設計模式】代理模式那些事兒:靜態代理,動態代理,JDK的動態代理,cglib,Spring AOP
Spring 配置檔案:
xml
<bean id="pony" class="com.xblzer.dp.proxy.springaop.Pony"></bean>
使用的時候直接這樣就能拿到定義的類了:
java
ApplicationContext ctx = new ClassPathXmlApplicationContext("app_aop.xml");
Pony pony = (Pony) ctx.getBean("pony");
那麼是怎麼做到的呢?就是通過 反射
。
Spring 通過配置檔案例項化物件,並將其放到容器的過程大概就是(模擬):
java
//虛擬碼
//1.解析<bean .../>元素的id屬性得到該字串值為“pony”
String idStr = "pony";
//解析<bean .../>元素的class屬性得到該字串值為“com.xblzer.dp.proxy.springaop.Pony”
String classStr = "com.xblzer.dp.proxy.springaop.Pony";
//利用反射機制,通過classStr獲取Class類物件
Class<?> cls = Class.forName(classStr);
//例項化物件
Object obj = cls.newInstance();
//放到Spring容器
Map<String, Object> container = new HashMap<>();
container.put(idStr, obj);
小結
多看一下 Class
類的API,諸多框架都用到了反射機制,而反射離不開呼叫這些基本的API。
JavaSE 8 API官網:http://docs.oracle.com/javase/8/docs/api/index.html
點個贊再走吧~
- Java最強大的技術之一:反射
- 【設計模式】通過一個簡單的案例理解-訪問者模式(Visitor Pattern)
- 【設計模式】模板模式,學會它咱也寫出優雅健壯的程式碼!
- 【設計模式】通俗易懂版責任鏈模式
- 【設計模式】代理模式那些事兒:靜態代理,動態代理,JDK的動態代理,cglib,Spring AOP
- 萬字長文 | Spring Cloud Alibaba元件之Nacos實戰及Nacos客戶端服務註冊原始碼解析
- 【設計模式】只需體驗三分鐘,你就會跟我一樣瞭解Facade和Mediator模式
- 【設計模式】工廠系列-FactoryMethod,AbstractFactory,Spring IOC
- 【資料結構】| 連結串列資料結構及其簡單玩法解析
- 系統架構演進與Spring Cloud Alibaba簡介
- 【FastDFS】一文學會一個分散式檔案系統!
- 【MySQL 8】MySQL 5.7都即將停只維護了,是時候學習一波MySQL 8了!
- 使用Rancher部署管理K8S叢集,真香!
- 玩轉Docker映象倉庫-Docker Registry及Harbor
- 【設計模式】各個擊破單例模式的8種寫法
- 容器化技術之Docker-從入地到上天
- 【RocketMQ】RocketMQ叢集,RocketMQ-on-DLedger可容災叢集
- 【RocketMQ】基於RocketMQ的分散式事務
- 【RocketMQ】近距離感受RocketMQ如何收發訊息,有備而來!
- 【RocketMQ】RocketMQ入門之閃電三連鞭:訊息佇列、RocketMQ介紹及安裝使用