Java基礎之—反射

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

簡單來說,可以總結爲:
1.在運行時,我們可以獲取任意一個類的所有方法和屬性
2.在運行時,我們可以調用任意一個對象的所有方法和屬性

反射使用的前提條件:
必須先獲取到代表該類字節碼的Class,Class類用於表示.Class文件(字節碼)

二,Class類的使用

1.在面向對象的世界裏,萬事萬物皆爲對象。在java語言中,類也是對象,類是java.lang.Class類的實例對象。,這個實例對象官方稱爲“該類的類類型”,習慣稱作“該類的字節碼”。因爲一個類在編譯之後都會生成一個.Class的字節碼文件。

2.創建Class類的實例對象的方式(以Student類舉例)

當我們想要創建Student類的實例時,

package cn.reflect.test;

public class ReflectDemo {
	
	public static void main(String[] args) {
		//創建Student的實例
		Student stu1 = new Student();
		
	}
}

//創建一個Student類
class Student{};

我們知道Student這個類,也是一個Class類的實例對象,那麼這個實例對象能不能也通過上面的方式表示呢?

在這裏插入圖片描述

通過查看Class類的源碼,我們發現,Class這個類的構造被私有化,並不能通過上面的方式,來進行創建實例。那麼我們該怎麼創建Class類的實例呢?我們可以查看JDK使用文檔。

在這裏插入圖片描述

在這裏插入圖片描述

獲取Class類的實例對象的三種方式

1.任何數據類型(包括基本數據類型),都有一個隱含的以static修飾的靜態成員變量class屬性

2.通過getClass()方式來獲取。因爲所有的類都繼承Object類,該方法存在於Object類中,所以任意一個類的對象都可以調用該方法

3.通過Class類的靜態方法:forName(String className)(推薦使用)**

package cn.reflect.test;

public class ReflectDemo {
	
	public static void main(String[] args) throws ClassNotFoundException {
		//創建Student的實例
		Student stu1 = new Student();
		
		//Student類也是Class類的一個實例對象,這個實例對象有三種表示方式
		
		//方式一----->說明每個類都有一個隱含的靜態成員變量class
		Class c1 = Student.class;    
		
		//方式二:通過Object類的getClass()方法來創建
		Class c2 = stu1.getClass();
		
		//方式三:通過Class的靜態方法forName(String className)來創建   (推薦使用)
		//注意:這種方式創建,傳入的方法參數必須是這個類全類名(包名.類名)
		Class<?> c3 = Class.forName("cn.reflect.test.Student");
		
		
		
	}
}

//創建一個Student類
class Student{};

注意:在運行期間,任意一個類,都只有一個Class對象產生,所以,上述代碼中c1,c2,c3這三個對象的地址值是相同的。

這三種方式,推薦使用第三種創建實例的方式。第一種,需要導入類的包,依賴性太強,不導入包,就出拋出編譯錯誤。第二種該類的對象都創建好了,還需要反射幹什麼。一般使用第三種,方法的參數可以傳入,也可以寫在配置文件中等多種方式。其實,在spring框架中,創建實例,採用的就是這種方式。

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

在這裏插入圖片描述

1.調用構造方法:
1).批量的方法
public Constructor[] getConstructors():所有"公有的"構造方法
public Constructor[] getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護、默認、公有)

2).獲取單個的方法,並調用:
public Constructor getConstructor(Class… parameterTypes):獲取單個的"公有的"構造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):獲取"某個構造方法"可以是私有的,或受保護、默認、公有;

3).使用newInstance方法創建一個類的實例

在這裏插入圖片描述

在這裏插入圖片描述

newInstance方法存在於Class類和Constructor類中,所以創建一個類的實例有兩種方式。當你創建一個類的實例,不需要用指定的初始化參數來初始化該實例時,使用Class類中的newInstance()方法更爲方便。反之,使用Constructor類中newInstance(Object… initargs)方法 來創建類的實例對象。

student類:

package cn.reflect.domain;

import java.util.Date;

public class Student {
	private String name;
	private int age;

	// 無參公共構造
	public Student() {
		System.out.println("我是無參公共構造方法");
	}

	// 有參公共構造
	public Student(String name, int age) {
		this.name = name;
		this.age=age;
		System.out.println("姓名: "+name+" 年齡: "+age);
	}

	// 有參私有構造
	private Student(String name) {
		this.name = name;
			System.out.println("姓名:"+name);
	}

	// 有多個參數的私有構造
	private Student(String name, boolean flag) {
		this.name = name;
		System.out.println("姓名:"+name+" flag: "+flag);
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
	
}

測試類:

package cn.reflect.test;

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

import cn.reflect.domain.Student;

/*
 * 通過Class對象可以獲取某個類中的:構造方法、成員變量、成員方法;並訪問成員;
 * 
 * 1.獲取構造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"構造方法
            public Constructor[] getDeclaredConstructors():獲取所有的構造方法(包括私有、受保護、默認、公有)
     
 * 		2).獲取單個的方法,並調用:
 * 			public Constructor getConstructor(Class... parameterTypes):獲取單個的"公有的"構造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):獲取"某個構造方法"可以是私有的,或受保護、默認、公有;
 * 		
 * 			調用構造方法:
 * 			Constructor-->newInstance(Object... initargs)
 */

public class ReflectDemo {
	
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		//創建Class類的實例對象
		Class<?> clazz = Class.forName("cn.reflect.domain.Student");//注意:參數爲Student類的全類名
		
		
		//2.獲取所有的公共構造方法
		System.out.println("**********************所有公共構造方法*********************************");
		Constructor[] consArrays = clazz.getConstructors();
		
		//遍歷數組
		System.out.println(Arrays.toString(consArrays));
		
		//3.獲取指定的公共構造方法
		System.out.println("**********************獲取公共,有參構造*********************************");
		Constructor<?> cons1 = clazz.getConstructor(String.class,int.class);
		System.out.println(cons1);
		
		//4.獲取所有的構造方法
		System.out.println("**********************獲取所有的構造方法*********************************");
		Constructor<?>[] allConsArrays = clazz.getDeclaredConstructors();
		//遍歷數組
		for (Constructor<?> c : allConsArrays) {
			System.out.println(c);
		}
		
		//4.獲取指定的私有構造方法,並調用創建Student類的實例
		System.out.println("**********************獲取私有,有參構造*********************************");
		cons1 = clazz.getDeclaredConstructor(String.class,boolean.class);
		System.out.println(cons1);
		
		/**由於這個構造是私有的,需要設置取消java的訪問檢查機制,進行暴力訪問,如果不進行設置,會報
		java.lang.IllegalAccessException: Class cn.reflect.test.ReflectDemo can not access a member of class cn.reflect.domain.Student with modifiers "private"
		沒有權限進行訪問
		**/
		cons1.setAccessible(true);
		
		Student stu = (Student) cons1.newInstance("zhangsan",true);
		
		//6.當你創建一個類的實例不需要設置成員變量的值時,可以使用下面這種方式創建
		stu = (Student) clazz.newInstance();
		
	}
}


後臺輸出:
在這裏插入圖片描述

四,獲取成員變量並使用

在這裏插入圖片描述

獲取成員變量並調用:
1.批量的
1).Field[] getFields():獲取所有的"公有字段"
2).Field[] getDeclaredFields():獲取所有字段,包括:私有、受保護、默認、公有;
2.獲取單個的:
1).public Field getField(String fieldName):獲取某個"公有的"字段;
2).public Field getDeclaredField(String fieldName):獲取某個字段(可以是私有的)
設置字段的值:
Field --> public void set(Object obj,Object value):
參數說明:
1.obj:要設置的字段所在的對象;
2.value:要爲字段設置的值;

student類:

package cn.reflect.domain;

import java.util.Date;

public class Student {
	private String name;
	public int age;


	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
	
	

}

測試類:

package cn.reflect.test;

import java.lang.reflect.Field;
import java.util.Arrays;

import cn.reflect.domain.Student;

/**
獲取成員變量並調用:
* 1.批量的
* 		1).Field[] getFields():獲取所有的"公有字段"
* 		2).Field[] getDeclaredFields():獲取所有字段,包括:私有、受保護、默認、公有;
* 2.獲取單個的:
* 		1).public Field getField(String fieldName):獲取某個"公有的"字段;
* 		2).public Field getDeclaredField(String fieldName):獲取某個字段(可以是私有的)
* 
* 	 設置字段的值:
* 		Field --> public void set(Object obj,Object value):
* 					參數說明:
* 					1.obj:要設置的字段所在的對象;
* 					2.value:要爲字段設置的值;
**/



public class Fields {
		
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException {
		//1.獲取Class類的實例對象
		Class<?> clazz = Class.forName("cn.reflect.domain.Student");
		
		
		//1.獲取所有的"公有字段"	
		System.out.println("**********************獲取所有的公有字段*********************************");
		Field[] fieldsArrays = clazz.getFields();
		
		//遍歷數組
		System.out.println(Arrays.toString(fieldsArrays));
		
		//2.獲取所有的字段	
		System.out.println("**********************獲取所有的字段*********************************");
		Field[] allFieldsArrays = clazz.getDeclaredFields();
		
		//遍歷數組
		System.out.println(Arrays.toString(allFieldsArrays));
		
		
		//3.獲取某個"公有的"字段	
		System.out.println("**********************獲取某個公有的字段	*********************************");
		Field field = clazz.getField("age");
		
		//遍歷數組
		System.out.println(field);
		
		//3.獲取某個字段(可以私有)	
		System.out.println("**********************獲取某個字段(可以私有)		*********************************");
		field = clazz.getDeclaredField("name");
		//遍歷數組
		System.out.println(field);
		
		
		System.out.println("**********************設置字段的值*********************************");
		
		//創建student類的實例
		Student stu = (Student) clazz.newInstance();
		
		//設置字段的值
		
		//由於要設置的字段name是私有的,所以要設置取消java的訪問檢查,進行暴力訪問
		field.setAccessible(true);
		
		field.set(stu, "張三");
		
		System.out.println(stu);
		
	}
}

後臺輸出:
在這裏插入圖片描述

五,獲取成員方法並使用

在這裏插入圖片描述

在這裏插入圖片描述

獲取成員方法並調用:

1.批量的
public Method[] getMethods():獲取所有"公有方法";(包含了父類的方法也包含Object類)
public Method[] getDeclaredMethods():獲取所有的成員方法,包括私有的(不包括繼承的)
2.獲取單個的:
public Method getMethod(String name,Class<?>… parameterTypes):
參數:
name : 方法名;
Class … : 形參的Class類型對象
public Method getDeclaredMethod(String name,Class<?>… parameterTypes)

調用方法:
Method --> public Object invoke(Object obj,Object… args):
參數說明:
obj : 要調用方法的對象;
args:調用方式時所傳遞的實參;

student類:

package cn.reflect.domain;

import java.util.Date;

public class Student {
	
	/************成員方法*******************/
	public void method1() {
		System.out.println("調用了公共的成員方法");
	}
	
	public void method2(String name) {
		System.out.println("調用了公共的成員方法,參數name:"+name);
	}
	
	private void method3() {
		System.out.println("調用了私有的成員方法");
	}
	
	private String method4(String name) {
		System.out.println("調用了私有的成員方法,參數name:"+name);
		return name;
	}

}

測試類:

package cn.reflect.test;

import java.lang.reflect.Method;
import java.util.Arrays;

import cn.reflect.domain.Student;

/*
 * 獲取成員方法並調用:
 * 
 * 1.批量的:
 * 		public Method[] getMethods():獲取所有"公有方法";(包含了父類的方法也包含Object類)
 * 		public Method[] getDeclaredMethods():獲取所有的成員方法,包括私有的(不包括繼承的)
 * 2.獲取單個的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					參數:
 * 						name : 方法名;
 * 						Class ... : 形參的Class類型對象
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 * 
 * 	 調用方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					參數說明:
 * 					obj : 要調用方法的對象;
 * 					args:調用方式時所傳遞的實參;
 */


public class Methods {
	public static void main(String[] args) throws Exception {
		
		//創建Class類的實例對象
		Class clazz = Class.forName("cn.reflect.domain.Student");
		
		//1.獲取所有的公有成員方法
		System.out.println("**********************獲取所有的公有成員方法*********************************");
		
		Method[] methods = clazz.getMethods();
	
		//遍歷數組
		for (Method method : methods) {
			System.out.println(method);
		}
		
		//2.獲取指定的method2()公共方法
		System.out.println("**********************獲取指定的公共方法method2()*********************************");
		
		Method method = clazz.getMethod("method2", String.class);
		System.out.println(method);
		
		//3.獲取所有的成員方法(包括私有成員方法)
		System.out.println("**********************獲取所有的成員方法*********************************");
		
		Method[] allMethods = clazz.getDeclaredMethods();
		
		//遍歷數組
		for (Method method2 : allMethods) {
			System.out.println(method2);
		}
		
		//4.獲取指定的method4()私有方法
		System.out.println("**********************獲取指定的私有方法method4()*********************************");
		
		method = clazz.getDeclaredMethod("method4", String.class);
		System.out.println(method);
		
		//5.調用invoke(Object obj,Object... args)執行方法
		System.out.println("**********************調用指定的方法method4*********************************");
		
		//創建Student類的實例對象
		Student obj = (Student) clazz.newInstance();
		
		
		//由於方法的成員方法是私有的,所以要設置取消java的訪問檢查機制,進行暴力訪問
		method.setAccessible(true);
		
		
		//執行方法,並接受方法的返回值
		String name = (String) method.invoke(obj, "zhangsan");
		
		System.out.println(name);
		
		
	}
}

後臺輸出:
在這裏插入圖片描述

總結:反射在java中佔有很大的位置,許多框架的底層都是利用反射類實現的,反射可以稱之爲框架設計的靈魂,所以理解反射對學習框架有很大的作用!

最後,新手創作,請大家多提批評意見,謝謝!

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