1. 程式人生 > 實用技巧 >[置頂] 【筆記】 springCloud--Alibaba--服務註冊和服務發現

[置頂] 【筆記】 springCloud--Alibaba--服務註冊和服務發現

反射

反射機制的概述

反射(Reflection)是被視為動態語言的關鍵,反射機制允許程式在執行期藉助於Reflection API取得任何類的內部資訊,並能直接操作任意物件的內部屬性及方法。

載入完類之後,在堆記憶體的方法區中就產生了一個Class型別的物件(一個類只有一個Class物件),這個物件就包含了完整的類的結構資訊。我們可以通過這個物件看到類的結構。

反射的特徵:動態性

  • 動態語言:執行時可以改變其結構的語言
  • 靜態語言:執行時結構不可變的語言

反射機制提供的功能

  • 在執行時判斷任意一個物件所屬的類
  • 在執行時構造任意一個類的物件
  • 在執行時判斷任意一個類所具有的成員變數和方法
  • 在執行時獲取泛型資訊
  • 在執行時呼叫任意一個物件的成員變數和方法
  • 在執行時處理註解
  • 生成動態代理

Class類

Class類的理解

Class類定義在java.lang包下(java.lang.Class)

類的載入過程

程式在經過javac.exe命令後,會生成一個或多個位元組碼檔案(.class結尾),此過程是編譯的過程。

使用java.exe命令對某個位元組碼檔案進行解釋執行,相當於將某個位元組碼檔案載入到記憶體中,此過程稱為類的載入。

載入到記憶體中的類稱為執行時類,此執行時類就作為Class的一個例項。Class的例項就對應一個執行時類。

  • 類載入的作用

    將class檔案位元組碼內容載入到記憶體中,並將這些靜態資料轉換成方法區的執行時資料結構,然後在堆中生成一個代表這個類的java.lang.Class物件,作為方法區中類資料的訪問入口。

  • 類快取

    標準的JavaSE類載入器可以按要求查詢類,但一旦某個類被載入到類載入器中,它將維持載入(快取)一段時間。不過JVM垃圾回收機制可以回收這些Class物件。

ClassLoader(類的載入器)的理解

類載入器作用是用來把類(class)裝載進記憶體的

獲取Class例項的方式

載入到記憶體中的執行時類會快取一定的時間,在此時間之內可以通過不同的方式來獲取此執行時類。以下幾種方式是獲取執行時類,不是建立執行時類。(personClass1、personClass2等等都是獲取的唯一存在的執行時類)

  • 方式一

    呼叫執行時類的屬性:.class

    • 有泛型
      Class<Person> personClass = Person.class;
      
    • 無泛型
      Class personClass = Person.class;
      
  • 方式二

    通過執行時類的物件呼叫getClass()方法

    Person p1 = new Person();
    Class personClass2 = p1.getClass();
    
  • 方式三(主要使用)

    可以更好的體現動態性

    呼叫Class的靜態方法:forName(String classPath)

    此時的classPath是包含包在內的完整路徑(否則無法準確知道準確的類)

    Class personClass3 = Class.forName("Understand.Person");
    
  • 方式四(瞭解)

    使用類的載入器:ClassLoader

    ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    Class personClass4 = classLoader.loadClass("Understand.Person");
    

Class例項對應的結構

  • class:外部類,成員(成員內部類,靜態內部類),區域性內部類,匿名內部類
  • interface:介面
  • []:陣列
    • 只要陣列的元素型別和維度一樣,就是同一個Class
  • enum:列舉
  • annotation:註解
  • primitive type:基本資料型別
  • void

建立執行時類的物件

newInstance()方法

呼叫此方法,建立對應的執行時類的物件。內部呼叫了執行時類的空參的構造器

使用要求:

  • 類必須有一個無引數的構造器。
  • 類的構造器的訪問許可權需要足夠,通常設定為public

類中要求提供一個public空參構造器的原因:

  • 便於通過反射去建立執行時類的物件
  • 便於子類繼承此執行時類時,預設呼叫super()時,保證父類有此構造器

使用過程

  1. 根據全類名獲取對應的Class物件。
  2. 呼叫newInstance()方法。
Class<Person> pclass = Person.class;
Person person = pclass.newInstance();
System.out.println(person);

獲取執行時類的完整結構

此時的Person類完整程式碼如下

@MyAnnotation(value = "hi")
public class Person extends Creature<String> implements Comparable<String>, MyInterface{

    private String name;
    int age;
    public int id;

    public Person(){

    }

    @MyAnnotation(value = "abc")
    private Person(String name){
        this.name = name;
    }

    Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    @MyAnnotation
    private String show(String nation){
        System.out.println("國籍:" + nation);
        return nation;
    }

    public String display(String interest){
        return interest;
    }

    @Override
    public void info() {
        System.out.println("我是一個人");
    }

    @Override
    public int compareTo(String s) {
        return 0;
    }

    private static void showDesc(){
        System.out.println("可愛的貓");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }
}

Person類的父類Creature類

public class Creature<T> implements Serializable {
    private char gender;
    public double weight;

    private void breath(){
        System.out.println("生物呼吸");
    }

    public void eat(){
        System.out.println("生物進食");
    }
}

自定義介面MyInterface

public interface MyInterface {
    void info();
}

自定義註釋

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE, ElementType.MODULE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "hello";
}

獲取當前執行時類的所有屬性

  • getFields():獲取當前執行時類及其父類中宣告為public訪問許可權的屬性

    Field[] fields = personClass.getFields();
    for(Field f : fields){
        System.out.println(f);
    }
    

  • getDeclaredFields():獲取當前執行時類當中宣告的所有屬性(不包含父類中宣告的屬性)

    Field[] declaredFields = personClass.getDeclaredFields();
    for(Field f : declaredFields){
        System.out.println(f);
    }
    

獲取屬性的相關資訊

  • 許可權修飾符

    getModifiers()

    該方法返回值為int,是因為Modifier類將各種許可權修飾符用數字來表示。想要顯示變數的許可權修飾符,要使用Modifier中的toString()方法

  • 資料型別

    getType()

  • 變數名

    getName()

Class personClass = Class.forName("Get.Person");
Field[] declaredFields = personClass.getDeclaredFields();
for(Field f : declaredFields){
    //1. 許可權修飾符
    int modifiers = f.getModifiers();
    System.out.println("許可權修飾符:" + Modifier.toString(modifiers));
    //2. 資料型別
    Class type = f.getType();
    System.out.println("資料型別:" + type.getName());
    //3. 變數名
    String name = f.getName();
    System.out.println("變數名:" + name);
}

獲取當前執行時類的方法結構

  • getMethods():獲取當前執行類及其父類所有宣告為public許可權的方法

    Method[] methods = personClass.getMethods();
    for(Method m : methods){
        System.out.println(m);
    }
    

  • getDeclaredMethods():獲取當前執行時類中宣告的所有的方法(不包含父類中宣告的方法)

    Method[] declaredMethods = personClass.getDeclaredMethods();
    for(Method m : declaredMethods){
        System.out.println(m);
    }
    

獲取方法的相關資訊

  • 註解

    getAnnotations()

  • 許可權修飾符

    getModifiers()

  • 返回值型別

    getReturnType()

  • 方法名

    getName()

  • 形參列表

    getParameterTypes()

  • 丟擲異常型別

    getExceptionTypes()

Class<?> personClass = Class.forName("Get.Person");
Method[] declaredMethods = personClass.getDeclaredMethods();
for(Method m : declaredMethods){
    //6. 註解
    Annotation[] annotations = m.getAnnotations();
    for(Annotation a : annotations){
        System.out.println("註解:" + a);
    }
    System.out.println();
    //1. 許可權修飾符
    int modifiers = m.getModifiers();
    System.out.print(Modifier.toString(modifiers) + "\t");
    //2. 返回值型別
    System.out.print(m.getReturnType().getName() + "\t");
    //3. 方法名
    System.out.print(m.getName() + "\t");

    System.out.print("(");
    //4. 形參列表
    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(")");
    //5. 丟擲異常
    Class[] exceptionTypes = m.getExceptionTypes();
    if(!(exceptionTypes == null || 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() + ",");
        }
    }
}

獲取當前執行時類的構造器

  • getConstructors():獲取當前執行時類當中宣告為public型別的構造器

    Class personClass = Class.forName("Get.Person");
    Constructor[] constructors = personClass.getConstructors();
    for(Constructor c : constructors){
        System.out.println(c);
    }
    

  • getDeclaredConstructors():獲取當前執行時類中所有的構造器

    Constructor[] declaredConstructors = personClass.getDeclaredConstructors();
    for(Constructor c : declaredConstructors){
        System.out.println(c);
    }
    

獲取執行時類的父類

  • getSuperclass():獲取執行時類的父類

    Class personClass = Class.forName("Get.Person");
    Class superclass = personClass.getSuperclass();
    System.out.println(superclass);
    
  • getGenericSuperclass():獲取執行時類帶泛型的父類

    Type genericSuperclass = personClass.getGenericSuperclass();
    System.out.println(genericSuperclass);
    

  • getActualTypeArguments():獲取執行時類的帶泛型父類的泛型

    Type genericSuperclass = personClass.getGenericSuperclass();
    ParameterizedType parameterizedType = (ParameterizedType)genericSuperclass;
    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
    System.out.println(Arrays.toString(actualTypeArguments));
    

獲取執行時類的介面、所在包、註解

  • getInterfaces():獲取執行時類實現的介面

    Class[] interfaces = aClass.getInterfaces();
    for(Class i : interfaces){
        System.out.println(i);
    }
    

  • getPackage():獲取執行時類當前所在的包

    Class aClass = Class.forName("Get.Person");
    Package aPackage = aClass.getPackage();
    System.out.println(aPackage);
    
  • getAnnotations():獲取執行時類所宣告的註解

    Annotation[] annotations = aClass.getAnnotations();
    for(Annotation a : annotations){
        System.out.println(a);
    }
    

呼叫執行時類的指定結構

屬性

  1. 建立執行時類的物件

  2. 獲取指定的屬性

    • getDeclaredField(String fieldName)

      通常採用getDeclaredField(String fieldName)方法獲取屬性。為獲取執行時類中指定變數名(fieldName)的屬性

  3. 保證當前屬性是可訪問的:使用setAccessible()方法

    name.setAccessible(true);
    

    只用使用這個方法,才可以對許可權是非public的變數進行修改。

    在編寫的時候,可以不管變數的許可權修飾符是那種,都寫上此方法

  4. 對屬性的操作

    • 設定當前屬性的值
      • set(Object obj, Object value)

        • Object obj:指明設定哪個物件的屬性
        • Object value:將此屬性設定的值
    • 獲取當前屬性的值
      • get(Object obj)
        • Object obj:指明獲取哪個物件的當前屬性值
Class aClass = Class.forName("Get.Person");
//1. 建立執行時類的物件
Person p = (Person) aClass.newInstance();

//2. 獲取指定的屬性
Field name = aClass.getDeclaredField("name");

name.setAccessible(true);
        
//設定當前屬性的值
name.set(p,"Tom");
//獲取當前屬性的值
String pName = (String) name.get(p);
System.out.println("name:" + pName);

方法

非靜態方法

  1. 建立執行時類的物件
  2. 獲取指定的方法
    • getDeclaredMethod()
      • 引數一:指明獲取方法的名稱
      • 引數二:指明獲取的方法的形參列表
    Method show = aClass.getDeclaredMethod("show", String.class)
    
  3. 保證當前方法是可訪問的:使用setAccessible()方法
    show.setAccessible(true);
    
  4. 呼叫方法
    • invoke()
      • 引數一:方法的呼叫者
      • 引數二:給方法形參賦值的實參
      • 該方法有返回值,即為對應類中呼叫方法的返回值,沒有返回值則返回null
    show.invoke(p,"CN");
    
Class aClass = Class.forName("Get.Person");

Person p = (Person) aClass.newInstance();

Method show = aClass.getDeclaredMethod("show", String.class);

show.setAccessible(true);

String returnCn = (String) show.invoke(p, "CN");
        System.out.println(returnCn);

靜態方法

利用 當前類.class或者null 表示當前類

Method showDesc = aClass.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
//Person.class表示當前類
 showDesc.invoke(Person.class);

構造器(不常用)

  1. 獲取指定的構造器
    • getDeclaredConstructor()
      • 引數一:指明構造器的引數列表
  2. 保證此構造器可訪問:使用setAccessible()方法
  3. 呼叫此構造器建立執行時類的物件:newInstance()方法
Class aClass = Class.forName("Get.Person");

Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);

Person p = (Person) declaredConstructor.newInstance("Tom");
System.out.println(p);