JavaSE(九)反射與註解

反射機制

傳統反射

  能夠分析類能力的程序就是反射,反射會破壞封裝性和安全性,但可以寫出通用性更強的代碼,一般不建議在業務代碼中使用反射,但在通用框架或者業務框架中使用反射可以讓框架更優雅
  Class類的每一個實例都表示一個類型(可以是基本類型),能夠通過Class實例獲取對應類的相關信息,實際上Class類是個泛型類Class,如String類型對應的類型爲Class<String>。Java運行時系統始終爲每一個對象維護一個類型標誌,而這類型標誌對應到虛擬機管理的許多Class對象中的一個。
  獲取Class對象的方法:1、T.class,T可以是類類型或基本類型或數組類型。2、從Object繼承下來的getClass()方法,同一個類的不同實體返回同一個單例。3、用Class類的靜態方法forName。
  根據Class對象創建Class對象對應的類的一個實例:Class對象.newInstance(); 只能調用class實例對應類的無參構造器,沒有無參構造器會出錯,如果在沒有無參構造器的情況下想要通過反射創建實例,需要用到Constructor類中的newInstance(Object[] args)方法,而Constructor可以通過Class實例獲取。
  判斷對象是否是某個類或其子類的實例,有關鍵字instanceof,如:obj instanceof String。判斷對象是否是某個類的實例,如obj.getClass() == String.class。
  java.lang.reflect包下有三個類Field、Method和Constructor分別描述類的一個域、一個方法和一個構造器。在通過反射訪問域或方法前,會檢查被訪問的域或方法對使用反射的類是否可見,只有可見時才能訪問,但Field、Method和Constructor都有setAccessible方法,如果強制設置爲true,會繞過可見性檢查直接訪問域或方法,爲了安全起見不建議強制調用該方法,以儘可能不破壞面向對象的思想,本座以前不管三七二十一都先強制設置爲true,本以爲這樣做百利而無一害,由程序員直接掌控調用任何方法(包括私有方法),但後來發現這樣做完全不對,讓面嚮對象語言提供的可見性控制彷彿沒有了任何意義。
  在編程中一般不直接讀寫域的值,而是使用域的getter與setter訪問域,但在反射框架中並沒有直接獲取一個成員變量的getter與setter的接口,但可以使用javabean框架下表示一個javabean屬性的類PropertyDescriptor來獲取該屬性的getter與setter方法。通過構造方法PropertyDescriptor(String propertyName, Class<?> beanClass)可以構建一個表示beanClass類中名爲propertyName的成員變量的PropertyDescriptor對象,通過PropertyDescriptor對象的getReadMethod()與getWriteMethod()可以獲取表示該屬性的getter與setter的Method對象,通過Method的invoke方法便可以調用屬性的getter與setter。

public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement {
    public static Class<?> forName(String name, boolean initialize, ClassLoader loader); // 使用加載器加載類全名爲name的類,並返回代表該類的Class對象
    public static Class<?> forName(String className); // 默認會初始化,使用該方法調用者的類的加載器
    
    public T newInstance(); // 創建一個Class對象代表的類的實例    
    public T cast(Object obj); // 將一個對象強轉爲該class對象表示的類的對象
    
    public ClassLoader getClassLoader();
    public Package getPackage();
    public String getSimpleName(); // 類名
    public String getName(); // 類全名,包名 + 類名
    public String getTypeName(); // 通常與getName一樣,只有在數組時不一樣,如getName返回[[Ljava.lang.String;而getTypeName返回java.lang.String[][]
    public String getCanonicalName(); // 與getTypeName相比,匿名內部類與局部內部類返回null,成員內部類與外部內部類之間的分割符號不同getTypeName爲$,而本方法爲.
    public TypeVariable<Class<T>>[] getTypeParameters(); // 返回類的泛型參數,如HashMap<K,V>類有兩個泛型參數
    public native Class<? super T> getSuperclass(); // 父類的Class對象
    public Class<?>[] getInterfaces(); // 實現的接口(不包括父類的接口)
    public native int getModifiers(); // 返回類的修飾符如public、static、final等,可以用Modifier的is開頭的靜態方法檢查是否包含某個修飾符,如Modifier.isPublic(String.class.getModifiers);    
    public java.net.URL getResource(String name); //getClassLoader().getResource(name);
    public boolean desiredAssertionStatus(); // 該類是否啓用了斷言,如果爲ture,該類中的斷言語句將被執行
    public java.security.ProtectionDomain getProtectionDomain(); // 獲取類的保護域

    public native boolean isInstance(Object obj); // 返回object instanceof 當前Class對象代表的類
    public native boolean isAssignableFrom(Class<?> cls); // 當前Class對象代表的類是否是cls代表的類或其超類
    public native boolean isInterface(); // 當前Class對象代表的類是否代表接口類型
    public native boolean isArray(); // 當前Class對象代表的類是否代表數組類型
    public boolean isEnum(); // 當前Class對象代表的類是否代表enum類型
    public boolean isAnnotation(); // 當前Class對象代表的類是否代表註解類型
    public native boolean isPrimitive();  // 當前Class對象代表的類是否代表基本類型
    public boolean isSynthetic();  // 非用戶編寫的類,自動生成的,詳見虛擬機一章節SYNTHETIC標識
    public boolean isAnonymousClass(); // 當前Class對象代表的類是否是匿名內部類
    public boolean isLocalClass(); // 當前Class對象代表的類是否是局部內部類
    public boolean isMemberClass(); // 當前Class對象代表的類是否是成員內部類    
    
	public Class<?> getDeclaringClass(); // 如果當前類是成員內部類,那麼返回其外部類
	public Class<?>[] getClasses(); // 返回所有public成員內部類,包括父類的public成員內部類
    public Class<?>[] getDeclaredClasses(); // 返回所有成員內部類,不包括父類的成員內部類,但包括private等聲明的成員內部類
    public Class<?> getEnclosingClass(); // 如果當前類是匿名內部類,那麼返回其外部類
    public native Class<?> getComponentType(); // 如果當前類是數組類型,那麼返回其元素類型,否則返回null
    public T[] getEnumConstants(); // 如果當前類是enum類型,返回其enum常量

    public Field[] getFields(); // 獲取所有public域,包括其父類的public域
    public Field[] getDeclaredFields(); // 返回所有域,但不包含其父類的域
    public Field getField(String name);
    public Field getDeclaredField(String name);
	public Method[] getMethods(); // 獲取所有public方法,包括其父類的public方法
    public Method[] getDeclaredMethods(); // 返回所有方法,但不包含其父類的方法
	public Method getMethod(String name, Class<?>... parameterTypes);
	public Method getDeclaredMethod(String name, Class<?>... parameterTypes);
	public Method getEnclosingMethod(); // 如果當前Class表示的是某個方法中的匿名內部類,則返回該方法,否則返回null
	public Constructor<?>[] getConstructors(); // 獲取所有public構造器,包括其父類的public構造器
	public Constructor<?>[] getDeclaredConstructors(); // 返回所有構造器,但不包含其父類的構造器
	public Constructor<T> getConstructor(Class<?>... parameterTypes);
	public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);
    public Constructor<?> getEnclosingConstructor(); // 如果當前Class表示的是某個構造器中的匿名內部類,則返回該構造器,否則返回null

	...實現了AnnotatedElement接口的相關方法,在註解部分介紹...
}

public final class Field extends AccessibleObject implements Member {
    public Class<?> getDeclaringClass(); // 返回域所屬的類
    public Class<?> getType(); // 返回域的類型
    public Type getGenericType(); // 與getType相比多了泛型參數類型
    public String getName(); // 域的名稱
    public int getModifiers(); // 域修飾符,通過Modifier類來判斷
    public boolean isEnumConstant(); // 判斷是否是枚舉元素,並非判斷是否是枚舉類型,如enum MyEnum {A, B, C},MyEnum.class.getField('A').isEnumConstant()返回true
    public boolean isSynthetic(); // 非用戶編寫的域,自動生成的,詳見虛擬機一章節SYNTHETIC標識
    
    public boolean isAccessible(); // 訪問域(調用getXxx和setXxx方法)前會首先調用該方法判斷是否有訪問權限
    public void setAccessible(boolean flag); // 設置爲true會讓沒有權限讀寫該域的代碼可以讀寫該域
    public Object get(Object obj); // 獲取obj對象類型爲引用類型的域
    public boolean/byte/char/short/int/long/float/double getBoolean/Byte/Char/Short/Int/Long/Float/Double(Object obj); // 獲取obj對象類型爲基本類型的域
    public void set(Object obj, Object value); // 設置obj對象類型爲引用類型的域
    public void setBoolean/Byte/Char/Short/Int/Long/Float/Double(Object obj, boolean/byte/char/short/int/long/float/double z); // 設置obj對象類型爲基本類型的域
    
    ...實現了AnnotatedElement接口的相關方法,在註解部分介紹...
}

public final class Method extends Executable {
    public Class<?> getDeclaringClass(); // 返回方法所屬的類
    public String getName(); // 方法名
    public int getModifiers();  // 方法修飾符,通過Modifier類來判斷
    public int getParameterCount(); // 參數個數
    public Class<?>[] getParameterTypes(); // 返回參數類型
    public Type[] getGenericParameterTypes(); // 返回參數類型,包含泛型參數信息
    public Parameter[] getParameters(); // 返回所有形參信息
    public Class<?> getReturnType(); // 返回值類型
    public Type getGenericReturnType(); // 返回值類型,包含泛型參數信息    
    public TypeVariable<Method>[] getTypeParameters(); // 返回方法的泛型參數
    public boolean isVarArgs(); // 是否是可變參數的方法
    public boolean isDefault(); // 是否是default方法,Java1.8+的接口的default方法
    public boolean isBridge(); // 是否是橋方法(橋方法見泛型一章)
    public boolean isSynthetic(); // 非用戶編寫的方法,自動生成的,詳見虛擬機一章節SYNTHETIC標識
    public abstract Class<?>[] getExceptionTypes(); // 返回聲明的異常
    public Object getDefaultValue(); // 返回方法的默認值,只有註解定義的方法纔有默認值
    
    public boolean isAccessible();  // 在調用invoke方法前會調用此方法判斷是否有訪問權限
    public void setAccessible(boolean flag);
    public Object invoke(Object obj, Object... args); // 對於靜態方法obj取null,該方法模仿了invokeXxx字節碼的執行過程
    
    public Annotation[][] getParameterAnnotations(); // 方法參數前的註解,每一個參數對應一個一維數組(該參數的所有註解),所有方法參數組成一個二維數組

	...實現了AnnotatedElement接口的相關方法,在註解部分介紹...
}

public final class Constructor<T> extends Executable {
    public Class<T> getDeclaringClass();  // 返回構造器所屬的類
    public String getName(); // 構造器名字
    public int getModifiers();  // 構造器修飾符,通過Modifier類來判斷
    public int getParameterCount(); // 參數個數
    public Class<?>[] getParameterTypes(); // 參數類型
    public Type[] getGenericParameterTypes(); // 參數類型,包括泛型參數信息        
    public Parameter[] getParameters(); // 返回所有形參信息
    public TypeVariable<Constructor<T>>[] getTypeParameters();  // 返回構造器的泛型參數
    public boolean isVarArgs(); // 是否是可變參數的構造器
    public boolean isSynthetic(); // 非用戶編寫的構造器,自動生成的,詳見虛擬機一章節SYNTHETIC標識
    public abstract Class<?>[] getExceptionTypes(); // 返回聲明的異常
    
	public boolean isAccessible(); // 在調用newInstance方法前會調用此方法判斷是否有訪問權限
    public void setAccessible(boolean flag); 
    public T newInstance(Object ... initargs); // 調用構造器創建一個新的對象

    public Annotation[][] getParameterAnnotations(); // 構造器參數前的註解,每一個參數對應一個一維數組(該參數的所有註解),所有構造器參數組成一個二維數組

	...實現了AnnotatedElement接口的相關方法,在註解部分介紹...
}

方法句柄

  方法句柄又被稱作現代化反射,其主要類位於java.lang.invoke包下,是Java7開始引入的新框架,與反射一樣,也是間接調用方法的一種途徑,但方法句柄比反射功能更強大、使用更靈活、性能也更好
  方法句柄框架中用MethodType類來描述方法的返回值類型以及參數類型,用MethodHandle來表示類的一個方法(有點像反射中的Method類,但它可以表示構造方法和域)。MethodHandle的類型由一個MethodType表示(其實MethodHandle的type()方法得到的MethodType並不能反映該MethodHandle的簽名,它只在方法句柄調用invoke時做類型檢查,MethodHanlder的實際簽名是不可更改的)。
  對於非靜態方法的MethodHandle,其MethodType的第一個參數必然是方法所屬類的類型,用來接收主體實例對象(方法內部通過this來訪問的對象),MethodHandle的該參數可以進行綁定,綁定後得到的是一個新的MethodHandle,其MethodType中不會再包含該參數,MethodHandle調用invoke的時候自然也不需要再傳遞主體實例對象。
  可以通過MethodHandles的靜態方法構建許多特殊類型的MethodHandle,但大多數時候還是通過MethodHandles的內部類Lookup來構建MethodHandle(調用不同的方法來構建MethodHandle可能得到的是MethodHandle的不同實現類(如靜態方法、域對應的MethodHandle實現類),這樣,在調用代表不同類型的MethodHandle時表現出不同的行爲)。通過Lookup創建非靜態方法的MethodHandle時,傳入的MethodType不包含主體實例參數,但構造出MethodHandle實例後,其MethodType便包含了此參數(可見Lookup的find方法對MethodType做了處理)。
  通過MethodHandle的asType方法可以構造出新的MethodHandle,新的MethodHandle的簽名不會發生改變(並不是符合新MethodType類型的簽名,這樣調用的還是原MethodHandle簽名的函數),新的MethodType只是在進行類型檢查時使用。
  與方法句柄相關的類如MethodHandle、MethodType以及Lookup通常都是不可變的,當調用可修改原對象的方法時通常修改的是一個克隆對象並返回被修改的克隆對象。
  Member(包括Method、Field以及Constructor)與MethodHandle可以相互轉換,通過Lookup的靜態方法unreflectXxx,可以將Member轉換爲MethodHandle,通過MethodHandles的靜態方法reflectAs可以將MethodHandle轉換爲Member。
  Member與MethodHandle都遵循java可見性的訪問權限,但Member會在invoke時再檢查權限,而在獲取MethodHandle時就會檢查權限,所以對同一個方法多次調用時,MethodHandle只會檢查一次權限,而Member會多次檢查權限,那麼MethodHandle的效率肯定要更高。

public abstract class MethodHandle {    
	// 方法調用的第一個參數爲方法調用的主體實例,如果爲靜態方法該參數不必傳入(Method的invoke方法執行靜態方法時第一個參數傳入null),之後的參數爲方法的實參
    public final native Object invokeExact(Object... args); // 要求參數與返回值類型同當前MethodHandle的MethodType嚴格的類型匹配(指兩個Class對象相等而非存在instanceof關係):
    		// 每一個實參的類型必須與其MethodType中的參數類型嚴格匹配(包括非靜態方法的第一個表示主體實例的參數);
    		// 返回值類型必須與MethodType中的返回值類型嚴格匹配,如果調用invoke方法後,返回值沒有賦值給任何變量(包括賦值給形參),那麼認爲實際返回值是void.class類型,如果進行了強轉後賦值給變量,那麼返回值被認爲是強轉的類型,如果沒有強轉直接賦值給了一個Object對象,那麼認爲返回值是Object類型。
    public final native Object invoke(Object... args); // 與嚴格的類型匹配相比,它可以進行自動適配類型,適配規則和重載尋找最貼切的方法一樣。
    		// 實際上它根據調用invoke方法的實參動態類型與接收返回值的變量的靜態類型(沒有變量接收時返回值類型爲void)構建一個新的MethodType,然後通過asType方法獲取一個新的MethodHandle並執行其invokeExact方法
    public Object invokeWithArguments(Object... args);
    public Object invokeWithArguments(List<?> args);
   
    public MethodType type(); // 如果當前方法非靜態方法,那麼第一個參數類型爲該方法所屬類的類型,該MethodType只用於參數與返回值類型檢查,並不能代表MethodHandle的方法簽名
	public MethodHandle asType(MethodType newType); // 克隆一個新的MethodHandle並將MethodType替換爲newType(非靜態方法需要包含主體實例的類型),新的MethodHandle與原MethodHandle只有type()的返回值不同,並不會改變MethodHandle對應函數的簽名。
			// MethodHandle存在的目的就是爲了執行方法,所以其參數個數必須與其代表的方法參數個數相同,而且各個參數與原參數還要存在可轉換的關係,如父子類、封裝類等
    
    public MethodHandle asSpreader(Class<?> arrayType, int arrayLength);
    public MethodHandle asCollector(Class<?> arrayType, int arrayLength);
    public MethodHandle asVarargsCollector(Class<?> arrayType);
    public boolean isVarargsCollector();
    public MethodHandle bindTo(Object x); 
}

// MethodType的所有實例都是不可變的,任何修改MethodType實例的方法都會返回一個新的MethodType實例,而原MethodType實例不會改變
public final class MethodType implements java.io.Serializable {
	// 通過靜態方法構建MethodType,返回值爲void可以用void.class來獲取Class對象
	public static MethodType methodType(Class<?> rtype, Class<?>[] ptypes); // rtype爲返回值類型,ptypes爲所有參數類型
	public static MethodType methodType(Class<?> rtype, List<Class<?>> ptypes);
    public static MethodType methodType(Class<?> rtype, Class<?>... ptypes);
    public static MethodType methodType(Class<?> rtype); // methodType(rtype, new Class<?>[0])
    public static MethodType methodType(Class<?> rtype, Class<?> ptype0); // methodType(rtype, new Class<?>[]{ ptype0 })
    public static MethodType methodType(Class<?> rtype, MethodType ptypes); // methodType(rtype, ptypes.parameterArray())
    
    // 創建一個通用MethodType實例,其返回值類型和形參都爲Object類型,objectArgCount表示參數個數。
    // 參數finalArray爲true時,在objectArgCount個Object參數後多添加一個Object[]參數,這樣MethodType參數的個數實際爲objectArgCount + 1。
    public static MethodType genericMethodType(int objectArgCount, boolean finalArray); 
    public static MethodType genericMethodType(int objectArgCount); // genericMethodType(objectArgCount, false)
    
    // 通過字節碼的方法描述信息來創建MethodType,如descriptor爲"([Ljava/lang/String;)Ljava/lang/String;"
    public static MethodType fromMethodDescriptorString(String descriptor, ClassLoader loader);
    
    public List<Class<?>> parameterList();
    public Class<?>[] parameterArray();
    public Class<?> parameterType(int num); 
    public Class<?> returnType();
    
    public boolean equals(Object x); // 參數必須爲MethodType類型,當兩個MethodType的返回值類型與參數類型全部相等時才返回true
    public boolean hasPrimitives(); // 在參數類型列表或返回值類型中有基本類型(在返回值中void也算基本類型)
    public boolean hasWrappers(); // 在參數類型列表或返回值類型中有封裝類型(在返回值中Void也算封裝類型)
    
    // 對MethodType對象的所有修改都會返回一個新的MethodType對象,而原MethodType對象不會改變。
    public MethodType changeParameterType(int num, Class<?> nptype); // 克隆一個MethodType並將第num個形參換成nptype類型
    public MethodType insertParameterTypes(int num, Class<?>... ptypesToInsert) ; // 克隆一個MethodType並在第num位置插入新的參數類型
    public MethodType appendParameterTypes(Class<?>... ptypesToInsert);
    public MethodType appendParameterTypes(List<Class<?>> ptypesToInsert);
    public MethodType dropParameterTypes(int start, int end); // 克隆一個新的MethodType並將start到end位置的參數去掉
    public MethodType changeReturnType(Class<?> nrtype); // 克隆一個新的MethodType並更改其返回值類型
    public MethodType generic(); // 克隆一個MethodType並將其返回值類型和參數類型全部更改爲Object類型
    public MethodType erase(); // 克隆一個MethodType並將其非基本類型的返回值類型和參數類型更改爲Object類型
    public MethodType wrap();  // 克隆一個新的MethodType並將所有的基本類型都轉換爲相應的封裝類型
    public MethodType unwrap();  // 克隆一個新的MethodType並將所有的封裝類型都轉換爲相應的基本類型
}    

// 全靜態方法,幫助構建擁有特殊功能的MethodHandle或通過其內部類Lookup幫助構建普通的MethodHandle
public class MethodHandles {
    public static Lookup lookup(); // 獲取一個lookup對象,其lookupClass爲該方法調用者,lookupModes爲PUBLIC|PRIVATE|PROTECTED|PACAKGE
    public static Lookup publicLookup(); // 獲取一個lookup對象,lookupClass爲Object.class,lookupModes爲PUBLIC
    
    // 將一個MethodHandle轉換爲Member(Method、Field或Constructor),expected表示Member的類型(Method.class、Field.class或Constructor.class,不確定時取Member.class),
    public static <T extends Member> T reflectAs(Class<T> expected, MethodHandle target);

	// 下列方法用於構建一些擁有特殊功能的MethodHandle,詳見API手冊
    public static MethodHandle arrayElementGetter(Class<?> arrayClass);
    public static MethodHandle arrayElementSetter(Class<?> arrayClass);
    public static MethodHandle explicitCastArguments(MethodHandle target, MethodType newType);
    public static MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder);
    public static MethodHandle constant(Class<?> type, Object value);
    public static MethodHandle identity(Class<?> type);
    public static MethodHandle insertArguments(MethodHandle target, int pos, Object... values);
    public static MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes);
    public static MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes);
    public static MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters);
    public static MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter);
    public static MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter);
    public static MethodHandle foldArguments(MethodHandle target, MethodHandle combiner);
    public static MethodHandle guardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback);
    public static MethodHandle catchException(MethodHandle target, Class<? extends Throwable> exType, MethodHandle handler);  // 調用該方法返回的MethodHandle時,執行target,當target拋出exType異常時執行handler處理
    public static MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType); // 調用該方法返回的MethodHandle時,直接拋出exType異常,returnType一般可取void.class
}

public static final class Lookup {
	// 通過findXxx獲取MethodHandle時,獲取的方法句柄必須是針對lookupClass可見,而且其可見性修飾符必須匹配lookupModes
    public int lookupModes(); // Lookup的模式,由四種基本模式(PUBLIC/PRIVATE/PROTECTED/PACAKGE)通過位或運算組合而成,其find方法只能查找具有相應權限的方法
    public Class<?> lookupClass();  // find方法時,必須針對該方法返回的類可見
    public Lookup in(Class<?> requestedLookupClass); // 創建一個新的Lookup對象,其lookupClass變爲requestedLookupClass,並且其查找的lookupModes權限比原Lookup對象會降低(這主要看新lookupClass與原lookupClass的關係,同包、繼承等)
    
    public MethodHandle findStatic(Class<?> refc, String name, MethodType type); 
    public MethodHandle findVirtual(Class<?> refc, String name, MethodType type); // 獲取普通非private方法,具有多態性(實際上也可以獲得private方法的MethodHandle,此MethodHandle不具備多態性)
    public MethodHandle findSpecial(Class<?> refc, String name, MethodType type, Class<?> specialCaller); // 獲取普通private方法,不具有多態性(也就是此MethodHandle執行refc對應類的方法,與invoke指定的主體實例無關),會根據specialCaller新建一個Lookup對象進行查詢
    public MethodHandle findConstructor(Class<?> refc, MethodType type); // 獲取構造方法,type的返回值永遠是void.class類型,但得到的MethodHandle的返回值類型爲refc
    public MethodHandle findGetter(Class<?> refc, String name, Class<?> type); // 獲取讀非靜態域對應的MethodHandle,調用此MethodHandle的invokeXxx時,返回的是域的值而不是執行其get方法的返回值,就算該域沒有get方法也會獲取到MethodHandle,其MethodType是(refc)type
    public MethodHandle findSetter(Class<?> refc, String name, Class<?> type); // 獲取寫非靜態域對應的MethodHandle,調用此MethodHandle的invokeXxx時,域的值被設置而不是執行其set方法,就算該域沒有set方法也會獲取到MethodHandle,其MethodType是(refc,type)void
    public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type); // 獲取讀靜態域對應的MethodHandle,其MethodType是()type
    public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type); // 獲取寫靜態域對應的MethodHandle,其MethodType是(type)void
    
    // 由反射獲取對應的MethodHandle
    public MethodHandle unreflect(Method m);
    public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller);
    public MethodHandle unreflectConstructor(Constructor<?> c);
    public MethodHandle unreflectGetter(Field f); // 如果f是靜態方法,那麼MethodType的參數爲空,如果f爲非靜態方法,那麼MethodType有一個f所屬類的參數,返回值都爲f代表域的類型
    public MethodHandle unreflectSetter(Field f); // 參數比unreflectGetter多一個參數,爲f代表域的類型,返回值void.class
    
    public MethodHandle bind(Object receiver, String name, MethodType type); // 獲取一個非靜態方法的MethodHandle並將主體實例receiver綁定到該MethodHandle上
}

變量的靜態類型與動態類型(深入瞭解多態)

  定義一個變量時,必須指定變量的類型,該類型就是變量的靜態類型,變量指向的對象的真實類型(變量getClass()方法獲取的類型)就是變量的動態類型,變量的靜態類型一經確定就不能更改,而變量動態類型可能發生改變(對象的類型是不可變的),如P p = new S1(),變量p的靜態類型爲P,動態類型爲S1,當執行p = new S2()時,p的動態類型變成了S2。
  編譯階段只能識別靜態類型,所以得到的字節碼中,方法的簽名也是使用靜態類型簽名的。假設父類P有非私有方法void func(Object var),子類S有非私有方法void func(String var),那麼P p = new S(); p.func(“var”)會調用到S類中方法func(Object var)(該方法從父類繼承並且沒有被重寫,如果重寫了就調用重寫方法)而非func(String var),因爲編譯時只能識別靜態類型,所以p.func(“var”)編譯時,p的靜態類型P中沒有完全匹配的方法func(String var),因此會被匹配到func(Object var)方法(重載方法的調用便是在該階段完成),所以會編譯出字節碼invokevirtual P.func: (Ljava/lang/Object;)v,在執行invokevirtual指令時,會根據操作數棧中的實際對象(這裏是new S(),即動態類型),調用到S類的func(Object var)方法;如果func(Object var)是私有方法,那麼就會編譯得到invokespecial P.func: (Ljava/lang/Object;)v,在執行該invokespecial時會直接調用靜態類型P的func(Object var)。對於不通過變量調用方法,如new S().func("var);編譯器會判斷出靜態類型,如這裏的S類。
  通過Method的invoke(Object obj, Object… args)方法調用函數時,調用結果與直接調用結果相同,其動態類型對應obj.getClass(),其靜態類型對應method.getDeclaringClass()返回的類型,也就是獲取該Method的Class對應的類型。
  通過MethodHandle的invoke(Object… args)方法調用函數時,調用結果與直接調用結果相同,動態類型對應args[0].getClass(),其靜態類型在構造MethodHandle時就已經確定,如Lookup的findXxx方法的第一個參數指定了其靜態類型,通過MethodHandle的asType()方法無法改變其靜態類型
  可見Method與MethodHandle並不是簡單地指向一個方法入口並通過其invoke方法跳到入口執行代碼,而是模仿了字節碼的invoke指令來實現多態等特性
  對於類的域,會繼承但不會體現多態性,如父類P有int域value,子類S也有int域value,那麼S的實例會有兩個域P.value和S.value(可見域也可繼承),那麼P p = new S(); p.value; 會獲取到S對象的P.value域,由於編譯階段只能識別靜態類型,所以得到的字節碼是getField P.value:I,這裏getField不會像invoke指令一樣多態查詢,而會直接獲取操作數棧中對象的P.value值,而不會獲取操作數棧中對象的實際類型S的S.value值。所以域的選擇是由靜態類型確定的
  Field與MethodHandle對域的訪問與直接訪問域依然保持一致性,由靜態類型確定訪問的域是父類的域還是子類的域

註解(Annotation)

  註解是作用於類、變量、方法、參數等的一種標記。註解本身不會對代碼的運行有任何影響,它須要與反射結合,由額外的代碼對註解的作用進行定義

註解的定義和使用

  須要通過@interface來定義註解,而且有幾個預定義的元註解可用來標記將要定義的新註解,所有的註解都是Annotation的子類。每一個註解都能有自己的屬性,其定義格式爲 attrType attrName() default deValue,其默認值爲可選(也就是可以沒有default deValue),當存在默認值時,在使用該註解時可以不顯式指定屬性值,否則必須顯式指定屬性值,屬性類型必須爲基本類型、String、Enum、Class或Annotation以及他們的數組之一,不能爲其他任何類型。包的註解需要在相應包下的package-info.java文件中進行註解。可標記註解的元註解如下:

註解 用途 說明
@Documented 是否加入JavaDoc 當註解定義時使用了該元註解時,javaDoc工具會生成其API文檔
@Inherited 是否繼承 具體見註解的繼承性一節
@Retention 生命週期(保留時段) value屬性爲RetentionPolicy枚舉類型,可取值RetentionPolicy.RUNTIME / RetentionPolicy.CLASS(加載過程中丟棄註解信息) / RetentionPolicy.SOURCE(編譯過程中丟棄註解信息),未標記該元註解的註解生命週期爲RetentionPolicy.CLASS
@Target 作用域(可用範圍) value屬性爲ElementType[]枚舉數組類型,可取值ElementType.TYPE(註解、類、接口、枚舉) / FIELD(域) / METHOD(方法) / PARAMETER(方法參數) / CONSTRUCTOR(構造器) / LOCAL_VARIABLE(局部變量) / ANNOTATION_TYPE(註解) / PACKAGE(包) / TYPE_PARAMETER / TYPE_USE,未標記該元註解的註解包含所有作用域
@Repeatable 可重複註解 Java8新加入的元註解,value屬性爲一個Class類型,代表可重複註解的容器註解,具體見可重複註解一節

  在使用註解時,只需要在被修飾的目標前用@註解名(attr1=attr1Value, attr2={attr2Value1, attr2Value2})標識即可,如果只需要顯式指定一個value屬性,那麼屬性名可以省略,如@註解名(valueAttrValue)(等效於@註解名(value=valueAttrValue)),如果某個屬性的值爲數組,但只有一個元素,可以不要大括號,如@註解(attr1={attr1Value})可以簡寫作@註解(attr1=attr1Value)。

// 定義一個註解
@Documented 
@Inherited 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.Type, ElementType.Method})
public @interface MyAnnotation {
	int value() default 0;
	String myAttr();
}

// 註解功能實現,例如如果當前類使用了該註解,那麼toString()方法返回value: myAttr,否則返回父類的toString()
@MyAnnotation(value=1, myAttr="myAttrValue")
public class Main {
	public String toString() {
		MyAnnotation myAnnotation = this.getClass().getAnnotation(MyAnnotation.class);
		if (MyAnnotation != null) {
			return myAnnotation.value() + ": " + myAnnotation.myAttr();
		}

		return super.toString();
	}
}

註解的繼承性

  註解的繼承方式主要分爲類的註解繼承、域和方法的註解繼承
  類的註解繼承是基於@Inherited的繼承。如果某個註解用元註解@Inherited標記,那麼表示該註解是可繼承的註解。當可繼承的註解用來標識一個類時,那麼它的子類可以繼承或覆蓋該註解,通過Class對象的getAnnotations()方法可以獲取直接標記此類的註解以及標記其祖先類的未被覆蓋的可繼承註解(未被@Inherited標記的註解不能繼承,如果當前類和祖先類都被同一個註解標記,那麼祖先類的註解就被覆蓋掉了),通過getDeclaredAnnotations()只能獲取直接標記此類的註解。這裏說的是子類繼承自父類,如果是一個類實現了一個接口,或者是子接口繼承自父接口,那麼類或子接口都無法獲取到上層接口的註解,除非先通過getInterfaces()獲取上層接口的Class對象,然後再通過上層接口的Class對象來獲取其註解。
  域和方法的註解繼承是基於類本身的繼承域或方法的註解,就是該類實際的域或方法的註解(相對於父類來說,子類實際的域或方法可能是繼承的、重寫的或新增的),與註解是否被@Inherited標記標記無關。對於繼承的域或方法,註解就是父類中該域或方法的註解;對於重寫的域或方法,註解就是重寫的域或方法的註解,與父類中被重寫的域或方法的註解沒任何關係;對於新增的域或方法,註解就是新增的域或方法的註解。Field和Method中,getAnnotations()與getDeclaredAnnotations()兩個方法沒有任何區別。如果一個子接口繼承了多個接口,這些被繼承的接口都對同一個域或方法進行了相同的註解,而子接口沒有顯示地寫出該方法,那麼會獲取第一個繼承的接口(extends排在最前面的接口)的域或方法的註解
  從上面的分析可以看出,只有標記在類上的註解才存在繼承性

可重複註解

  可重複註解是Java8引入的新特徵,它使得同一個目標可以被多個相同的註解標記。在Java8之前,同一個目標不能被多個相同的註解標註。
  如果我們希望對一個目標標記兩次MyAnnotation註解(可能兩次標註的註解屬性不同),在Java8之前,我們需要先定義一個註解容器ContainerAnnotation,讓某個屬性(如value屬性)返回一個MyAnnotation數組,這樣就可以用ContainerAnnotation來標記目標,其解析方式就是普通註解的解析方式。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    int value() default 0;
    String desc() default "default";
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ContainerAnnotation {
    MyAnnotation[] value() default {};
}

@ContainerAnnotation({
        @MyAnnotation,
        @MyAnnotation(value = 1, desc = "abc")
})
public class Main {
    public static void main(String[] vars) {
        ContainerAnnotation containerAnnotation = Main.class.getAnnotation(ContainerAnnotation.class);
        if (null != containerAnnotation) {
            for (MyAnnotation myAnnotation : containerAnnotation.value()) {
                System.out.println(myAnnotation.value() + ": " + myAnnotation.desc());
            }
        }
    }
}

  在Java8之後,我們同樣需要定義一個註解容器ContainerAnnotation,讓其value屬性返回一個MyAnnotation數組(必須是value屬性),並且用@Repeatable(ContainerAnnotation.class)標記MyAnnotation,這樣就可以多次使用MyAnnotation註解來標記目標
  如果目標只標記了一次MyAnnotation註解,那麼此時的MyAnnotation就相當於一個普通註解(解析目標的註解時只能獲取到MyAnnotation註解,無法獲取到ContainerAnnotation註解),如果目標標記了多次MyAnnotation註解,那麼此時多個MyAnnotation註解就相當於標註了一個ContainerAnnotation(該註解的返回值爲所有標記的MyAnnotation的數組,解析目標的註解只能獲取到ContainerAnnotation註解而無法獲取到MyAnnotation註解)。不僅在解析時可以做這樣的假設,繼承也可以做這樣的假設,如果父類有多個可繼承的可重複註解,那麼相當於父類標記了一個可繼承的容器註解,子類如果也多次標記可重複註解,那麼相當於子類也標記了一個容器註解,此時子類的容器註解會覆蓋父類的容器註解,子類如果只標記了一次可重複註解,那麼子類就會有一個新標記的可重複註解以及一個繼承的容器註解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ContainerAnnotation.class)
@Inherited
@interface MyAnnotation {
    int value() default 0;
    String desc() default "default";
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface ContainerAnnotation {
    MyAnnotation[] value() default {};
}

@MyAnnotation(value = 2, desc = "father")
@MyAnnotation(value = 2, desc = "father")
class Father {

}

@MyAnnotation(value = 1, desc = "main")
public class Main extends Father {
    public static void main(String[] vars) {
        ContainerAnnotation containerAnnotation = Main.class.getAnnotation(ContainerAnnotation.class);
        if (null != containerAnnotation) {
            for (MyAnnotation myAnnotation : containerAnnotation.value()) {
                System.out.println(myAnnotation.value() + ": " + myAnnotation.desc());
            }
        }

        System.out.println("-----------------------");

        MyAnnotation myAnnotation = Main.class.getAnnotation(MyAnnotation.class);
        if (null != myAnnotation) {
            System.out.println(myAnnotation.value() + ": " + myAnnotation.desc());
        }
    }
}

註解的解析

  註解的作用域涉及多個目標,這些目標對應的類如Class、Field、Method、Package等都實現了AnnotatedElement接口。那麼AnnotatedElement接口就是對註解的目標的標記
  在Java8引入可重複註解後,AnnotatedElement添加了兩個ByType方法。當ByTpye方法的參數類型爲不可重複註解時(容器註解未使用@Repeatable標記,所以容器註解也不是可重複註解),它們與對應的非ByType方法效果相同,只是以單元素數組的形式返回結果;當ByTpye方法的參數類型爲可重複註解時,它不會把多個可重複註解認爲是一個容器註解的屬性,而會以數組的形式返回所有可重複註解,如果當前目標爲類且可重複註解是可繼承的,那麼只有在子類未被可重複註解標記時纔會繼承父類的全部可重複註解,只要子類標記了可重複註解(無論多少個),那麼父類的可重複註解(無論多少個)會被全部覆蓋

public interface AnnotatedElement {
	boolean isAnnotationPresent(Class<? extends Annotation> annotationClass); // 當前目標是否被指定的註解標識,如果當前目標是類,還會檢查祖先類上的可繼承註解
	
    <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass); // 獲取直接在當前目標上標記的指定註解
	<T extends Annotation> T getAnnotation(Class<T> annotationClass); // 與getDeclaredAnnotation相比,如果當前目標是類,當前目標的祖先類(不包括extends和implements的接口)上標記的未被覆蓋的可繼承註解也能被識別
    Annotation[] getDeclaredAnnotations();
    Annotation[] getAnnotations();  
    
    <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass); 
    <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass); 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章