《java程式設計思想——第十章(內部類)》
內部類
可以將一個類的定義放在另一個類的定義內部,這就是內部類。
10.1 建立內部類
把類的定義置於外圍類的裡面。
/**
* 建立內部類
* @author Administrator
*/
public class Parcel1 {
class Contents{
private int i = 11;
public int value() {
return i;
}
}
class Destination{
private String label;
public Destination(String whereTo) {
label = whereTo;
}
String readLabel(){
return label;
}
}
public void ship(String dest){
Contents c = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel1 p = new Parcel1();
p.ship("哈哈");
}
}
10.2 連結到外部類
內部類自動擁有外部類所有成員的訪問權。構建內部類物件時,需要一個指向其外圍類物件的引用。
10.3 使用.this和.new
如果想要生成對外部物件的引用,可以用外部類名後跟.this的形式。
public class DotThis {
void f(){
System.out.println("DotThis.f()");
}
public class Inner {
public DotThis outer(){
return DotThis.this;
}
}
public Inner inner() {
return new Inner();
}
public static void main(String[] args) {
DotThis dt = new DotThis();
DotThis.Inner dti = dt.inner();
dti.outer().f();
}
}
如果要建立內部類的物件,必須在new表示式時提供其外部類的引用。形式是外部類引用.new形式。
public class DotNew {
public class Inner {}
public static void main(String[] args) {
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new Inner();
}
}
10.4 內部類與向上轉型
內部類向上轉型為其基類,或者介面時,所得到的只是指向基類或介面的引用,能夠很方便的隱藏細節。
/**
* 內部類向上轉型
* @author Administrator
*/
class Parcel4{
private class pContents implements Content{
private int i = 11;
@Override
public int value() {
return i;
}
}
protected class PDestination implements Destinations{
private String label;
public PDestination(String whereTo) {
label = whereTo;
}
public String readLabel(){
return label;
}
}
public Destinations destinations(String s) {
return new PDestination(s);
}
public Content contents() {
return new pContents();
}
}
public class TestParcel {
public static void main(String[] args) {
Parcel4 p = new Parcel4();
Content c = p.contents();
Destinations d = p.destinations("哈哈");
}
}
10.5 在方法和作用域的內部類
/**
* 方法內部類
* @author Administrator
*
*/
public class Parcel5 {
public Destinations destination(String s) {
class pDestination implements Destinations{
private String label;
public pDestination(String whereTo) {
label = whereTo;
}
public String readLabel(){
return label;
}
}
return new pDestination(s);
}
}
/**
* 作用域內部類
* @author Administrator
*
*/
public class Parcel6 {
private void internalTracking(boolean b) {
if(b){
class TrackingSlip{
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip(){
return id;
}
}
TrackingSlip ts = new TrackingSlip("slip");
String s= ts.getSlip();
}
}
public void track() {
internalTracking(true);
}
public static void main(String[] args) {
Parcel6 p = new Parcel6();
p.track();
}
}
10.6 匿名內部類
預設構造匿名內部類
/**
* 匿名內部類
* @author Administrator
*
*/
public class Parcel7 {
public Content content() {
return new Content() {
private int i = 11;
@Override
public int value() {
return i;
}
};
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Content c = p.content();
}
}
帶引數匿名內部類
public class Parcel8 {
public Wrapping wrapping(int x) {
return new Wrapping(x) {
private int i = 11;
@Override
public int value() {
return super.value() * 47;
}
};
}
public static void main(String[] args) {
Parcel8 p = new Parcel8();
Wrapping w = p.wrapping(8) ;
}
}
匿名內部類如果使用一個在其外部定義的物件,其引數的引用必須是final 的。
10.7 巢狀類
如何不需要內部類和外部類有聯絡,可以將內部類宣告為static,通常稱為巢狀類。
特性:建立巢狀類的物件,並不需要其外圍物件。
不能從巢狀類的物件中訪問非靜態的外圍類物件
- 介面中的內部類
正常情況下介面中不能放置任何程式碼,但巢狀類可以作為介面中國的一部分。
/**
* 介面中的內部類
* @author Administrator
*
*/
public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface{
@Override
public void howdy() {
System.out.println("hai");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
10.8 為什麼需要內部類
每個內部類都能獨立地繼承自一個實現,所以無論外圍類是否已經繼承了某個實現,對應內部類都沒有影響。
- 內部類可以有多個例項,每個例項都有自己的狀態資訊,並且與外部類資訊相獨立。
- 在單個外圍類中,可以讓多個內部類以不同的方式實現同一個介面,或繼承同一個類。
- 建立內部類物件的時刻並不依賴於外圍類物件的建立。
閉包與回撥:閉包是一個可呼叫物件,它記錄了一些資訊,這些資訊來自於建立它的域。
回撥的價值在於可以在執行時動態地決定呼叫什麼方法。
內部類與控制框架:
框架的完整實現是由單個類建立,從而使得實現的細節被封裝了起來。
內部類能夠很容易的訪問外圍類的任意成員。
(這部分demo無法執行)
10.9 內部類的繼承
因為內部類的構造器必須連線到指向其外圍類物件的引用,所以在繼承內部類的時候,必須用特殊的語法。
/**
* 內部類的繼承
* @author Administrator
*
*/
class WithInner{
class Inner{}
}
public class InheritInner extends WithInner.Inner{
public InheritInner(WithInner wi) {
wi.super(); //初始化
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}
10.10 內部類可以別覆蓋嗎
不能被覆蓋。繼承了外圍類並覆蓋了內部類時,兩個內部類是是完全獨立的實體,各自在自己的名稱空間裡。
可以明確的繼承某一個內部類。
/**
* 明確繼承內部類,覆蓋方法
* @author Administrator
*
*/
class Egg2 {
protected class Yolk{
public Yolk() {
System.out.println("Egg2.Yolk()");
}
public void f() {
System.out.println("Egg2.Yolk.f()");
}
}
private Yolk y = new Yolk();
public Egg2(){
System.out.println("new Egg2");
}
public void insertYolk(Yolk yy) {
y = yy;
}
public void g() {
y.f();
}
}
public class BigEgg2 extends Egg2{
public class Yolk extends Egg2.Yolk{
public Yolk() {
System.out.println("BigEgg2.Yolk()");
}
public void f() {
System.out.println("BigEgg2.Yolk.f()");
}
}
public BigEgg2() {
insertYolk(new Yolk());
}
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
}
10.11 區域性內部類
區域性內部類可以定義在方法體內,不能有訪問說明符,因為它不是外圍類的一部分;但是可以訪問外圍類成員。
/**
* 區域性內部類
* @author Administrator
*/
interface Counter{
int next();
}
public class LocalInnerClass {
private int count = 0;
Counter getCounter (final String name){
class LocalCounter implements Counter{
public LocalCounter() {
System.out.println("LocalCounter()");
}
@Override
public int next() {
System.out.println(name +" "+count);
return count++;
}
}
return new LocalCounter();
}
Counter getCounter2 (final String name){
return new Counter() {
{
System.out.println("Counter()");
}
@Override
public int next() {
System.out.println(name +" "+count);
return count++;
}
};
}
public static void main(String[] args) {
LocalInnerClass lic = new LocalInnerClass();
Counter c1 = lic.getCounter("Local Inner");
Counter c2 = lic.getCounter2("Anonymous Inner");
for (int i = 0; i < 5; i++) {
c1.next();
}
for (int i = 0; i < 5; i++) {
c2.next();
}
}
}
與匿名內部類比較:可以有一個命名的構造器,可以建立不止一個區域性內部類物件。
10.12 內部類識別符號
每個類都會產生.class檔案,內部類也會產生一個.class檔案,命名格式:外圍類名字+$+內部類名字。
10.13 總結
介面和內部類結合起來就能解決多重繼承的問題。