一、反射概述
1. java.lang.Class:是反射的源頭
我們建立一個類,通過編譯,生成對應的.calss檔案,之後使用java.exe載入(jvm的類載入器)此.class檔案,此.class檔案載入到記憶體以後,就是一個執行時類,存在快取區,那麼這個執行時類的本身就是一個class的例項
- 每一個執行時類只載入一次
- 有了Class例項以後,我們才可以進行如下的操作:
- 建立對應的執行時類的物件(重點)
- 可以獲取物件的執行時類的完整結構(屬性、方法、構造器、內部類、、、)(理解)
- 呼叫對應的執行時類的指定的結構(屬性、方法)(重點)
在反射以前,如何建立一個類的物件,並呼叫其中的方法屬性
public void test1() {
Person p = new Person();
p.setAge(10);
p.setName("AA");
p.show();
System.out.println(p);
}
有了反射,可以通過反射建立一個類的物件,並呼叫其中的方法,下面詳細說
public void test2() throws Exception {
Class clazz = Person.class;
//1.建立clazz對應的執行時類Person類的物件
Person p = (Person)clazz.newInstance();
System.out.println(p);
//2.通過反射呼叫執行時類的指定屬性,public name的修改方式
Field f1 = clazz.getField("name");
f1.set(p, "LiuDaHua");
System.out.println(p);
//private age的方式
Field f2 = clazz.getDeclaredField("age");
f2.setAccessible(true);
f2.set(p, 20);
System.out.println(p);
//3.通過反射呼叫執行時類的指定方法 public修飾的
Method m1 = clazz.getMethod("show");
// 執行
m1.invoke(p);
// 帶引數的方法
Method m2 = clazz.getMethod("display", String.class);
m2.invoke(p, "cn");
}
二、如何獲取Class的例項
1.呼叫執行時類本身的.class屬性
Class clazz = Person.class;
System.out.println(clazz.getName());
Class clazz1 = String.class;
System.out.println(clazz1.getName());
2.通過執行時類的物件獲取
Person p = new Person();
Class clazz2 = p.getClass();
System.out.println(clazz.getName());
3.通過Class的靜態方法獲取,通過此方式,體會反射的動態性
String className = "com.atguigu.java.Person";
Class clazz4 = Class.forName(className);
System.out.println(clazz4);
4.通過類的載入器
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz5 = classLoader.loadClass(className);
System.out.println(clazz5.getName());
整個程式碼
public void test4() throws Exception {
//1.呼叫執行時類本身的.class屬性
Class clazz = Person.class;
System.out.println(clazz.getName());
Class clazz1 = String.class;
System.out.println(clazz1.getName());
//2.通過執行時類的物件獲取
Person p = new Person();
Class clazz2 = p.getClass();
System.out.println(clazz.getName());
//3.通過Class的靜態方法獲取,通過此方式,體會反射的動態性
String className = "com.atguigu.java.Person";
Class clazz4 = Class.forName(className);
System.out.println(clazz4);
//4.通過類的載入器
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz5 = classLoader.loadClass(className);
System.out.println(clazz5.getName());
}
三、建立執行時類物件
1. 獲取Class的例項
通常直接使用類名.class , 例如Person.class
當然了,上面的幾種方法都是可以的
String className = "com.atguigu.java.Person";
Class clazz = Class.forName(className);
2.建立執行時類物件
建立對應的執行時類的物件,使用的是newInstance(),實際上就是運用了執行時類的空引數的構造器
要想能夠建立成功
- 要求對應的執行時類要有空引數的構造器
- 構造器的許可權修飾符的許可權要足夠
Object obj = clazz.newInstance();//呼叫的是空參構造器
Person p = (Person)obj;
System.out.println(p);
全部程式碼
public void test1() throws Exception {
// 獲取Class例項
Class clazz = Person.class;
//建立對應的執行時類的物件,使用的是newInstance(),實際上就是運用了執行時類的空引數的構造器
//要想能夠建立成功,①要求對應的執行時類要有空引數的構造器,②構造器的許可權修飾符的許可權要足夠
Object obj = clazz.newInstance();//呼叫的是空參構造器
Person p = (Person)obj;
System.out.println(p);
}
四、通過反射獲取類的完整結構
1.獲取執行時類的屬性
1.getFields() 返回 :表示公共欄位的 Field 物件的陣列,只能獲取執行時類中以及父類中宣告的為public的屬性
Field[] fiels = clazz.getFields();
for(int i=0;i<fiels.length;i++) {
System.out.println(fiels[i]);
}
2.getDeclaredFields() :獲取執行時類本身宣告的所有的屬性,包括私有的
Field[] fiels1 = clazz.getDeclaredFields();
for(int i=0;i<fiels1.length;i++) {
System.out.println(fiels1[i].getName());
}
//增強for迴圈
for(Field f:fiels1) {
System.out.println(f.getName());
}
2.獲取屬性的各個部分的內容
許可權修飾符 變數型別 變數名
1.獲取每個屬性的許可權修飾符
Field[] field = clazz.getDeclaredFields();
for(Field i:field) {
//1.獲取每個屬性的許可權修飾符
int a = i.getModifiers();
String str1 = Modifier.toString(a);
System.out.print(str1+" ");
}
2.獲取屬性的變數型別
Field[] field = clazz.getDeclaredFields();
for(Field i:field) {
//2.獲取屬性的變數型別
Class type = i.getType();
System.out.print(type+" ");
}
3.獲取屬性名
Class clazz = Person.class;
Field[] field = clazz.getDeclaredFields();
for(Field i:field) {
//3.獲取屬性名
System.out.print(i.getName());
System.out.println();
}
3.獲取執行時類的方法(重點)
1.getMethods() 獲取執行時類及其父類中所有宣告為public的方法
Class clazz = Person.class;
Method[] m1 = clazz.getMethods();
for(Method m:m1) {
System.out.println(m);
}
2.getDeclaredMethods() 獲取執行時類本身宣告的所有的方法
Method[] methods = clazz.getDeclaredMethods();
for(int i=0;i<methods.length;i++) {
System.out.println(methods[i]);
}
4.獲取方法的各個部分的內容
註解 許可權修飾符 返回值型別 方法名 形參列表 異常
1.註解
Class clazz = Person.class;
Method[] m1 = clazz.getMethods();
for(Method m:m1) {
Annotation[] an = m.getAnnotations();
for(Annotation a:an) {
System.out.println(a);
}
}
2.許可權修飾符
int a = m.getModifiers();
String str1 = Modifier.toString(a);
System.out.print(str1+" ");
3.返回值型別
Class return1 = m.getReturnType();
System.out.print(return1+" ");
4.方法名
System.out.print(m.getName()+" ");
5.形參列表
System.out.print("(");
Class[] params = m.getParameterTypes();
for(Class p : params) {
System.out.print(p.getName());
}
System.out.println(")"+" ");
6.拋的異常
Class[] ex = m.getExceptionTypes();
for(Class e:ex) {
System.out.print(e.getName());
}
// for(int i=0;i<ex.length;i++) {
// System.out.print(ex[i].getName());
// }
5.獲取構造器
@Test
public void test5() throws Exception {
Class clazz = Class.forName("com.atguigu.java.Person");
Constructor[] cons = clazz.getDeclaredConstructors();
for(Constructor c : cons) {
System.out.println(c);
}
}
6.獲取執行時類的父類
@Test
public void test6() {
Class clazz = Person.class;
Class super1 = clazz.getSuperclass();
System.out.println(super1);
}
7.獲取帶泛型的父類
@Test
public void test7() {
Class clazz = Person.class;
Type type1 = clazz.getGenericSuperclass();
System.out.println(type1);
}
8.獲取父類的泛型(重點)
@Test
public void test8() {
Class clazz = Person.class;
Type type1 = clazz.getGenericSuperclass();
ParameterizedType param= (ParameterizedType)type1;
Type[] ars = param.getActualTypeArguments();
System.out.println((Class)ars[0]);
}
9.獲取實現的介面
@Test
public void test9() {
Class clazz = Person.class;
Class[] i = clazz.getInterfaces();
for(Class a:i) {
System.out.println(a);
}
}
10.獲取所在的包
@Test
public void test10() {
Class clazz = Person.class;
Package p = clazz.getPackage();
System.out.println(p);
}
全部程式碼如下:
package com.atguigu.java;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.junit.Test;
/**
* 通過反射獲取類的完整結構
*
* @author MD
*
*/
public class TestField {
/**
* 1.獲取執行時類的屬性
*/
@Test
public void test1() {
Class clazz = Person.class;
//1.getFields() 返回 :表示公共欄位的 Field 物件的陣列
// 只能獲取執行時類中以及父類中宣告的為public的屬性
// Field[] fiels = clazz.getFields();
// for(int i=0;i<fiels.length;i++) {
// System.out.println(fiels[i]);
// }
//2.getDeclaredFields() :獲取執行時類本身宣告的所有的屬性
Field[] fiels1 = clazz.getDeclaredFields();
for(int i=0;i<fiels1.length;i++) {
System.out.println(fiels1[i].getName());
}
//增強for迴圈
for(Field f:fiels1) {
System.out.println(f.getName());
}
}
/**
* 許可權修飾符 變數型別 變數名
* 2.獲取屬性的各個部分的內容
*
*/
@Test
public void tset2() {
Class clazz = Person.class;
Field[] field = clazz.getDeclaredFields();
for(Field i:field) {
//1.獲取每個屬性的許可權修飾符
int a = i.getModifiers();
String str1 = Modifier.toString(a);
System.out.print(str1+" ");
//2.獲取屬性的變數型別
Class type = i.getType();
System.out.print(type+" ");
//3.獲取屬性名
System.out.print(i.getName());
System.out.println();
}
}
/**
* 3.獲取執行時類的方法
*/
@Test
public void test3() {
Class clazz = Person.class;
//1.getMethods() 獲取執行時類及其父類中所有宣告為public的方法
// Method[] m1 = clazz.getMethods();
// for(Method m:m1) {
// System.out.println(m);
// }
//2.getDeclaredMethods() 獲取執行時類本身宣告的所有的方法
Method[] methods = clazz.getDeclaredMethods();
for(int i=0;i<methods.length;i++) {
System.out.println(methods[i]);
}
}
/**
* 4.獲取方法的各個部分的內容
* 註解 許可權修飾符 返回值型別 方法名 形參列表 異常
*
*/
@Test
public void test4() {
Class clazz = Person.class;
Method[] m1 = clazz.getMethods();
for(Method m:m1) {
//1.註解
Annotation[] an = m.getAnnotations();
for(Annotation a:an) {
System.out.println(a);
}
//2.許可權修飾符
int a = m.getModifiers();
String str1 = Modifier.toString(a);
System.out.print(str1+" ");
//3.返回值型別
Class return1 = m.getReturnType();
System.out.print(return1+" ");
//4.方法名
System.out.print(m.getName()+" ");
//5.形參列表
System.out.print("(");
Class[] params = m.getParameterTypes();
for(Class p : params) {
System.out.print(p.getName());
}
System.out.println(")"+" ");
//6.拋的異常
Class[] ex = m.getExceptionTypes();
for(Class e:ex) {
System.out.print(e.getName());
}
// for(int i=0;i<ex.length;i++) {
// System.out.print(ex[i].getName());
// }
System.out.println();
}
}
/**
* 5.獲取構造器
* @throws Exception
*/
@Test
public void test5() throws Exception {
Class clazz = Class.forName("com.atguigu.java.Person");
Constructor[] cons = clazz.getDeclaredConstructors();
for(Constructor c : cons) {
System.out.println(c);
}
}
/**
*
* 6.獲取執行時類的父類
*/
@Test
public void test6() {
Class clazz = Person.class;
Class super1 = clazz.getSuperclass();
System.out.println(super1);
}
/**
* 7.獲取帶泛型的父類
*/
@Test
public void test7() {
Class clazz = Person.class;
Type type1 = clazz.getGenericSuperclass();
System.out.println(type1);
}
/**
* 8.獲取父類的泛型
*/
@Test
public void test8() {
Class clazz = Person.class;
Type type1 = clazz.getGenericSuperclass();
ParameterizedType param= (ParameterizedType)type1;
Type[] ars = param.getActualTypeArguments();
System.out.println((Class)ars[0]);
}
/**
* 9.獲取實現的介面
*/
@Test
public void test9() {
Class clazz = Person.class;
Class[] i = clazz.getInterfaces();
for(Class a:i) {
System.out.println(a);
}
}
/**
* 10.獲取所在的包
*/
@Test
public void test10() {
Class clazz = Person.class;
Package p = clazz.getPackage();
System.out.println(p);
}
}
五、呼叫執行時類的指定結構
1.呼叫執行時類指定的屬性並賦值
1. 獲取指定的屬性
getField(String fieldName):獲取執行時類中宣告為public的指定的屬性名為fieldName的屬性
Field name = clazz.getField("name");
2.建立執行時類的物件
Person p = (Person) clazz.newInstance();
System.out.println(p);
3.將執行時類的指定屬性賦值
name.set(p, "Jerry");
System.out.println(p);
給age賦值,private需要注意
getDeclareField(String fieldName):獲取執行時類中指明為filedName的屬性
Field age = clazz.getDeclaredField("age");
//由於屬性許可權修飾符的限制,為了保證可以給屬性賦值,需要在操作前使得此屬性可被操作
age.setAccessible(true);//私有的設定成可以訪問的
age.set(p,1);
System.out.println(p);
給id賦值,預設的修飾符
Field id = clazz.getDeclaredField("id");
id.set(p,10);
System.out.println(p);
2.呼叫執行時類中指定的方法
1.getMethod(String methodName,Class...params)獲取指定的public方法,方法名,引數列表
Class clazz = Person.class;
Method m1 = clazz.getMethod("show");
2.建立執行時類的物件
Person p =(Person)clazz.newInstance();
3.和屬性相似,這裡是invoke關鍵字裡面是物件和引數列表,或許還有返回值,用Object接收
Object returnVal = m1.invoke(p);
System.out.println(returnVal);//沒返回值的列印為null
4.獲取toString()有返回值的
Method m2 = clazz.getMethod("toString");
Object returnVal1 = m2.invoke(p);
System.out.println(returnVal1);
5.獲取display()帶引數的
Method m3 = clazz.getMethod("display",String.class);
m3.invoke(p, "china");
6.獲取info()靜態的方法
Method m4 = clazz.getMethod("info");
m4.invoke(Person.class);
7.獲取Test() 私有的帶引數的有返回值的
Method m5 = clazz.getDeclaredMethod("Test",String.class,Integer.class);
m5.setAccessible(true);
Object o = m5.invoke(p,"測試",5);
System.out.println(o);
3.呼叫指定的構造器,建立類物件
public void test3() throws InstantiationException, Exception {
Class clazz = Person.class;
Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
cons.setAccessible(true);
Person p = (Person)cons.newInstance("迪麗熱巴",20);
System.out.println(p);
}
全部程式碼
package com.atguigu.java;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.junit.Test;
/**
* ******呼叫對應的執行時類的指定的結構(屬性、方法)
* @author MD
*
*/
public class TestField1 {
/**
* 呼叫執行時類指定的屬性
* @throws Exception
* @throws NoSuchFieldException
*/
@Test
public void test1() throws Exception {
Class clazz = Person.class;
//1.獲取指定的屬性
//getField(String fieldName):獲取執行時類中宣告為public的指定的屬性名為fieldName的屬性
Field name = clazz.getField("name");
//2.建立執行時類的物件
Person p = (Person) clazz.newInstance();
System.out.println(p);
//3.將執行時類的指定屬性賦值
name.set(p, "Jerry");
System.out.println(p);
//給age賦值,private需要注意
//getDeclareField(String fieldName):獲取執行時類中指明為filedName的屬性
Field age = clazz.getDeclaredField("age");
//由於屬性許可權修飾符的限制,為了保證可以給屬性賦值,需要在操作前使得此屬性可被操作
age.setAccessible(true);//私有的設定成可以訪問的
age.set(p,1);
System.out.println(p);
//給id賦值,預設的修飾符
Field id = clazz.getDeclaredField("id");
id.set(p,10);
System.out.println(p);
}
/**
* 呼叫執行時類中指定的方法
* @throws Exception
* @throws NoSuchMethodException
*/
@Test
public void test2() throws NoSuchMethodException, Exception {
Class clazz = Person.class;
//getMethod(String methodName,Class...params)獲取指定的public方法,方法名,引數列表
Method m1 = clazz.getMethod("show");
//建立執行時類的物件
Person p =(Person)clazz.newInstance();
//和屬性相似,這裡是invoke關鍵字裡面是物件和引數列表,或許還有返回值,用Object接收
Object returnVal = m1.invoke(p);
System.out.println(returnVal);
//獲取toString()有返回值的
Method m2 = clazz.getMethod("toString");
Object returnVal1 = m2.invoke(p);
System.out.println(returnVal1);
//獲取display()帶引數的
Method m3 = clazz.getMethod("display",String.class);
m3.invoke(p, "china");
//獲取info()靜態的方法
Method m4 = clazz.getMethod("info");
m4.invoke(Person.class);
//獲取Test() 私有的帶引數的有返回值的
Method m5 = clazz.getDeclaredMethod("Test",String.class,Integer.class);
m5.setAccessible(true);
Object o = m5.invoke(p,"測試",5);
System.out.println(o);
}
/**
* 呼叫指定的構造器,建立類物件
* @throws Exception
* @throws InstantiationException
*/
@Test
public void test3() throws InstantiationException, Exception {
Class clazz = Person.class;
Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
cons.setAccessible(true);
Person p = (Person)cons.newInstance("迪麗熱巴",20);
System.out.println(p);
}
}
六、ClassLoader
類載入器是用來把類(class)裝載進記憶體
1.獲取一個系統類載入器
ClassLoader loader1 = ClassLoader.getSystemClassLoader();
System.out.println(loader1);
2.獲取系統類載入器的父類載入器,即擴充套件類載入器
ClassLoader loader2 = loader1.getParent();
System.out.println(loader2);
3.獲取擴充套件類載入器的父類載入器,即引導類載入器,載入的是核心庫,列印為null
ClassLoader loader3 = loader2.getParent();
System.out.println(loader3);
4.測試當前類由哪個類載入器進行載入
Class clazz1 = Person.class;
ClassLoader loader4 = clazz1.getClassLoader();
System.out.println(loader4);//系統類載入器
1. 描述一下JVM載入class檔案的原理機制?
JVM中類的裝載是由ClassLoader和它的子類來實現的,
Java ClassLoader 是一個重要的Java執行時系統元件。它負責在執行時查詢和裝入類檔案的類。