反射-Class
通過獲取類的信息你可以獲取以下相關的內容:
Class對象
類名
修飾符
包信息
父類
實現的接口
構造器
方法
變量
注解
示例類定義
接口
public interface IEat {void eat(); } public interface IRun {void run(); }父類
public abstract class Animal implements IRun,IEat {private String name;private String skin;private double weight;private int legs;public String getSkin() {return skin;}public void setSkin(String skin) {this.skin = skin;}public double getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}public int getLegs() {return legs;}public void setLegs(int legs) {this.legs = legs;}public String getName() {return name;}@Overridepublic void run() {// TODO Auto-generated method stub}@Overridepublic void eat() {// TODO Auto-generated method stub}public Animal(String name){this.name =name;}public void sleep() {beforeSleep();//sleep}protected void beforeSleep(){} }子類
public class Dog extends Animal{public Dog(String name) {super(name);// TODO Auto-generated constructor stub}@Overridepublic void run() {// TODO Auto-generated method stub}@Overridepublic void eat() {// TODO Auto-generated method stub}public void bark(){} } public class Horse extends Animal {public Horse(String name) {super(name);// TODO Auto-generated constructor stub}@Overridepublic void run() {// TODO Auto-generated method stub}@Overridepublic void eat() {// TODO Auto-generated method stub}}獲取類信息
類名
獲取類名有幾種方法
Class<?> clazzHorse = horse.getClass();//getName:study.reflection.HorseSystem.out.println("getName:" + clazzHorse.getName());//getTypeName:study.reflection.HorseSystem.out.println("getTypeName:" + clazzHorse.getTypeName());//getSimpleName:HorseSystem.out.println("getSimpleName:" + clazzHorse.getSimpleName());//getCanonicalName:study.reflection.HorseSystem.out.println("getCanonicalName:" + clazzHorse.getCanonicalName());byte[] a = new byte[10];Class<?> clazzList= a.getClass();//getName:[BSystem.out.println("getName:" + clazzList.getName());//getTypeName:byte[]System.out.println("getTypeName:" + clazzList.getTypeName());// getSimpleName:byte[]System.out.println("getSimpleName:" + clazzList.getSimpleName());// getCanonicalName:byte[]System.out.println("getCanonicalName:" + clazzList.getCanonicalName());?
getName()
返回該類對象作為字符串表示的實體(類、接口、數組類、基本數據類型或void)的名稱
可以理解為返回的是 虛擬機中Class對象的表示
當我們 動態加載類 的時候,會用到該方法的返回值,如: 使用 Class.forName() 方法
如果是 內部類,則使用 $ 符號進行連接
如果是 數組,則使用 [ 來表示,數組是幾維,[ 就有幾個
其余情況的表示如下
getCanonicalName()
??? 返回Java語言規范定義的底層類的規范名稱
??? 如果是一個本地類或匿名類或其組件的數組類型沒有規范名稱的,則返回null
??? 可以理解為返回的是 正常的包含路徑的類名
??? 該方法的返回值可以在 import語句中使用
??? 能唯一標識一個類,還可以在toString()或日志操作期間使用
?
??? public String getCanonicalName() {
??????? if (isArray()) {
??????????? String canonicalName = getComponentType().getCanonicalName();
??????????? if (canonicalName != null)
??????????????? return canonicalName + "[]";
??????????? else
??????????????? return null;
??????? }
??????? if (isLocalOrAnonymousClass())
??????????? return null;
??????? Class<?> enclosingClass = getEnclosingClass();
??????? if (enclosingClass == null) { // top level class
??????????? return getName();
??????? } else {
??????????? String enclosingName = enclosingClass.getCanonicalName();
??????????? if (enclosingName == null)
??????????????? return null;
??????????? return enclosingName + "." + getSimpleName();
??????? }
??? }
- isArray()
判斷該Class對象是否為數組,如果是返回true,不是返回false
- getComponentType()
返回表示數組類型(元素類型)的類,如果該Class對象不是一個數組,則返回null
- isLocalOrAnonymousClass()
該方法是一個私有方法,用來判斷該Class對象是否為本地類或匿名類
- getEnclosingClass()
返回該Class對象的封裝類,如果是該Class對象是頂級類,則返回null
getSimpleName()
- 返回源代碼中給出的基礎類的簡單名稱,如果基礎類是匿名的,則返回空字符串
- 可以理解為返回的是僅僅是 類名,不包含路徑
- 不能保證能唯一標識一個類,可以在toString()或日志操作期間使用
??? public String getSimpleName() {
??????? if (isArray())
??????????? return getComponentType().getSimpleName()+"[]";
??????? String simpleName = getSimpleBinaryName();
??????? if (simpleName == null) { // top level class
??????????? simpleName = getName();
??????????? return simpleName.substring(simpleName.lastIndexOf(".")+1); // strip the package name
??????? }
???
??????? int length = simpleName.length();
??????? if (length < 1 || simpleName.charAt(0) != '$')
??????????? throw new InternalError("Malformed class name");
??????? int index = 1;
??????? while (index < length && isAsciiDigit(simpleName.charAt(index)))
??????????? index++;
??????? // Eventually, this is the empty string iff this is an anonymous class
??????? return simpleName.substring(index);
??? }
- getSimpleBinaryName()
該方法是一個私有方法,返回該Class對象的的“簡單二進制名稱”,如果該Class對象是頂級類,則返回null,用 BBB.class.getSimpleName() 為例,該方法會返回 $BBB
- isAsciiDigit(char c)
該方法也是一個私有方法,用于判斷 getSimpleBinaryName() 返回的值下標為 1 處的字符是否位于 ‘0’ - ‘9’ 之間,如果是返回true且index遞增,不是返回false
用 new Object(){}.getClass().getSimpleName() 為例,使用 new Object(){}.getClass() 得到的結果為 class lang.reflect.AAA$1,再調用 getSimpleName(),最終會返回空字符串
getTypeName()
- 返回此類型名稱的信息字符串
- 可以理解為返回的是該類類型的名稱
修飾符
可以通過Class對象來訪問一個類的修飾符,即public,private,static等等的關鍵字,你可以使用如下方法來獲取類的修飾符
int modifier = clazzAnimal.getModifiers();System.out.println("getTypeName:" + modifier);Modifier.isAbstract(modifier);Modifier.isFinal(modifier);Modifier.isInterface(modifier);Modifier.isNative(modifier);Modifier.isPrivate(modifier);Modifier.isProtected(modifier);Modifier.isPublic(modifier);Modifier.isStatic(modifier);Modifier.isStrict(modifier);Modifier.isSynchronized(modifier);Modifier.isTransient(modifier);Modifier.isVolatile(modifier);包信息、父類、實現的接口
//getPackage:study.reflectionjava.lang.Package aPackage = clazzAnimal.getPackage();System.out.println("getPackage:" + aPackage.getName());//getSuperclass:study.reflection.AnimalClass superclass = clazzHorse.getSuperclass();System.out.println("getSuperclass:" + superclass.getName());//getInterfaces -Animal:study.reflection.IRun;study.reflection.IEat;System.out.println("getInterfaces -Animal:" + printList( clazzAnimal.getInterfaces() ));//getInterfaces -Hourse:System.out.println("getInterfaces -Hourse:" + printList( clazzHorse.getInterfaces() ));構造器
| getConstructor(Class...<?> parameterTypes) | 獲得該類中與參數類型匹配的公有構造方法 |
| getConstructors() | 獲得該類的所有公有構造方法 |
| getDeclaredConstructor(Class...<?> parameterTypes) | 獲得該類中與參數類型匹配的構造方法 |
| getDeclaredConstructors() | 獲得該類所有構造方法 |
- 構造方法參數
Class[] parameterTypes = constructor.getParameterTypes();?
- ?利用Constructor對象實例化一個類
Constructor<?> con = clazzHorse.getConstructor(String.class);
Animal animal = (Animal)?? con.newInstance("horse");
?
方法
| getMethod(String name, Class...<?> parameterTypes) | 獲得該類某個公有的方法 |
| getMethods() | 獲得該類所有公有的方法 |
| getDeclaredMethod(String name, Class...<?> parameterTypes) | 獲得該類某個方法 |
| getDeclaredMethods() | 獲得該類所有方法 |
- 調用方法
Method method = clazzHorse.getMethod("run", null);
method.invoke(horse);
- 方法參數以及返回類型?
Class[] parameterTypes = method.getParameterTypes();
Class returnType = method.getReturnType();?
字段
| getField(String name) | 獲得某個公有的屬性對象 |
| getFields() | 獲得所有公有的屬性對象 |
| getDeclaredField(String name) | 獲得某個屬性對象 |
| getDeclaredFields() | 獲得所有屬性對象 |
注解
| getAnnotation(Class<A> annotationClass) | 返回該類中與參數類型匹配的公有注解對象 |
| getAnnotations() | 返回該類所有的公有注解對象 |
| getDeclaredAnnotation(Class<A> annotationClass) | 返回該類中與參數類型匹配的所有注解對象 |
| getDeclaredAnnotations() | 返回該類所有的注解對象 |
getDeclaredXXX和getXXX的區別
getDeclaredMethod*()獲取的是類自身聲明的所有方法,包含public、protected和private方法。
getMethod*()獲取的是類的所有共有方法,這就包括自身的所有public方法,和從基類繼承的、從接口實現的所有public方法。
類中其他重要的方法
| isAnnotation() | 如果是注解類型則返回true |
| isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定類型注解類型則返回true |
| isAnonymousClass() | 如果是匿名類則返回true |
| isArray() | 如果是一個數組類則返回true |
| isEnum() | 如果是枚舉類則返回true |
| isInstance(Object obj) | 如果obj是該類的實例則返回true |
| isInterface() | 如果是接口類則返回true |
| isLocalClass() | 如果是局部類則返回true |
| isMemberClass() | 如果是內部類則返回true |
數組
利用反射機制來處理數組會有點棘手。尤其是當你想要獲得一個數組的Class對象,比如int[]等等。
通過反射機制創建數組和獲取數組的Class對象。
java.lang.reflect.Array
Java反射機制通過java.lang.reflect.Array這個類來處理數組。不要把這個類與Java集合套件(Collections suite)中的java.util.Arrays混淆。
創建數組、訪問數組
int[] intArray = (int[]) Array.newInstance(int.class, 3);Array.set(intArray, 0, 123); Array.set(intArray, 1, 456); Array.set(intArray, 2, 789);System.out.println("intArray[0] = " + Array.get(intArray, 0)); System.out.println("intArray[1] = " + Array.get(intArray, 1)); System.out.println("intArray[2] = " + Array.get(intArray, 2));?獲取數組的Class對象
- 獲取數組的Class對象:
?? ?Class stringArrayClass = String[].class;
- 如果使用Class.forName()方法來獲取Class對象
你可以像這樣來獲得一個原生數據類型(primitive)int數組的Class對象:
??? Class intArray = Class.forName("[I");
在JVM中字母I代表int類型,左邊的‘[’代表我想要的是一個int類型的數組,這個規則同樣適用于其他的原生數據類型。
對于普通對象類型的數組有一點細微的不同:
??? Class stringArrayClass = Class.forName("[Ljava.lang.String;");
注意‘[L’的右邊是類名,類名的右邊是一個‘;’符號。這個的含義是一個指定類型的數組。
需要注意的是,不能通過Class.forName()方法獲取一個原生數據類型的Class對象。
下面這兩個例子都會報ClassNotFoundException:
??? Class intClass1 = Class.forName("I");
?? ?Class intClass2 = Class.forName("int");
通常會用下面這個方法來獲取普通對象以及原生對象的Class對象:
??? public Class getClass(String className){
?? ?? if("int" .equals(className)) return int .class;
?? ?? if("long".equals(className)) return long.class;
?? ?? ...
?? ?? return Class.forName(className);
?? ?}
一旦獲取了類型的Class對象,你就有辦法輕松的獲取到它的數組的Class對象,你可以通過指定的類型創建一個空的數組,然后通過這個空的數組來獲取數組的Class對象。這樣做有點討巧,不過很有效。如下例:
??? Class theClass = getClass(theClassName);
?? ?Class stringArrayClass = Array.newInstance(theClass, 0).getClass();
這是一個特別的方式來獲取指定類型的指定數組的Class對象。無需使用類名或其他方式來獲取這個Class對象。
為了確保Class對象是不是代表一個數組,你可以使用Class.isArray()方法來進行校驗:
??? Class stringArrayClass = Array.newInstance(String.class, 0).getClass();
?? ?System.out.println("is array: " + stringArrayClass.isArray());
?
總結