反射
java.lang.Class類
Java反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為Java語言的反射機制。
要想解剖一個類,必須先要獲取到該類的Class物件。而剖析一個類或用反射解決具體的問題就是使用相關API(1)java.lang.Class(2)java.lang.reflect.*。所以,Class物件是反射的根源。
獲取Class物件的四種方式
(1)型別名.class
要求編譯期間已知型別
(2)物件.getClass()
獲取物件的執行時型別
(3)Class.forName(型別全名稱)
可以獲取編譯期間未知的型別
(4)ClassLoader的類載入器物件.loadClass(型別全名稱)
可以用系統類載入物件或自定義載入器物件載入指定路徑下的型別
public class TestClass {
@Test
public void test05() throws ClassNotFoundException{
Class c = TestClass.class;
ClassLoader loader = c.getClassLoader();
Class c2 = loader.loadClass("com.atguigu.test05.Employee");
Class c3 = Employee.class;
System.out.println(c2 == c3);
}
@Test
public void test03() throws ClassNotFoundException{
Class c2 = String.class;
Class c1 = "".getClass();
Class c3 = Class.forName("java.lang.String");
System.out.println(c1 == c2);
System.out.println(c1 == c3);
}
}
反射的應用
獲取型別的詳細資訊
可以獲取:包、修飾符、型別名、父類(包括泛型父類)、父介面(包括泛型父介面)、成員(屬性、構造器、方法)、註解(類上的、方法上的、屬性上的)
獲取:包、修飾符、型別名、父類、實現的介面名
public class TestClassInfo {
public static void main(String[] args) throws NoSuchFieldException, SecurityException {
//1、先得到某個型別的Class物件
Class clazz = String.class;
//比喻clazz好比是鏡子中的影子
//2、獲取類資訊
//(1)獲取包物件,即所有java的包,都是Package的物件
Package pkg = clazz.getPackage();
System.out.println("包名:" + pkg.getName());
//(2)獲取修飾符
//其實修飾符是Modifier,裡面有很多常量值
/*
* 0x是十六進位制
* PUBLIC = 0x00000001; 1 1
* PRIVATE = 0x00000002; 2 10
* PROTECTED = 0x00000004; 4 100
* STATIC = 0x00000008; 8 1000
* FINAL = 0x00000010; 16 10000
* ...
*
* 設計的理念,就是用二進位制的某一位是1,來代表一種修飾符,整個二進位制中只有一位是1,其餘都是0
*
* mod = 17 0x00000011
* if ((mod & PUBLIC) != 0) 說明修飾符中有public
* if ((mod & FINAL) != 0) 說明修飾符中有final
*/
int mod = clazz.getModifiers();
System.out.println(Modifier.toString(mod));
//(3)型別名
String name = clazz.getName();
System.out.println(name);
//(4)父類,父類也有父類對應的Class物件
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
//(5)父介面們
Class[] interfaces = clazz.getInterfaces();
for (Class class1 : interfaces) {
System.out.println(class1);
}
成員(屬性、構造器、方法)
//(1)類的屬性, 你宣告的一個屬性,它是Field的物件
/* Field clazz.getField(name) 根據屬性名獲取一個屬性物件,但是隻能得到公共的
Field[] clazz.getFields(); 獲取所有公共的屬性
Field clazz.getDeclaredField(name) 根據屬性名獲取一個屬性物件,可以獲取已宣告的
Field[] clazz.getDeclaredFields() 獲取所有已宣告的屬性
*/
Field valueField = clazz.getDeclaredField("value");
// System.out.println("valueField = " +valueField);
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
//修飾符、資料型別、屬性名
int modifiers = field.getModifiers();
System.out.println("屬性的修飾符:" + Modifier.toString(modifiers));
String name2 = field.getName();
System.out.println("屬性名:" + name2);
Class<?> type = field.getType();
System.out.println("屬性的資料型別:" + type);
}
//(2)構造器們
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
//修飾符、構造器名稱、構造器形參列表 、丟擲異常列表
int modifiers = constructor.getModifiers();
System.out.println("構造器的修飾符:" + Modifier.toString(modifiers));
String name2 = constructor.getName();
System.out.println("構造器名:" + name2);
//形參列表
System.out.println("形參列表:");
Class[] parameterTypes = constructor.getParameterTypes();
for (Class parameterType : parameterTypes) {
System.out.println(parameterType);
}
//異常列表
System.out.println("異常列表:");
Class<?>[] exceptionTypes = constructor.getExceptionTypes();
for (Class<?> exceptionType : exceptionTypes) {
System.out.println(exceptionType);
}
}
//(3)方法們
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
//修飾符、返回值型別、方法名、形參列表 、異常列表
int modifiers = method.getModifiers();
System.out.println("方法的修飾符:" + Modifier.toString(modifiers));
Class<?> returnType = method.getReturnType();
System.out.println("返回值型別:" + returnType);
String name2 = method.getName();
System.out.println("方法名:" + name2);
//形參列表
System.out.println("形參列表:");
Class[] parameterTypes = method.getParameterTypes();
for (Class parameterType : parameterTypes) {
System.out.println(parameterType);
}
//異常列表
System.out.println("異常列表:");
Class<?>[] exceptionTypes = method.getExceptionTypes();
for (Class<?> exceptionType : exceptionTypes) {
System.out.println(exceptionType);
}
}
建立任意引用型別的物件並賦值(調構造器賦值)
兩種方式:
1、直接透過Class物件來例項化(要求必須有無參構造)
2、透過獲取構造器物件來進行例項化
方式一的步驟:
(1)獲取該型別的Class物件(2)建立物件
@Test
public void test2()throws Exception{
Class<?> clazz = Class.forName("com.atguigu.test.Student");
//Caused by: java.lang.NoSuchMethodException: com.atguigu.test.Student.<init>()
//即說明Student沒有無參構造,就沒有無參例項初始化方法<init>
Object stu = clazz.newInstance();
System.out.println(stu);
}
@Test
public void test1() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
// AtGuigu obj = new AtGuigu();//編譯期間無法建立
Class<?> clazz = Class.forName("com.atguigu.test.AtGuigu");
//clazz代表com.atguigu.test.AtGuigu型別
//clazz.newInstance()建立的就是AtGuigu的物件
Object obj = clazz.newInstance();
System.out.println(obj);
}
方式二的步驟:
(1)獲取該型別的Class物件(2)獲取構造器物件(3)建立物件
如果構造器的許可權修飾符修飾的範圍不可見,也可以呼叫setAccessible(true)
示例程式碼:
public class TestNewInstance {
@Test
public void test3()throws Exception{
//(1)獲取Class物件
Class<?> clazz = Class.forName("com.atguigu.test.Student");
/*
* 獲取Student型別中的有參構造
* 如果構造器有多個,我們通常是根據形參【型別】列表來獲取指定的一個構造器的
* 例如:public Student(int id, String name)
*/
//(2)獲取構造器物件
Constructor<?> constructor = clazz.getDeclaredConstructor(int.class,String.class);
//(3)建立例項物件
// T newInstance(Object... initargs) 這個Object...是在建立物件時,給有參構造的實參列表
Object obj = constructor.newInstance(2,"張三");
System.out.println(obj);
}
//訪問私有構造器
@Test
public void test05() throws Exception{
//1.獲取Class 物件
Class<Person> personClass = Person.class;
//2.獲取指定的構造器
Constructor<Person> c = personClass.getDeclaredConstructor(String.class, int.class, double.class);
//3.禁止java語言的許可權訪問檢測
c.setAccessible(true);
//4.給成員變數賦值
Person person = c.newInstance("王安石", 30, 9898.6);
//5.展示結果
System.out.println("person = " + person);
}
}
操作任意型別的屬性
(1)獲取該型別的Class物件
Class clazz = Class.forName("com.atguigu.bean.User");
(2)獲取屬性物件
Field field = clazz.getDeclaredField("username");
(3)設定屬性可訪問
field.setAccessible(true);
(4)建立例項物件:如果操作的是非靜態屬性,需要建立例項物件
Object obj = clazz.newInstance();
(4)設定屬性值
field.set(obj,"chai");
(5)獲取屬性值
Object value = field.get(obj);
如果操作靜態變數,那麼例項物件可以省略,用null表示
示例程式碼:
//操作靜態變數
@Test
public void test01() throws NoSuchFieldException, IllegalAccessException {
//1.獲取Class物件
Class<Monkey> monkeyClass = Monkey.class;
//2.獲取指定的屬性
Field message = monkeyClass.getDeclaredField("message");
//3.設定私有的屬性可以操作
message.setAccessible(true);
//4.給屬性賦值
message.set(null,"HelloWorld");
//5.獲取屬性值
Object value = message.get(null);
System.out.println("value = " + value);
}
//操作普通變數
@Test
public void test02() throws Exception {
//1.獲取Class物件
Class<Monkey> monkeyClass = Monkey.class;
//2.獲取指定的屬性
Field gender = monkeyClass.getDeclaredField("gender");
//todo 設定私有的屬性可以訪問
gender.setAccessible(true);
//3.建立物件
Monkey monkey = monkeyClass.newInstance();
System.out.println("monkey = " + monkey);
//4.給屬性賦值
//todo 給monkey 物件的gender 屬性賦值 為 男
gender.set(monkey,'男');
//5.展示物件
System.out.println("monkey = " + monkey);
}
呼叫任意型別的方法
(1)獲取該型別的Class物件
Class clazz = Class.forName("com.atguigu.service.UserService");
(2)獲取方法物件
Method method = clazz.getDeclaredMethod("login",String.class,String.class);
(3)建立例項物件
Object obj = clazz.newInstance();
(4)呼叫方法
Object result = method.invoke(obj,"chai","123);
如果方法的許可權修飾符修飾的範圍不可見,也可以呼叫setAccessible(true)
如果方法是靜態方法,例項物件也可以省略,用null代替
示例程式碼:
//操作靜態方法
@Test
public void test04() throws Exception{
//todo 私有的靜態方法
//1.獲取Class物件
Class<Animal> animalClass = Animal.class;
//2.獲取指定的方法
Method isPrimeNum = animalClass.getDeclaredMethod("isPrimeNum", int.class);
//3.設定私有方法可以使用
isPrimeNum.setAccessible(true);
//4.執行方法 獲取結果
Object invoke = isPrimeNum.invoke(null, 20);
//5.輸出結果
System.out.println("invoke = " + invoke);
}
//操作普通方法
@Test
public void test03() throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
//todo 公共的例項方法
//1.獲取Class物件
Class<Animal> aClass = Animal.class;
//2.獲取指定的方法
Method sum = aClass.getMethod("sum", int.class, int.class);
//3.建立物件
Animal animal = aClass.newInstance();
//4.呼叫方法
sum.invoke(animal,89,20);
}