目錄
1.內部類概述
1.1 什麼是內部類?
1.1.1 內部類顧名思義,將類置於其他類的內部(接口內部也可以,具體看靜態內部類部分詳解);
1.2 爲什麼需要內部類?(可以後看)
1.2.1 內部類提供了某種進入其外部類的窗口;
1.2.2 每個內部類都能獨立地繼承一個(接口的)實現,所以無論外圍類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響;這句話是最精闢的
1.2.3 Java不支持類的多重繼承,但是提供接口間接以實現Java的多重繼承功能,而內部類的出現使多重繼承變得更加完整;
1.2.4 在單個外部類中,可以讓多個內部類以不同的方式實現同一個接口,或者繼承同一個類;
1.2.5 對於靜態內部類,內部類的對象創建並不依賴於外部類對象;
1.2.4 舉個栗子,比如波音飛機繼承了飛機類,但是波音飛機還想繼承交通工具類的屬性功能,不存在接口的情況下,只能通過使用內部類實現多重繼承:
1)交通工具類
package org.ssm.java.innerClass;
/*
* 交通工具類
* */
public class Vehicle {
/*
* 方法:載客
* */
void carry(){
System.out.println("載客功能。。。");
}
}
2)飛機類
package org.ssm.java.innerClass;
/*
* 飛機
* */
public class Plane {
private String name;
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
3)波音飛機類
package org.ssm.java.innerClass;
import com.hp.hpl.sparta.xpath.ThisNodeTest;
/*
* 波音飛機
* */
public class Boeing extends Plane {
private String num;
private void descript(){
System.out.println("我是波音飛機");
}
class Inner extends Vehicle{
Vehicle getVehicle(){
return new Vehicle();
}
}
private Vehicle getInner(){
return new Inner();
}
public static void main(String[] args) {
Boeing boeing = new Boeing();
Boeing.Inner inner = (Inner) boeing.getInner();
inner.carry();
Vehicle vehicle = inner.getVehicle();
vehicle.carry();
}
}
2.成員內部類
package org.ssm.java.innerClass.member;
/*成員內部類*/
public class Outer {
/*成員屬性*/
public int num;
/*私有屬性*/
private String name;
/*靜態成員*/
private final static String TYPE = "Outer";
/*帶參構造器*/
public Outer(int num, String name) {
super();
this.num = num;
this.name = name;
}
/*成員方法*/
private void descript(){
System.out.println("I am "+name);
}
/*靜態方法*/
private static boolean isOuter(){
return true;
}
/*內部類*/
class Inner{
public int innnerNum = 0;
/*內部類屬性、構造器和方法*/
private String name;
public Inner(String name) {
this.name = name;
}
/*內部類成員方法*/
private void descript(){
System.out.println("I am "+name);
}
/*訪問外部類成員:包括靜態和成員屬性*/
private void getOuterAttribute(){
System.out.println("-----------------------");
System.out.println("我訪問外部類的成員屬性num:"+num);
System.out.println("我訪問外部類的靜態屬性Type:"+TYPE);
System.out.println("我訪問外部類的成員方法:");
Outer.this.descript();
System.out.println("我訪問外部類的靜態方法:"+Outer.this.isOuter());
}
}
/*提供創建內部類對象的方法*/
private Inner getInner(String name){
return new Inner(name);
}
public static void main(String[] args) {
/*創建外部類對象*/
Outer outer = new Outer(888, "outer");
/*創建內部類對象*/
Outer.Inner inner = outer.new Inner("inner");
//Outer.Inner inner2 = outer.getInner("inner");
inner.descript();
/*內部類對象訪問外部類成員*/
inner.getOuterAttribute();
}
}
/*控制檯輸出:
I am inner
-----------------------
我訪問外部類的成員屬性num:888
我訪問外部類的靜態屬性Type:Outer
我訪問外部類的成員方法:
I am outer
我訪問外部類的靜態方法:true*/
1.1 成員內部類作爲外部類的一個成員存在,與外部類的屬性、方法並列;
1.2 外部類的非靜態方法之外的任意位置創建某個內部類對象,必須具體的指明這個對象的類型:OuterClassName.InnerClassName;創建內部類對象必須使用外部類對象來創建,可以使用new,也可以在外部類中提供返回內部類對象的方法;
/*創建內部類對象*/
Outer.Inner inner = outer.new Inner("inner");
//Outer.Inner inner2 = outer.getInner("inner");
1.3 內部類和外部類之間還存在通信,當生成一個內部類的對象時,此對象與它的外部類對象之間還存在聯繫,內部類對象能訪問外圍對象的所有成員,而不需要任何特殊條件,內部類擁有其外部類的所有元素的訪問權;這個是怎麼做到的呢?當某個外部類對象創建內部類對象時,此內部類對象必定會祕密捕獲一個指向那個外圍類對象的引用,然後,在你訪問外部類成員時,就是用那個引用來選擇外部類的成員;
1.4 內部類訪問外部類的實例變量:外部類名.this.屬性,如上Outer.this.num;
1.5 成員內部類不能定義靜態成員,只能定義對象成員;但是靜態內部類可以;
1.6 上述說到創建內部類對象之前外部類對象必須先創建,是因爲內部類對象會暗暗地連接到創建他的內部類對象上,但是我們不想內部類對象和外部類對象扯上聯繫怎麼辦呢?那就需要靜態內部類了;靜態內部類又是什麼妖魔鬼怪呢?下面來了解一下:
2.靜態內部類
2.1 在內部類聲明爲static即爲靜態內部類,也叫嵌套類;
package org.ssm.java.innerClass.staticInner;
import aj.org.objectweb.asm.Type;
/*靜態內部類*/
public class Outer {
private String name;
private static String TYPE = "Outer";
public Outer(String name) {
this.name = name;
}
private static void descript(){
System.out.println("I am "+TYPE);
}
private static class Inner{
/*靜態屬性*/
public static String TYPE ="Inner";
private String name;
public Inner(String name) {
this.name = name;
}
/*內部類成員方法*/
private void descript(){
System.out.println("I am "+name);
}
/*訪問外部類成員:包括靜態和成員屬性*/
private void getOuterAttribute(){
System.out.println("-----------------------");
System.out.println("我訪問外部類的靜態屬性Type:"+TYPE);
System.out.println("我訪問外部類的靜態方法:");
Outer.descript();
}
}
private Inner getInner(String name){
return new Inner(name);
}
public static void main(String[] args) {
Inner inner = new Inner("Inner");
inner.descript();
Outer.Inner inner2 = new Outer("Outer").getInner("Inner2");
inner2.descript();
inner2.getOuterAttribute();
/* 輸出:
I am Inner
I am Inner2
-----------------------
我訪問外部類的靜態屬性Type:Inner
我訪問外部類的靜態方法:
I am Outer
*/
}
}
2.2 既然內部類被聲明爲static,那要創建內部類對象就不需要創建外部類對象,與此同時靜態內部類的對象也不能訪問外部類對象的非靜態成員和方法;因爲類的非靜態成員的訪問必須依賴於類的實例對象;
2.3 在介紹成員內部類時介紹到,成員內部類不能定義靜態成員,但是靜態內部類可以;
2.4 在成員內部類裏,可以通過外部類名.this鏈接到外部類對象,但是靜態內部類就沒有this;
2.5 靜態內部類可以定義在接口內部,特殊的是放置在接口中的內部類都自動是public和static的,這是接口的規則;
3.局部內部類
3.1 在一個方法裏或者在任意的作用域內定義內部類;
package org.ssm.java.innerClass.partInner;
public class Outer {
private String name ;
private PartInterface partInterface(){
class Inner implements PartInterface{
private String name;
@Override
public void descript() {
System.out.println("局域內部類描述");
}
}
return new Inner();
}
public static void main(String[] args) {
Outer outer = new Outer();
PartInterface partInterface = outer.partInterface();
partInterface.descript();
}
}
3.2 局部內部類訪問作用域內的局部變量,該局部變量需要使用final修飾;
4.匿名內部類
4.1 先舉個栗子瞭解一下:
package org.ssm.java.innerClass.anonymous;
public class SuperInner {
private void descript(){
System.out.println("內部類的父類");
}
}
package org.ssm.java.innerClass.anonymous;
public interface SuperInterface {
void descript();
}
package org.ssm.java.innerClass.anonymous;
/*
* 匿名內部類
* */
public class Outer {
/*
* 繼承父類
* */
public SuperInner superInner(){
return new SuperInner(){
private String name;
public void descript(){
System.out.println("我是內部類1號");
}
public void content() {
System.out.println("333332");
}
};
}
/*
* 實現接口
* */
public SuperInterface superInterface() {
return new SuperInterface() {
@Override
public void descript() {
System.out.println("我是內部類2號");
}
};
}
public static void main(String[] args) {
Outer outer = new Outer();
SuperInner superInner = outer.superInner();
superInner.descript();
superInner.content();
SuperInterface superInterface = outer.superInterface();
superInterface.descript();
/*
* 輸出:
我是內部類1號
333332
我是內部類2號*/
}
}
4.2 注意上述代碼中Outer類中的方法superInner(),是不是感覺很突兀,這種奇怪的代碼意思大概是:創建一個繼承自SuperInnner的匿名類的對象,換句話說就是這個方法返回一個對象,這個對象的類是方法返回類型SuperInnner的子類,這個子類沒有名字定義,方法返回子類的對象,然後通過向上轉型將子類對象的引用轉型爲父類SuperInnner的引用;
4.3 下面是匿名類定義的格式:
匿名內部類定義和實例化形式如下:
new 父類構造方法(參數){
//注:該方法名必須在父類中已經存在
修飾符 返回參數類型 方法名(參數列表){
。。。
}
}
4.3 觀察上述提供的匿名內部類格式,匿名內部類必須繼承於一個類,抽象的也可以,或者實現一個接口,二選其一,不能兼備,不能多繼承接口;
4.3.1 如果是繼承於抽象類,匿名內部類中必須實現父類所有的方法;
4.3.2 如果是實現接口,內部類必須重寫接口中所有的方法;
4.4 匿名內部類不能是抽象類,所以不能包含抽象方法,因爲方法會返回一個實例,抽象類無法創建實例;
4.5 既然是匿名內部類,所以它也沒有構造器,因爲內部類沒有名字,與構造器規則不符;
4.6 可以定義新的方法和屬性(不能使用static修飾),但是無法顯式的通過“實例名.方法名(參數)”的形式調用,因爲使用new創建的是“上轉型對象”(即父類聲明指向子類對象),如果想調用,只能在父類定義同樣的方法和屬性;
5.內部類標識符
5.1 我們都知道,每個類都會產生一個.class文件,內部類也會產生一個.class文件,內部類的命名規則爲:外部類名字$內部類名字.class,所以不要問同事某個文件名字怎麼這麼起,顯得自己業餘;
分享一個博客,裏面做的表格比對各類內部類很好,值得推薦:https://www.cnblogs.com/dorothychai/p/6007709.html