Class的本質也是一個類,只不過它是將咱們定義類的共同的部分進行抽象,好比咱們常定義的類都含有構造方法,類變量,函數,而Class這個類就是來操做這些屬性和方法的。固然咱們常定義的類包含的類型均可以經過Class間接的來操做。而類的類型包含通常的類,接口,枚舉類型,註解類型等等。這麼說可能有點太理論,咱們看下面這個例子:html
咱們將生活中的一類事物抽象爲一個類的時候,每每是由於他們具備相同的共性和不一樣的個性。定義一個類的做用就是將相同的共性抽離出來。通常的類都包含屬性和方法(行爲),下面咱們定義水果和汽車這兩個大類:java
代碼以下:apache
汽車類:api
class Car{ // 定義屬性 private String name; private String color; /** * 定義兩個構造方法 */ public Car(){ } public Car(String name,String color){ this.name = name; this.color = color; } /** * 定義兩個普通方法(行爲) */ public void use(){ } public void run(){ } /** * 屬性的get和set方法 * @return */ public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
水果類:數組
class Fruit{ // 定義屬性 private String name; private int size; /** * 定義兩個構造方法 */ public Fruit(){ } public Fruit(String name,int size){ this.name = name; this.size =size; } /** * 定義兩個方法(行爲) */ public void use(){ } public void doFruit(){ } /** * 屬性的get和set方法 * @return */ public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } }
能夠看到水果和汽車這兩個類都有共同的部分,也就是一個類共同的部分,那就是屬性和方法,而Class就是來操做咱們定義類的屬性和方法。安全
小試牛刀:經過Class這個類來獲取Fruit這個類中定義的方法;oracle
public static void main(String[] args) { Fruit fruit = new Fruit(); Class fruitClass = fruit.getClass(); Method[] fruitMethods = fruitClass.getMethods(); System.out.println("方法個數:" + fruitMethods.length); for (Method method : fruitMethods) { //獲得返回類型 System.out.print("方法名稱和參數:" + method.getName() + "("); //取得某個方法對應的參數類型數組 Class[] paramsType = method.getParameterTypes(); for (Class paramType : paramsType) { System.out.print(paramType.getTypeName() + " "); } System.out.print(")"); Class returnType = method.getReturnType(); System.out.println("返回類型:" + returnType.getTypeName()); } }
運行結果:框架
方法個數:15 方法名稱和參數:getName()返回類型:java.lang.String 方法名稱和參數:setName(java.lang.String )返回類型:void 方法名稱和參數:getSize()返回類型:int 方法名稱和參數:setSize(int )返回類型:void 方法名稱和參數:use()返回類型:void 方法名稱和參數:doFruit()返回類型:void 方法名稱和參數:wait()返回類型:void 方法名稱和參數:wait(long int )返回類型:void 方法名稱和參數:wait(long )返回類型:void 方法名稱和參數:equals(java.lang.Object )返回類型:boolean 方法名稱和參數:toString()返回類型:java.lang.String 方法名稱和參數:hashCode()返回類型:int 方法名稱和參數:getClass()返回類型:java.lang.Class 方法名稱和參數:notify()返回類型:void 方法名稱和參數:notifyAll()返回類型:void
這裏可能有人疑惑了,Fruit類並無定義的方法爲何會出現,如wait(),equals()方法等。這裏就有必要說一下java的繼承和反射機制。在繼承時,java規定每一個類默認繼承Object這個類,上述這些並無在Fruit中定義的方法,都是Object中的方法,咱們看一下Object這個類的源碼就會一清二楚:jvm
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } public final native void wait(long timeout) throws InterruptedException; public final void wait() throws InterruptedException { wait(0); }
而Class類中的getMethods()方法默認會獲取父類中的公有方法,也就是public修飾的方法。因此Object中的公共方法也出現了。ide
注: 要想得到父類的全部方法(public、protected、default、private),可使用apache commons包下的FieldUtils.getAllFields()能夠獲取類和父類的全部(public、protected、default、private)屬性。
是否是感受很是的強大 ,固然,使用Class來獲取一些類的方法和屬性的核心思想就是利用了Java反射特性。萬物皆反射,可見反射的強大之處,至於反射的原理,期待個人下一個博客。
源碼以下:
private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }
能夠看到Class類只有一個構造函數,而且是私有的。也就是說不能經過new來建立這個類的實例。官方文檔的解釋:私有構造函數,僅Java虛擬機建立Class對象。我想可能就是爲了安全,具體緣由不是很瞭解。若是有了解的話,能夠在評論區內共同的交流。
那麼既然這個class構造器私有化,那咱們該如何去構造一個class實例呢,通常採用下面三種方式:
1.運用.class的方式來獲取Class實例。對於基本數據類型的封裝類,還能夠採用.TYPE來獲取相對應的基本數據類型的Class實例,以下的示例。
// 普通類獲取Class的實例。接口,枚舉,註解,均可以經過這樣的方式進行得到Class實例 Class fruitClass = Fruit.class; // 基本類型和封裝類型得到Class實例的方式,二者等效的 Class intClass = int.class; Class intClass1 = Integer.TYPE;
下面的表格兩邊等價:
boolean.class | Boolean.TYPE |
---|---|
char.class | Character.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對象的。
2.利用對象.getClass()方法獲取該對象的Class實例;
這是利用了Object提供的一個方法getClass() 來獲取當着實例的Class對象,這種方式是開發中用的最多的方式,一樣,它也不能獲取到未知的類,好比說某個接口的實現類的Class對象。
Object類中的getClass()的源碼以下:
public final native Class<?> getClass();
源碼說明:
能夠看到,這是一個native方法(一個Native Method就是一個java調用非java代碼的接口),而且不容許子類重寫,因此理論上全部類型的實例都具備同一個 getClass 方法。
使用:
Fruit fruit = new Fruit(); Class fruitClass = fruit.getClass();
3.使用Class類的靜態方法forName(),用類的名字獲取一個Class實例(static Class forName(String className) ),這種方式靈活性最高,根據類的字符串全名便可獲取Class實例,能夠動態加載類,框架設計常常用到;
源碼以下:
/* 因爲方法區 Class 類型信息由類加載器和類全限定名惟一肯定,因此參數name必須是全限定名, 參數說明 name:class名,initialize是否加載static塊,loader 類加載器 */ public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException { Class<?> caller = null; // 1.進行安全檢查 SecurityManager sm = System.getSecurityManager(); if (sm != null) { .... } } // 2.調用本地的方法 return forName0(name, initialize, loader, caller); } // 3.核心的方法 private static native Class<?> forName0(String name, boolean initialize, ClassLoader loader, Class<?> caller) throws ClassNotFoundException; /* 這個 forName是上述方法的重載,平時通常都使用這個 方法默認使用調用者的類加載器,將類的.class文件加載 到 jvm中 這裏傳入的initialize爲true,會去執行類中的static塊 */ public static Class<?> forName(String className) throws ClassNotFoundException { Class<?> caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller); }
源碼說明已在註釋中說明,有些人會疑惑, static native Class<?> forName0()這個方法的實現。
這就要說到java的不完美的地方了,Java的不足除了體如今運行速度上要比傳統的C++慢許多以外,Java沒法直接訪問到操做系統底層(如系統硬件等),爲此Java使用native方法來擴展Java程序的功能。
基本使用:
Class fruitClass = Class.forName("cn.chen.test.util.lang.Fruit");
注: 這種方式必須使用類的全限定名,,這是由於因爲方法區 Class 類型信息由類加載器和類全限定名惟一肯定,不然會拋出ClassNotFoundException的異常。
Class類的通常的方法總共有六十多種,其實看到這麼多方法咱也不要慫,這裏面還有不少重載的方法,根據二八原則,咱們平時用的也就那麼幾個方法,因此這裏只對如下幾個方法的使用和實現進行交流,其餘的方法能夠移步Java官方文檔:
這個方法主要是用來了解一個類的構造方法有哪些,包含那些參數,特別是在單例的模式下。通常包含的方法以下:
public Constructor[] getConstructors() :獲取類對象的全部可見的構造函數
public Constructor[] getDeclaredConstructors():獲取類對象的全部的構造函數
public Constructor getConstructor(Class... parameterTypes): 獲取指定的可見的構造函數,參數爲:指定構造函數的參數類型數組,若是該構造函數不可見或不存在,會拋出 NoSuchMethodException 異常
基本使用:
Constructor[] constructors = fruitClass.getConstructors(); for (Constructor constructor : constructors) { System.out.println("得到共有的構造方法:"+constructor); }
輸出結果:
得到共有的構造方法:public cn.chen.test.util.lang.Fruit() 得到共有的構造方法:public cn.chen.test.util.lang.Fruit(java.lang.String,int)
能夠看到咱們前面定義的來個構造方法,都被打印出來了。注意getConstructors()只能得到被public修飾的構造方法,若是要得到被(protected,default,private)修飾的構造方法,就要使用的getDeclaredConstructors()這個方法了。接下來,修改Fruit中的一個構造方法爲private:
private Fruit(String name,int size){ this.name = name; this.size =size; }
使用getConstructors()和getDeclaredConstructors()着兩個方法進行測試:
Class fruitClass = Fruit.class; Constructor[] constructors = fruitClass.getConstructors(); Constructor[] constructors1 = fruitClass.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println("得到共有的構造方法:"+constructor); } System.out.println("================================================="); for (Constructor constructor : constructors1) { System.out.println("得到全部的構造方法:"+constructor); }
輸出結果:
得到共有的構造方法:public cn.chen.test.util.lang.Fruit() ===================分隔線============================= 得到全部的構造方法:public cn.chen.test.util.lang.Fruit() 得到全部的構造方法:private cn.chen.test.util.lang.Fruit(java.lang.String,int)
能夠看到二者的區別。因此,反射在必定程度上破壞了java的封裝特性。畢竟人無完人,語言亦是同樣。
getConstructors()的源碼分析:
public Constructor<?>[] getConstructors() throws SecurityException { // 1.檢查是否容許訪問。若是訪問被拒絕,則拋出SecurityException。 checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return copyConstructors(privateGetDeclaredConstructors(true)); } private static <U> Constructor<U>[] copyConstructors(Constructor<U>[] arg) { // 2.使用克隆,獲得當前類的全部構造函數 Constructor<U>[] out = arg.clone(); // 3.使用ReflectionFactory構造一個對象,也是不使用構造方法構造對象的一種方式。 ReflectionFactory fact = getReflectionFactory(); // 4.遍歷,將構造函數進行拷貝返回,注意在調用fact.copyConstructor(out[i])這個方法的時候,還會進行安全檢查,用的就是下面的LangReflectAccess() 這個方法。 for (int i = 0; i < out.length; i++) { out[i] = fact.copyConstructor(out[i]); } return out; } private static LangReflectAccess langReflectAccess() { if (langReflectAccess == null) { Modifier.isPublic(1); } return langReflectAccess; }
經過打斷點調試,能夠看到下面的信息:
代碼的調用邏輯在註釋裏已進行說明。
主要獲取類的屬性字段,瞭解這個類聲明瞭那些字段。
通常有四個方法:
基本使用:
首先咱們在Fruit的類中加入一個public修飾的屬性:
public double weight;
Class fruitClass = Fruit.class; Field[] field2 = fruitClass.getFields(); for (Field field : field2) { System.out.println("定義的公有屬性:"+field); } Field[] fields = fruitClass.getDeclaredFields(); for (Field field : fields) { System.out.println("定義的全部屬性:"+field); }
輸出結果:
定義的公有屬性:public double cn.chen.test.util.lang.Fruit.weight ========================分隔線============================ 定義的全部屬性:private java.lang.String cn.chen.test.util.lang.Fruit.name 定義的全部屬性:private int cn.chen.test.util.lang.Fruit.size 定義的全部屬性:public double cn.chen.test.util.lang.Fruit.weight
源碼分析,就以getFileds()這個方法爲例,涉及如下幾個方法:
public Field[] getFields() throws SecurityException { // 1.檢查是否容許訪問。若是訪問被拒絕,則拋出SecurityException。 checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); return copyFields(privateGetPublicFields(null)); } private static Field[] copyFields(Field[] arg) { // 2\. 聲明一個Filed的數組,用來存儲類的字段 Field[] out = new Field[arg.length]; // 3.使用ReflectionFactory構造一個對象,也是不使用構造方法構造對象的一種方式。 ReflectionFactory fact = getReflectionFactory(); // 4.遍歷,將字段複製後返回。 for (int i = 0; i < arg.length; i++) { out[i] = fact.copyField(arg[i]); } return out; } public Field copyField(Field var1) { return langReflectAccess().copyField(var1); } // 再次檢查屬性的訪問權限 private static LangReflectAccess langReflectAccess() { if (langReflectAccess == null) { Modifier.isPublic(1); } return langReflectAccess; }
就是獲取一個類中的方法,通常有如下幾個方法:
public Method[] getMethods(): 獲取全部可見的方法
public Method[] getDeclaredMethods() :獲取全部的方法,不管是否可見
public Method getMethod(String name, Class... parameterTypes)
參數說明:
null
做爲參數類型數組,或者不傳值)null
做爲參數類型數組,或者不傳值)基本使用:
//在fruit中定義一個這樣的方法 private void eat(String describe){ System.out.println("經過getMethod()方法調用了eat()方法: "+describe); }
調用這個方法:
Class fruitClass = Fruit.class; Method method = fruitClass.getDeclaredMethod("eat",String.class); method.setAccessible(true); method.invoke(fruitClass.newInstance(),"我是該方法的參數值");
輸出結果:
經過getMethod()方法調用了eat()方法:我是該方法的參數值
分析getDeclaredMethod()涉及的源碼:
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { // 1.檢查方法的修飾符 checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); // 2.searchMethods()方法的第一個參數肯定這個方法是否是私有方法,第二個參數咱們定義的方法名,第三個參數就是傳入的方法的參數類型 Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes); if (method == null) { throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); } return method; } // 這個方法就是經過傳入的方法名找到咱們定義的方法,而後使用了Method的copy()方法返回一個Method的實例,咱們經過操做mehtod這個實例就能夠操做咱們定義的方法。 private static Method searchMethods(Method[] methods, String name, Class<?>[] parameterTypes) { Method res = null; String internedName = name.intern(); for (int i = 0; i < methods.length; i++) { Method m = methods[i]; if (m.getName() == internedName && arrayContentsEq(parameterTypes, m.getParameterTypes()) && (res == null || res.getReturnType().isAssignableFrom(m.getReturnType()))) res = m; } return (res == null ? res : getReflectionFactory().copyMethod(res)); } public Method copyMethod(Method var1) { return langReflectAccess().copyMethod(var1); } // 檢查屬性的訪問權限 private static LangReflectAccess langReflectAccess() { if (langReflectAccess == null) { Modifier.isPublic(1); } return langReflectAccess; }
這類型的方法顧名思義,就是來判斷這個類是什麼類型,是接口,註解,枚舉,仍是通常的類等等。部分方法以下表
boolean |
isAnnotation() 判斷是否是註解 |
---|---|
boolean |
isArray() 判斷是否爲數組 |
boolean |
isEnum() 判斷是否爲枚舉類型 |
boolean |
isInterface() 是否爲接口類型 |
boolean |
isMemberClass() 當且僅當基礎類是成員類時,返回「true」 |
boolean |
isPrimitive() 肯定指定的「類」對象是否表示原始類型。 |
boolean |
isSynthetic() 若是這個類是合成類,則返回' true ';不然返回「false」。 |
基本用法:
// 定義一個接口: interface Animal{ public void run(); }
判斷是否是一個接口:
Class AnimalClass = Animal.class; boolean flag = AnimalClass.isInterface(); System.out.println(flag);
輸出結果:
true
源碼分析isInterface():
public native boolean isInterface();
這是一個native方法,你們都知道native方法是非Java語言實現的代碼,供Java程序調用的,由於Java程序是運行在JVM虛擬機上面的,要想訪問到比較底層的與操做系統相關的就沒辦法了,只能由靠近操做系統的語言來實現。
將對象轉換爲字符串。字符串表示形式是字符串「類」或「接口」,後跟一個空格,而後是該類的全限定名。
基本使用:
// 這是前面定義的兩個類Fruit和Car,Car是一個接口 Class fruitClass = Fruit.class; Class AnimalClass = Animal.class; System.out.println(AnimalClass.toString()); System.out.println(fruitClass.toString());
輸出結果:
// 格式 字符串「類」或「接口」,後跟一個空格,而後是該類的全限定名 interface cn.chen.test.util.lang.Animal class cn.chen.test.util.lang.Fruit
源碼以下:
public String toString() { // 先是判斷是接口或者類,而後調用getName輸出類的全限定名 return (isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) + getName(); } public native boolean isInterface(); public native boolean isPrimitive();
追本溯源,方能闊步前行。