Java中的反射技術--小白新手向
反射的原理:將一個類中的各部分封裝成其他物件
反射的好處:
1.可以在程式執行中,操作這些物件
2.可以解耦,提高程式的可擴充套件性
下面用一副我畫的圖來簡單解釋一下Java程式在計算機中執行經歷的階段,以及各階段我們用反射技術是如何建立物件的
上圖我已經寫出獲取Class物件的三個方式:
1.class.forName(“全類名(包名.類名)”):將位元組碼檔案載入進記憶體,返回class物件
2.類名.class;通過類名的屬性class獲取
3.物件.getClass():getClass()方法在Object類中定義
先做個實驗來熟悉一下建立Class物件的方法
package reflect;
import person_reflect.Person;
public class reflect_01 {
public static void main(String[] args) throws ClassNotFoundException {
//1.Class.forName(全類名)
Class c1 = Class.forName("person_reflect.Person");
System.out.println(c1);
//2.類名.class
Class c2 = Person.class;
System.out.println(c2);
//3.物件.getClass();
Person p = new Person();
Class c3 = p.getClass();
System.out.println(c3);
//驗證一下這三個物件一不一樣
System.out.println(c1==c2);
System.out.println(c2==c3);
System.out.println(c1==c3);
//結論: 同一個位元組碼檔案(*.class)在一次程式的執行過程中,只會被載入一次,不論通過哪種方法獲取到的物件都是同一個
}
}
從上述程式碼中我們可以看出,同一個位元組碼檔案在一次程式執行過程中,只會被載入一次,不論通過哪一種方式獲取的Class物件都是同一個
下面我們來正式學習一下反射的實際用法
首先建立一個Person類
package person_reflect;
public class Person {
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", a=" + a + ", b=" + b + ", c=" + c + ", d=" + d + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private String name;
private int age;
public int a;
private int b;
int c;
private int d;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {}
public void eat () {
System.out.println("eat");
}
public void eat(String food) {
System.out.println("eat"+food);
}
}
一個類中首先我們能想到什麼,肯定是成員變數,那麼我們就說一下利用反射技術獲取類成員變數的方法
* Field[] getFields():獲取所有public修飾的成員變數
* Field getfield(String name):獲取指定名稱的成員變數
*
* Field[] getDeclaredField()
* Field getDeclaredField(String name)
首先我們要獲取Person類的Class類物件
Class pc = Person.class;
然後我們就可以用到上面的四種方法了
先用Field[] getFields():舉例
java.lang.reflect.Field[] fields = pc.getFields();
for(java.lang.reflect.Field field :fields){
System.out.println(field);
}
利用這個方法,我們可以獲取所有用public修飾的成員變數
那麼如果我們想要獲取類中特定的變數怎麼辦呢?
java.lang.reflect.Field field = pc.getField("a");
System.out.println(field);
這時候我們就可以用到getField(String name);方法了,注意這個方法是要傳進去一個變數名的字串變數的。
那麼我們獲取變數的目的是什麼呢,肯定是獲得值或者給其賦值啦
和類中的get/set一樣,Class類物件獲得目標類的變數值和賦值時也有get/set方法
java.lang.reflect.Field a = pc.getField("a");
Person p = new Person();
Object value = a.get(p);//獲取Person類中public修飾的成員變數a的值
System.out.println(value);
//給Person中public修飾的成員變數a賦值
a.set(p,1);
System.out.println(p);
上面這都是獲取public修飾符修飾的變數,如果我想要呼叫private、protect、default修飾符修飾的變數怎麼辦?
那就要用到 * Field[] getDeclaredField()
-
Field getDeclaredField(String name)方法了
/* Field[] getDeclaredField():獲取類中全部的成員變數,不考慮修飾符
Field getDeclaredField(String name):獲取類中指定名字的成員變數
*/
//獲取全部變數
java.lang.reflect.Field[] fields_1 = pc.getDeclaredFields();
for(java.lang.reflect.Field field_1 : fields_1) {
System.out.println(field_1);
}
//獲取d變數併為其賦值 2
java.lang.reflect.Field field_1 = pc.getDeclaredField("d");
Object value2 = field_1.get(p);
field_1.set(p, 2);
System.out.println(p);
如果執行了的小夥伴會發現,這段程式報錯了,為什麼呢?
/*
//錯誤提示
* Exception in thread "main" java.lang.IllegalAccessException: class reflect.reflect_02 cannot access a member of class person_reflect.Person with modifiers "private"
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:385)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:693)
at java.base/java.lang.reflect.Field.checkAccess(Field.java:1096)
at java.base/java.lang.reflect.Field.get(Field.java:417)
at reflect.reflect_02.main(reflect_02.java:53)
*/
因為上面Person類的程式碼中我們定義了d是由private修飾符修飾的,現在我們知道,雖然getDeclaredField(String name)方法雖然可以獲取所有修飾符修飾的變數,但是獲取它的值和對其賦值可是不被允許的,那麼怎麼辦呢?
這時我們就要加上一行程式碼了
java.lang.reflect.Field[] fields_1 = pc.getDeclaredFields();
for(java.lang.reflect.Field field_1 : fields_1) {
System.out.println(field_1);
}
java.lang.reflect.Field field_1 = pc.getDeclaredField("d");
field_1.setAccessible(true); //===================所以我們要加上一行程式碼 忽略訪問許可權修飾符的安全檢查 **暴力反射**===========================
Object value2 = field_1.get(p);
field_1.set(p, 2);
System.out.println(p);
細心的小夥伴都發現了 上下兩塊程式碼有一行不一樣,對了 就是這裡
field_1.setAccessible(true);
這行程式碼是暴力反射 忽略訪問許可權
解釋:private修飾的變數get或者set值是不能直接獲取的 所以我們要加上一行
獲取類的成員變數的物件名.setAccessible(true):暴力反射 忽略訪問許可權
到這獲取類中成員變數的方法就基本結束了,那麼接下來是什麼呢?對了,就是類的構造方法
同獲取類的成員變數一樣,獲取類的構造方法我們同樣有四個方法
***/*
* 獲取類的構造方法
*
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructors(類<?>... parameterTypes)
*
* Constructor<?>[] getDeclaredConstructors()
* Constructor<T> getDeclaredConstructors(類<?>... parameterTypes)
*/***
相信有的小夥伴已經發現了,這不是和獲取成員變數的方法一樣嗎?還真是,用法基本是一樣的
同樣我們先獲得Class類物件pc
Class pc = Person.class;
因為和獲取成員變數的方法基本一樣嘛,所以這裡我只舉一個栗子,因為後面我們還有更重要的東西要說:
// Constructor<T> getConstructors(類<?>... parameterTypes)
Constructor constructor = pc.getConstructor(String.class,int.class);
//返回一個構造器
System.out.println(constructor);
這個方法就是獲得Person類中有引數且引數型別為String和int的構造方法
返回的這個constructor就是一個構造器 ,那麼重點來了,這個構造器能幹什麼?既然叫構造器了那麼肯定能建立點東西吧,欸,它就是用來建立物件的。
T newInstance(Object… initargs)
下面我們舉個例子,來解釋一下用這個構造器怎麼才能建立個物件出來,因為是一個物件,所以我們要用Object型別的person變數來接收這個值
//用構造器來建立物件
Object person = constructor.newInstance("張三",11);
System.out.println(person);
Constructor constructor1 = pc.getConstructor();
Object person1 = constructor1.newInstance();
System.out.println(person1);//用一個空參的構造方法建立物件
pc.newInstance(); // 可以直接建立無參新物件
這樣我們就用這個構造器建立了一個物件,並且我們給這個物件傳了兩個引數,下面來看看執行結果
//Person [name=張三, age=11, a=0, b=0, c=0, d=0]
這樣我們就用Class類的構造器constructor來反射建立了一個Person物件。我們也可以選擇更簡單的方法,直接用Class類物件pc反射建立一個Class類指向的類的無參物件,這裡也就是Person
pc.newInstance();
只要這一行程式碼就建立好了,我們來看一下它的無參結果
//Person [name=null, age=0, a=0, b=0, c=0, d=0]
現在構造方法和變數的獲取我們都說過了,那麼一個類中還有什麼呢?欸,對了,就是成員方法,那麼成員方法怎麼獲取呢,沒錯,還是那四個方法。
**/*
* 獲取類的成員方法
* Method[] getMethods()
* Method getMethod(String name,類<?>... parameterTypes)
*
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name,類<?>... parameterTypes)
*/**
因為都一樣,我只舉一個例子:
Class pc = Person.class;
java.lang.reflect.Method eat_method = pc.getMethod("eat");
Person p = new Person();
//因為是空參 只新增一個物件名即可
eat_method.invoke(p);
java.lang.reflect.Method eat1_method = pc.getMethod("eat", String.class);
eat1_method.invoke(p,"shit");
//獲取所有public修飾的方法 還有很多隱藏方法
java.lang.reflect.Method[] methods = pc.getMethods();
for(java.lang.reflect.Method method1s : methods ) {
System.out.println(method1s);
String name = method1s.getName();
System.out.println(name);//獲取方法名
這裡我們就用到了多型,兩個eat方法但是引數不同,一個是無參,一個要傳進一個String引數,上面提到過,獲取帶引數的成員方法或者構造方法要怎麼樣了,沒錯就是要用**資料型別.class;**這裡我們是String.class
有細心的小夥伴應該發現了,成員方法是可以只獲取名字的 ,沒錯就是這行程式碼
String name = method1s.getName();
System.out.println(name);//獲取方法名
同樣,被反射的類也是可以被獲取到類名的
String name = pc.getName();
System.out.println(name);
執行後會發現,獲取到的類名是
person_reflect.Person
這就回到了我們一開始建立的類,是在person_reflect包下建立的,所以可以知道Class類物件獲取到的被反射的類的名字是它的包加上它的類名。
相關文章
- java反射技術Java反射
- Java SE之反射技術[Class,Field](一)Java反射
- JAVA核心技術學習筆記--反射Java筆記反射
- java框架基礎技術之--------反射機制Java框架反射
- Java技術分享:小白如何入門Mybatis?JavaMyBatis
- 好程式設計師Java培訓分享Java之反射技術程式設計師Java反射
- 小白都能學會的Java註解與反射機制Java反射
- Java核心技術梳理-類載入機制與反射Java反射
- 淺談java中的反射Java反射
- Java 中的 反射機制Java反射
- Java中Servlet技術JavaServlet
- 影片操縱中的新AI技術轉向AI
- Java中的類反射機制Java反射
- Golang反射技術初始入門Golang反射
- 視訊操縱中的新AI技術轉向AI
- 關於Java中的反射機制Java反射
- Java的反射Java反射
- 物件導向 -- 反射物件反射
- Restcloud新手小白使用感受RESTCloud
- Java中反射的概述及瞭解ClassLoaderJava反射
- Java匹馬行天下之JavaSE核心技術——物件導向Java物件
- Java 方法的反射Java反射
- 錢包開發技術新手指南,打破技術壁壘
- 從新手小白到運維大咖,SysOM 多場景當機例項解析 | 龍蜥技術運維
- 【Java 反射學習】Java 反射基礎Java反射
- [Java 反射學習] Java 反射基礎Java反射
- Java中的反射到底是個啥?Java反射
- Mybatis技術內幕(2.3.6):反射模組-WrapperMyBatis反射APP
- Mybatis技術內幕(2.3.7):反射模組-TypeParameterResolverMyBatis反射
- Mybatis技術內幕(2.3.3):反射模組-InvokerMyBatis反射
- Mybatis技術內幕(2.3.4):反射模組-ObjectFactoryMyBatis反射Object
- Mybatis技術內幕(2.3.1):反射模組-ReflectorMyBatis反射
- Java反射Java反射
- Java——反射Java反射
- Java 反射Java反射
- 技術文章 | windows橫向滲透中的令牌完整性限制Windows
- PHP 核心技術 --物件導向PHP物件
- Java反射—初探反射基礎Java反射