JAVA反射機制
一、Java反射機制概述( Java Reflection )
反射相關的主要API:
二、理解Class類並獲取Class的實例
① 理解Class類(描述類的類)
在Object類中定義了以下的方法,此方法將被所有子類繼承:
● public final Class getClass() , 以上的方法返回值的類型是一個Class類,此類是Java反射的源頭,即:可以通過對象反射求出類的名稱。
② Class類的常用方法
③ 獲取Class類實例的四種方式
④ 哪些類型可以有Class對象?
三、類的加載與ClassLoader的理解
① 類的加載過程
② ClassLoader(類加載器)
類加載器作用是用來把類(class)裝載進內存的。JVM 規範定義瞭如下類型的類的加載器。
③ 代碼示例:
//1.獲取一個系統類加載器
ClassLoader classloader = ClassLoader.getSystemClassLoader();
System.out.println(classloader);
//2.獲取系統類加載器的父類加載器,即擴展類加載器
classloader = classloader.getParent();
System.out.println(classloader);
//3.獲取擴展類加載器的父類加載器,即引導類加載器
classloader = classloader.getParent();
System.out.println(classloader);
//4.測試當前類由哪個類加載器進行加載
classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader();
System.out.println(classloader);
//5.測試JDK提供的Object類由哪個類加載器加載
classloader =
Class.forName("java.lang.Object").getClassLoader();
System.out.println(classloader);
//*6.關於類加載器的一個主要方法:getResourceAsStream(String str):獲取類路徑下的指定文件的輸入流
InputStream in = null;
in = this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties");
System.out.println(in);
四、創建運行時類的對象
創建類的對象:調用Class對象的newInstance()方法,內部調用了運行時類的空參的構造器。 要求:1)類必須有一個無參數的構造器。 2)類的構造器的訪問權限需要足夠。
//1.根據全類名獲取對應的Class對象
Class clazz = Class.forName(“atguigu.java.Person");
//2.調用指定參數結構的構造器,生成Constructor的實例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通過Constructor的實例創建對應類的對象,並初始化類屬性
Person p2 = (Person) con.newInstance("Peter",20);
System.out.println(p2);
五、獲取運行時類的完整結構
通過反射獲取運行時類的完整結構,Field、Method、Constructor、Superclass、Interface、Annotation 。使用反射可以取得:
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for(Class c : interfaces){
System.out.println(c);
}
System.out.println();
//獲取運行時類的父類實現的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for(Class c : interfaces1){
System.out.println(c);
}
Class clazz = Person.class;
//getConstructors():獲取當前運行時類中聲明爲public的構造器
Constructor[] constructors = clazz.getConstructors();
for(Constructor c : constructors){
System.out.println(c);
}
System.out.println();
//getDeclaredConstructors():獲取當前運行時類中聲明的所有的構造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for(Constructor c : declaredConstructors){
System.out.println(c);
}
Class clazz = Person.class;
//getMethods():獲取當前運行時類及其所有父類中聲明爲public權限的方法
Method[] methods = clazz.getMethods();
for(Method m : methods){
System.out.println(m);
}
System.out.println();
//getDeclaredMethods():獲取當前運行時類中聲明的所有方法。(不包含父類中聲明的方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
for(Method m : declaredMethods){
System.out.println(m);
}
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
//1.獲取方法聲明的註解
Annotation[] annos = m.getAnnotations();
for (Annotation a : annos) {
System.out.println(a);
}
//2.權限修飾符
System.out.print(Modifier.toString(m.getModifiers()) + "\t");
//3.返回值類型
System.out.print(m.getReturnType().getName() + "\t");
//4.方法名
System.out.print(m.getName());
System.out.print("(");
//5.形參列表
Class[] parameterTypes = m.getParameterTypes();
if (!(parameterTypes == null && parameterTypes.length == 0)) {
for (int i = 0; i < parameterTypes.length; i++) {
if (i == parameterTypes.length - 1) {
System.out.print(parameterTypes[i].getName() + " args_" + i);
break;
}
System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
}
}
System.out.print(")");
//6.拋出的異常
Class[] exceptionTypes = m.getExceptionTypes();
if (exceptionTypes.length > 0) {
System.out.print("throws ");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i == exceptionTypes.length - 1) {
System.out.print(exceptionTypes[i].getName());
break;
}
System.out.print(exceptionTypes[i].getName() + ",");
}
}
System.out.println();
}
//獲取屬性結構
Class clazz = Person.class;
//getFields():獲取當前運行時類及其父類中聲明爲public訪問權限的屬性
Field[] fields = clazz.getFields();
for(Field f : fields){
System.out.println(f);
}
System.out.println();
//getDeclaredFields():獲取當前運行時類中聲明的所有屬性。(不包含父類中聲明的屬性)
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
System.out.println(f);
}
//權限修飾符 數據類型 變量名
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
//1.權限修飾符
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier) + "\t");
//2.數據類型
Class type = f.getType();
System.out.print(type.getName() + "\t");
//3.變量名
String fName = f.getName();
System.out.print(fName);
}
//獲取註解
Class clazz = Person.class;
Annotation[] annotations = clazz.getAnnotations();
for(Annotation annos : annotations){
System.out.println(annos);
}
@Test
public void test4(){
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
//獲取泛型類型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
// System.out.println(actualTypeArguments[0].getTypeName());
System.out.println(((Class)actualTypeArguments[0]).getName());
}
//獲取運行時所在的包
@Test
public void test6(){
Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
}
示例代碼:通過反射,創建對象,調用類的屬性、方法、構造器
@Test
public void test2() throws Exception{
Class clazz = Person.class;
//1.通過反射,創建Person類的對象
Constructor cons = clazz.getConstructor(String.class,int.class);
Object obj = cons.newInstance("Tom", 12);
Person p = (Person) obj;
System.out.println(p.toString());
//2.通過反射,調用對象指定的屬性、方法
//調用屬性
Field age = clazz.getDeclaredField("age");
age.set(p,10);
System.out.println(p.toString());
//調用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);
System.out.println("*******************************");
//通過反射,可以調用Person類的私有結構的。比如:私有的構造器、方法、屬性
//調用私有的構造器
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person) cons1.newInstance("Jerry");
System.out.println(p1);
//調用私有的屬性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1,"HanMeimei");
System.out.println(p1);
//調用私有的方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(p1,"中國");//相當於String nation = p1.showNation("中國")
System.out.println(nation);
}
六、調用運行時類的指定結構
① 如何操作運行時類中的指定的屬性 – 需要掌握
@Test
public void testField1() throws Exception {
Class clazz = Person.class;
//創建運行時類的對象
Person p = (Person) clazz.newInstance();
//1. getDeclaredField(String fieldName):獲取運行時類中指定變量名的屬性
Field name = clazz.getDeclaredField("name");
//2.保證當前屬性是可訪問的
name.setAccessible(true);
//3.獲取、設置指定對象的此屬性值
name.set(p,"Tom");
System.out.println(name.get(p));
}
② 如何操作運行時類中的指定的方法 – 需要掌握
@Test
public void testMethod() throws Exception {
Class clazz = Person.class;
//創建運行時類的對象
Person p = (Person) clazz.newInstance();
/*
1.獲取指定的某個方法
getDeclaredMethod():參數1 :指明獲取的方法的名稱 參數2:指明獲取的方法的形參列表
*/
Method show = clazz.getDeclaredMethod("show", String.class);
//2.保證當前方法是可訪問的
show.setAccessible(true);
/*
2. 調用方法的invoke():參數1:方法的調用者 參數2:給方法形參賦值的實參
invoke()的返回值即爲對應類中調用的方法的返回值。
*/
Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
System.out.println(returnValue);
System.out.println("*************如何調用靜態方法*****************");
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
//如果調用的運行時類中的方法沒有返回值,則此invoke()返回null
// Object returnVal = showDesc.invoke(null);
Object returnVal = showDesc.invoke(Person.class);
System.out.println(returnVal);//null
}
③ 如何調用運行時類中的指定的構造器
@Test
public void testConstructor() throws Exception {
Class clazz = Person.class;
//private Person(String name)
/*
1.獲取指定的構造器
getDeclaredConstructor():參數:指明構造器的參數列表
*/
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//2.保證此構造器是可訪問的
constructor.setAccessible(true);
//3.調用此構造器創建運行時類的對象
Person per = (Person) constructor.newInstance("Tom");
System.out.println(per);
}
七、反射的應用:動態代理
動態代理相比於靜態代理的優點:
抽象角色中(接口)聲明的所有方法都被轉移到調用處理器一個集中的方法中處理,這樣,我們可以更加靈活和統一的處理衆多的方法。
代理設計模式的原理:
使用一個代理將對象包裝起來, 然後用該代理對象取代原始對象. 任何對原始對象的調用都要通過代理. 代理對象決定是否以及何時將方法調用轉到原始對象上。
Java動態代理相關API:
動態代理步驟:
靜態代理與動態代理模式示例代碼:
靜態代理舉例 特點:代理類和被代理類在編譯期間,就確定下來了。
interface ClothFactory{
void produceCloth();
}
//代理類
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;//用被代理類對象進行實例化
public ProxyClothFactory(ClothFactory factory){
this.factory = factory;
}
@Override
public void produceCloth() {
System.out.println("代理工廠做一些準備工作");
factory.produceCloth();
System.out.println("代理工廠做一些後續的收尾工作");
}
}
//被代理類
class NikeClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("Nike工廠生產一批運動服");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
//創建被代理類的對象
ClothFactory nike = new NikeClothFactory();
//創建代理類的對象
ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
proxyClothFactory.produceCloth();
}
}
動態代理舉例:
interface Human{
String getBelief();
void eat(String food);
}
//被代理類
class SuperMan implements Human{
@Override
public String getBelief() {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("我喜歡喫" + food);
}
}
class HumanUtil{
public void method1(){
System.out.println("==============通用方法一========");
}
public void method2(){
System.out.println("============通用方法二===============");
}
}
/*
要想實現動態代理,需要解決的問題?
問題一:如何根據加載到內存中的被代理類,動態的創建一個代理類及其對象。
問題二:當通過代理類的對象調用方法a時,如何動態的去調用被代理類中的同名方法a。
*/
class ProxyFactory{
//調用此方法,返回一個代理類的對象。解決問題一
public static Object getProxyInstance(Object obj){//obj:被代理類的對象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//需要使用被代理類的對象進行賦值
public void bind(Object obj){
this.obj = obj;
}
//當我們通過代理類的對象調用方法a時,會自動的調用如下的方法:invoke()
//將被代理類要執行的方法a的功能就聲明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HumanUtil util = new HumanUtil();
util.method1();
//method:即爲代理類對象調用的方法,此方法也就作爲了被代理類對象要調用的方法
//obj:被代理類的對象
Object returnValue = method.invoke(obj,args);
util.method2();
//上述方法的返回值就作爲當前類中的invoke()的返回值。
return returnValue;
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
//proxyInstance:代理類的對象
Human proxyInstance = (Human)
ProxyFactory.getProxyInstance(superMan);
//當通過代理類對象調用方法時,會自動的調用被代理類中同名的方法
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("四川麻辣燙");
System.out.println("*****************************");
NikeClothFactory nikeClothFactory = new NikeClothFactory();
ClothFactory proxyClothFactory = (ClothFactory)
ProxyFactory.getProxyInstance(nikeClothFactory);
proxyClothFactory.produceCloth();
}
}