[置頂] 【筆記】 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()時,保證父類有此構造器
使用過程
- 根據全類名獲取對應的Class物件。
- 呼叫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); }
呼叫執行時類的指定結構
屬性
-
建立執行時類的物件
-
獲取指定的屬性
-
getDeclaredField(String fieldName)
通常採用getDeclaredField(String fieldName)方法獲取屬性。為獲取執行時類中指定變數名(fieldName)的屬性
-
-
保證當前屬性是可訪問的:使用setAccessible()方法
name.setAccessible(true);
只用使用這個方法,才可以對許可權是非public的變數進行修改。
在編寫的時候,可以不管變數的許可權修飾符是那種,都寫上此方法
-
對屬性的操作
- 設定當前屬性的值
-
set(Object obj, Object value)
- Object obj:指明設定哪個物件的屬性
- Object value:將此屬性設定的值
-
- 獲取當前屬性的值
- get(Object obj)
- Object obj:指明獲取哪個物件的當前屬性值
- get(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);
方法
非靜態方法
- 建立執行時類的物件
- 獲取指定的方法
- getDeclaredMethod()
- 引數一:指明獲取方法的名稱
- 引數二:指明獲取的方法的形參列表
Method show = aClass.getDeclaredMethod("show", String.class)
- getDeclaredMethod()
- 保證當前方法是可訪問的:使用setAccessible()方法
show.setAccessible(true);
- 呼叫方法
- invoke()
- 引數一:方法的呼叫者
- 引數二:給方法形參賦值的實參
- 該方法有返回值,即為對應類中呼叫方法的返回值,沒有返回值則返回null
show.invoke(p,"CN");
- invoke()
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);
構造器(不常用)
- 獲取指定的構造器
- getDeclaredConstructor()
- 引數一:指明構造器的引數列表
- getDeclaredConstructor()
- 保證此構造器可訪問:使用setAccessible()方法
- 呼叫此構造器建立執行時類的物件: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);