Java-反射1
阿新 • • 發佈:2022-04-16
1.反射
概述:
- 通過一個Class檔案物件去使用或者修改檔案物件中的成員變數,構造方法,成員方法(無論是否私有,都能獲取並使用)
之前學習:
- 我們在使用成員之前,得先有一個java檔案,然後在例項化的時候,new一下就完事了。
Person p = new Person();
p.eat("漢堡");
現在的船新概念:
- 總歸來說,無論是呼叫什麼成員,都是有個物件,歸根結底,物件是根據類來的,實際上底層是依賴一個class檔案
對於java程式來說,依賴的是這個class檔案對應的Class檔案物件。
class類:
- 成員變數:Field
- 構造方法:Constructor
- 成員方法:Method
如何獲取一個類的對應的Class檔案物件呢?
- 方式1:通過Object類中的getClass()方法,返回此Object類的執行時的類
- 方式2:在不new的前提下,獲取該類的class檔案物件,java中每一個類都有一個靜態的屬性class
- 方式3:Class類中有一個靜態的方法,獲取該類的class檔案物件 (最常用的)
獲取物件對應的class檔案物件程式碼
public class ReflexDemo1 { public static void main(String[] args) { //方式一:通過Object類中的getClass()方法,返回此Object類執行時的類 Person person1 = new Person(); Class<? extends Person> c1 = person1.getClass(); Person person2 = new Person(); Class<? extends Person> c2 = person2.getClass(); System.out.println(c1 == c2); //true //方式二:在不 new 的情況下,獲取該類的class檔案物件,java中的每一個類都有一個靜態的屬性class Class<Person> c3 = Person.class; System.out.println(c1 == c3); //true System.out.println(c2 == c3); //true //方式三:Class類中有一個靜態方法,獲取該類的class檔案物件(最常用的) //static Class<?> forName(String className) //返回與給定字串名稱的類或介面相關聯的 類物件。 try { Class<?> c4 = Class.forName("com.bigdat.java.day29.Person"); System.out.println(c4); System.out.println(c1 == c4); //true System.out.println(c1 == c4); //true } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
如何通過反射獲取Class檔案物件並使用其中的構造方法建立物件呢?
- getConstructors() 返回一個包含Constructor物件的陣列,反映由此Constructor物件表示的類的所有公共類函式
- getDeclaredConstructors() 獲取的是該類中的所有構造方法,包括私有,被保護的,預設的,公共的
- getConstructor() 返回一個 Constructor物件,該物件反映 Constructor物件表示的類的指定的公共 類函式。
- getDeclaredConstructor() 獲取單個私有的帶引數的構造方法
注意點:
- newInstance(Object... initargs)
使用此 Constructor物件表示的建構函式,使用指定的初始化引數來建立和初始化建構函式的宣告類的新例項。如果使用帶引數的構造方法用newInstance建立物件,引數必須一樣。形如:Object o = con1.newInstance("張三");- 如果獲取帶引數的構造方法,裡面填的引數是:**型別.class** 。
如:Constructor<?> con2 = c1.getDeclaredConstructor(String.class, int.class);
public class ReflexDemo2 {
public static void main(String[] args) {
//獲取person類的Class檔案物件
Class<?> c1 = null;
try {
c1 = Class.forName("com.bigdatjava.day29.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//獲取Class檔案物件的構造方法
//public Constructor<?>[] getConstructors() throws SecurityException
//返回一個包含Constructor物件的陣列,反應由此Constructor物件表示的類的所有的公共類函式
Constructor<?>[] cons1 = c1.getConstructors();
for (Constructor<?> c : cons1) {
System.out.println(c);
}
System.out.println("===========================================================");
//public Constructor<?>[] getDeclaredConstructors() throws SecurityException
//返回Constructor 物件表名的類宣告的所有Constructor物件的陣列類
//獲取的是該類中的所有構造方法,包括私有,被保護,預設和公共的
Constructor<?>[] cons2 = c1.getDeclaredConstructors();
for (Constructor<?> c : cons2) {
System.out.println(c);
}
System.out.println("=================================================================");
//獲取單個的構造方法
//Constructor<T> getConstructor(類<?>... parameterTypes)
//返回一個 Constructor物件,該物件反映 Constructor物件表示的類的指定的公共 類函式。
Constructor<?> con1 = null;
try {
con1 = c1.getConstructor(String.class);
System.out.println(con1);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//獲取單個私有的帶引數的構造方法
// Constructor<?> con2 = c1.getConstructor(String.class, int.class);
// System.out.println(con2);
Constructor<?> con2 = null;
try {
con2 = c1.getDeclaredConstructor(String.class, int.class);
System.out.println(con2);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
System.out.println("======================================================================");
//使用公共的構造方法建立物件
//使用公共的構造方法建立物件
//T newInstance(Object... initargs)
//使用此 Constructor物件表示的建構函式,使用指定的初始化引數來建立和初始化建構函式的宣告類的新例項。
try {
Object o = con1.newInstance("我真蠢!");
Person p = (Person)o; // 向下轉型
System.out.println(p);
} catch (Exception e) {
e.printStackTrace();
}
try {
con2.setAccessible(true);
Object two = con2.newInstance("我是小垃圾", 19);
// 向下轉型
Person o1 = (Person) two;
System.out.println(o1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
通過反射獲取Class檔案物件中私有的構造方法並建立物件
重點
- 當還是象訪問公共物件去訪問私有變數時會報 IllegalAccessException 非法的訪問異常
解決辦法:暴力訪問
- 通過setAccessible(true)方法暴力訪問。值是true的化,表示反射物件在使用的時候,取消Java語言的訪問檢查
public class ReflexDemo3 {
public static void main(String[] args) {
Class<?> a = null;
try {
a = Class.forName("com.bigdat.java.day29.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//獲取私有的構造方法
Constructor<?> dec1 = null;
try {
dec1 = a.getDeclaredConstructor(String.class, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
// .IllegalAccessException 非法的訪問異常
//構造方法是私有的,可以獲取的到,但是不能這麼使用
//通過構造方法建立物件
//暴力訪問
dec1.setAccessible(true); // 如果這裡是 true 的話,表示反射物件在這裡用的話,取消Java語言的訪問檢查
Object xh = dec1.newInstance("小花", 100);
//向下轉型
Person p = (Person) xh;
System.out.println(p);
p.eat("紅燒肉");
} catch (Exception e) {
e.printStackTrace();
}
}
}
反射獲取類Class檔案物件中的成員變數並使用
- getFields() 返回包含一個數組 Field物件反射由此表示的類或介面的所有可訪問的公共欄位 類物件。獲取當前類所有被public修飾的成員變數
- getDeclaredFields() 返回的陣列 Field物件反映此表示的類或介面宣告的所有欄位 類物件。獲取當前類中所有的成員變數,包括私有的,公共的,被保護的,預設的欄位,不包括繼承的欄位。
- getField() 獲取單個的成員變數
- getDeclaredField() 獲取私有的成員變數並賦值
- 暴力訪問: name.setAccessible(true);
public class ReflexDemo4 {
public static void main(String[] args)throws Exception {
Class<?> c1 = null;
//獲取Class的檔案物件
try {
c1 = forName("com.bigdat.java.day29.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//Field[] getFields()
//返回一個包含一個數組的 Filed 物件反射由此表示的類或介面的所有可訪問的公共欄位 類物件。
//獲取當前類所有被public修飾的成員變數
Field[] fields = c1.getFields();
for (Field field : fields) {
System.out.println(field); //public java.lang.String com.bigdat.java.day29.Person.id
}
System.out.println("==============================================================");
//Field[] getDeclaredFields()
//返回的陣列 Field物件反映此表示的類或介面宣告的所有欄位 類物件。
//獲取當前類中所有的成員變數,包括私有的,公共的,被保護的,預設的欄位,不包括繼承的欄位。
Field[] dec = c1.getDeclaredFields();
for (Field field : dec) {
System.out.println(field);
}
System.out.println("-----------------------------------------------------------------");
//此方法並不能獲得該類繼承的其父類的任何屬性
Class<?> c2 = null;
try {
c2 = forName("com.bigdat.java.day29.B");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Field[] declaredFields = c2.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("========================================================");
//獲取Class檔案的物件
Class<?> c = forName("com.bigdat.java.day29.Person");
//通過 getConstructor 方法來建立一個構造器
Constructor<?> ctr = c.getConstructor();
//通過構造方法來建立一個物件
Object o = ctr.newInstance();
//獲取單個的成員變數
Field id = c1.getField("id");
//void set(Object obj, Object value)
//將指定物件引數上的此 Field物件表示的欄位設定為指定的新值。
//id.set(o,"1001");//給物件o中的成員變數id賦值為"1001"
id.set(o, "1001");
System.out.println(o);
//獲取私有的成員變數並賦值
Field name = c1.getDeclaredField("name");
name.setAccessible(true); // 在使用私有的成員變數時需要進行暴力訪問
name.set(o, "名字");
System.out.println(o); // IllegalAccessException 不暴力訪問的話會報異常
System.out.println("================================================================");
Field age = c1.getDeclaredField("age");
age.set(o, 101);
System.out.println(o);
Field address = c1.getDeclaredField("address");
address.set(o,"中國-雲南-保山");
System.out.println(o);
}
}
class A{
private String aaa;
public int bbb;
}
class B extends A{
}
通過反射獲取Class檔案物件中的成員方法並使用
-== Method[] getMethods()== 返回包含一個數組 方法物件反射由此表示的類或介面的所有公共方法 類物件,包括那些由類或介面和那些從超類和超介面繼承的宣告。
- getDeclaredMethods() 獲取本類中的所有方法,包括私有的,被保護的,預設的,公共的。不獲取繼承關係父類中的方法
public class ReflexDemo5 {
public static void main(String[] args) throws Exception{
//獲取person類的class檔案物件
Class<?> c = Class.forName("com.bigdat.java.day29.Person");
//獲取方法
//Method[] getMethods()
//返回包含一個數組 方法物件反射由此表示的類或介面的所有公共方法 類物件,包括那些由類或介面和那些從超類和超介面繼承的宣告。
//獲取本類以及父類中所有的公共方法(被public所修飾的方法)
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("===========================================================");
//獲取本類中的所有方法,包括私有,被保護,預設,公共的方法,但是不獲取繼承關係的父類方法
Method[] declaredMethods = c.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("======================================================================");
//建立物件的構造器
Constructor<?> con = c.getConstructor();
Object o = con.newInstance();
//獲取單個的方法並使用
//Object invoke(Object obj, Object... args)
//在具有指定引數的 方法物件上呼叫此 方法物件表示的底層方法。
Method fun1 = c.getMethod("fun1" );
fun1.invoke( o );
System.out.println("================================================================================");
//獲取私有的成員方法使用
Method fun2 = c.getDeclaredMethod("fun2",String.class);
`fun2.setAccessible(true);`
fun2.invoke( o ,"你好!");
System.out.println("==================================================================================");
Method fun3 = c.getDeclaredMethod("fun3");
fun3.invoke( o );
Method fun4 = c.getDeclaredMethod("fun4" );
fun4.invoke( o );
}
}