new和反射產生java內部類的實例化詳解

前兩天看到一道口試題,是關於內部類的知識,以爲頗有意思。 java

這道題是這樣的: 數組

根據註釋填寫(1),(2),(3)處的代碼 測試

public class Test{ spa

?????? public static void main(String[] args){ 對象

????????????? // 初始化Bean1 接口

????????????? (1) 文檔

????????????? bean1.I++; get

????????????? // 初始化Bean2 it

????????????? (2) io

????????????? bean2.J++;

????????????? //初始化Bean3

????????????? (3)

????????????? bean3.k++;

?????? }

?

?????? class Bean1{

????????????? public int I = 0;

?????? }

?

?????? static class Bean2{

????????????? public int?J = 0;

?????? }

}

public class Bean{

?????? public class Bean3{

????????????? public int k = 0;

?????? }

}

?

這實在就是實例化內部類對象的題。

從上面的題能夠看出,Bean1爲非靜態內部類,Bean2爲靜態內部類,而Bean3則不是在Test類內部了,而是在一個外部類Bean的內部(是否是很拗口),呵呵。現經過new和反射來具體講解其產生原理。

1.new

咱們知道,內部類分爲兩種,一種是靜態內部類,一種是非靜態內部類。前者不用產生外部類的實例化對象便可產生內部類的實例化對象,後者必須先產生外部類的實例化對象,才能產生內部類的實例化對象。

?

實例化靜態內部類對象的模板是:? 外部類類名.內部類類名 *** = new 外部類類名.內部類類名()

實例化非靜態內部類對象的模板是:外部類類名.內部類類名 *** = 外部類對象名.new 內部類類名()

?

1>>實例化非靜態內部類Bean1

java代碼 :

Test test = new Test();
?? Test.Bean1 b1 = test.new Bean1();
?? b1.I++;

總共3行代碼,是否是很簡單呢。

2>>實例化靜態內部類Bean2

java代碼:

?Test.Bean2 b2 = new Test.Bean2();
?? b2.j++;

3>>實例化非靜態內部類Bean3

Bean bean = new Bean();
????Bean.Bean3 b3 =? bean.new Bean3();
????System.out.println(b3.k++);

?

總結:經過new方式產生內部類的實例化對象實現起來比較簡單,也很輕易理解,假如要深層次瞭解其產生,請用下面講解的方式,反射。

2.反射

1>>反射產生非靜態內部類Bean1的實例化對象

java代碼:

try {
????Class<?> cla2 = Class.forName("com.lovo.nio.Test$Bean1");
???} catch (ClassNotFoundException e) {
????// TODO Auto-generated catch block
????e.printStackTrace();
???}

解析:咱們知道,內部類的class文件是以"外部類$內部類.class"的形式存在的,因此獲取Class對象的時候,必須使用forName("包名+外部類$內部類")的形式才能獲得Class對象

獲得Class對象cla2後,確定有人會說用下面的方法獲得Bean1的實例化對象:

Bean1 b6 = (Bean1)cla2.newInstance();
????運行上述代碼,出現異常:InstantiationException,查看Java API文檔,下面引用其原話:

當應用程序試圖使用 Class 類中的 newInstance 方法建立一個類的實例,而指定的類對象沒法被實例化時,拋出該異常。實例化失敗有不少緣由,包括但不只限於如下緣由:

  • 類對象表示一個抽象類、接口、數組類、基本類型、void
  • 類沒有非 null 構造方法

在這裏的緣由是:Bean1的構造方法不公然,意思就是說,他的構造方法不是public的,不能經過newInstance()方式產生他的實例化對象。

那麼咱們是否能夠查看其是什麼訪問修飾符的構造方法呢?答案是能夠的,能夠經過如下方式獲得:

Constructor<?>[] c = cla2.getDeclaredConstructors();
????int i = c[0].getModifiers();
????//獲得訪問修飾符
????String str = Modifier.toString(i);
????System.out.println(str+" aaaaaaaaa");????? //留意:此處的aaaaaaaaaaa僅表示有這個輸出

?運行以上代碼,輸出「?? aaaaaaaaa」,能夠看出並無輸出str,實際上已經輸出了,就是default,default在Java中能夠省略不寫的。如今該明白了吧!~

那要如何才能實例化他的內部類對象呢,剛纔咱們說過,要實例化非靜態的內部類對象,必須先實例化外部類的對象,但是咱們任然沒有實例化外部類的對象。咱們查看JAVA PAI文檔,發現Constructor類有一個方法,newInstance(Object...?initargs),因而咱們想到下面這種方式來構建:

?//反射產生非靜態內部類Bean1的實例化對象
???try {
????Class<?> cla2 = Class.forName("com.lovo.nio.Test$Bean1");
//????Bean1 b6 = (Bean1)cla2.newInstance();
//????System.out.println(b6);
????Constructor<?>[] c = cla2.getDeclaredConstructors();
????int i = c[0].getModifiers();
????//獲得訪問修飾符
????String str = Modifier.toString(i);
????System.out.println(str+" aaaaaaaaa");
????Bean1 bean1 = (Bean1)c[0].newInstance(new Test());
????System.out.println(bean1.I++);
???} catch (ClassNotFoundException e) {
????// TODO Auto-generated catch block
????e.printStackTrace();
???} catch (IllegalArgumentException e) {
????// TODO Auto-generated catch block
????e.printStackTrace();
???} catch (InstantiationException e) {
????// TODO Auto-generated catch block
????e.printStackTrace();
???} catch (IllegalAccessException e) {
????// TODO Auto-generated catch block
????e.printStackTrace();
???} catch (InvocationTargetException e) {
????// TODO Auto-generated catch block
????e.printStackTrace();
???}

?? 運行以上代碼,正常。

?2>>反射產生靜態內部類Bean2的實例化對象

?Java代碼:

//反射產生靜態內部類Bean2的實例化對象
???try {
????// 初始化Bean2?
????????? Class<?> cla = null;
????cla = Class.forName("com.lovo.nio.Test$Bean2");
????Bean2 bean2 = (Bean2)cla.newInstance();
????System.out.println(bean2.j++);
???} catch (ClassNotFoundException e1) {
????// TODO Auto-generated catch block
????e1.printStackTrace();
???} catch (InstantiationException e) {
????// TODO Auto-generated catch block
????e.printStackTrace();
???} catch (IllegalAccessException e) {
????// TODO Auto-generated catch block
????e.printStackTrace();
???}
???解析:

?首先來看看static的相關知識:

static內部類意味着: ?
? ? ? ? (1) ? 爲建立一個static內部類的對象,咱們不須要一個外部類對象。 ?
? ? ? ? (2) ? 不能從static內部類的一個對象中訪問一個外部類對象. ?
? ? ? ? ? 假若爲了建立內部類的對象而不須要建立外部類的一個對象,那麼可將全部東西都設爲static。爲了能正常工做,同時也必須將內部類設爲static。 ?
? ? ? ? ? 此外,也可考慮用一個static內部類容納本身的測試代碼。
?

在這裏咱們一樣可使用上面的方法獲取他的構造方法的修飾符,也是default的,(靜態內部類是有構造方法的,並且是無參的構造方法).

java代碼:

try {
????
????// 初始化Bean2?
????????? Class<?> cla = null;
????cla = Class.forName("com.lovo.nio.Test$Bean2");
????Bean2 bean2 = (Bean2)cla.newInstance();
????Constructor<?>[] cs = cla.getDeclaredConstructors();
//????Modifier.toString(cs[0]);
????System.out.println("******************");

??? System.err.println(cs.length);
????System.out.println(cs[0].getModifiers());
????System.out.println("******************");

????System.out.println(bean2.j++);
???} catch (ClassNotFoundException e1) {
????// TODO Auto-generated catch block
????e1.printStackTrace();
???} catch (InstantiationException e) {
????// TODO Auto-generated catch block
????e.printStackTrace();
???} catch (IllegalAccessException e) {
????// TODO Auto-generated catch block
????e.printStackTrace();
???}

運行以上代碼,紅色部分代碼輸出:

******************
1

******************

能夠看出,有一個默認的(方法修飾符沒寫的,不是沒有方法修飾符)構造方法。

假如咱們要使用反射產生Bean2的實例化對象的話,只能用getDeclaredConstructors()方法。如上面的代碼所示。

?3>>反射產生 外部類的內部類Bean3 的實例化對象

分析:要產生外部類的內部類的實例化對象,則要先產生外部類的實例化對象。再經過getClass()方法獲得外部類的實例化對象的Class對象,再經過getClasses()方法獲得外部類的全部公共類和接口,包括內部類。

Java代碼:

try {
??????Class<?> c3 = bean.getClass();
??????Class<?>[] cl = c3.getClasses();
//??????Bean3 b4 = (Bean3)c3.newInstance();
//??????System.out.println(b4);
??????//使用反射產生Bean3實例化對象
??????Constructor<?>[] cc = cl[0].getDeclaredConstructors();
??????//獲取構造方法的個數,用以斷定其構造方法是不是公共的,假如直接經過c3.newInstance()方法來實例化Bean3的話,則會包異常:java.lang.ClassCastException
??????System.out.println(cc.length);
??????Bean3 bb = (Bean3)cc[0].newInstance(new Bean());
??????System.out.println(bb.k++);
?????} catch (IllegalArgumentException e) {
??????// TODO Auto-generated catch block
??????e.printStackTrace();
?????} catch (InstantiationException e) {
??????// TODO Auto-generated catch block
??????e.printStackTrace();
?????} catch (IllegalAccessException e) {
??????// TODO Auto-generated catch block
??????e.printStackTrace();
?????} catch (InvocationTargetException e) {
??????// TODO Auto-generated catch block
??????e.printStackTrace();
?????}

?

最後關於反射的一點點總結:

1.Java的反射是Java類的「自審」,能夠經過反射探知類的結構,好比,得到類的屬性,方法,修飾符,返回類型。是在程序運行時動態的天生的,那麼有人會問,既然能夠的到類的屬性,方法等,那麼是否能夠增長或者刪除它的屬性或者方法呢?答案是否認的,因爲Java是靜態語言,不是動態語言,那麼就不能修改類的屬性,方法,只能探知類的屬性,方法等。

2.既然能夠經過反射獲得類的屬性及其父類的屬性,方法等以及實現的接口的相關內容,那麼是否能夠經過接口獲得他的實現類的屬性,方法呢?答案是否認的,因爲在子類中有個關鍵字implements,是經過他獲得類所實現的接口,可是假如要經過接口獲得子類的相關內容,是行不通的,因爲在接口中沒有任何與實現類相關聯的代碼(好比關鍵字)。

相關文章
相關標籤/搜索