JAVA打怪之路 - JAVA反射機制

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();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章