不少書上都說,在java的世界裏,一切皆對象。其實從某種意義上說,在java中有兩種對象:實例對象和Class對象。實例對象就是咱們日常定義的一個類的實例:java
/** * Created by aristark on 3/28/16. */ public class Person { }
而後利用new關鍵字:微信
public class Person { public static void main(String[] args){ Person p = new Person(); } }
而Class對象是沒辦法用new關鍵字獲得的,由於它是jvm生成用來保存對應類的信息的,換句話說,當咱們定義好一個類文件並編譯成.class字節碼後,編譯器同時爲咱們建立了一個Class對象並將它保存.class文件中。我不知道這樣描述是否穩當,由於我也見過某些書上直接把.class文件稱之爲Class對象。同時在jvm內部有一個類加載機制,即在須要的時候(懶加載)將.class文件和對應的Class對象加載到內存中。總之要有這樣一個意識,Person.java文件編譯成Person.class的同時也會產生一個對應的Class對象。jvm
上面說了,Class對象是jvm用來保存對象實例對象的相關信息的,除此以外,咱們徹底能夠把Class對象當作通常的實例對象,事實上全部的Class對象都是類Class的實例。獲得一個實例對象對應的Class對象有如下三種方式:
1.經過實例變量的getClass()方法:學習
Dog dog = new Dog(); Class d = dog.getClass();
2.經過類Class的靜態方法forName():this
try { Class dog1 = Class.forName("Dog"); } catch (ClassNotFoundException e) { e.printStackTrace(); }
3.直接給出對象類文件的.class:code
Class dog2 = Dog.class;
JAVA反射機制是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
簡而言之,咱們能夠從.class逆向到.java(反編譯),咱們能夠經過反射機制來訪問一個類java對象的屬性,方法,甚至咱們能夠輕易改變一個私有成員,看代碼,咱們先來定義一個Cat類:對象
class Cat{ public static int count; public int age; private String name; static { count = 0; } public Cat(){ age = count++; System.out.println("this is class Cat!"); } public void run(){ } private void ruff(){} }
注意到咱們的類中包含靜態成員,私有變量,靜態初始化以及私有方法。這裏在提一下所謂的懶加載:當Cat.java編譯成Cat.class文件後並不會當即被加載到內存,而是在它的的靜態成員第一次被訪問時才被加載(這麼看來,Cat的默認構造方法也是靜態的!)內存
Class c = Cat.class; Field[] fields = c.getDeclaredFields(); for (Field field : fields){ System.out.println(field); }
結果以下:get
public static int Cat.count public int Cat.age private java.lang.String Cat.name
能夠看到咱們垂手可得的獲得了Cat類的字段信息,再來:編譯器
Method[] methods = c.getDeclaredMethods(); for (Method method : methods){ System.out.println(method); }
結果以下 :
public void Cat.run() private void Cat.ruff()
好玩吧,咱們居然能夠在運行時獲得類的信息。同時咱們發現Cat類中的靜態初始化代碼段並無執行。接下來咱們經過Class對象來得到對應的實例對象:
try { Cat cat = (Cat) c.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); }
這時候靜態代碼塊執行了:
this is class Cat!
接下來咱們作一件神奇的事情:
try { Class catClass = Class.forName("Cat"); Field name = catClass.getDeclaredField("name"); name.setAccessible(true); Cat cat2 = (Cat) catClass.newInstance(); name.set(cat2,"Aristark"); System.out.println(cat2.getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); }
此次咱們使用Class.forname()來獲取Class對象,它的做用是讓jvm查找並加載指定的類,也就是說Cat類的靜態代碼塊會被執行。其次值得注意的是,咱們經過Class的幾個方法訪問了本來不能夠被訪問的name屬性:
this is class Cat! Aristark
從這個意義上來講,反射機制並不符合OOP的思想,因此咱們僅在必要的時候使用這個特性就好了。
理解好Class對象不只能讓咱們更好的認識一切皆對象這個觀點,對以後學習泛型,類型擦除都是頗有幫助的,而對於java反射機制咱們只需在適當的場合利用它便可。:)關於這兩個知識的深刻學習稍後我會貼出一些有借鑑意義的文章,你們要關注哦~
個人微信號是aristark,歡迎交流指正!