獲取Java類型系統,主要有兩個方式:一種是傳統的RTTI(Run-Time Type Identification),它假定咱們在編譯時已經知道了全部的類型信息;另外一種是反射(Reflect),它容許咱們在程序運行時獲取並使用類型信息。
假若有一個簡單的繼承體系,讓咱們看下在RTTI和Reflect不一樣狀況下如何獲取類型信息。java
Animal爲接口,定義getType以返回不一樣動物的類型,Cat、Dog、Elephant等爲具體實現類,均實現getType接口。通常狀況下,咱們會建立一個具體的對象(Cat,Dog,Elephant等),把它向上轉型爲Animal,並在程序後面直接使用Animal引用。設計模式
具體樣例代碼以下:數組
/** * 動物 */ public interface Animal { /** * 獲取動物類型 * @return */ String getType(); } /** * 動物具體子類 貓 */ public class Cat implements Animal{ @Override public String getType() { return "貓"; } } /** * 動物具體子類 狗 */ public class Dog implements Animal{ @Override public String getType() { return "狗"; } } /** * 動物具體實現 大象 */ public class Elephant implements Animal{ @Override public String getType() { return "大象"; } }
讓咱們看下相同的功能經過硬編碼與反射兩個機制如何實現。安全
RTTI假定在編譯期,已經知道了全部的類型信息。在編碼時,能夠直接使用具體的類型信息,這是咱們最多見的類型用法。
在編譯期,編譯器經過容器、泛型保障類型系統的完整性;在運行時,由類型轉換操做來確保這一點。app
硬編碼樣例以下:框架
public static void main(String... args){ List<Animal> animals = createAnimals(); for (Animal animal : animals){ System.out.println(animal.getType()); } } /** * RTTI假定咱們在編譯時已經知道了全部的類型 * @return */ private static List<Animal> createAnimals() { List<Animal> animals = new ArrayList<>(); animals.add(new Cat()); // 已知類型Cat animals.add(new Elephant()); // 已知類型Elephant animals.add(new Dog()); // 已知類型 Dog return animals; }
在這個例子中,咱們把Cat、Elephant、Dog等向上轉型爲Animal並存放於List<Animal>中,在轉型過程當中丟失了具體的類型信息(只保留了接口信息Animal);當咱們從List<Animal>中取出元素時,這時容器(容器內部全部的元素都被當作Object)會自動將結果轉型成Animal。這是RTTI最基本的用法,在Java中全部的類型轉換都是在運行時進行有效性檢查。這也是RTTI的含義,在運行時,識別一個對象的類型。ide
Reflect容許咱們在運行時獲取並使用類型信息,它主要用於在編譯階段沒法得到全部的類型信息的場景,如各種框架。
反射樣例以下:函數
private static final String[] ANIMAL_TYPES = new String[]{ "com.example.reflectdemo.base.Cat", "com.example.reflectdemo.base.Elephant", "com.example.reflectdemo.base.Dog" }; public static void main(String... args){ List<Object> animals = createAnimals(); for (Object animal : animals){ System.out.println(invokeGetType(animal)); } } /** * 利用反射API執行getType方法(等同於animal.getType) * @param animal * @return */ private static String invokeGetType(Object animal){ try { Method getTypeMethod = Animal.class.getMethod("getType"); return (String) getTypeMethod.invoke(animal); }catch (Exception e){ return null; } } /** * 反射容許咱們在運行時獲取類型信息 * @return */ private static List<Object> createAnimals() { List<Object> animals = new ArrayList<>(); for (String cls : ANIMAL_TYPES){ animals.add(instanceByReflect(cls)); } return animals; } /** * 使用反射機制,在運行時動態的實例化對象(等同於new關鍵字) * @param clsStr * @return */ private static Object instanceByReflect(String clsStr) { try { // 經過反射獲取類型信息 Class cls = Class.forName(clsStr); // 經過Class實例化對象 Object object = cls.newInstance(); return object; }catch (Exception e){ e.printStackTrace(); return null; } }
反射,能夠經過一組特殊的API,在運行時,動態執行全部Java硬編碼完成的功能(如對象建立、方法調用等)。性能
相比硬編碼,Java反射API要複雜的多,但其給咱們帶來了更大的靈活性。this
要理解RTTI在Java中的工做原理,首先須要知道類型信息在Java中是如何表示的。這個工做是由稱爲Class對象的特殊對象完成的,它包含了與類相關的全部信息。Java使用Class對象來執行RTTI。
類是程序的一部分,每一個類都會有一個Class對象。每當編寫並編譯一個新類(動態代理、CGLIB、運行時編譯都能建立新類),就會產生一個Class對象,爲了生成這個類的對象,運行這個程序的JVM將使用稱爲「類加載器」的子系統。
類加載器子系統,是JVM體系重要的一環,主要完成將class二進制文件加載到JVM中,並將其轉換爲Class對象的過程。
類加載器子系統其實是一條類加載器鏈,可是隻有一個原生類加載器,它是JVM實現的一部分。原生類加載器加載的是可信類,包括Java API類,他們一般是從本地加載。在這條鏈中,一般不須要添加額外的類加載器,可是若是有特殊需求,能夠掛載新的類加載器(好比Web容器)。
全部的類都是在第一次使用時,動態加載到JVM中的,當程序建立第一次對類的靜態成員引用時,就會加載這個類。實際上構造函數也是類的靜態方法,所以使用new關鍵字建立類的新對象也會被當作對類的靜態引用,從而觸發類加載器對類的加載。
Java程序在它開始運行以前並不是被所有加載,各個部分是在須要時按需加載的。類加載器在加載類以前,首先檢查這個類的Class是否已經加載,若是還沒有加載,加載器會按照類名查找class文件,並對字節碼進行有效性校驗,一旦Class對象被載入內存,它就用來建立這個類的全部對象。
static初始化塊在類加載時調用,所以能夠用於觀察類在何時進行加載,樣例以下:
static class C1{ static { System.out.println("C1"); } } static class C2{ static { System.out.println("C2"); } } static class C3{ static { System.out.println("C3"); } } public static void main(String... args) throws Exception{ System.out.println("new start"); // 構造函數爲類的靜態引用,觸發類型加載 new C1(); new C1(); System.out.println("new end"); System.out.println(); System.out.println("Class.forName start"); // Class.forName爲Class上的靜態函數,用於強制加載Class Class.forName("com.example.reflectdemo.classloader.ClassLoaderTest$C2"); Class.forName("com.example.reflectdemo.classloader.ClassLoaderTest$C2"); System.out.println("Class.forName end"); System.out.println(); System.out.println("C3.class start"); // Class引用,會觸發Class加載,可是不會觸發初始化 Class c1 = C3.class; Class c2 = C3.class; System.out.println("C3.class end"); System.out.println(); System.out.println("c1.newInstance start"); // 調用class上的方法,觸發初始化邏輯 c1.newInstance(); System.out.println("c1.newInstance end"); }
輸出結果爲:
new start C1 new end Class.forName start C2 Class.forName end C3.class start C3.class end c1.newInstance start C3 c1.newInstance end
看結果,C3.class的調用不會自動的初始化該Class對象(調用static塊)。爲了使用Class而作的準備工做主要包括三個步驟:
Class對象做爲Java類型體系的入口,如何獲取實例成爲第一個要解決的問題。
Class對象的獲取主要有如下幾種途徑:
對於基本數據類型對於的包裝器類,還提供了一個TYPE字段,指向對應的基本類型的Class對象。
基本類型 | TYPE類型 |
---|---|
boolean.class | Boolean.TYPE |
char.class | Char.TYPE |
byte.class | Byte.TYPE |
short.class | Short.TYPE |
int.class | Integer.TYPE |
long.class | Long.TYPE |
float.class | Float.TYPE |
double.class | Double.TYPE |
void.class | Void.TYPE |
Class對象存儲了一個class的全部類型信息,當獲取到Class對象後,便能經過API獲取到全部信息。
在進入Class類型信息以前,須要簡單的瞭解下幾個反射的基類,以便更好的理解反射實現體系。
Class API基礎主要是爲反射API提供通用特性的接口或基類。因爲其通用性,現統一介紹,在具體的API中將對其進行忽略。
AnnotatedElement爲Java1.5新增接口,該接口表明程序中能夠接受註解的程序元素,並提供統一的Annotation訪問方式,賦予API經過反射獲取Annotation的能力,當一個Annotation類型被定義爲運行時後,該註解才能是運行時可見,當class文件被裝載時被保存在class文件中的Annotation纔會被虛擬機讀取。
AnnotatedElement接口是全部註解元素(Class、Method、Field、Package和Constructor)的父接口,因此程序經過反射獲取了某個類的AnnotatedElement對象以後,程序就能夠調用該對象的下列方法來訪問Annotation信息:
方法 | 含義 |
---|---|
<T extends Annotation> T getAnnotation(Class<T> annotationClass) | 返回程序元素上存在的、指定類型的註解,若是該類型註解不存在,則返回null |
Annotation[] getAnnotations() | 返回該程序元素上存在的全部註解 |
boolean is AnnotationPresent(Class<?extends Annotation> annotationClass) | 判斷該程序元素上是否包含指定類型的註解,存在則返回true,不然返回false |
Annotation[] getDeclaredAnnotations() | 返回直接存在於此元素上的全部註釋。與此接口中的其餘方法不一樣,該方法將忽略繼承的註釋。(若是沒有註釋直接存在於此元素上,則返回長度爲零的一個數組)該方法的調用者能夠隨意修改返回的數組;這不會對其餘調用者返回的數組產生任何影響。 |
AnnotatedElement子類涵蓋全部能夠出現Annotation的地方,其中包括:
樣例以下:
public class AnnotatedElementTest { public static void main(String... args){ System.out.println("getAnnotations:"); for (Annotation annotation : A.class.getAnnotations()){ System.out.println(annotation); } System.out.println(); System.out.println("getAnnotation:" + A.class.getAnnotation(TestAnn1.class)); System.out.println(); System.out.println("isAnnotationPresent:" + A.class.isAnnotationPresent(TestAnn1.class)); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnn1{ } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnn2{ } @TestAnn1 @TestAnn2 public class A{ } }
輸出結果以下:
getAnnotations: @com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn1() @com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn2() getAnnotation:@com.example.reflectdemo.annotatedElement.AnnotatedElementTest$TestAnn1() isAnnotationPresent:true
Member用於標記反射中簡單元素。
所涉及方法以下:
方法 | 含義 |
---|---|
getDeclaringClass | 元素所在類 |
getName | 元素名稱 |
getModifiers | 元素修飾 |
isSynthetic | 是否爲Synthetic,synthetic是由編譯器引入的字段、方法、類或其餘結構,主要用於JVM內部使用。 |
其子類主要包括:
AccessibleObject可訪問對象,其對元素的可見性進行統一封裝。同時實現AnnotatedElement接口,提供對Annotation元素的訪問。
所涉及方法以下:
方法 | 含義 |
---|---|
isAccessible | 是否可訪問 |
setAccessible | 從新訪問性 |
其中AccessibleObject所涉及的子類主要包括:
AccessibleObject 對可見性提供了強大的支持,使咱們可以經過反射擴展訪問限制,甚至能夠對private成員進行訪問。
樣例代碼以下:
public class TestBean { private String id; public String getId() { return id; } private void setId(String id) { this.id = id; } } public class AccessibleObjectBase { public static void main(String... args) throws Exception{ TestBean testBean = new TestBean(); // private方法, 不能直接調用 Method setId = TestBean.class.getDeclaredMethod("setId", String.class); System.out.println("setId:" + setId.isAccessible()); try { setId.invoke(testBean, "111"); }catch (Exception e){ System.out.println("private不能直接調用"); } setId.setAccessible(true); System.out.println("設置可訪問:" + setId.isAccessible()); setId.invoke(testBean, "111"); System.out.println("設置可訪問後,能夠繞過private限制,進行調用,結果爲:" + testBean.getId()); } }
輸出結果以下:
setId:false private不能直接調用 設置可訪問:true 設置可訪問後,能夠繞過private限制,進行調用,結果爲:111
Executable表示可執行元素的一種封裝,能夠獲取方法簽名相關信息。
所涉及方法以下:
方法 | 含義 |
---|---|
getName | 獲取名稱 |
getModifiers | 獲取修飾符 |
getTypeParameters | 獲取類型參數(泛型) |
getParameterTypes | 獲取參數列表 |
getParameterCount | 獲取參數數量 |
getGenericParameterTypes | 獲取參數類型 |
getExceptionTypes | 獲取異常列表 |
getGenericExceptionTypes | 獲取異常列表 |
鎖涉及的子類主要有:
樣例代碼以下:
public class TestBean { private String id; public <T, R>TestBean(String id) throws IllegalArgumentException, NotImplementedException { this.id = id; } public String getId() { return id; } private void setId(String id) { this.id = id; } } public class ExecutableTest { public static void main(String... args) throws Exception{ for (Constructor constructor : TestBean.class.getConstructors()){ System.out.println("getName: " + constructor.getName()); System.out.println(); System.out.println("getModifiers: " + Modifier.toString(constructor.getModifiers())); System.out.println(); System.out.println("getTypeParameters:"); for (TypeVariable<Constructor> t : constructor.getTypeParameters()){ System.out.println("type var:" + t.getName()); } System.out.println(); System.out.println("getParameterCount:" + constructor.getParameterCount()); System.out.println(); System.out.println("getParameterTypes:"); for (Class cls : constructor.getParameterTypes()){ System.out.println(cls.getName()); } System.out.println(); System.out.println("getExceptionTypes:"); for (Class cls : constructor.getExceptionTypes()){ System.out.println(cls.getName()); } } } }
輸出結果爲:
getName: com.example.reflectdemo.reflectbase.TestBean getModifiers: public getTypeParameters: type var:T type var:R getParameterCount:1 getParameterTypes: java.lang.String getExceptionTypes: java.lang.IllegalArgumentException sun.reflect.generics.reflectiveObjects.NotImplementedException
整個反射機制存在着通用的命名規則,瞭解這些規則,能夠大大減小理解方法的阻力。
getXXX和getDeclaredXXX, 二者主要區別在於獲取元素的可見性不一樣,通常狀況下getXXX返回public類型的元素,而getDeclaredXXX獲取全部的元素,其中包括private、protected、public和package。
Class自身信息包括類名、包名、父類以及實現的接口等。
Class類實現AnnotatedElement接口,以提供對註解的支持。除此之外,涉及方法以下:
方法 | 含義 |
---|---|
getName | 獲取類名 |
getCanonicalName | 獲得目標類的全名(包名+類名) |
getSimpleName | 等同於getCanonicalName |
getTypeParameters | 獲取類型參數(泛型) |
getSuperclass | 獲取父類 |
getPackage | 獲取包信息 |
getInterfaces | 獲取實現接口 |
getModifiers | 獲取修飾符 |
isAnonymousClass | 是否匿名類 |
isLocalClass | 是否局部類 |
isMemberClass | 是否成員類 |
isEnum | 是否枚舉 |
isInterface | 是不是接口 |
isArray | 是不是數組 |
getComponentType | 獲取數組元素類型 |
isPrimitive | 是不是基本類型 |
isAnnotation | 是不是註解 |
getEnumConstants | 獲取枚舉全部類型 |
getClasses | 獲取定義在該類中的public類型 |
getDeclaredClasses | 獲取定義在該類中的類型 |
實例以下:
class Base<T> implements Callable<T> { @Override public T call() throws Exception { return null; } } public final class BaseClassInfo<T, R extends Runnable> extends Base<T> implements Runnable, Serializable { @Override public void run() { } public static void main(String... args){ Class<BaseClassInfo> cls = BaseClassInfo.class; System.out.println("getName:" + cls.getName()); System.out.println(); System.out.println("getCanonicalName:" + cls.getCanonicalName()); System.out.println(); System.out.println("getSimpleName:" + cls.getSimpleName()); System.out.println(); System.out.println("getSuperclass:" + cls.getSuperclass()); System.out.println(); System.out.println("getPackage:" + cls.getPackage()); System.out.println(); for (Class c : cls.getInterfaces()){ System.out.println("interface : " + c.getSimpleName()); } System.out.println(); for (TypeVariable<Class<BaseClassInfo>> typeVariable : cls.getTypeParameters()){ System.out.println("type var : " + typeVariable.getTypeName()); } System.out.println(); System.out.println("getModifiers:" + Modifier.toString(cls.getModifiers())); } }
輸出結果爲:
getName:com.example.reflectdemo.classdetail.BaseClassInfo getCanonicalName:com.example.reflectdemo.classdetail.BaseClassInfo getSimpleName:BaseClassInfo getSuperclass:class com.example.reflectdemo.classdetail.Base getPackage:package com.example.reflectdemo.classdetail interface : Runnable interface : Serializable type var : T type var : R getModifiers:public final
Class類型判斷,實例以下:
public class ClassTypeTest { public static void main(String... args){ Runnable runnable = new Runnable() { @Override public void run() { printClassType(getClass()); } }; System.out.println("匿名內部類"); runnable.run(); class M implements Runnable{ @Override public void run() { printClassType(getClass()); } } System.out.println("方法內部類"); new M().run(); System.out.println("內部類"); new ClassTypeTest().new T().run(); System.out.println("靜態內部類"); new S().run(); System.out.println("枚舉"); printClassType(EnumTest.class); System.out.println("接口"); printClassType(Runnable.class); System.out.println("數組"); printClassType(int[].class); System.out.println("int"); printClassType(int.class); System.out.println("註解"); printClassType(AnnTest.class); } class T implements Runnable{ @Override public void run() { printClassType(getClass()); } } static class S implements Runnable{ @Override public void run() { printClassType(getClass()); } } enum EnumTest{ A, B, C } @interface AnnTest{ } private static void printClassType(Class cls){ System.out.println("Class:" + cls.getName()); System.out.println("isAnonymousClass:" + cls.isAnonymousClass()); System.out.println("isLocalClass:" + cls.isLocalClass()); System.out.println("isMemberClass:" + cls.isMemberClass()); System.out.println("isEnum:" + cls.isEnum()); System.out.println("isInterface:" + cls.isInterface()); System.out.println("isArray:" + cls.isArray()); System.out.println("isPrimitive:" + cls.isPrimitive()); System.out.println("isAnnotation:" + cls.isAnnotation()); if (cls.isEnum()){ System.out.println("getEnumConstants:"); for (Object o : cls.getEnumConstants()){ System.out.println(o); } } if (cls.isArray()){ System.out.println("getComponentType:" + cls.getComponentType()); } System.out.println(); } }
輸出結果以下:
匿名內部類 Class:com.example.reflectdemo.classdetail.ClassTypeTest$1 isAnonymousClass:true isLocalClass:false isMemberClass:false isEnum:false isInterface:false isArray:false isPrimitive:false isAnnotation:false 方法內部類 Class:com.example.reflectdemo.classdetail.ClassTypeTest$1M isAnonymousClass:false isLocalClass:true isMemberClass:false isEnum:false isInterface:false isArray:false isPrimitive:false isAnnotation:false 內部類 Class:com.example.reflectdemo.classdetail.ClassTypeTest$T isAnonymousClass:false isLocalClass:false isMemberClass:true isEnum:false isInterface:false isArray:false isPrimitive:false isAnnotation:false 靜態內部類 Class:com.example.reflectdemo.classdetail.ClassTypeTest$S isAnonymousClass:false isLocalClass:false isMemberClass:true isEnum:false isInterface:false isArray:false isPrimitive:false isAnnotation:false 枚舉 Class:com.example.reflectdemo.classdetail.ClassTypeTest$EnumTest isAnonymousClass:false isLocalClass:false isMemberClass:true isEnum:true isInterface:false isArray:false isPrimitive:false isAnnotation:false getEnumConstants: A B C 接口 Class:java.lang.Runnable isAnonymousClass:false isLocalClass:false isMemberClass:false isEnum:false isInterface:true isArray:false isPrimitive:false isAnnotation:false 數組 Class:[I isAnonymousClass:false isLocalClass:false isMemberClass:false isEnum:false isInterface:false isArray:true isPrimitive:false isAnnotation:false getComponentType:int int Class:int isAnonymousClass:false isLocalClass:false isMemberClass:false isEnum:false isInterface:false isArray:false isPrimitive:true isAnnotation:false 註解 Class:com.example.reflectdemo.classdetail.ClassTypeTest$AnnTest isAnonymousClass:false isLocalClass:false isMemberClass:true isEnum:false isInterface:true isArray:false isPrimitive:false isAnnotation:true
內部類型樣例以下:
public class InnerClassTest { public static void main(String... args){ System.out.println("getClasses"); for (Class cls : InnerClassTest.class.getClasses()){ System.out.println(cls.getName()); } } public interface I{ } public class A implements I{ } public class B implements I{ } }
輸出結果以下:
getClasses com.example.reflectdemo.classdetail.InnerClassTest$B com.example.reflectdemo.classdetail.InnerClassTest$A com.example.reflectdemo.classdetail.InnerClassTest$I
對象實例化,主要經過Constructor實例完成,首先經過相關方法獲取Constructor對象,而後進行實例化操做。
所涉及的方法以下:
方法 | 含義 |
---|---|
newInstance | 使用默認構造函數實例化對象 |
getConstructors | 獲取public構造函數 |
getConstructor(Class<?>... parameterTypes) | 獲取特定public構造函數 |
getDeclaredConstructors | 獲取全部的構造函數 |
getDeclaredConstructor | 獲取特定構造函數 |
實例化涉及的核心類爲Constructor,Constructor繼承自Executable,擁有AnnotatedElement、AccessibleObject、Executable等相關功能,其核心方法以下:
方法 | 含義 |
---|---|
newInstance | 調用構造函數,實例化對象 |
樣例以下:
public class TestBean { private final Integer id; private final String name; public <T, R>TestBean(Integer id, String name) throws IllegalArgumentException, NotImplementedException { this.id = id; this.name = name; } @Override public String toString() { return "TestBean{" + "id=" + id + ", name='" + name + '\'' + '}'; } } public class ConstructorTest { public static void main(String... args) throws Exception{ for (Constructor constructor : TestBean.class.getConstructors()){ TestBean bean = (TestBean) constructor.newInstance(1, "Test"); System.out.println("newInstance:" + bean); } } }
輸出結果爲:
newInstance:TestBean{id=1, name='Test'}
對象屬性是類型中最主要的信息之一,主要經過Field表示,首先經過相關方法獲取Field實例,而後進行屬性值操做。
所涉及的方法以下:
方法 | 含義 |
---|---|
getFields | 獲取public字段 |
getField(String name) | 獲取特定public字段 |
getDeclaredFields | 獲取全部的的屬性 |
getDeclaredField | 獲取特定字段 |
Field繼承自AccessibleObject實現Member接口,擁有AccessibleObject、AnnotatedElement、Member相關功能,其核心方法以下:
方法 | 含義 |
---|---|
isEnumConstant | 是否枚舉常量 |
getType | 獲取類型 |
get | 獲取屬性值 |
getBoolean | 獲取boolean值 |
getByte | 獲取byte值 |
getChar | 獲取chat值 |
getShort | 獲取short值 |
getInt | 獲取int值 |
getLong | 獲取long值 |
getFloat | 獲取float值 |
getDouble | 獲取double值 |
set | 設置屬性值 |
setBoolean | 設置boolean值 |
setByte | 設置byte值 |
setChar | 設置char值 |
setShort | 設置short值 |
setInt | 設置int值 |
setLong | 設置long值 |
setFloat | 設置float值 |
setDouble | 設置double值 |
實例以下:
public enum EnumTest { A } public class FieldBean { private EnumTest aEnum; private String aString; private boolean aBoolean; private byte aByte; private char aChar; private short aShort; private int anInt; private long aLong; private float aFloat; private double aDouble; } public class FieldTest { public static void main(String... args) throws NoSuchFieldException, IllegalAccessException { FieldBean fieldBean = new FieldBean(); Field aEnum = getByName("aEnum"); Field aString = getByName("aString"); Field aBoolean = getByName("aBoolean"); Field aByte = getByName("aByte"); Field aChar = getByName("aChar"); Field aShort = getByName("aShort"); Field anInt = getByName("anInt"); Field aLong = getByName("aLong"); Field aFloat = getByName("aFloat"); Field aDouble = getByName("aDouble"); aEnum.set(fieldBean, EnumTest.A); System.out.println("isEnumConstant: " + aEnum.isEnumConstant()); System.out.println("set and get enum : " + aEnum.get(fieldBean)); aString.set(fieldBean, "Test"); System.out.println("set and get String : " + aString.get(fieldBean)); aBoolean.setBoolean(fieldBean, true); System.out.println("set and get Boolean : " + aBoolean.getBoolean(fieldBean)); aByte.setByte(fieldBean, (byte) 1); System.out.println("set and get Byte : " + aByte.getByte(fieldBean)); aChar.setChar(fieldBean, 'a'); System.out.println("set and get Char : " + aChar.getChar(fieldBean)); aShort.setShort(fieldBean, (short) 1); System.out.println("set and get Short : " + aShort.getShort(fieldBean)); anInt.setInt(fieldBean, 1); System.out.println("set and get Int : " + anInt.getInt(fieldBean)); aLong.setLong(fieldBean, 1L); System.out.println("set and get Long : " + aLong.getLong(fieldBean)); aFloat.setFloat(fieldBean, 1f); System.out.println("set and get Float : " + aLong.getFloat(fieldBean)); aDouble.setDouble(fieldBean, 1.1); System.out.println("set and get Double : " + aLong.getDouble(fieldBean)); } private static Field getByName(String name) throws NoSuchFieldException { Field field = FieldBean.class.getDeclaredField(name); field.setAccessible(true); return field; } }
類型中的方法經過Method表示,首先經過相關方法獲取Method實現,而後經過反射執行方法。
所涉及的方法以下:
方法 | 含義 |
---|---|
getMethods | 獲取public方法 |
getMethod(String name, Class<?>... parameterTypes) | 獲取特定public方法 |
getDeclaredMethods | 獲取全部方法 |
getDeclaredMethod | 獲取特定方法 |
Method繼承自Executable,擁有AnnotatedElement、AccessibleObject、Executable等相關功能,其核心方法以下:
方法 | 含義 |
---|---|
getReturnType | 獲取方法返回類型 |
invoke | 調用方法 |
isBridge | 是否爲橋接方法。橋接方法是 JDK 1.5 引入泛型後,爲了使Java的泛型方法生成的字節碼和 1.5 版本前的字節碼相兼容,由編譯器自動生成的方法。咱們能夠經過Method.isBridge()方法來判斷一個方法是不是橋接方法。 |
isDefault | 是否爲默認方法 |
實例以下:
public interface SayHi { String get(); default void hi(){ System.out.println("Hi " + get()); } } public class MethodBean implements Function<String, String>, SayHi { private final String name; public MethodBean(String name) { this.name = name; } @Override public String get() { return "Hi " + name; } @Override public String apply(String s) { return s + name; } } public class MethodTest { public static void main(String... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Method strMethod = MethodBean.class.getDeclaredMethod("apply", String.class); Method objMethod = MethodBean.class.getDeclaredMethod("apply", Object.class); Method hiMethod = SayHi.class.getDeclaredMethod("hi"); MethodBean methodBean = new MethodBean("張三"); System.out.println("Return Type:"); System.out.println("getMethod(String):" + strMethod.getReturnType()); System.out.println("getMethod(Object):" + objMethod.getReturnType()); System.out.println("hi():" + hiMethod.getReturnType()); System.out.println(); System.out.println("isBridge:"); System.out.println("getMethod(String):" + strMethod.isBridge()); System.out.println("getMethod(Object):" + objMethod.isBridge()); System.out.println("hi():" + hiMethod.isBridge()); System.out.println(); System.out.println("isDefault:"); System.out.println("getMethod(String):" + strMethod.isDefault()); System.out.println("getMethod(Object):" + objMethod.isDefault()); System.out.println("hi():" + hiMethod.isDefault()); System.out.println(); System.out.println("invoke:"); System.out.println("invoke(String):" + strMethod.invoke(methodBean, "Test")); System.out.println("invoke(Object):" + objMethod.invoke(methodBean, "Test")); System.out.println("hi():" + hiMethod.invoke(methodBean)); } }
輸出結果:
Return Type: getMethod(String):class java.lang.String getMethod(Object):class java.lang.Object hi():void isBridge: getMethod(String):false getMethod(Object):true hi():false isDefault: getMethod(String):false getMethod(Object):false hi():true invoke: invoke(String):Test張三 invoke(Object):Test張三 Hi Hi 張三 hi():null
除上述核心方法外,Class對象提供了一些使用方法。
所涉及方法以下:
方法 | 含義 |
---|---|
isInstance | 判斷某對象是不是該類的實例 |
isAssignableFrom | 斷定此 Class 對象所表示的類或接口與指定的 Class 參數所表示的類或接口是否相同,或是不是其超類或超接口。若是是則返回 true;不然返回 false。 |
getClassLoader | 獲取加載當前類的ClassLoader |
getResourceAsStream | 根據該ClassLoader加載資源 |
getResource | 根據該ClassLoader加載資源 |
public class Task implements Runnable{ @Override public void run() { } } public class OtherTest { public static void main(String...args){ Task task = new Task(); System.out.println("Runnable isInstance Task:" + Runnable.class.isInstance(task)); System.out.println("Task isInstance Task:" + Task.class.isInstance(task)); System.out.println("Task isAssignableFrom Task:" + Task.class.isAssignableFrom(Task.class)); System.out.println("Runnable isAssignableFrom Task :" + Runnable.class.isAssignableFrom(Task.class)); } }
輸出結果:
Runnable isInstance Task:true Task isInstance Task:true Task isAssignableFrom Task:true Runnable isAssignableFrom Task :true
代理是基本的設計模式之一,它是咱們爲了提供額外的或不一樣的操做,而插入的用來代替「實際」對象的對象。這些操做一般與「實際」對象通訊,所以代理一般充當中間人的角色。
例如,咱們已有一個Handler接口,和一個實現類HandlerImpl,現須要對其進行性能統計,使用代理模式,代碼以下:
/** * handler接口 */ public interface Handler { /** * 數據處理 * @param data */ void handle(String data); } /** * Handler 實現 */ public class HandlerImpl implements Handler{ @Override public void handle(String data) { try { TimeUnit.MILLISECONDS.sleep(100); System.out.println(data); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * Handler代理<br /> * 實現Handler接口,記錄耗時狀況,並將請求發送給目標對象 */ public class HandlerProxy implements Handler{ private final Handler handler; public HandlerProxy(Handler handler) { this.handler = handler; } @Override public void handle(String data) { long start = System.currentTimeMillis(); this.handler.handle(data); long end = System.currentTimeMillis(); System.out.println("cost " + (end - start) + " ms"); } } public static void main(String... args){ Handler handler = new HandlerImpl(); Handler proxy = new HandlerProxy(handler); proxy.handle("Test"); }
採用代理模式,比較優雅的解決了該問題,但若是Handler接口存在多個方法,而且須要對全部方法進行性能監控,那HandlerProxy的複雜性將會提升。
Java動態代理比代理更進一步,由於它能夠動態的建立代理並動態的處理對所代理方法的調用。在動態代理上所作的全部調用都會被重定向到單一的調用處理器上。
InvocationHandler 是由動態代理處理器實現的接口,對代理對象的方法調用,會路由到該處理器上進行統一處理。
其只有一個核心方法:
/** * proxy : 代理對象 * method : 調用方法 * args : 調用方法參數 **/ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
Proxy 用於生成代理對象。
其核心方法爲:
/** * 獲取代理類<br /> * loader : 類加載器 * interfaces: 類實現的接口 * */ Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces); /* * 生成代理對象<br /> * loader : 類加載器 * interfaces : 類實現的接口 * h : 動態代理回調 */ Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h); /* * 判斷是否爲代理類<br /> * * cl : 待判斷類 */ public static boolean isProxyClass(Class<?> cl); /* * 獲取代理對象的InvocationHandler <br /> * * proxy : 代理對象 */ InvocationHandler getInvocationHandler(Object proxy);
對於以前的性能監控,使用Java動態代理怎麼實現?
/** * 定義代理方法回調處理器 */ public class CostInvocationHandler implements InvocationHandler { // 目標對象 private final Object target; public CostInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("call method " + method + " ,args " + args); long start = System.currentTimeMillis(); try { // 將請求轉發給目標對象 return method.invoke(this.target, args); }finally { long end = System.currentTimeMillis(); System.out.println("cost " + (end - start) + "ms"); } } } public static void main(String... args){ Handler handler = new HandlerImpl(); CostInvocationHandler invocationHandler = new CostInvocationHandler(handler); Class cls = Proxy.getProxyClass(DHandlerMain.class.getClassLoader(), Handler.class); Handler proxy = (Handler) Proxy.newProxyInstance(DHandlerMain.class.getClassLoader(), new Class[]{Handler.class}, invocationHandler); System.out.println("invoke method"); proxy.handle("Test"); System.out.println("isProxyClass: " + Proxy.isProxyClass(cls)); System.out.println("getInvocationHandler: " + (invocationHandler == Proxy.getInvocationHandler(proxy))); }
SPI 全稱爲 (Service Provider Interface) ,是JDK內置的一種服務提供發現機制。 目前有很多框架用它來作服務的擴展發現,它是一種動態替換髮現的機制。
具體用法是在JAR包的"META-INF/services/"目錄下創建一個文件,文件名是接口的全限定名,文件的內容能夠有多行,每行都是該接口對應的具體實現類的全限定名。而後使用 ServiceLoader.load(Interface.class) 對插件進行加載。
假定,現有個場景,須要對消息進行處理,但消息處理器的實現須要放開,及能夠動態的對處理器進行加載,當有新消息到達時,依次調用處理器對消息進行處理,讓咱們結合SPI和反射構造一個簡單的Plugin系統。
首先咱們須要一個插件接口和若干個實現類:
/** * 插件接口 */ public interface Handler { void handle(String msg); } /** * 實現1 */ public class Handler1 implements Handler{ @Override public void handle(String msg) { System.out.println("Handler1:" + msg); } } /** * 實現2 */ public class Handler2 implements Handler{ @Override public void handle(String msg) { System.out.println("Handler2:" + msg); } }
而後,咱們添加SPI配置,及在META-INF/services/com.example.reflectdemo.plugin.Handler添加配置信息:
com.example.reflectdemo.plugin.Handler1 com.example.reflectdemo.plugin.Handler2
其次,咱們實現DispatcherInvocationHandler類繼承自InvocationHandler接口,將方法調用分發給目標對象。
/** * 分發處理器<br /> * 將請求挨個轉發給目標對象 */ public class DispatcherInvocationHandler implements InvocationHandler { // 目標對象集合 private final List<Object> targets; public DispatcherInvocationHandler(List<Object> targets) { this.targets = targets; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { for (Object target : targets){ // 將請求轉發給目標對象 method.invoke(target, args); } return null; } }
實現主流程,經過SPI加裝插件,將插件做爲轉發對象實例化DispatcherInvocationHandler,在經過Proxy構建動態代理對象,最後調用handle方法進行業務處理。
public static void main(String... args){ // 使用SPI加載插件 ServiceLoader<Handler> serviceLoader = ServiceLoader.load(Handler.class); List<Object> handlers = new ArrayList<>(); Iterator<Handler> handlerIterator = serviceLoader.iterator(); while (handlerIterator.hasNext()){ Handler handler = handlerIterator.next(); handlers.add(handler); } // 將加載的插件組裝成InvocationHandler,以進行分發處理 DispatcherInvocationHandler invocationHandler = new DispatcherInvocationHandler(handlers); // 生成代理對象 Handler proxy = (Handler) Proxy.newProxyInstance(HandlerMain.class.getClassLoader(), new Class[]{Handler.class}, invocationHandler); // 調用handle方法 proxy.handle("Test"); }
運行結果以下:
Handler1:Test Handler2:Test
Java類型系統、反射、動態代理,做爲Java的高級應用,大量用於各大框架中。對其的掌握有助於加深對框架的理解。