Java反射總結(史上最全,有這一篇就夠了)

一、概述

什麼是反射?

Java反射機制指的是在Java程序運行狀態中,對於任何一個類,都可以獲得這個類的所有屬性和方法;對於給定的一個對象,都能夠調用它的任意一個屬性和方法。

這種動態獲取類的內容以及動態調用對象的方法稱爲反射機制

爲什麼使用反射?

在計算機科學領域,反射是指一類能夠自我描述和自控制的應用。

在Java編程語言中,反射是一種強有力的工具,是面向抽象編程的一種實現方式,它能使代碼語句更加靈活,極大提高代碼的運行時裝配能力。Java反射機制允許編程人員在對類未知的情況下,獲取類相關信息的方式變得更加多樣靈活,調用類中相應方法,是Java增加其靈活性與動態性的一種機制。

總結一下,Java反射機制有如下作用

  1. 反射機制極大的提高了程序的靈活性和擴展性,降低模塊的耦合性,提高自身的適應能力;
  2. 通過反射機制可以讓程序創建和控制任何類的對象,無需提前硬編碼目標類;
  3. 使用反射機制能夠在運行時構造一個類的對象、判斷一個類所具有的成員變量和方法、調用一個對象的方法;
  4. 反射機制是構建框架技術的基礎所在,使用反射可以避免將代碼寫死在框架中。

二、反射的原理

我們知道了什麼是反射以及反射的作用,那麼在Java中是如何支持反射的呢?

首先我們需要了解Java程序運行的過程,該過程包含兩個階段:編譯運行

在程序編譯階段,Java代碼會通過JDK編譯成 .class字節碼文件;
在程序運行階段,JVM會去調用業務邏輯對應需要的的字節碼文件,生成對應的Class對象,並調用其中的屬性方法完成業務邏輯。

Java的反射機制原理就是在程序運行階段,主動讓JVM去加載某個 .class文件生成Class對象,並調用其中的方法和屬性。 如下圖:

Java類加載過程
注:

  • Class類在java.lang包中,繼承了Object;
  • Class對象的由來是將.class文件讀入內存,併爲之創建一個Class對象,一個.class文件對應一個Class對象;

三、反射的使用

通過第二節的原理描述,我們知道使用Java反射時,有一個息息相關的類——Class類,其實不單單是Class類,還有三個主要使用的類,如下表:
在這裏插入圖片描述
下面我們來一一介紹:

1、獲取Java類(Class類的使用)

這是使用Java反射的第一步,獲取了對應的Class類之後,我們就可以生成對象,然後調用對象的方法和屬性,這一步有三種實現方式。

方法一

        Book book = new Book();
		Class bookClass = book.getClass();
		
		//輸出類名
		System.out.println(bookClass.getName());

這個方法其實是Object的一個方法,Class繼承了Object,所以我們可以直接使用。

方法二

        Class bookClass = Book.class;
		
		//輸出類名
		System.out.println(bookClass.getName());

方法三

	    Class bookClass;
		try {
			bookClass = Class.forName("test.Book");
			//輸出類名
			System.out.println(bookClass.getName());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

方法三是使用最多的方法。

2、獲取類的構造函數(Constructor類的使用)

我們獲取到一個類的Class對象之後,可以調用Class對象的getDeclaredConstructors()方法獲取該類的構造函數,如下:

	
	    // 反射所有聲明的構造方法
	public static void reflectAllConstructor() {
		System.out.println(TAG + "=============獲取所有的聲明的構造函數==============");
		try {
			Class<?> classBook = Class.forName("test.Book");
			Constructor<?>[] constructorsBook = classBook
					.getDeclaredConstructors();
			for (Constructor constructor : constructorsBook) {
				System.out.println(TAG + constructor);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

獲取了構造函數之後,調用Constructor類對象的newInstance()即可構造出我們想要類的對象,如下:

Book book = (Book)constructor.newInstance();

3、獲取類的函數(Method類的使用)

當我們得到了一個Class對象之後,我們可以獲取該類的所有方法,如下:

    // 反射所有的public的函數
	public static void reflectPublicMethods() {
		System.out.println(TAG + "=============獲取所有的public的函數==============");
		try {
			Class<?> classBook = Class.forName("test.Book");
			Method[] methodsBook = classBook.getMethods();
			for (Method method : methodsBook) {
				System.out.println(TAG + method);
			}

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	// 反射所有的聲明的方法
	public static void reflectAllMethods() {
		System.out.println(TAG + "=============獲取所有的聲明的函數==============");
		try {
			Class<?> classBook = Class.forName("test.Book");
			Method[] methodsBook = classBook.getDeclaredMethods();
			for (Method method : methodsBook) {
				System.out.println(TAG + method);
			}

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

getDeclaredMethods()和getMethods()都可以獲取到類的方法,那麼他們有什麼區別呢?

getMethods()執行結果如下:
在這裏插入圖片描述
我們看到 getMethods()不僅只獲取了自己定義的公用方法(private獲取不了),還把Object父類的公用方法也獲取了

getDeclaredMethods()執行結果如下:
在這裏插入圖片描述
我們看到 getDeclaredMethods()只能獲取自己類中定義的方法,但是可以獲取到private方法

4、獲取類的屬性(Field類的使用)

當我們得到了一個Class對象之後,我們可以獲取該類的所有屬性,代碼如下:

// 反射所有的public的屬性
	public static void reflectPublicFields() {
		System.out.println(TAG + "=============獲取所有的public的屬性==============");
		try {
			Class<?> classBook = Class.forName("test.Book");
			Field[] fieldsBook = classBook.getFields();
			for (Field field : fieldsBook) {
				System.out.println(TAG + field);
			}

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	// 反射所有的聲明的屬性
	public static void reflectAllFields() {
		System.out.println(TAG + "=============獲取所有的聲明的屬性==============");
		try {
			Class<?> classBook = Class.forName("test.Book");
			Field[] fieldsBook = classBook.getDeclaredFields();
			for (Field field : fieldsBook) {
				System.out.println(TAG + field);
			}

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

同Methods,獲取屬性也有getDeclaredFields()和getFields()兩種。

四、反射帶來的問題

反射雖然能夠給我帶來諸多便利,但是反射有一個致命問題:反射的效率比直接調用低很多,所以我們要慎用反射。

至於爲什麼反射效率低?

其實很好理解,使用反射時要通知JVM加載.class文件並且生成Class對象,而直接調用則不用,所以反射效率低。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章