1. 程式人生 > >學習 Java程式設計思想 Thinking in Java 第十章:內部類

學習 Java程式設計思想 Thinking in Java 第十章:內部類

前言

和我一起讀書書,一起喵喵喵喵喵~
在讀 《Java程式設計思想 Thinking in Java》 這本書的時候,會有很多示例程式碼。為了鞏固和實踐,所以將書上的程式碼都寫上一遍(其實是逼自己寫程式碼)。目前我已經讀到第十章:內部類 了,程式碼也會從這裡開始寫。前面的程式碼就算了,不補了,Just do it!

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

先寫個介面Destination:

package com.innerClass;

public interface Destination {
    String readLabel();
}

接下來就寫,在方法的作用域中建立一個完整的類。這被叫做區域性內部類

!!!

package com.innerClass;

public class Parcel5 {
    public Destination destination(String s){
        class PDestination implements  Destination{
            private String label;
            private PDestination(String whereTo){
                label = whereTo;
            }
            public String readLabel(){
                return label;
            }
        }
        return  new PDestination(s);
    }

    public static void main(String[] args) {
        Parcel5 p = new Parcel5();
        Destination d = p.destination("Tasmania");
    }
}

下面展示,如何在任意作用域內嵌入一個內部類:

package com.innerClass;

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();;
    }
}

回答程式碼中的問題,答案是不可以哦,因為那裡是 if的作用域外,超出了TrackingSlip的作用域了!

10.6 匿名內部類

先寫個介面,做鋪墊啊

package com.innerClass;

public interface Contents {
    int value();
}

馬上寫一個匿名內部類啊

package com.innerClass;

public class Parcel7 {
    public Contents contents(){
        return new Contents() {
            private int i = 11;
            @Override
            public int value() {
                return i;
            }
        };
    }

    public static void main(String[] args) {
        Parcel7 p = new Parcel7();
        Contents c = p.contents();
    }
}

在contents方法中,return的那個類,沒有名字,看吧,是不是很奇怪。
在return一個new的Contents的時候,彷彿聽見一個聲音,說:“等等,我想在這裡插入一個內的定義”。結果就成這個樣子了。
這就是匿名內部類啊!!!
對這個奇怪的語法正兒八經的總結一下就是:“建立一個繼承自Contents的匿名內部類的物件。通過new表示式返回的引用被自動向上轉型為對Contents的引用。

上述匿名內部類的語法是下述形式的簡化形式:

package com.innerClass;

public class Parcel7b {
    class MyContents implements Contents{
        private int i = 11;
        @Override
        public int value() {
            return i;
        }
    }
    public Contents contents(){
        return new MyContents();
    }
    public static void main(String[] args) {
        Parcel7b p = new Parcel7b();
        Contents c = p.contents();
    }
}

有沒有覺得第一種書寫方式更裝逼一點呢!

咦?咦咦咦?
我發現上面那個匿名內部類使用了預設構造器來生成Contents,但是如果基類需要一個有引數的構造器,就像下面這樣,改怎麼辦呢?

package com.innerClass;

public class Parcel8 {
    public Wrapping wrapping(int x){
        return new Wrapping(x){
            @Override
            public int value() {
                return super.value() * 47;
            }
        };
    }
    public static void main(String[] args) {
        Parcel8 p = new Parcel8();
        Wrapping w = p.wrapping(10);
    }
}

Wrapping這個類我該怎樣寫,才能正確的被Parcel8使用呢?
很簡單!只需要傳遞合適的引數給基類的構造器即可,就像這樣:

package com.innerClass;

public class Wrapping {
    private int i;
    public Wrapping(int x){
        i = x;
    }
    public int value(){
        return i;
    }
}

雖然Wrapping只是一個具有具體實現的普通類,但還是被其匯出類當作公共“介面”來使用!

如果在匿名類中定義欄位時,還要對其執行初始化操作,該咋寫呢?就該像下面這樣寫呀:

package com.innerClass;

public class Parcel9 {
    public Destination destination(final String dest){
        return new Destination() {
            private String label = dest;
            @Override
            public String readLabel() {
                return label;
            }
        };
    }
    public static void main(String[] args) {
        Parcel9 p = new Parcel9();
        Destination d = p.destination("Tasmania");
    }
}

注意到了嗎?用到了關鍵字final哦,如果定義一個匿名內部類,並且希望它使用一個在其外部定義的物件時,那麼編譯器就會要求其引數是final的。

實現一個為匿名內部類建立一個構造器的效果:

package com.innerClass;

public abstract class Base {
    public Base(int i){
        System.out.println("Base constructor, i = "+i);
    }
    public abstract void f();
}
package com.innerClass;

import com.sun.org.apache.xpath.internal.SourceTree;

public class AnonymousConstructor {
    public static Base getBase(int i){
        return new Base(i) {
            @Override
            public void f() {}
        };
    }
    public static void main(String[] args) {
        Base base = getBase(47);
        base.f();
    }
}

這個例子中呢,變數i,不要求是final的。因為 i 被傳遞給匿名類的基類的構造器,並沒有在匿名類內部直接使用。

帶例項初始化的“parcel”形式。

package com.innerClass;

public class Parcel10 {
    public Destination destination(final String dest, final float price){
        return new Destination() {
            private int cost;
            {
                cost = Math.round(price);
                if(cost > 100){
                    System.out.println("Over budget");
                }
            }
            private String label = dest;
            @Override
            public String readLabel() {
                return label;
            }
        };
    }
    public static void main(String[] args) {
        Parcel10 p = new Parcel10();
        Destination d = p.destination("Tasmania", 101.395F);
    }
}

10.6.1 再訪工廠方法
使用匿名內部類改造 interfaces/Factories.java 示例:

package com.innerClass;

public interface Service {
    void method1();
    void method2();
}
package com.innerClass;

public interface ServiceFactory {
    Service getService();
}
package com.innerClass;

public class Implementation1 implements Service{
    private Implementation1(){}
    @Override
    public void method1() {
        System.out.println("Implementation1 method1");
    }

    @Override
    public void method2() {
        System.out.println("Implementation1 method2");
    }
    public static ServiceFactory factory = new ServiceFactory() {
        @Override
        public Service getService() {
            return new Implementation1();
        }
    };
}
package com.innerClass;


public class Implementation2 implements Service{
    private Implementation2(){}
    @Override
    public void method1() {
        System.out.println("Implementation2 method1");
    }

    @Override
    public void method2() {
        System.out.println("Implementation2 method2");
    }
    public static ServiceFactory factory = new ServiceFactory() {
        @Override
        public Service getService() {
            return new Implementation2();
        }
    };
}
package com.innerClass;

public class Factories {
    public static void serviceConsumer(ServiceFactory fact){
        Service s = fact.getService();
        s.method1();
        s.method2();
    }

    public static void main(String[] args) {
        serviceConsumer(Implementation1.factory);
        serviceConsumer(Implementation2.factory);
    }
}

改造 interfaces/Games.java 示例:

package com.innerClass.games;

public interface Game {
    boolean move();
}
package com.innerClass.games;

public interface GameFactory {
    Game getGame();
}
package com.innerClass.games;

public class Checkers implements Game{
    private Checkers(){}
    private int moves = 0;
    private static final int MOVES = 3;

    @Override
    public boolean move() {
        System.out.println("Checkers move " + moves);
        return ++moves != MOVES;
    }
    public static GameFactory factory = new GameFactory() {
        @Override
        public Game getGame() {
            return new Checkers();
        }
    };
}
package com.innerClass.games;

public class Chess implements Game{
    private Chess(){}
    private int moves = 0;
    private static final int MOVES = 4;
    @Override
    public boolean move() {
        System.out.println("Chess move "+ moves);
        return ++moves != MOVES;
    }
    public static GameFactory factory = new GameFactory() {
        @Override
        public Game getGame() {
            return new Chess();
        }
    };
}
package com.innerClass.games;

public class games {
    public static void playGame(GameFactory factory){
        Game s = factory.getGame();
        while (s.move()){
            ;
        }
    }

    public static void main(String[] args) {
        playGame(Checkers.factory);
        playGame(Chess.factory);
    }
}

10.7 巢狀類

相關推薦

學習 Java程式設計思想 Thinking in Java 部類

前言 和我一起讀書書,一起喵喵喵喵喵~ 在讀 《Java程式設計思想 Thinking in Java》 這本書的時候,會有很多示例程式碼。為了鞏固和實踐,所以將書上的程式碼都寫上一遍(其實是逼自己寫程式碼)。目前我已經讀到第十章:內部類 了,程式碼也會從這裡開

Java程式設計思想(二)14-型別資訊

目錄: 1. RTTI(Runtime Type Identification)執行階段型別識別 1.1 用途:   為了確定基類指標實際指向的子類的具體型別。——《C++ Primer Plus》 1.2 工作原理:   通過型別轉換運算子回答“是否可以

Java程式設計思想(三)15-泛型

目錄: 泛型(generics)的概念是Java SE5的重大變化之一。泛型實現了引數化型別(parameterized types)的概念,使程式碼可以應用於多種型別。“泛型”這個術語的意思是:“適用於許多許多的型別”。 1 泛型方法   泛型方法與其所在的類

Java程式設計思想(六)19-列舉型別

目錄: 19.4 values()的神祕之處   通過反編譯列舉類,values()是由編譯器新增的static()方法。編譯器將列舉類(enum)標記為final類,所以enum類無法被繼承。 19.5 實現而非繼承   所有的enum類都繼承自java.lan

Java程式設計思想(七)20-註解

目錄:   註解(也被稱為元資料)為我們在程式碼中新增資訊提供了一種形式化的方法,使我們可以在稍後某個時刻非常方便地使用這些資料。 1 基本語法   被註解的方法與其他的方法沒有區別。註解可以

Java程式設計思想(五)18-Java IO系統

目錄: 1 File類   File(檔案)類這個名字有一定的誤導性;我們可能會認為它指代的是檔案,實際上卻並非如此。它既能代表一個特定檔案的名稱,又能代表一個目錄下的一組檔案的名稱。實際上,FilePath(檔案路徑)對這個類來說是更好的名字。   如果它指的

Java程式設計思想》筆記之——訪問許可權控制

本文只摘錄很少一部分,作文筆記。訪問控制(或隱藏具體實現)與“最初的實現並不恰當”有關當編寫一個Java原始碼檔案時,此檔案通被稱為編譯單元(有時也被稱為轉譯單元)。每個編譯單元都必須有一個字尾名為.java,而編譯單元內測可以有一個public類,該類的名稱必須與檔名稱相同

java 程式設計思想課後題(

練習1: public class twoSum { public static void main(String[] args){ for(int i = 1;i <= 100;i ++){ System.out.pr

Java程式設計思想閱讀筆記(10部類

內部類 內部類是指在一個外部類的內部再定義一個類。內部類作為外部類的一個成員,並且依附於外部類而存在的 可以將一個類的定義放在另一個類定義內部,這就是內部類 內部類自動擁有對包裹它的基類所有成員的訪問許可權 內部類可為靜態,可用protected和priva

“全棧2019”Java部類可以向上或向下轉型嗎?

難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第九十章:內部類可以向上或向下轉型嗎? 下一章 “全棧2019”Java第九十一章:內部類具

Python程式設計從入門到實踐檔案和異常

檔案和異常 從檔案中讀取資料 1.函式open() 要以任何方式使用檔案,都得先開啟檔案,這樣才能訪問它. 接受一個引數:要開啟檔案的名稱. 返回一個表示檔案的物件.Python將這個物件儲存在後面要使用

核同步方法

font 長時間 加鎖 height 讀取數據 簡單 允許 接口 優先 10.1 原子操作 同步方法中的原子操作是其他同步方法的基石; 原子操作可以保證指令以原子的方式執行------執行過程不被打斷。 原子操作可以把讀取和增加變量的行為包含在一個單步中執行,從而防止

[隨筆][Java][讀書筆記][thinking in java][ 類型信息]

found 構造 att main 數組 test 第一個 eating urn 主要理解如何在運行時獲取類型信息。主要有兩種方式:一是RTTI,假定我們在編譯時已經知道了所有的類型;二是反射機制,允許在運行時發現和使用類的信息。 14.1 為什麽需要RTTI 一個多

[隨筆][Java][讀書筆記][thinking in java][ 部類]

10.6 效果 getc tps 啟動 implement bool 多個 tina 可以將一個類定義在另一個類的內部,這就是內部類。 10.1 創建內部類 public class Parcell { class Contents { priv

[隨筆][Java][讀書筆記][thinking in java][ Java I/O系統]

參數 數列 == tar 目錄樹 返回 匿名類 string 筆記 18.1 File類 目錄列表器。兩種方法使用File對象查看一個目錄列表。 import java.util.regex.*; import java.io.*; import java.util.*

使用ant編譯Java程式設計思想出現時 java.lang.UnsupportedClassVersionError: Bad version number in .class問題

問題詳情如下圖所示 解決方法是將TIJ4/code/build.xml中的net/build.xml刪除即可編譯成功,過段時間後確定是當前所用的javassist.jar與jdk 1.5.0_22 不相容造成這個原因,之前javaassisst.jar使用的是3.16.1版本,經測試確

201711671208 《Java程式設計》之執行緒 一週學習計劃

使用Thread類的子類建立執行緒寫法,如: public class SpeakElephant extends Thread { public void run(){ ...} } SpeakElephant xxx; //宣告 xxx=new SpeakEl

Java程式設計思想之讀書筆記系列一 --- 十三 --- 字串

String物件是不可變的,具有隻讀特性 預先指定StringBuilder的大小可以避免多次重新分配緩衝(那麼:如果超出預先指定的大小,會出現什麼情況呢?) 重寫自定義類的toString()方法

Java 記憶體分配——Thinking in Java 4th 讀書筆記

做開發多年,一直忙於專案,從沒好好的整理知識,從現在開始,儘量每週多抽時間整理知識,分享在部落格,在接下來的部落格中,我將為大家分享我讀《Java程式設計思想4th》英文版讀書筆記,一來便於知識的梳理,二來分享給需要的朋友,評價很高的一本書,推薦大家閱讀,因為書的邊幅比較長,如果不想閱讀整本書歡迎來

java程式設計的邏輯讀書筆記——

類的擴充套件 介面和抽象類 interface 宣告介面 implements實現介面 介面約定的是功能,而不是具體實現 介面中方法的預設修飾符為public abstract 介面中變數的預設修飾符為public static final 介面