Java 接口和抽象類可以被new么?


 

背景:

    最近有同事跟我說了他面試時遇到的問題,考官問:“接口和抽象類可以被new嘛?”。這可能不是考官的原話,但是據他表達考官大概就是這個意思了。聽到這個問題,我的第一反應是肯定不行啊,直接對接口和抽象類調用new,編譯器都過不去。但是他說,考官說可以,用匿名內部類實現。聽見這個回到,我感覺那個考官太………,有點無語。我們可以仔細分析下這個問題。

 

直接new接口和抽象類

   首先先明確一點,直接new接口和抽象類,這肯定行不通,編譯器會提示Cannot instantiate the type XX的錯誤。這個實驗就不做了,沒意思。

 

使用匿名內部類

     下面的代碼是一個匿名內部類的Demo,也就是考官說的可以new。 

 1 package com.saillen.test;
 2  
 3 interface A {
 4     void f();
 5 }
 6  
 7 public class T {
 8  
 9     public T(A a) {
10         a.f();
11     }
12  
13     public static void main(String[] args) {
14         T t = new T(new A() {
15             public void f() {
16                 System.out.println("我是匿名內部類");
17                 System.out.println("Class對象是:" + this.getClass());
18                 System.out.println("類名字是:" + this.getClass().getSimpleName());
19             }
20         });
21     }
22  
23 }

 

   上面的程序很簡單,我們使用匿名內部類,然后“new”一個接口A的對象,輸出它的類名了Class對象,輸出如下:

1 我是匿名內部類
2 Class對象是:class com.saillen.test.T$1
3 類名字是:

 

   通過輸出可以看到,內部類的類名是“”也就是一個空字符串,但是它確確實實是有類型的。而且查看編譯后的class文件,會發現,會多一個T$1.class,這個class就是匿名內部類的原型,

  用javap反編譯這個文件,可以看到這個類的源碼樣式如下:

   通過反編譯后的文件可以證明,我們的“匿名內部類”的類名是Test$1。所以new針對的還是普通的class(雖然內部類和普通類有很大不一樣),只不過這個class的寫法稍有不同,它是編譯器幫我們從匿名內部類中提取的。

 

結論:

 通過上面的實驗,我仍然堅持,接口和抽象類不可以被new!匿名內部類只是一種寫法上的迷惑而已。這個考官的答案很不靠譜,不能說他的想法就是絕對的錯,但是絕對不應該這么問這個問題,這不是一個能不能就回答的。或者說不能從字面上就證明可以對interface或者abstract class使用new。

 

內部類總結

    Java編程思想第十章專門介紹了內部類,內部類確實神奇而且復雜。但是內部類在編程中被應用的場景很少,這主要是看設計者的設計思想,不過它的語法和特性卻有很多。對於內部類來說要記住可以繼承內部類但是不能覆蓋

 

普通內部類:

    普通的內部類,就是在class里面普通的定義一個類,eg:

1 public class A{
2     public class B{
3         
4     }
5     
6 }

 

普通內部類,或者說平凡的內部類,有如下特性:(總結自《Java編程思想》
   (1)這個類在外部類的外面不能被直接訪問,需要通過OuterClassName.InnerClassName方式訪問。比如Map的Map.Entry就是一個內部接口,只能通過Map.Entry方式來使用它。

   (2)內部類對象在創建后會秘密的鏈接到外部類對象上,隱含的有一個指向外部類對象的引用,所以沒有外部類對象,是無法實例化內部類對象的。也就是我們無法獨立於外部類創建一個內部類對象。(這里不包括聲明為static的內部類,那不是平凡的內部類

    (3)因為內部類隱含的有一個指向外部類的指針,所以內部類可以訪問外圍類的成員,而且是外圍類的所有成員,包括private的成員。

    (4)在內部類中使用OuterClassName.this可以訪問外圍類對象。

    (4)如果想要在外部類外面實例化內部類對象,那么可以同.new語法,也就是outerObject.new InnerClass()的方式,eg:

 1 package com.saillen.test;
 2  
 3 public class A {
 4  
 5     public void f() {
 6         A.B b = this.new B();
 7         B b2 = new B();
 8     }
 9  
10     public class B {
11              void g(){
12                 System.out.println(A.this);
13              }
14     }
15  
16     public static void main(String[] args) {
17         A a = new A();
18         A.B b = a.new B();
19     }
20  
21 }

 

  (5)內部類最大的用途是:它可以實現接口或者繼承某個類,這樣使用內部類時,用基類引用內部類對象,可以屏蔽內部類的細節。這樣的好處是,可以實現偽“多重繼承”等。

  (6)普通的內部類不能有static字段和static數據,也不能包含嵌套類。

 

在方法和作用域內的內部類:

   如果內部類出現在了方法和作用域內,那么它就不是“平凡”的內部類了,而且這個內部類的作用域就是在這個方法的作用域內,方法外面是無法訪問到的!但是它仍然具有“平凡”的內部類特性。

 

匿名內部類:

   匿名內部類,在內部類的基礎上減少了對class的定義,直接用new 后面跟一個接口或者基類,然后類體里面實現方法即可,這樣JVM會調用編譯器生成的構造器來生成這個內部類對象,並且編譯器幫忙生成這個內部類的類結構。匿名內部類好處是語法簡單,但是不方便閱讀,在Android編程中,對Button的監聽經常會用匿名內部類。匿名內部類有一些限制:匿名內部既可以擴展類,也可以實現接口,但是不能兩者兼備,而且只能實現一個接口。

 

嵌套類:

   嵌套類就是在內部類的基礎上加上static聲明,也就是靜態的內部類。嵌套類跟普通的內部類特性有很大不同,特點:

   (1)在構造時,不需要外圍類的對象,但是同樣,它只能訪問外圍類中的static字段;

   (2)嵌套類可以有static數據和字段;

   (3)嵌套類可以作為接口的一部分,這樣在接口中就可以用公用代碼出現;

   (4)嵌套類是可以多重嵌套的,嵌套多少層不重要,它都可以訪問它所有外圍類成員。

 

內部類標識符:

    部類必須生成一個class文件以包含它的Class對象信息,這些類文件有嚴格的命名規則:外圍類的名字,加上“$”,再加上內部類的名字。

 

為什么需要內部類:

   《Java編程思想》中很明確指出:    

   每個內部類都能獨立的繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個實現,對於內部類都沒有影響。

   利用內部類可以實現多重繼承等好處,使用內部類還可以實現java版的閉包和回調,而且比指針更靈活、更安全。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM