【Java】深入理解反射機制

反射是非常重要的知識


JAVA讓我們運行時識別對象和類的信息,有倆個方式:一個是傳統的RTTI,它假定我們在編譯時已經知道了所有的類型信息。另一種是反射機制,它允許我們運行時發現和使用類的信息。
–編程思想

反射的描述

JAVA反射機制是運行狀態中,對於任意一個類,能夠知道這個類的所有屬性和方法,對於任意一個對象,能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用方法的功能稱爲java語言的反射機制。

要想達到這樣的效果,必須先要獲取到該類的字節碼文件對象,而達到這樣效果使用的是Class類中的方法,所以先要獲取到每個字節碼文對應的Class類型對象。

每個類都會產生一個對應的Class對象,且每個類只有一個,也就是保存在.class文件。所有類都是在對其第一次使用時,動態加載到JVM

JAVA中反射的實現過程:
JAVA語言在編譯之後會產生成一個.class文件,反射就是通過字節碼文件找到某一個類,類中的方法以及屬性等。
下圖2中,就是找到Class文件並創建Class對象(摘自網上)
在這裏插入圖片描述
下面是jdk到解釋,說明反射就是把類中的各個成分反映爲一個個對象

Class類的類表示正在運行的Java應用程序中的類和接口。 枚舉是一種類,一個註釋是一種界面。 每個數組也屬於一個反映爲類對象的類,該對象由具有相同元素類型和維數的所有數組共享。 原始Java類型( boolean , byte , char , short , int , long , float和double ),和關鍵字void也表示爲類對象。

類沒有公共構造函數。 相反, 類對象由Java虛擬機自動構建,因爲加載了類,並且通過調用類加載器中的defineClass方法。

我們不需要自己創建Class類,JVM自動幫我們創建好了。

反射的作用

JAVA反射的作用是什麼:
反射機制指的是程序在運行時能夠獲取自身的信息。在JAVA中,只要給定類的名字,那麼就可以通過反射機制來獲取類的所有信息。

獲取Class對象的三種方式

//1.java中每個類型都有class 屬性**
    Class cls1 = String.class;
    Class cls2 = int.class;
    Class cls3 = Employee.class;	(Employee是一個類)
    
//2.通過class類的靜態方法 Class.forName(ClassName)
    Class cls1 = Class.forName("Employee");
    Class cls2 = Class.forName("java.lang.String");
    
//3.通過類的getClass()方法(Object的方法,因爲所以方法都繼承Object)
    Employee employee = new Employee();
    Class cls = employee.getClass();

反射的實現主要藉助以下四個類:class:類的對象,Constructor:類的構造方法,field:類中的屬性對象,Method類中的方法對象。

通過Class對象可以獲取某個類中的:構造方法、成員變量、成員方法;並訪問:

通過反射獲取構造方法並使用

package fanshe;

public class Student {
	//默認的構造方法
	Student(String str){
		System.out.println("默認的構造方法 s= "+str);
	}
	
	//無參數構造方法
	public Student() {
		System.out.println("調用了公有,無參數構造方法執行了。。。");
	}
	
	//有一個參數的構造方法
	public Student(char name) {
		System.out.println("姓名:"+name);
	}
	
	//有多個參數的構造方法
	public Student(String name,int age) {
		System.out.println("姓名:"+name+"年齡:"+age);
	}
	
	//受保護的構造方法
	protected Student(boolean n) {
		System.out.println("受保護的構造方法 n = "+n);
	}
	
	//私有構造方法
	private Student(int age) {
		System.out.println("私有的構造方法   年齡:"+age);
	}
}
package fanshe;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Constructors {
	//加載class對象
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		//加載class對象
		Class cls = Class.forName("fanshe.Student");
		//Class cls = new Student().getClass();
		//Class cls = Student.class;
		
		//獲取所有公有的構造方法
		System.out.println("**********************所有公有構造方法*********************************");
		Constructor[] conArray = cls.getConstructors();
		for(Constructor c:conArray) {
			System.out.println(c);
		}
		
		System.out.println("************所有的構造方法(包括:私有、受保護、默認、公有)***************");
		conArray = cls.getDeclaredConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}
		
		System.out.println("*****************獲取公有、無參的構造方法*******************************");
		Constructor con = cls.getConstructor(null);
		System.out.println("con = " + con);
		//創建此對象的一個新實例對象
		Object obj = con.newInstance();
		
		System.out.println("******************獲取私有構造方法,並調用*******************************");
		con = cls.getDeclaredConstructor(char.class);
		System.out.println(con);
		
		con.setAccessible(true);
		//用新實例對象的方法設置參數
		obj = con.newInstance('男');

	}
}

輸出
在這裏插入圖片描述

獲取成員變量並調用

package fanshe;
public class Employee {
	public Employee() {	
	}	
	//***********字段******************
	public String name;
	protected int age;
	char sex;
	private String phoneNum;
	
	@Override
	public String toString() {
		return "Employee [name=" + name + ", age=" + age + ", sex=" + sex + ", phoneNum=" + phoneNum + "]";
	}
}
package fanshe;


import java.lang.reflect.Field;

public class Fields {
	public static void main(String[] args) throws Exception {
		//1.獲取class對象
		Class cls = Class.forName("fanshe.Employee");
		
		//2.獲取字段
		System.out.println("***********獲取所有公有字段************************");
		Field []fieldArray = cls.getFields();
		for(Field f: fieldArray) {
			System.out.println(f);
		}
		System.out.println("***********獲取所有的字段(包括私有,受保護,默認的)*******");
		fieldArray = cls.getDeclaredFields();
		for(Field f: fieldArray) {
			System.out.println(f);
		}
		
		System.out.println("************獲取所有的字段(包括私有、受保護、默認的)********************");
		fieldArray = cls.getDeclaredFields();
		for(Field f : fieldArray){
			System.out.println(f);
		}
		System.out.println("*************獲取公有字段**並調用***********************************");
		Field f = cls.getField("name");
		System.out.println(f);
		//獲取一個對象
		Object obj = cls.getConstructor().newInstance();//產生Student對象--> Employee emp = new Employee();
		//爲字段設置值
		f.set(obj, "劉德華");
		//驗證
		Employee emp = (Employee)obj;
		System.out.println("驗證姓名:" + emp.name);
		
		
		System.out.println("**************獲取私有字段****並調用********************************");
		f = cls.getDeclaredField("phoneNum");
		System.out.println(f);
		f.setAccessible(true);//暴力反射,解除私有限定
		f.set(obj, "18888889999");
		System.out.println("驗證電話:" + emp);

	}
}

在這裏插入圖片描述

獲取成員方法並調用

package fanshe;

public class School {
		public void show1(String s){
			System.out.println("調用了:公有的,String參數的show1(): s = " + s);
		}
		protected void show2(){
			System.out.println("調用了:受保護的,無參的show2()");
		}
		void show3(){
			System.out.println("調用了:默認的,無參的show3()");
		}
		private String show4(int age){
			System.out.println("調用了,私有的,並且有返回值的,int參數的show4(): age = " + age);
			return "abcd";
		}
}

package fanshe;

import java.lang.reflect.Method;

public class MethodClass {
 
	public static void main(String[] args) throws Exception {
		//1.獲取Class對象
		Class cls = Class.forName("fanshe.School");
		//2.獲取所有公有方法
		System.out.println("***************獲取所有的”公有“方法*******************");
		cls.getMethods();
		Method[] methodArray = cls.getMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("***************獲取所有的方法,包括私有的*******************");
		methodArray = cls.getDeclaredMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("***************獲取公有的show1()方法*******************");
		Method m = cls.getMethod("show1", String.class);
		System.out.println(m);
		//實例化一個Student對象
		Object obj = cls.getConstructor().newInstance();
		m.invoke(obj, "劉德華");
		
		System.out.println("***************獲取私有的show4()方法******************");
		m = cls.getDeclaredMethod("show4", int.class);
		System.out.println(m);
		m.setAccessible(true);//解除私有限定
		Object result = m.invoke(obj, 20);//需要兩個參數,一個是要調用的對象(獲取有反射),一個是實參
		System.out.println("返回值:" + result);	
	}
}

在這裏插入圖片描述

利用反射運行配置文件

在src目錄下建一個pro.txt配置文件,用來更新操作
在這裏插入圖片描述
pro.txt內容:

ClassName=fanshe.Student1
MethodName=show

package fanshe;

public class Student1 {
	public void show() {
		System.out.println("is show");
	}
}
package fanshe;

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

public class Demo {
	public static void main(String[] args) throws Exception {
		//通過反射獲取class對象
		Class cls = Class.forName(getValue("ClassName"));
		//獲取show方法
		Method m = cls.getMethod(getValue("MethodName"));
		//調用show方法
		m.invoke(cls.getConstructor().newInstance());
		
	}

	private static String getValue(String key) throws Exception {
		Properties pro = new Properties();//獲取文件配置對象
		FileReader in = new FileReader("pro.txt");
		pro.load(in);	//將流加載到配置文件對象中
		in.close();
		return pro.getProperty(key);
	}
}

運行後

is show

當我們需要更改類時,創建一個新類Student2,然後只需要更改pro.txt的內容,就可以運行了。

ClassName=fanshe.Student2
MethodName=show

package fanshe;

public class Student2 {
	public void show() {
		System.out.println("is show2");
	}
}

其他不用改
輸出:

is show2

以前寫過的一些方法,那時理解很淺
點我

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