初步瞭解Java內部類

參考:
Java核心技術
Java編程思想
https://www.cnblogs.com/dolphin0520/p/3811445.html
https://www.cnblogs.com/chenssy/p/3388487.html
局部內部類:http://baijiahao.baidu.com/s?id=1600724548575261991&wfr=spider&for=pc
外部類和內部類數據訪問:https://blog.csdn.net/weixin_40707866/article/details/79652084

內部類的概念

將一個類的定義放在另一個類的定義內部,這就是內部類。雖然內部類存在於外部類定義之中,但是編譯之後還是會產生屬於自己的.class文件,依舊遵循着每個類都會產生一個.class文件。一般來說內部類包括四種:成員內部類、局部內部類、匿名內部類和嵌套類(也叫靜態內部類)。其中局部內部類定義在方法或作用域內,靜態內部類使用static修飾。

內部類與外部類之間的數據訪問

內部類就相當於一個外部類的成員變量,所以可以直接訪問外部類的成員,包括成員變量和成員函數。但是外部類不能直接訪問內部類成員,必須通過創建內部類實例的方法進行訪問。

爲什麼使用內部類

如果沒有內部類提供的,可以繼承多個具體的或抽象的類的能力,一些設計與編程問題就很難解決。使用內部類的最大好處就是它使得Java的多重繼承的解決方案更加完整,因爲內部類允許繼承多個非接口類型(即類和抽象類)
當多重繼承應用於接口時,那麼看不出使用內部類的必要性。因爲使用多實現仍然可以實現功能,但是如果擁有的是抽象類或具體的類,那麼就只能使用內部類實現多重繼承。

package learninnerclass;

interface A{}
interface B{}

class X implements A,B{     }

class Y implements A{
	B makeB(){
		return new B(){};
	}
}

public class MultiInterfaces {
	static void takesA(A a){}
	static void takesB(B b){}
	
	public static void main(String[] args) {
		X x = new X();
		Y y = new Y();
		
		takesA(x);
		takesA(y);
		takesB(x);
		takesB(y.makeB());
	}
}

只能使用內部類實現多重繼承的情況

package learninnerclass;

class D{}
abstract class E{}
class Z extends D{
	E makeE(){ return new E(){};}
}

public class MultiImplementation {
	static void takesD(D d){}
	static void takesE(E e){}

	public static void main(String[] args){
		Z z = new Z();
		takesD(z);
		takesE(z.makeE());
	}
}

.this和.new

內部類的對象有一個隱式引用(即.this),它引用了實例化該內部對象的外部類對象。通過這個引用,可以訪問外部類對象的全部狀態。這個引用在內部類的定義中是不可見的,是由編譯器自動生成的。具體使用如下:

外部類名.this;   // 該表達式表示外部類的引用

外部類對象.new 內部類名(構造方法的參數);   // 該表達式表示創建內部類對象

外部類名.內部類名;  // 該表達表示在外部類的作用域之外,引用內部類

成員內部類

在成員內部類中要注意兩點,1) 成員內部類中不能存在任何static的變量和方法,static只能用在靜態常量的聲明上;2)成員內部類是依附於外部類的,所以只有先創建了外部類對象才能夠創建內部類對象。
在成員內部類的定義中可以對外部類所有元素進行訪問,即使該元素使用private修飾。其實這就相當於將成員內部類當做外部類的一個元素(成員)了。正因爲如此,所以可以使用private、包訪問權限、protected、public等權限修飾符來修飾成員內部類,而不像外部類只能使用public或包訪問權限來修飾。
在非靜態內部類中,不能定義靜態的成員變量和成員方法,包括成員內部類,局部內部類,匿名局部類,但可以在靜態內部中定義靜態的成員變量和方法。值得注意的是,可以在所有的內部類中定義靜態常量,即static final
雖然成員內部類可以無條件地訪問外部類的成員,而外部類想訪問成員內部類的成員卻不是這麼隨心所欲了。在外部類定義中如果要訪問成員內部類的成員,必須先創建一個成員內部類的對象,再通過指向該內部類對象的引用來訪問內部類的域或者方法。
不過需要注意的是,當成員內部類擁有和外部類同名的成員變量或方法時,會發生隱藏現象。默認情況下,訪問的是成員內部類的成員。如果想要訪問外部類的同名成員,則需要使用下面的形式進行訪問。

示例

一般示例

package com.thinkinginjava.chapter10;

public class Parcel3 { // 外部類

	class Contents{  // 成員內部類
		private int i = 11;
		public int value() { return i;  }	
	}

	class Destination{// 成員內部類
		private String label;
		Destination(String whereTo) {
			label = whereTo;
		}
		String readLabel() { return label;}
	}
	
	public static void main(String[] args) {
		Parcel3 p = new Parcel3();  // 生成外部類對象
		// 必須通過外部類對象才能創建內部類對象
		Parcel3.Contents c = p.new Contents();  // 生成Contents成員內部類對象
		System.out.println(c.value());
		Parcel3.Destination d = p.new Destination("Suzhou");// 生成Destination成員內部類對象
		System.out.println(d.readLabel());
	}
}

訪問同名屬性

class MemberInnerClass{
	private String str = "外部類同名屬性";// 與內部類數據有重名
	
	class Contents{  // 成員內部類
		private String str = "內部類同名屬性";
		public String value() { return str;  }// 只會訪問到內部類的同名屬性
		// 要在內部類中訪問外部類同名屬性,需要藉助外部類對象
		public String FatherValue() { return new MemberInnerClass().str;  }
	}
	
	public void useInnerClass(){
		System.out.println("在外部類中,str = "+str); //訪問同名變量(只會訪問到外部類的)
		// 要想在外部類中訪問內部類同名屬性需要藉助內部類對象
		Contents c = new Contents();
		System.out.println("Contents中的域 :"+c.str);
		System.out.println("Contents中的方法 value: "+ c.value());
	}
}

public class MemberInnerClassTest {
	
	public static void main(String[] args) {
		MemberInnerClass mic = new MemberInnerClass();
		MemberInnerClass.Contents c = mic.new Contents();
		
		System.out.println("通過內部類的方法訪問內部類的屬性 str:"+c.value());
		System.out.println("通過內部類的方法訪問外部類的屬性 str:"+c.FatherValue());
		System.out.println("*****************");
		mic.useInnerClass();
	}
}

匿名內部類

匿名內部類的特點:1)沒有名字的局部內部類;2)匿名內部類是一種沒有構造器的類;3)匿名內部類沒有訪問修飾符和static修飾符;4)匿名內部類其實是隱式地繼承某一個父類或者實現某一個接口。這句話的意思就是如果聲明匿名內部類,前提是這個父類或者接口必須先存在。如下代碼所示,Contents是一個早已存在的接口,如果Contents接口不存在則無法創建匿名內部類。換句話說,匿名內部類是實現一個接口或者繼承一個父類並重寫的簡潔方式。

示例

package learninnerclass;
// 已存在的一個接口
interface Contents{
	int value();
}
public class AnonymousInnerClassTest {
	
	public Contents contents() {
		return new Contents() {   // 匿名內部類
			private int i = 11;
			public int value() { return i;}
		};
	}
	
	public static void main(String[] args) {
		AnonymousInnerClassTest p = new AnonymousInnerClassTest();
		Contents c = p.contents();
		System.out.println(c.value());
	}
}

其實上面的代碼,如果不使用匿名內部類,則等價於下面的寫法。

public class Parcel7b {
	// 繼承接口並實現
	class MyContents implements Contents{
		private int i =11;
		public int value() { return i;   }
	}
	
	public Contents contents() {   return new MyContents();   }
	
	public static void main() {
		Parcel7b p = new Parcel7b();
		Contents c = p.contents();
		System.out.println(c.value());
	}
}

局部內部類

局部內部類定義在方法或作用域(代碼塊)內,不能使用任何訪問修飾符。局部內部類的優勢在於,除包含該類的方法,其他代碼均不知道局部內部類的存在。同其他內部類比較,局部內部類不僅可以訪問包含它們的外圍類,而且還可以訪問局部變量。
1) 如果局部內部類定義在靜態方法中,它可以訪問外部類中所有靜態成員,包含私有。
2) 如果局部內部類定義在實例方法中,它可以訪問外部類中所有的成員,包含私有。

除此之外,局部內部類還可以有構造方法。如果局部內部類要訪問局部變量,那麼局部變量必須聲明爲final類型。在實踐中,局部內部類是所有內部類中最少使用的一種形式。

示例

package learninnerclass;

interface Counter{
	int next();
}

public class LocalInnerClass {
	
	private int count = 10;
	private static double salary = 13.14;
	
	public int getNum(){
		return count;
	}
	public static double getSalary(){
		return salary;
	}
	// 1.在實例方法中的局部內部類
	Counter getCounter(final String name)
	{
		class LocalCounter implements Counter
		{   //局部內部類中可以有構造方法
			public LocalCounter(){
				System.out.println("局部內部類的構造器...");
			}
			@Override
			public int next() {
				System.out.println("name = "+name);
				// 1.1 實例方法中的局部內部類能訪問外部類的實例變量和實例方法
				System.out.println("count = "+count);
				System.out.println("通過方法獲取count = "+getNum());
				// 1.2 實例方法中的局部內部類能訪問外部類的靜態變量和靜態方法
				System.out.println("salary = "+salary);
				System.out.println("通過方法獲取salary = "+getSalary());
				return count++;
			}
		}
		return new LocalCounter();
	}
	// 2.在靜態方法中的局部內部類
	static Counter getCounter2(final String name)
	{
		class LocalCounter implements Counter
		{   //局部內部類中可以有構造方法
			public LocalCounter(){
				System.out.println("局部內部類的構造器...");
			}
			@Override
			public int next() {
				System.out.println("name = "+name);
				// 2.1 靜態方法中的局部內部類不能訪問外部類的實例變量和實例方法
//				System.out.println("count = "+count);
//				System.out.println("通過方法獲取count = "+getNum());
				// 2.2 靜態方法中的局部內部類只能訪問外部類的靜態變量和靜態方法
				System.out.println("salary = "+salary);
				System.out.println("通過方法獲取salary = "+getSalary());
				return 0;
			}
		}
		return new LocalCounter();
	}
	
	public static void main(String[] args) 
	{
		LocalInnerClass lic = new LocalInnerClass();
		Counter c1 = lic.getCounter("實例方法中的局部內部類");
		c1.next();
		
		Counter c2 = lic.getCounter2("靜態方法中的局部內部類");
		c2.next();
	}
}

靜態內部類(嵌套類)

如果不需要內部類對象與其對應的外部類對象之間存在聯繫,那麼就可以將內部類聲明爲static的,以便消除產生的引用。換句話說,當內部類是靜態內部類的時候,它並不會隱式地保存this引用(該引用指向創建它的外部類對象,普通內部類訪問外部類對象的數據也是通過該引用才能夠實現。),因此靜態內部類對象和外部類對象之間也就不存在聯繫了。
如果創建了一個靜態內部類,那就意味着:
1) 要創建靜態內部類的對象,並不需要其外部類的對象;
2) 在靜態內部類中不能訪問外部類的非靜態(static)的成員變量和成員方法,只能訪問靜態的成員變量和靜態成員方法。

示例

package learninnerclass;

public class StaticInnerClassTest02 {
	
	private int num =10;
	private static double salary=100.4;
	
	public int getNum(){
		return num;
	}
	public static double getSalary(){
		return salary;
	}
	
	public static class Inner{
		// 1.訪問外圍類的成員變量
		// 1.1 在靜態內部類中不能訪問外部類的非靜態屬性
//		public void info1(){// 這段代碼是錯誤的
//			System.out.println("num : "+num);
//		}
		// 1.2訪問外圍類的靜態成員變量
		public void info2(){// 可以訪問外部類的靜態成員變量
			System.out.println("salary : "+salary);
		}
		// 2.訪問外圍類的成員方法
		// 2.1 在靜態內部類中不能訪問外部類的非靜態方法
//		public void info3(){// 這段代碼是錯誤的
//			System.out.println("num :"+ getNum());
//		}
		// 2.2 在靜態內部類中可以訪問外部類的靜態方法
		public void info4(){// 可以訪問外部類的靜態成員方法
			System.out.println("salary : "+getSalary());
		}
		
	}
	
	public static void main(String[] args) {
		// 創建靜態內部類對象(創建靜態內部類對象的時候並沒有通過外部類對象,即:外部類對象.new 內部類名() )
		StaticInnerClassTest02.Inner in = new Inner();
		in.info2();
		in.info4();
	}
}

另外,聲明在接口中的內部類也會自動成爲static和public。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章