反射 筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html) 一個需求引出反射 請看下麵的問題 根據配置文件 re.properties 指定信息,創建對象並調用方法(以下為文件內容) classfullpath=com.hspedu.Ca ...
反射
筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html)
一個需求引出反射
請看下麵的問題
-
根據配置文件 re.properties 指定信息,創建對象並調用方法(以下為文件內容)
classfullpath=com.hspedu.Cat
method=hi思考:使用現有技術,你能做的嗎?
-
這樣的需求在學習框架時特別多,即通過外部文件配置,在不修改源碼情況下,來控製程序,也符合設計模式的ocp原則(開閉原則)
-
快速入門
import com.hspedu.Cat; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Properties; /** * 反射問題的引入 */ @SuppressWarnings({"all"}) public class ReflectionQuestion { public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //根據配置文件 re.properties 指定信息, 創建Cat對象並調用方法hi //傳統的方式 new 對象 -》 調用方法 // Cat cat = new Cat(); // cat.hi(); ===> cat.cry() 修改源碼. //我們嘗試做一做 -> 明白反射 //1. 使用Properties 類, 可以讀寫配置文件 Properties properties = new Properties(); properties.load(new FileInputStream("src\\re.properties")); String classfullpath = properties.get("classfullpath").toString();//"com.hspedu.Cat" String methodName = properties.get("method").toString();//"hi" System.out.println("classfullpath=" + classfullpath); System.out.println("method=" + methodName); //2. 創建對象 , 傳統的方法,行不通 =》 反射機制 //new classfullpath(); //3. 使用反射機制解決 //(1) 載入類, 返回Class類型的對象cls Class cls = Class.forName(classfullpath); //(2) 通過 cls 得到你載入的類 com.hspedu.Cat 的對象實例 Object o = cls.newInstance(); System.out.println("o的運行類型=" + o.getClass()); //運行類型 //(3) 通過 cls 得到你載入的類 com.hspedu.Cat 的 methodName"hi" 的方法對象 // 即:在反射中,可以把方法視為對象(萬物皆對象) Method method1 = cls.getMethod(methodName); //(4) 通過method1 調用方法: 即通過方法對象來實現調用方法 System.out.println("============================="); method1.invoke(o); //傳統方法 對象.方法() , 反射機制 方法.invoke(對象) } }
//這部分程式中的Cat在下麵的代碼中還會用到 package com.hspedu; public class Cat { private String name = "招財貓"; public int age = 10; //public的 public Cat() {} //無參構造器 public Cat(String name) { this.name = name; } public void hi() { //常用方法 //System.out.println("hi " + name); } public void cry() { //常用方法 System.out.println(name + " 喵喵叫.."); } }
反射機制
Java Reflection
- 反射機制允許程式在執行期藉助於Reflection API取得任何類的內部信息(比如成員變數,構造器,成員方法等等),並能操作對象的屬性及方法。反射在設計模式和框架底層都會用到
- 載入完類之後,在堆中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象包含了類的完整結構信息。通過這個對象得到類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構,所以,形象的稱之為:反射
Java反射機制原理示意圖
反射相關的主要類:
- java.lang.Class:代表一個類,Class對象表示某個類載入後在堆中的對象
- java.lang.reflect.Method:代表類的方法,Method對象表示某個類的方法
- java.lang.reflect.Field:代表類的成員變數, Field對象表示某個類的成員變數
- java.lang.reflect.Constructor:代表類的構造方法, Constructor對象表示構造器
這些類在java.lang.reflection
結合前面案例演示:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;
public class Reflection01 {
public static void main(String[] args) throws Exception {
//1. 使用Properties 類, 可以讀寫配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.get("classfullpath").toString();//"com.hspedu.Cat"
String methodName = properties.get("method").toString();//"hi"
//2. 使用反射機制解決
//(1) 載入類, 返回Class類型的對象cls
Class cls = Class.forName(classfullpath);
//(2) 通過 cls 得到你載入的類 com.hspedu.Cat 的對象實例
Object o = cls.newInstance();
System.out.println("o的運行類型=" + o.getClass()); //運行類型
//(3) 通過 cls 得到你載入的類 com.hspedu.Cat 的 methodName"hi" 的方法對象
// 即:在反射中,可以把方法視為對象(萬物皆對象)
Method method1 = cls.getMethod(methodName);
//(4) 通過method1 調用方法: 即通過方法對象來實現調用方法
System.out.println("=============================");
method1.invoke(o); //傳統方法 對象.方法() , 反射機制 方法.invoke(對象)
//java.lang.reflect.Field: 代表類的成員變數, Field對象表示某個類的成員變數
//得到name欄位
//getField不能得到私有的屬性
Field nameField = cls.getField("age"); //
System.out.println(nameField.get(o)); // 傳統寫法 對象.成員變數 , 反射 : 成員變數對象.get(對象)
//java.lang.reflect.Constructor: 代表類的構造方法, Constructor對象表示構造器
Constructor constructor = cls.getConstructor(); //()中可以指定構造器參數類型, 返回無參構造器
System.out.println(constructor);//Cat()
Constructor constructor2 = cls.getConstructor(String.class); //這裡老師傳入的 String.class 就是String類的Class對象
System.out.println(constructor2);//Cat(String name)
}
}
反射優點和缺點
- 優點:可以動態的創建和使用對象(也是框架底層核心),使用靈活,沒有反射機制,框架技術就失去底層支撐。
- 缺點:使用反射基本是解釋執行,對執行速度有影響.(相比傳統方法執行效率非常低)
- 應用實例:
反射調用優化-關閉訪問檢查(能夠一定程度提升效率,但提升較小。但相比傳統方法依然很慢)
-
Method和Field、Constructor對象都有setAccessible()方法
-
setAccessible作用是啟動和禁用訪問安全檢查的開關
-
參數值為true表示反射的對象在使用時取消訪問檢查,提高反射的效率。參數值為false則表示反射的對象執行訪問檢查
***.setAccessible(true);//取消訪問檢查,提高反射的效率
***.setAccessible(false);//執行訪問檢查
package com.hspedu.reflection;
import com.hspedu.Cat;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 測試反射調用的性能,和優化方案
*/
public class Reflection02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//Field
//Method
//Constructor
m1();
m2();
m3();
}
//傳統方法來調用hi
public static void m1() {
Cat cat = new Cat();
long start = System.currentTimeMillis();
for (int i = 0; i < 90; i++) {
cat.hi();
}
long end = System.currentTimeMillis();
System.out.println("m1() 耗時=" + (end - start));
}
//反射機制調用方法hi
public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class cls = Class.forName("com.hspedu.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
hi.invoke(o);//反射調用方法
}
long end = System.currentTimeMillis();
System.out.println("m2() 耗時=" + (end - start));
}
//反射調用優化 + 關閉訪問檢查
public static void m3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class cls = Class.forName("com.hspedu.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
hi.setAccessible(true);//在反射調用方法時,取消訪問檢查
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
hi.invoke(o);//反射調用方法
}
long end = System.currentTimeMillis();
System.out.println("m3() 耗時=" + (end - start));
}
}
Class類
基本介紹:
-
Class也是類,因此也繼承Object類[類圖]
-
Class類對象不是new出來的,而是系統創建的[演示]
-
對於某個類的Class類對象,在記憶體中只有一份,因為類只載入一次[演示]
-
每個類的實例都會記得自己是由哪個Class 實例所生成
-
通過Class對象可以完整地得到一個類的完整結構,通過一系列API
-
Class對象是存放在堆的
-
類的位元組碼二進位數據,是放在方法區的,有的地方稱為類的元數據(包括方法代碼,變數名,方法名,訪問許可權等等)https://www.zhihu.com/question/38496907【示意圖】
import com.hspedu.Cat;
import java.util.ArrayList;
/**
* 對Class類特點的梳理
*/
public class Class01 {
public static void main(String[] args) throws ClassNotFoundException {
//看看Class類圖
//1. Class也是類,因此也繼承Object類
//Class
//2. Class類對象不是new出來的,而是系統創建的
//(1) 傳統new對象
/* ClassLoader類
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
*/
//Cat cat = new Cat();
//(2) 反射方式, 剛纔老師沒有debug到 ClassLoader類的 loadClass, 原因是,我沒有註銷Cat cat = new Cat();
/*
ClassLoader類, 仍然是通過 ClassLoader類載入Cat類的 Class對象
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
*/
Class cls1 = Class.forName("com.hspedu.Cat");
//3. 對於某個類的Class類對象,在記憶體中只有一份,因為類只載入一次
Class cls2 = Class.forName("com.hspedu.Cat");
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
Class cls3 = Class.forName("com.hspedu.Dog");
System.out.println(cls3.hashCode());
}
}
Class類的常用方法
方法名 | 功能說明 |
---|---|
static Class forName(String name) | 返回指定類名name的Class對象 |
Object newlnstance() | 調用預設構造函數,返回該Class對象的一個實例 |
getName() | 返回此Class對象所表示的實體(類、介面、數組類、基本類型等)名稱 |
Class[] getInterfaces() | 獲取當前Class對象的介面 |
ClassLoader getClassLoader() | 返回該類的類載入器 |
Class getSuperclass() | 返回表示此Class所表示的實體的超類的Class |
Constructor[] getConstructors() | 返回一個包含某些Constructor對象的數組 |
Field[] getDeclaredFields() | 返回Field對象的一個數組 |
Method getMethod(String name Class ... paramType) | 返回一個Method對象,此對象的形參類型為paramType |
應用實例:
import java.lang.reflect.Field;
/**
* 演示Class類的常用方法
*/
public class Class02 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
String classAllPath = "com.hspedu.Car";
//1 . 獲取到Car類 對應的 Class對象
//<?> 表示不確定的Java類型
Class<?> cls = Class.forName(classAllPath);
//2. 輸出cls
System.out.println(cls); //顯示cls對象, 是哪個類的Class對象 com.hspedu.Car
System.out.println(cls.getClass());//輸出cls運行類型 java.lang.Class
//3. 得到包名
System.out.println(cls.getPackage().getName());//包名
//4. 得到全類名
System.out.println(cls.getName());
//5. 通過cls創建對象實例
Car car = (Car) cls.newInstance();
System.out.println(car);//car.toString()
//6. 通過反射獲取屬性 brand
Field brand = cls.getField("brand");
System.out.println(brand.get(car));//寶馬
//7. 通過反射給屬性賦值
brand.set(car, "賓士");
System.out.println(brand.get(car));//賓士
//8 我希望大家可以得到所有的屬性(欄位)
System.out.println("=======所有的欄位屬性====");
Field[] fields = cls.getFields();
for (Field f : fields) {
System.out.println(f.getName());//名稱
}
}
}
class Car {
public String brand = "寶馬";//品牌
public int price = 500000;
public String color = "白色";
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
", color='" + color + '\'' +
'}';
}
}
獲取Class類對象
-
前提:已知一個類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName()獲取,可能拋出ClassNotFoundException,實例:Class cls1 =Class.forName( "java.lang.Cat" );
應用場景:多用於配置文件,讀取類全路徑,載入類.
-
前提:若已知具體的類,通過類的class 獲取,該方式最為安全可靠,程式性能最高實例:Class cls2 = Cat.class;
應用場景:多用於參數傳遞,比如通過反射得到對應構造器對象.
-
前提:已知某個類的實例,調用該實例的getClass()方法獲取Class對象,實例:Class clazz=對象.getClass0;
應用場景:通過創建好的對象,獲取Class對象.
-
其他方式
ClassLoader cl =對象.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“類的全類名”);
-
基本數據(int, char,boolean float,double,byte,long,short)按如下方式得到Class類對象
Class cls =基本數據類型.class
-
基本數據類型對應的包裝類,可以通過.TYPE得到Class類對象
Class cls =包裝類.TYPE
/**
* 演示得到Class對象的各種方式(6)
*/
public class GetClass_ {
public static void main(String[] args) throws ClassNotFoundException {
//1. Class.forName
String classAllPath = "com.hspedu.Car"; //通過讀取配置文件獲取
Class<?> cls1 = Class.forName(classAllPath);
System.out.println(cls1);
//2. 類名.class , 應用場景: 用於參數傳遞
Class cls2 = Car.class;
System.out.println(cls2);
//3. 對象.getClass(), 應用場景,有對象實例
Car car = new Car();
Class cls3 = car.getClass();
System.out.println(cls3);
//4. 通過類載入器【4種】來獲取到類的Class對象
//(1)先得到類載入器 car
ClassLoader classLoader = car.getClass().getClassLoader();
//(2)通過類載入器得到Class對象
Class cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);
//cls1 , cls2 , cls3 , cls4 其實是同一個對象
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
System.out.println(cls4.hashCode());
//5. 基本數據(int, char,boolean,float,double,byte,long,short) 按如下方式得到Class類對象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
Class<Boolean> booleanClass = boolean.class;
System.out.println(integerClass);//int
//6. 基本數據類型對應的包裝類,可以通過 .TYPE 得到Class類對象
Class<Integer> type1 = Integer.TYPE;
Class<Character> type2 = Character.TYPE; //其它包裝類BOOLEAN, DOUBLE, LONG,BYTE等待
System.out.println(type1);
System.out.println(integerClass.hashCode());//?
System.out.println(type1.hashCode());//?
}
}
class Car {
public String brand = "寶馬";//品牌
public int price = 500000;
public String color = "白色";
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
", color='" + color + '\'' +
'}';
}
}
哪些類型有Class類對象
如下類型有Class對象
- 外部類,成員內部類,靜態內部類,局部內部類,匿名內部類
- interface:介面
- 數組
- enum:枚舉
- annotation:註解
- 基本數據類型
- void
import java.io.Serializable;
/**
* 演示哪些類型有Class對象
*/
public class AllTypeClass {
public static void main(String[] args) {
Class<String> cls1 = String.class;//外部類
Class<Serializable> cls2 = Serializable.class;//介面
Class<Integer[]> cls3 = Integer[].class;//數組
Class<float[][]> cls4 = float[][].class;//二維數組
Class<Deprecated> cls5 = Deprecated.class;//註解
//枚舉
Class<Thread.State> cls6 = Thread.State.class;
Class<Long> cls7 = long.class;//基本數據類型
Class<Void> cls8 = void.class;//void數據類型
Class<Class> cls9 = Class.class;//
System.out.println(cls1);
System.out.println(cls2);
System.out.println(cls3);
System.out.println(cls4);
System.out.println(cls5);
System.out.println(cls6);
System.out.println(cls7);
System.out.println(cls8);
System.out.println(cls9);
}
}
類載入
基本說明
反射機制是java實現動態語言的關鍵,也就是通過反射實現類動態載入。
-
靜態載入:編譯時載入相關的類,如果沒有則報錯,依賴性太強
例如:Dog dog=new Dog();
-
動態載入:運行時載入需要的類,如果運行時不用該類,即使不存在該類,也不報錯,降低了依賴性
例如:cLass cls = cLass.forName( "Person");
類載入時機
- 當創建對象時(new) //靜態載入
- 當子類被載入時,父類也載入 //靜態載入
- 調用類中的靜態成員時 //靜態載入
- 通過反射 //動態載入
類載入過程圖
類載入各階段完成任務
載入階段
JVM在該階段的主要目的是將位元組碼從不同的數據源(可能是class文件、也可能是jar包,甚至網路)轉化為二進位位元組流載入到記憶體中,並生成一個代表該類的java.lang.Class對象
連接階段-驗證
- 目的是為了確保Class文件的位元組流中包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全。
- 包括:文件格式驗證(是否以魔數oxcafebabe開頭)、元數據驗證、位元組碼驗證和符號引用驗證[舉例說明]
- 可以考慮使用-Xverify:none 參數來關閉大部分的類驗證措施,縮短虛擬機類載入的時間。
連接階段-準備
-
JVM 會在該階段對靜態變數,分配記憶體並預設初始化(對應數據類型的預設初始值,如0、0L、null、false 等)。這些變數所使用的記憶體都將在方法區中進行分配
/** * 我們說明一個類載入的鏈接階段-準備 */ public class ClassLoad02 { public static void main(String[] args) { } } class A { //屬性-成員變數-欄位 //老韓分析類載入的鏈接階段-準備 屬性是如何處理 //1. n1 是實例屬性, 不是靜態變數,因此在準備階段,是不會分配記憶體 //2. n2 是靜態變數,分配記憶體 n2 是預設初始化 0 ,而不是20 //3. n3 是static final 是常量, 他和靜態變數不一樣, 因為一旦賦值就不變 n3 = 30 public int n1 = 10; public static int n2 = 20; public static final int n3 = 30; }
連接階段-解析
- 虛擬機將常量池內的符號引用替換為直接引用的過程。(可理解為將邏輯上的聯繫直接替換為對方的準確地址)
初始化階段
- 到初始化階段,才真正開始執行類中定義的Java程式代碼,此階段是執行<clinit>()方法的過程。
- <clinit>(方法是由編譯器按語句在源文件中出現的順序,依次自動收集類中的靜態變數的賦值動作和靜態代碼塊中的語句,併進行合併。
- 虛擬機會保證一個類的<clinit>()方法在多線程環境中被正確地加鎖、同步,如果多個線程同時去初始化一個類,那麼只會有一個線程去執行這個類的<clinit>()方法,其他線程都需要阻塞等待,直到活動線程執行<clinit>()方法完畢[debug源碼]
/**
* 演示類載入-初始化階段
*/
public class ClassLoad03 {
public static void main(String[] args) throws ClassNotFoundException {
//老韓分析
//1. 載入B類,並生成 B的class對象
//2. 鏈接 num = 0
//3. 初始化階段
// 依次自動收集類中的所有靜態變數的賦值動作和靜態代碼塊中的語句,併合並
/*
clinit() {
System.out.println("B 靜態代碼塊被執行");
//num = 300;
num = 100;
}
合併: num = 100
*/
//new B();//類載入
//System.out.println(B.num);//100, 如果直接使用類的靜態屬性,也會導致類的載入
//看看載入類的時候,是有同步機制控制
/*
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
//正因為有這個機制,才能保證某個類在記憶體中, 只有一份Class對象
synchronized (getClassLoadingLock(name)) {
//....
}
}
*/
B b = new B();
}
}
class B {
static {
System.out.println("B 靜態代碼塊被執行");
num = 300;
}
static int num = 100;
public B() {//構造器
System.out.println("B() 構造器被執行");
}
}
通過反射獲取類的結構信息
第一組: java.lang.Class類
-
getName:獲取全類名
-
getSimpleName:獲取簡單類名
-
getFields:獲取所有public修飾的屬性,包含本類以及父類的
-
getDeclaredFields:獲取本類中所有屬性
-
getMethods:獲取所有public修飾的方法,包含本類以及父類的
-
getDeclaredMethods:獲取本類中所有方法
-
getConstructors:獲取所有public修飾的構造器,包含本類
-
getDeclaredConstructors:獲取本類中所有構造器
-
getPackage:以Package形式返回包信息
-
getSuperClass:以Class形式返回父類信息
-
getlnterfaces:以Class形式返回介面信息
-
getAnnotations:以Annotation[]形式返回註解信息
第二組: java.lang.reflect.Field類
- getModifiers:以int形式返回修飾符
[說明:預設修飾符是0,public是1,private是2,protected是4,static是8, final是16], public(1) + static (8) = 9 - getType:以Class形式返回類型
- getName:返回屬性名
第三組: java.lang.reflect.Method類
- getModifiers:以int形式返回修飾符
[說明:預設修飾符是0,public是1,private是2,protected是4static是8,final是16] - getReturnType:以Class形式獲取返回類型
- getName:返回方法名
- getParameterTypes:以Class[]返回參數類型數組
第四組: java.lang.reflect.Constructor類
-
getModifiers:以int形式返回修飾符
-
getName:返回構造器名(全類名)
-
getParameterTypes:以Class[]返回參數類型數組
import org.junit.jupiter.api.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 演示如何通過反射獲取類的結構信息
*/
public class ReflectionUtils {
public static void main(String[] args) {
}
@Test
public void api_02() throws ClassNotFoundException, NoSuchMethodException {
//得到Class對象
Class<?> personCls = Class.forName("com.hspedu.reflection.Person");
//getDeclaredFields:獲取本類中所有屬性
//規定 說明: 預設修飾符 是0 , public 是1 ,private 是 2 ,protected 是 4 , static 是 8 ,final 是 16
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本類中所有屬性=" + declaredField.getName()
+ " 該屬性的修飾符值=" + declaredField.getModifiers()
+ " 該屬性的類型=" + declaredField.getType());
}
//getDeclaredMethods:獲取本類中所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本類中所有方法=" + declaredMethod.getName()
+ " 該方法的訪問修飾符值=" + declaredMethod.getModifiers()
+ " 該方法返回類型" + declaredMethod.getReturnType());
//輸出當前這個方法的形參數組情況
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("該方法的形參類型=" + parameterType);
}
}
//getDeclaredConstructors:獲取本類中所有構造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("====================");
System.out.println("本類中所有構造器=" + declaredConstructor.getName());//這裡老師只是輸出名
Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("該構造器的形參類型=" + parameterType);
}
}
}
//第一組方法API
@Test
public void api_01() throws ClassNotFoundException, NoSuchMethodException {
//得到Class對象
Class<?> personCls = Class.forName("com.hspedu.reflection.Person");
//getName:獲取全類名
System.out.println(personCls.getName());//com.hspedu.reflection.Person
//getSimpleName:獲取簡單類名
System.out.println(personCls.getSimpleName());//Person
//getFields:獲取所有public修飾的屬性,包含本類以及父類的
Field[] fields = personCls.getFields();
for (Field field : fields) {//增強for
System.out.println("本類以及父類的屬性=" + field.getName());
}
//getDeclaredFields:獲取本類中所有屬性
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本類中所有屬性=" + declaredField.getName());
}
//getMethods:獲取所有public修飾的方法,包含本類以及父類的
Method[] methods = personCls.getMethods();
for (Method method : methods) {
System.out.println("本類以及父類的方法=" + method.getName());
}
//getDeclaredMethods:獲取本類中所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本類中所有方法=" + declaredMethod.getName());
}
//getConstructors: 獲取所有public修飾的構造器,包含本類
Constructor<?>[] constructors = personCls.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("本類的構造器=" + constructor.getName());
}
//getDeclaredConstructors:獲取本類中所有構造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("本類中所有構造器=" + declaredConstructor.getName());//這裡老師只是輸出名
}
//getPackage:以Package形式返回 包信息
System.out.println(personCls.getPackage());//com.hspedu.reflection
//getSuperClass:以Class形式返回父類信息
Class<?> superclass = personCls.getSuperclass();
System.out.println("父類的class對象=" + superclass);//
//getInterfaces:以Class[]形式返回介面信息
Class<?>[] interfaces = personCls.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println("介面信息=" + anInterface);
}
//getAnnotations:以Annotation[] 形式返回註解信息
Annotation[] annotations = personCls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("註解信息=" + annotation);//註解
}
}
}
class A {
public String hobby;
public void hi() {
}
public A() {
}
public A(String name) {
}
}
interface IA {
}
interface IB {
}
@Deprecated
class Person extends A implements IA, IB {
//屬性
public String name;
protected static int age; // 4 + 8 = 12
String job;
private double sal;
//構造器
public Person() {
}
public Person(String name) {
}
//私有的
private Person(String name, int age) {
}
//方法
public void m1(String name, int age, double sal) {
}
protected String m2() {
return null;
}
void m3() {
}
private void m4() {
}
}
通過反射創建對象
-
方式一:調用類中的public修飾的無參構造器
-
方式二:調用類中的指定構造器
-
Class類相關方法
newlnstance :調用類中的無參構造器,獲取對應類的對象
getConstructor(Class...clazz):根據參數列表,獲取對應的public構造器對象getDecalaredConstructor(Class..clazz):根據參數列表,獲取對應的所有構造器對象 -
Constructor類相關方法
setAccessible:暴破
newlnstance(Object...obj):調用構造器
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 演示通過反射機制創建實例
*/
public class ReflecCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1. 先獲取到User類的Class對象
Class<?> userClass = Class.forName("com.hspedu.reflection.User");
//2. 通過public的無參構造器創建實例
Object o = userClass.newInstance();
System.out.println(o);
//3. 通過public的有參構造器創建實例
/*
constructor 對象就是
public User(String name) {//public的有參構造器
this.name = name;
}
*/
//3.1 先得到對應構造器
Constructor<?> constructor = userClass.getConstructor(String.class);
//3.2 創建實例,並傳入實參
Object hsp = constructor.newInstance("hsp");
System.out.println("hsp=" + hsp);
//4. 通過非public的有參構造器創建實例
//4.1 得到private的構造器對象
Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
//4.2 創建實例
//暴破【暴力破解】 , 使用反射可以訪問private構造器/方法/屬性, 反射面前,都是紙老虎
constructor1.setAccessible(true);
Object user2 = constructor1.newInstance(100, "張三豐");
System.out.println("user2=" + user2);
}
}
class User { //User類
private int age = 10;
private String name = "韓順平教育";
public User() {//無參 public
}
public User(String name) {//public的有參構造器
this.name = name;
}
private User(int age, String name) {//private 有參構造器
this.age = age;
this.name = name;
}
public String toString() {
return "User [age=" + age + ", name=" + name + "]";
}
}
通過反射訪問類中的成員
訪問屬性
-
根據屬性名獲取Field對象
Field f = clazz對象.getDeclaredField(屬性名); -
暴破:f.setAccessible(true);//f 是Field
-
訪問
f.set(o,值);//設置對象o中f的值//o表示對象
System.out.println(f.get(o));//輸出對象o中f的值//o表示對象
-
註意:如果是靜態屬性,則set和get中的參數o,可以寫成null
案例演示:
import java.lang.reflect.Field;
/**
* 演示反射操作屬性
*/
public class ReflecAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
//1. 得到Student類對應的 Class對象
Class<?> stuClass = Class.forName("com.hspedu.reflection.Student");
//2. 創建對象
Object o = stuClass.newInstance();//o 的運行類型就是Student
System.out.println(o.getClass());//Student
//3. 使用反射得到age 屬性對象
Field age = stuClass.getField("age");
age.set(o, 88);//通過反射來操作屬性
System.out.println(o);//
System.out.println(age.get(o));//返回age屬性的值
//4. 使用反射操作name 屬性
Field name = stuClass.getDeclaredField("name");
//對name 進行暴破, 可以操作private 屬性
name.setAccessible(true);
//name.set(o, "老韓");
name.set(null, "老韓~");//因為name是static屬性,因此 o 也可以寫出null
System.out.println(o);
System.out.println(name.get(o)); //獲取屬性值
System.out.println(name.get(null));//獲取屬性值, 要求name是static
}
}
class Student {//類
public int age;
private static String name;
public Student() {//構造器
}
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
}
訪問方法
- 根據方法名和參數列表獲取Method方法對象:Method m =clazz.getDeclaredMethod(方法名,XX.class);
- 獲取對象:Object o=clazz.newlnstance();
- 暴破:m.setAccessible(true);
- 訪問:Object returnValue = m.invoke(o,實參列表);
- 註意:如果是靜態方法,則invoke的參數o,可以寫成null!
應用案例:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 演示通過反射調用方法
*/
public class ReflecAccessMethod {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
//1. 得到Boss類對應的Class對象
Class<?> bossCls = Class.forName("com.hspedu.reflection.Boss");
//2. 創建對象
Object o = bossCls.newInstance();
//3. 調用public的hi方法
//Method hi = bossCls.getMethod("hi", String.class);//OK
//3.1 得到hi方法對象
Method hi = bossCls.getDeclaredMethod("hi", String.class);//OK
//3.2 調用
hi.invoke(o, "韓順平教育~");
//4. 調用private static 方法
//4.1 得到 say 方法對象
Method say = bossCls.getDeclaredMethod("say", int.class, String.class, char.class);
//4.2 因為say方法是private, 所以需要暴破,原理和前面講的構造器和屬性一樣
say.setAccessible(true);
System.out.println(say.invoke(o, 100, "張三", '男'));
//4.3 因為say方法是static的,還可以這樣調用 ,可以傳入null
System.out.println(say.invoke(null, 200, "李四", '女'));
//5. 在反射中,如果方法有返回值,統一返回Object , 但是他運行類型和方法定義的返回類型一致
Object reVal = say.invoke(null, 300, "王五", '男');
System.out.println("reVal 的運行類型=" + reVal.getClass());//String
//在演示一個返回的案例
Method m1 = bossCls.getDeclaredMethod("m1");
Object reVal2 = m1.invoke(o);
System.out.println("reVal2的運行類型=" + reVal2.getClass());//Monster
}
}
class Monster {}
class Boss {//類
public int age;
private static String name;
public Boss() {//構造器
}
public Monster m1() {
return new Monster();
}
private static String say(int n, String s, char c) {//靜態方法
return n + " " + s + " " + c;
}
public void hi(String s) {//普通public方法
System.out.println("hi " + s);
}
}