反射是非常重要的知識
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
以前寫過的一些方法,那時理解很淺
點我