Java 接口和抽象类可以被new么?——顺便总结内部类
轉載:https://blog.csdn.net/hackersaillen/article/details/47281549
背景:
? ? 最近有同事跟我說了他面試時遇到的問題,考官問:“接口和抽象類可以被new嘛?”。這可能不是考官的原話,但是據他表達考官大概就是這個意思了。聽到這個問題,我的第一反應是肯定不行啊,直接對接口和抽象類調用new,編譯器都過不去。但是他說,考官說可以,用匿名內部類實現。聽見這個回到,我感覺那個考官太…………,有點無語。我們可以仔細分析下這個問題。
?
直接new接口和抽象類
? ?首先先明確一點,直接new接口和抽象類,這肯定行不通,編譯器會提示Cannot instantiate the type XX的錯誤。這個實驗就不做了,沒意思。
?
使用匿名內部類
? ? ?下面的代碼是一個匿名內部類的Demo,也就是考官說的可以new。
package com.saillen.test;
interface A {
void f();
}
public class T {
public T(A a) {
a.f();
}
public static void main(String[] args) {
T t = new T(new A() {
public void f() {
System.out.println("我是匿名內部類");
System.out.println("Class對象是:" + this.getClass());
System.out.println("類名字是:" + this.getClass().getSimpleName());
}
});
}
}
? ?上面的程序很簡單,我們使用匿名內部類,然后“new”一個接口A的對象,輸出它的類名了Class對象,輸出如下:
我是匿名內部類
Class對象是:class com.saillen.test.T$1
類名字是:
? ?通過輸出可以看到,內部類的類名是“”也就是一個空字符串,但是它確確實實是有類型的。而且查看編譯后的class文件,會發現,會多一個T$1.class,這個class就是匿名內部類的原型,
? 用javap反編譯這個文件,可以看到這個類的源碼樣式如下:
? ?通過反編譯后的文件可以證明,我們的“匿名內部類”的類名是Test$1。所以new針對的還是普通的class(雖然內部類和普通類有很大不一樣),只不過這個class的寫法稍有不同,它是編譯器幫我們從匿名內部類中提取的。
?
結論:
?通過上面的實驗,我仍然堅持,接口和抽象類不可以被new!匿名內部類只是一種寫法上的迷惑而已。這個考官的答案很不靠譜,不能說他的想法就是絕對的錯,但是絕對不應該這么問這個問題,這不是一個能不能就回答的。或者說不能從字面上就證明可以對interface或者abstract class使用new。
?
內部類總結
? ? Java編程思想第十章專門介紹了內部類,內部類確實神奇而且復雜。但是內部類在編程中被應用的場景很少,這主要是看設計者的設計思想,不過它的語法和特性卻有很多。對于內部類來說要記住可以繼承內部類但是不能覆蓋。
?
普通內部類:
? ? 普通的內部類,就是在class里面普通的定義一個類,eg:
public class A{
public class B{
}
}
普通內部類,或者說平凡的內部類,有如下特性:(總結自《Java編程思想》)
 ? ?(1)這個類在外部類的外面不能被直接訪問,需要通過OuterClassName.InnerClassName方式訪問。比如Map的Map.Entry就是一個內部接口,只能通過Map.Entry方式來使用它。
? ?(2)內部類對象在創建后會秘密的鏈接到外部類對象上,隱含的有一個指向外部類對象的引用,所以沒有外部類對象,是無法實例化內部類對象的。也就是我們無法獨立于外部類創建一個內部類對象。(這里不包括聲明為static的內部類,那不是平凡的內部類)
? ? (3)因為內部類隱含的有一個指向外部類的指針,所以內部類可以訪問外圍類的成員,而且是外圍類的所有成員,包括private的成員。
? ? (4)在內部類中使用OuterClassName.this可以訪問外圍類對象。
? ? (4)如果想要在外部類外面實例化內部類對象,那么可以同.new語法,也就是outerObject.new InnerClass()的方式,eg:
package com.saillen.test;
public class A {
public void f() {
A.B b = this.new B();
B b2 = new B();
}
public class B {
void g(){
System.out.println(A.this);
}
}
public static void main(String[] args) {
A a = new A();
A.B b = a.new B();
}
}
? (5)內部類最大的用途是:它可以實現接口或者繼承某個類,這樣使用內部類時,用基類引用內部類對象,可以屏蔽內部類的細節。這樣的好處是,可以實現偽“多重繼承”等。
? (6)普通的內部類不能有static字段和static數據,也不能包含嵌套類。
?
在方法和作用域內的內部類:
? ?如果內部類出現在了方法和作用域內,那么它就不是“平凡”的內部類了,而且這個內部類的作用域就是在這個方法的作用域內,方法外面是無法訪問到的!但是它仍然具有“平凡”的內部類特性。
?
匿名內部類:
? ?匿名內部類,在內部類的基礎上減少了對class的定義,直接用new 后面跟一個接口或者基類,然后類體里面實現方法即可,這樣JVM會調用編譯器生成的構造器來生成這個內部類對象,并且編譯器幫忙生成這個內部類的類結構。匿名內部類好處是語法簡單,但是不方便閱讀,在Android編程中,對Button的監聽經常會用匿名內部類。匿名內部類有一些限制:匿名內部既可以擴展類,也可以實現接口,但是不能兩者兼備,而且只能實現一個接口。
?
嵌套類:
? ?嵌套類就是在內部類的基礎上加上static聲明,也就是靜態的內部類。嵌套類跟普通的內部類特性有很大不同,特點:
? ?(1)在構造時,不需要外圍類的對象,但是同樣,它只能訪問外圍類中的static字段;
? ?(2)嵌套類可以有static數據和字段;
? ?(3)嵌套類可以作為接口的一部分,這樣在接口中就可以用公用代碼出現;
? ?(4)嵌套類是可以多重嵌套的,嵌套多少層不重要,它都可以訪問它所有外圍類成員。
?
內部類標識符:
? ? 部類必須生成一個class文件以包含它的Class對象信息,這些類文件有嚴格的命名規則:外圍類的名字,加上“$”,再加上內部類的名字。
?
為什么需要內部類:
? ?《Java編程思想》中很明確指出: ? ?
???每個內部類都能獨立的繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個實現,對于內部類都沒有影響。
? ?利用內部類可以實現多重繼承等好處,使用內部類還可以實現java版的閉包和回調,而且比指針更靈活、更安全。
總結
以上是生活随笔為你收集整理的Java 接口和抽象类可以被new么?——顺便总结内部类的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 编程:休息片刻的好处
- 下一篇: java 接口不是不能new吗?- ja
