1. 程式人生 > >Java程式設計思想讀書筆記(一)第1~13、16章

Java程式設計思想讀書筆記(一)第1~13、16章

目錄:

第1章 物件導論

1.1 伴隨多型的可互換物件

  面向物件程式設計語言使用了後期繫結的概念。當向物件傳送訊息時,被呼叫的程式碼直到執行時才能確定。也叫動態繫結。   編譯器確保被呼叫方法的存在,並對呼叫引數和返回值執行型別檢查(Java是強型別的語言,無法提供此類保證的語言被稱為是弱型別的),但是並不知道將被執行的確切程式碼。   在某些語言中,必須明確地宣告希望某個方法具備後期繫結屬性所帶來的靈活性(C++是使用virtual關鍵字來實現的)。在這些語言中,方法在預設情況下不是動態繫結的。而在Java中,動態繫結是預設行為(Java中除了static方法、final方法和private方法之外,其它所有方法都是動態繫結),不需要新增額外的關鍵字來實現多型。   把將匯出類看做是它的基類的過程稱為向上轉型(upcasting)

1.2 單根繼承結構

  在Java中(事實上還包括除C++以外的所有OOP語言),所有的類最終都繼承自單一的基類,這個終極基類的名字就是Object。   單根繼承結構的好處:

  • 在單根繼承結構中所有物件都具有一個共用介面,所以它們歸根到底都是相同的基本型別。
  • 單根繼承結構保證所有物件都具備某些功能。
  • 單根繼承結構使垃圾回收器的實現變得容易得多,而垃圾回收器正是相對C++的重要改進之一。由於所有物件都保證具有其型別資訊,因此不會因無法確定物件的型別而陷入殭屍。這對於系統級操作(如異常處理)顯得尤其重要,並且給程式設計帶來了更大的靈活性。

1.3 物件的建立和生命期

  • 物件的資料位於何處(作用域):
    • 將物件置於堆疊(它們有時被稱為自動變數(automatic variable)或限域變數(scoped variable))或靜態儲存區域內來實現。這種方式將儲存空間分配和釋放置於優先考慮的位置,某些情況下這樣控制非常有價值。但是,也犧牲了靈活性。
    • 第二種方式是在被稱為堆(heap)的記憶體池中動態地建立物件。在這種方式中,直到執行時才知道需要多少物件,它們的生命週期如何,以及它們的具體型別是什麼。

  Java完全採用了動態記憶體分配方式(基本型別只是一種特例)。每當想要建立物件時,就要使用new關鍵字來構建物件的動態例項。

  • 物件的生命週期: Java的垃圾回收器被設計用來處理記憶體釋放問題。

第2章 一切都是物件

2.1 特例:基本型別

  基本型別是一個並非是引用的“自動”變數。這個變數直接儲存“值”,並置於堆疊中,因此更加高效。Java的基本型別所佔儲存空間大小不隨機器硬體架構的變化而變化。這種所佔儲存空間大小的不變性

是Java程式比用其他大多數語言編寫的程式更具可移植性的原因之一

基本型別 大小 最小值 最大值 包裝器類
boolean - - - Boolean
byte 8 bits -128 +127 Byte
char 16 bits Unicode 0 Unicode 216-1 Character
short 16 bits -215 +215-1 Short
int 32 bits -232 +232-1 Integer
float 32 bits IEEE754 IEEE754 Float
long 64 bits -263 +263-1 Long
double 64 bits IEEE754 IEEE754 Double
void - - - Void

   所有數值型別都有正負號    boolean型別所佔儲存空間的大小沒有明確指定(要看具體虛擬機器的實現),僅定義為能夠取字面值true或false

2.2 Java中的陣列

  Java確保陣列會被初始化。而且不能在它的範圍之外被訪問。這種範圍檢查,是以每個陣列上少量的記憶體開銷及執行時的下標檢查為代價的。

《深入理解Java虛擬機器》: Java 語言中對陣列的訪問比C/C++相對安全是因為:如有一維陣列,其元素型別為 mypackage.MyClass,則虛擬機器會自動生成一個直接繼承於java.lang.Object的子類[Lmypackage.MyClass,建立動作由位元組碼指令newarray觸發。這個類代表了一個元素型別為mypackage.MyClass的一維陣列,陣列中應有的屬性和方法(使用者可直接使用的只有被修飾為public的length屬性和clone()方法)都實現在這個類裡。Java語言中對陣列的訪問比C/C++相對安全就是因為這個類封裝了陣列元素的訪問方法(準確地說,越界檢查不是封裝在陣列元素訪問的類中,而是封裝在陣列訪問的xaloadxastore位元組碼中),而C/C++直接翻譯為對陣列指標的移動

// NotInit.java
package mypackage;
class MyClass{
    static{
        System.out.println("MyClass Init...");
    }
}
public class NotInit{
    public static void main(String[] args){
        MyClass[] a = new MyClass[3];
    }   
}
/*
* 沒有輸出,說明陣列元素沒有初始化(虛擬機器自動生成了別的類)。
*/
  • 陣列物件,實際就是一個引用陣列,每個元素會被初始化為null
  • 基本資料型別的陣列,編譯器會將這種陣列所佔的記憶體全部置為零
  • 在Java語言中,當檢查到發生陣列越界時會丟擲java.lang.ArrayIndexOutOfBoundsException異常。

2.3 作用域(scope)

  1. Java 與 C/C++ 關於作用域的區別:如下,對於Java,非法,而對於 C/C++ 合法。(在 C/C++ 裡將一個作用域的變數“隱藏”起來的做法,在Java裡是不允許的。因為Java設計者認為這樣做會導致程式混亂。)

{
    int x = 12;
    {
        int x = 96;     // Illegal for Java, but legal for C/C++
    }
}

  2. Java物件不具備和基本型別一樣的生命週期。當用new建立一個Java物件時,它可以存活於作用域之外。如:

{
    String s = new String("a string");
}   // End of scope

引用 s 在作用域終點就消失了。然而,s 指向的 String 物件仍繼續佔據記憶體空間

2.4 import 關鍵字

  import關鍵字指示編譯器匯入一個包,也就是一個類庫(在其他語言中,一個庫不僅包含類,還可能包括方法和資料,但是Java中所有的程式碼都必須寫在類裡)。   特定類 java.lang 會被自動匯入到每一個Java檔案中。

2.5 static 關鍵字

  5.1 通過 static 關鍵字可以滿足以下兩方面情形的需要:

  • 只想為某一特定域分配單一儲存空間,而不去考慮空間要建立多少物件,甚至根本就不建立任何物件。
  • 希望某個方法不與包含它的類的任何物件關聯在一起。也就是說,即使沒有建立物件,也能夠呼叫這個方法

  有些面嚮物件語言採用類資料類方法兩個術語,代表那些資料和方法只是作為整個類,而不是類的某個特定物件而存在的。例:

  5.2 static 欄位

class StaticTest{
    static int i = 47;
}

  如下建立兩個物件,st1.i 和 st2.i 指向同一儲存空間,共享同一個 i ,因此它們具有相同的值47。

StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();

  5.3 static 方法

  •   static作用於欄位時,會改變資料的建立方式,但作用於方法時,差別卻沒有那麼大。static方法的一個重要用法就是在不建立任何物件的前提下就可以呼叫它。這一點對定義main()方法很重要(所以main()方法是一個 satic 方法),這個方法是執行一個應用時的入口點。
  •   和其它任何方法一樣,static方法可以建立或使用與其型別相同的被命名物件,因此,static方法常常拿來做“牧羊人”的角色,負責看護與其隸屬同一型別的例項群。
  •   static方法的含義static方法就是沒有this的方法。關於static方法內部是否能呼叫非靜態方法:因為沒有this,就沒有物件,所以不能直接呼叫非靜態方法,但可以傳遞一個物件引用到靜態方法裡,然後通過這個引用(和this效果相同)來呼叫非靜態方法和訪問非靜態資料成員
  • 有些人認為static方法不是面向物件”的,因為它們的確具有全域性函式的語義;使用static方法時,由於不存在this,所以不是通過“向物件傳送訊息”的方式來完成的。

第3章 關係操作符

3.1 測試物件的等價性

  • ==!= 比較的是物件的引用
  • 特殊方法equals()預設行為也是比較引用
// Equivalence.java
public class Equivalence{
    public static void main(String[] args){
        Integer n1 = new Integer(47);
        Integer n2 = new Integer(47);
        System.out.println(n1 == n2);
        System.out.println(n1 != n2);
        System.out.println(n1.equals(n2));

        Value v1 = new Value();
        Value v2 = new Value();
        v1.i = v2.i = 47;
        System.out.println(v1.equals(v2));
    }

}
class Value{
    int i;
}
/* Output:
* false
* true
* true
* false
*/

  以上, 1. n1n2 是兩個不同的引用(明顯是兩個不同的儲存區域),所以二者 !=。 2. equals() 方法是所有物件的特殊方法(繼承自Object類),Integer重定義了equals()方法以比較其內容是否相等,所以這裡n1.equals(n2)trueequals()不適用於“基本型別”,基本型別直接使用==!=即可。 3. v1.equals(v2)false 驗證了 equals()方法預設行為是比較引用,除非在自定義類Value中重定義 equals()方法。

3.2 直接常量

  • 有時直接常量的型別是模稜兩可的,這就需要與直接常量相關的某些字元來額外增加一些資訊以“指導”編譯器,使其能夠準確地知道要生成什麼樣的型別。如果編譯器能夠正確地識別型別,就不必在數值後增加字元。
  • 在C、C++或者Java中,二進位制數沒有直接常量表示方法。但是,在使用十六進位制和進位制的記數法時,以二進位制形式顯示結果將非常有用。通過使用IntegerLong類的靜態方法toBinaryString()可以很容易地實現這一點。注意,如果將比較小的型別傳遞給Integer.toBinaryString()方法,則該型別將自動轉換為int
// Literals.java
public class Literals{
    public static void main(String[] args){
        int i1 = 0x2f;  // Hexadecimal (lowercase)
        System.out.println("i1: " + Integer.toBinaryString(i1));
        int i2 = 0X2F;  // Hexadecimal (uppercase)
        System.out.println("i2: " + Integer.toBinaryString(i2));
        int i3 = 0177;  // Octal (leading zero)
        System.out.println("i3: " + Integer.toBinaryString(i3));
        char c = 0xffff;    // max char hex value
        System.out.println("c: " + Integer.toBinaryString(c));
        byte b = 0x7f;  // max short hex value
        System.out.println("b: " + Integer.toBinaryString(b));
        short s = 0x7fff;   // max short hex value
        System.out.println("s: " + Integer.toBinaryString(s));
        long n1 = 200L; // long suffix
        long n2 = 200l; // long suffix (but can be confusing)
        long n3 = 200;
        float f1 = 1;
        float f2 = 1F;  // float suffix
        float f3 = 1f;  // float suffix
        double d1 = 1d; // double suffix
        double d2 = 1D; // dobule suffix
        // (Hex and Octal also work with long)
    }
    /* OUtput:
    * i1: 101111
    * i2: 101111
    * i3: 1111111
    * c: 1111111111111111
    * b: 1111111
    * s: 111111111111111
    * */
}
  • 指數計數法。在C、C++以及Java中,e 代表“10的冪次”,與科學與工程領域中“e”代表自然對數的基數(約等於2.718,Java中的Math.E給出了更精確的double型的值)不同
    根據John Kirkham的描述,Java語言中 e 與 科學工程領域不同,可能跟60年代的FORTRAN有關。
// Exponents.java
// "e" means "10 to the power."
public class Exponents {
    public static void main(String[] args){
        // Uppercase and lowercase 'e' are the same:
        float expFloat = 1.39E-43f;
        expFloat = 1.39e-43f;
        System.out.println(expFloat);
        double expDouble = 47e47d;  // 'd' is optional
        double expDouble2 = 47e47;  // Automaticall double
        System.out.println(expDouble);
    }
    /* Output:
     *1.39E-43
     *4.7E48
     */
}

3.3 型別轉換(cast)操作符

  • Java中布林型別,不允許進行任何型別的轉換處理,其它基本型別都可轉換成別的基本資料型別。
  • 將float和double轉型為整型值時,總是對該數字執行截尾。如果想要得到舍入的結果,就需要使用java.lang.Math中的round()方法。

    // CastingNumbers.java
    // What happens when you cast a float or double to an integral value ?
    public class CastingNumbers{
        public static void main(String[] args){
    
            double above = 0.7, below = 0.4;
            float fabove = 0.7f, fbelow = 0.4f;
    
            System.out.println("(int)above: " + (int)above);
            System.out.println("(int)below: " + (int)below);
            System.out.println("(int)fabove: " + (int)fabove);
            System.out.println("(int)fbelow: " + (int)fbelow);
    
            System.out.println("Math.round(above): " + Math.round(above));
            System.out.println("Math.round(above): " + Math.round(above));
            System.out.println("Math.round(below): " + Math.round(below));
            System.out.println("Math.round(fabove): " + Math.round(fabove));
            System.out.println("Math.round(fbelow): " + Math.round(fbelow));
    
        }
    }
    /* Output:
    (int)above: 0
    (int)below: 0
    (int)fabove: 0
    (int)fbelow: 0
    Math.round(above): 1
    Math.round(below): 0
    Math.round(fabove): 1
    Math.round(fbelow): 0
    */
  • 提升。如果對基本型別執行算術運算或按位運算,只要型別比int小(即charbyte或者short),那麼在運算之前,這些值會自動轉換成int。這樣一來,最終生成的結果就是int型。如果想把結果賦值給較小的型別,就必須使用型別轉換(既然把結果賦給了較小的型別,就可能出現資訊丟失)。通常,表示式中出現的最大的資料型別決定了表示式最終結果的資料型別。如果一個float值與一個double值相乘,結果就是double,如果將一個int和一個long值相加,則結果就為long。
  • 溢位。如果對兩個足夠大的int值執行乘法運算,結果就會溢位。編譯器不會發出錯誤或警告資訊,執行時也不會出現異常。這說明Java雖然是好東西,但也沒有那麼好!

    // Overflow.java
    // Surprise! Java lets you overflow.
    public class Overflow{
        public static void main(String[] args){
            int big = Integer.MAX_VALUE;
            System.out.println("big = " + big);
            int big1 = big + 1;
            System.out.println("big1 = " + big1);
            int bigger = big * 4;
            System.out.println("bigger = " + bigger);       
        }
    }
    /* Output:
    big = 2147483647
    big1 = -2147483648
    bigger = -4
    */

3.4 Java沒有sizeof()操作符

  在C和C++中,sizeof()操作符可以告訴你為資料項分配的位元組數。使用這個操作符的最大原因是為了進行一些與儲存空間有關的運算,使程式可以在不同平臺上“移植”。而Java不需要sizeof()操作符來滿足這方面的需要,因為所有資料型別在所有機器中的大小是相同的。我們不必考慮移植問題——它已經被設計在語言中了。

第4章 控制執行流程

4.1 true 和 false

  注意Java不允許我們將一個數字作為布林值使用,這與C和C++ 不同(C/C++中,“真”是非零,而“假”是零)。如果將數字作為布林表示式,Java編譯器會直接報錯

4.2 switch

  switch要求使用一個選擇因子:

  • 在JDK5之前,選擇因子必須是int或char那樣的整數值。
  • JDK1.5開始,Java增加了新特性enum,使得enum可以與switch協調工作。
  • JDK1.7開始,switch開始支援String作為選擇因子。在switch語句中,String的比較用的是String.equals()。因此,需要注意,傳給switch的String變數不能為null,同時switch的case子句中使用的字串也不能為null。顯然是因為:
    • 如果switch傳入的是null,則在執行時對null物件呼叫hashCode(String.equals()會呼叫)方法會出現NullPointException
    • 如果case寫的是null,那麼在編譯時無法求出hashCode,因此編譯時就會報錯。

  switch支援String只是一個語法糖,由javac來負責生成相應的程式碼。底層的JVM在switch上並沒有進行修改

第5章 初始化與清理(cleanup)

5.1 方法過載(method overloading)

  過載方法,方法名相同,形式引數列表不同(引數列表又叫引數簽名,包括引數的型別、引數的個數和引數的順序,只要有一個不同就叫做引數列表不同)。過載是面向物件的一個基本特性。

  • 宣告為final的方法不能被過載
  • 宣告為static的方法不能過載,但是能夠被再次宣告。
  • 過載方法的返回型別可以相同也可以不同,但僅返回型別不同不足以成為方法的過載。
  • 編譯器根據呼叫方法的簽名逐個匹配,以選擇對應方法的過程叫做過載分辨(Overload Resolution,或叫過載決議)
    1. 《深入理解Java虛擬機器》:虛擬機器(準確地說是編譯器)在過載時是通過引數的靜態型別(Static Type )或叫外觀型別(Apparent Type)而不是實際型別(Actual Type)作為判定依據的2. 《深入理解Java虛擬機器》:編譯期間選擇靜態分派目標的過程是Java語言實現方法過載的本質

5.2 this 關鍵字

  `this` 關鍵字只能在方法內部使用,表示對“**呼叫方法的那個物件**”的引用。

5.3 清理:終結處理和垃圾回收

5.3.1 finalize()

  • Java中的finalize()不等於C++中的解構函式
  • 發生“垃圾回收”時,finalize()才得到呼叫
  • Java裡的物件並非總是被垃圾回收(因為Java的“垃圾回收”並不能保證一定會發生)
    • 物件可能不被垃圾回收
    • 垃圾回收並不等於“析構”
  • Java並未提供“解構函式”或相似的概念,Java的“垃圾回收不保證一定會發生,所以要做類似的清理工作,必須自己動手建立一個執行清理工作的普通方法。
  • 只要程式沒有瀕臨儲存空間用完的那一刻,垃圾回收可能就會一直沒有發生。這個策略是恰當的,因為垃圾回收本身也有開銷,要是不使用它,那就不用支付這部分開銷了。

5.3.2 finalize()用途何在

  由於垃圾回收器會負責釋放物件佔據的所有記憶體,這就將finalize()的需求限制到一種特殊情況,即通過某種建立物件方式以外的方式為物件分配了儲存空間。由於Java中一切皆為物件,所以那種特殊情況主要發生在使用“本地方法”的情況下,本地方法是一種在Java中呼叫非Java程式碼的方式。   不要過多地使用finalize(),它不是進行普通的清理工作的合適場所。

Joshua Bloch在題為“避免使用終結函式”一節中走得更遠,他提到:“終結無法預料,常常是危險的,總之是多餘的。”《Effective Java》,第20頁,(Addison-Wesley 2001)

5.4 成員初始化

  Java盡力保證:所有變數在使用前都能得到恰當的初始化

  • 對於方法的區域性變數,如果使用前沒有初始化,Java以編譯時錯誤(注意,如果方法內的區域性變數未被使用,將不會編譯錯誤)的形式來貫徹這種保證。
  • 對於類的成員變數:

    • 成員變數是基本型別,Java會自動初始化初值0
    • 成員變數是引用型別,Java會自動初始化初值null;
    // InitialValues.java
    public class InitialValues{
        int j;
        char c;
        MyClass mc;
        public static void main(String[] args){
            int i;
            //i++; // Error -- i not initialized
            InitialValues obj = new InitialValues();
            System.out.println(obj.c);
            System.out.println(obj.j);
            System.out.println(obj.mc);
        }
    }
    class MyClass{}
    • 無法阻止自動初始化的進行,它將在構造器被呼叫之前發生,如下,i首先會被置0,然後變成7。
    // Counter.java
    public class Counter{
        int i;
        Counter(){
            i = 7;
        }
    }

5.5 物件的建立過程

  假設有個名為Dog的類:

  • 靜態方法或域。當首次建立類物件時(構造器可以看成靜態方法,但不是)或類的靜態方法/靜態域首次被訪問時,Java直譯器必須查詢類路徑,以定位Dog.class檔案;
  • 載入Dog.class,執行靜態初始化的所有動作,且只執行這一次;
  • 當呼叫new Dog(),首先將在堆上分配儲存空間;
  • 儲存空間清零。所以成員變數會置成0或null;
  • 執行所有出現於欄位定義處的初始化動作。
  • 執行構造器

5.6 陣列初始化

  可以將Java中的陣列作為一種陣列型別來理解

  • int[] a; 可以認為是 a 是一個數組引用,初始值為null
  • 初始化:
    • int[] a = new int[3]; 初始化各元素值為0,對於boolean,初始值為false;
    • int[] a = {1, 2, 3}; 初始化元素分別為1, 2, 3;

第6章 訪問許可權控制

6.1 Java直譯器的執行過程:

  • 首先,找出環境變數CLASSPATH,用作查詢.class檔案的根目錄。
  • 然後,從根目錄開始,直譯器獲取包的名稱並將句點替換成反斜槓(於是,package net.mrliuli.training 就變為 net\mrliuli\training 或 net/mrluli/training 或其他,這一切取決於作業系統)以從CLASSPATH根中獲取一個相對路徑
  • 將CLASSPATH根目錄與上面獲取的相對路徑相連線得到一個絕對路徑,用來查詢.class檔案。

Sun 將Java2中的JDK改造得更聰明瞭一些。在安裝後你會發現,即使你未設立CLASSPATH,你也可以編譯並執行基本的Java程式。

6.2 類的訪問許可權的一些限制

  • 同一個.java檔案,只能有一個與檔案同名的public類,可以有其它非public類
  • 同一個package內的不同檔案中的類,可以互相訪問。
  • 不同package中的類,如需訪問,需要使用全限定名,如biz.superalloy.MyClass或通過import把biz.superalloy包引進來;
  • 類中的成員變數,不宣告訪問修飾符時,為“包訪問許可權”,有時也表示friendly,同一個檔案的不同類之間可以互相訪問。
  • 如果沒能為類訪問許可權指定一個訪問修飾符,它將會預設得到包訪問許可權

第7章 複用類

7.1 名稱遮蔽

  在C++中,如果基類擁有一個已被多次過載的方法名稱,那麼在其派生類中重新定義該方法名稱,就會遮蔽其基類中的任何版本,這叫做名稱遮蔽。但是在Java中,就種情況下,不會發生名稱遮蔽,即無論在派生類還是在基類中對方法進行定義,過載機制都可以正常工作。   如下C++會產生名稱遮蔽

  而Java不會產生

// Hide.java
class Homer{
    void doh(char c){
        System.out.println("doh(char)");
    }
    void doh(float f){
        System.out.println("doh(float)");
    }
}

class Milhouse{}

class Bart extends Homer{
    /* 如果使用這個註解,編譯時會報錯:
    * “方法不會覆蓋或實現超型別的方法” -- method does not override a method from its superclass
    * 因為你是想要重寫的,但卻進行了過載。
    */
    //@Override 
    void doh(Milhouse m){
        System.out.println("doh(Milhouse)");
    }
}

public class Hide{
    public static void main(String[] args){
        Bart b = new Bart();
        b.doh('x');
        b.doh(1.0f);
        b.doh(new Milhouse());
    }
}
/* Output:
*  doh(char)
*  doh(float)
*  doh(Milhouse)
*/

7.2 @Override註解

  Java SE5新增加了@Override註解,可以把它當作關鍵字來用,它的作用是告訴編譯器我想重寫這個方法,因為Java不會產生名稱遮蔽,所以如果我不留心過載了,編譯器就會報錯來告訴我違背了我的初衷。

7.3 final關鍵字

  根據上下文環境,Java的關鍵字final的含義存在著細微的區別,但通常它指的是“這是無法改變的。”不想改變可能出於兩種理由:設計或效率。可能使用到final的三種情況:資料、方法和類。

7.3.1 final資料

  • final 基本型別資料

  基本型別變數應用final關鍵字時,將向編譯器告之此變數是恆定不變的,即它是編譯期常量。這樣編譯器可在編譯時執行計算式,從而減輕了執行時負擔(提高效率)。編譯期常量在定義(宣告)時必須對其賦值(宣告時也可以不賦(此時叫空白final),但必須在構造器中賦值,所以final域在使用前總是被初始化。)。final常量常與static一起使用,強調只有一份。編譯期常量(帶有恆定初始值),即 static final 的基本型別變數全用大寫字母命名,並且字與字之間用下劃線隔開(這就像C常量一樣,C常量是這一命名傳統的發源地)。ds - final 物件引用

  用於物件引用,則引用恆定不變,即一旦引用初始化指向一個物件,就無法再把它改變為指向另一個引用,但物件其自身是可以被修改的。這種情形同樣適用陣列,因為如前面所述,Java陣列也可(看作)是引用

  • final引數

  指明為final的方法引數,意味著方法內只能讀而不能修改引數,這一特性主要用來向匿名內部類傳遞資料。

7.3.2 final方法

  • 使用final方法的原因有兩個:
    • 鎖定方法,以防任何繼承類修改它的含義。這是出於設計的考慮。
    • 效率。在Java早期版本中,方法宣告為final,就是同意編譯器針對該方法的所有呼叫都轉為內嵌呼叫。而在Java SE5/6時,應該讓編譯器和JVM雲處理效率問題,只有在想要明確禁止覆蓋時,才將方法設定為final的
  • finalprivate關鍵字
    • 類中所有的private方法都隱式地指定為是final。由於無法取用private方法,所以也就無法覆蓋它。
    • 派生類中試圖“覆蓋”父類中一個private方法(隱含是final的),似乎奏效,編譯器不會出錯,但實際上只是在派生類中生成了一個新的方法,此時並沒有覆蓋父類的private方法。
// FinalOverridingIllusion.java
class WithFinals{
    private final void f(){
        System.out.println("WithFinals.f()");
    }
    // Automatically "final"
    private void g(){
        System.out.println("WithFinals.g()");
    }
}

class OverridingPrivate extends WithFinals{
    public final void f(){
        System.out.println("OverridingPrivate.f()");
    }
    public void g(){
        System.out.println("OverridingPrivate.g()");
    }
}

public class FinalOverridingIllusion{
    public static void main(String[] args){
        OverridingPrivate op = new OverridingPrivate();
        op.f();
        op.g();
        // You can upcast
        WithFinals wf = op;
        // But you can't call the methods:
        //wf.f();
        //wf.g();
    }
}
/* Output:
* OverridingPrivate.f()
* OverridingPrivate.g()
* */

7.3.3 final類

  final類表明對該類的設計永不需要變動,或者出於安全的考慮,你不希望它有子類。因為final類禁止繼承,所以final類中所有的方法都隱式指定為是final的,因為無法覆蓋它們。在final類中可以給方法新增final修飾詞,但這不會增添任何意義。

7.3.4 有關final的忠告

  在設計類時,將方法指明是final的,應該說是明智的。 - Java1.0/1.1中Vector類中的方法均沒有設計成final的,然後Statck繼承了Vector,就是說Stack是個Vector,這從邏輯觀點看是不正確的,還有Vector中的addElement()elementAt()是同步的,導致執行開銷大,可能會抹煞final的好處。所以Vector的設計不合理,現代Java的容器ArrayList替代了VectorArrayList要合理得多,但遺憾的是仍然存在用舊容器庫編寫新程式程式碼的情況。 - Java1.0/1.1中的Hashtable類也是不包含任何final方法。現代Java的容器庫用HashMap代替了Hastable

7.4. 初始化及類的載入

7.4.1 類的載入

  Java採用了一種不同的對類載入的方式,Java每個類的編譯程式碼都存在於它自己的獨立的檔案中(.class檔案)。該檔案只在其程式碼需要被使用時才會被載入(That file isn’t loaded until the code is needed)。通常,可以說“類的程式碼在初次使用時才載入。”這通常是指載入發生於構造類的第一個物件之時,但是當訪問static域或static方法時,也會發生載入。(構造器也是static方法,儘管static關鍵字並沒有地寫出來。因此更準確地講,++類是在其任何static成員被訪問時載入的++。)

7.4.2 初始化

  初次使用之處也是staic初始化發生之處。所有的static物件和static程式碼段都會在載入時依程式中的順序(即,定義類時的書寫順序)而依次初始化。當然,定義為static的東西只會被初始化一次。

第8章 多型(Polymorphism)

  多型(也稱作動態繫結後期繫結執行時繫結)。

8.1 方法呼叫繫結(Method-call binding)

  將一個方法呼叫與一個方法主體關聯起來稱作繫結。Connecting a mehtod call to a mehtod body is called binding.

  • 若在程式執行前進行繫結(如果有的話,由編譯器和連線程式實現),叫做前期繫結。它是面向過程語言中不需要選擇就預設的繫結方式。例如,C只有一種方法呼叫,那就是前期繫結。

    When binding is performed before the program is run (by the compiler and linker, if there is one), it’s called early binding. You might not have heared the term before because it has never been an option with procedural language. C compilers have only one kind of method call, and that’s early binding.

  • 後期繫結就是在執行時根據物件的型別進行繫結後期繫結也叫做動態繫結或執行時繫結。如果一種語言想實現後期繫結,就必須具有某種機制,以便在執行時能判斷物件的型別,從而呼叫恰當的方法。也就是說,編譯器一直不知道物件的型別,但是方法呼叫機制能找到正確的方法體,並加以呼叫。後期繫結機制隨程式語言的不同而有所不同,但是隻要想一下就會得知,不管怎樣都必須在物件中安置某種“型別資訊”

    The solution is called late binding, which means that the binding occurs at run time, based on the type of object. Late binding is also called dynamic binding or runtime binding. When a language implements late binding, there must be some mechanism to determine the type of the object at run time and to call the appropriate method. That is, the compiler still doesn’t know the object type, but the mehtod-callmechanism finds out and calls the correct method body. The late-binding mechanism varies from language to language, but you can imagine that some sort of type information must be installd in the objects.

  • 再談final方法。   如Chapter7所說,final方法可以防止其他人覆蓋該方法。但更重要的一點是:這樣做可以有效地關閉動態繫結,或者說,告訴編譯器不需要對其進行動態繫結。這樣,編譯器就可以為final方法呼叫生成更有效的程式碼。然而,大多數情況下,這樣做對程式的整體效能不會有什麼改觀。所以,最好根據設計來決定是否使用final,而不是出於試圖提高效能的目的來使用final

    Why would you declare a method final? As noted in the last chapter, it prevents anyone from overriding that method. Perhaps more important, it effectively “turns offdynamic binding, or rather it tells the compiler that dynamic binding isn’t necessary.This allows the compiler to generate slightly more efficient code for final method calls. However, in most cases it won’t make any overall performance diffeence in your program, so it’s best to only use final as a design decision, and not as an attempt to improve performance.

8.2 域與靜態方法

  • 域是不具有多型性的,只有普通的方法呼叫是多型的。如果直接訪問某個域,這個訪問就將在編譯期進行解析,即域是靜態解析的。   如下,當Sub物件轉型為Super引用時,任何域訪問操作都將由編譯器解析,因此不是多型的。Super.field和Sub.field分配了不同的儲存空間。這樣,Sub實際上包含兩個稱為field的域:它自己的和它從Super處得到的。
// FieldAccess.java
// Direct field access is determined at compile time.

class Super{
    public int field = 0;
    public int getField(){return field;}
}
class Sub extends Super{
    public int field = 1;
    public int getField(){return field;}
    public int getSuperField(){return super.getField();}
}
public class FieldAccess{
    public static void main(String[] args){
        Super sup = new Sub();  // Upcast
        System.out.println("sup.field = " + sup.field + ". sup.getField() = " + sup.getField());
        Sub sub = new Sub();
        System.out.println("sub.field = " + sub.field + ". sub.getFiled() = " + sub.getField() + ". sub.getSuperField() = " + sub.getSuperField());
    }
}
/** Output:
 *  sup.field = 0. sup.getField() = 1
 *  sub.field = 1. sub.getFiled() = 1. sub.getSuperField() = 0
 */
  • 靜態方法也是不具有多型性的,如前文所述,靜態方法是與類,而非與單個的物件相關聯的。

8.3 構造器內部的多型方法的行為

  如果在構造器內部呼叫正在構造的物件的某個動態繫結方法,由於動態繫結是在執行時才決定的,而此時,該物件還正在構造中,所以它不知道自己屬於哪個類(父類還是自己),並且方法所操縱的成員可能還未進行初始化,這可能會產生一引起難於發現的隱藏錯誤。

// PolyConstructors.java
// Constructors and polymorphism
// don't produce what you might expect
class Glyph{
    void draw(){
        System.out.println("Glyph.draw()");
    }
    Glyph(){
        System.out.println("Glyph() before draw()");
        draw();
        System.out.println("Glyph() after draw()");
    }
}
class RoundGlyph extends Glyph{
    RoundGlyph(int r){
        radius = r;
        System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
    }   
    private int radius = 1;
    void draw(){
        System.out.println("RoundGlyph.draw(), radius = " + radius);
    }
}
public class PolyConstructors {
    public static void main(String[] args){
        new RoundGlyph(5);
    }
}
/**Output:
 * Glyph() before draw()
 * RoundGlyph.draw(), radius = 0
 * Glyph() after draw()
 * RoundGlyph.RoundGlyph(), radius = 5
 */

  以上程式碼,構造RoundGlyph物件時,先呼叫父類構造器Glyph(),父類構造器中如我們所期,呼叫了多型的draw(),但是,由於 子類還沒構造完成,所以列印的成員變數radius的值是0,而並不是我們想象的其預設的初始值1

8.4 初始化的實際過程

  1. 在其他任何事物發生之前,將分配給物件的儲存空間初始化成二進位制的零。
  2. 如前所述那樣呼叫構造器。
  3. 按照宣告的順序呼叫成員的初始化方法。
  4. 呼叫匯出類(派生類)的構造器主體。

第9章 介面

9.1 在C++中,只有抽象類的概念(沒有abstract關鍵字),沒有介面的說法

  • C++通過virtual關鍵字將類內方法宣告為虛擬函式(如virtual void f();)來實現多型(在C++中,派生類只能重寫父類的虛擬函式,而在Java中,除static方法外,其它方法都是可以被重寫的,即預設都是多型的。)。除此以外,包含虛擬函式的類與其它類沒有區別。
  • 對虛擬函式如virtual void f() = 0;宣告時,構成純虛擬函式。因為純虛擬函式沒有函式體,不是完整的函式,無法呼叫,也無法為其分配記憶體空間,無法例項化,也就無法建立物件,所以在C++中含有純虛擬函式的類被稱為抽象類(Abstract Class,注意在C++中,沒有abstract關鍵字)。抽象類通常作為基類(叫做抽象基類),讓派生類去實現純虛擬函式。派生類必須實現純虛擬函式才能被例項化。

9.2 在Java中,有abstractinterface 關鍵字,通過它們來定義抽象類和介面

  • class前新增abstract關鍵字,定義成抽象類
    • 抽象類不能例項化,即不能通過new生成物件,但注意可以追加{}生成匿名實現類,仍然不是它自己的例項化。
    • 抽象類可以有建構函式,但不能直接呼叫,通常由實現類建構函式呼叫。
    • 抽象類的方法前新增abstract關鍵字,定義抽象方法相當於C++的純虛擬函式,派生類必須重寫該方法,然後才能例項化。Java類中如有抽象方法,則類符號前必須也要新增abstract關鍵字,定義為抽象類可以沒有抽象方法)。
    • 抽象類中可以沒有抽象方法,即可以全部是含方法體的非抽象方法。
  • 抽象類進一步抽象,即所有方法都沒有具體實現,只聲明瞭方法的形式(同C++標頭檔案中函式的宣告格式),並且class關鍵字改成interface關鍵字,這就建立了一個介面
    • 介面可以包含域,且隱式地是staticfinal的,顯然,介面中的域不能是空final這些域不是介面的一部分,它們儲存在該 介面的靜態儲存區域內
    • 介面關鍵字interface前可以新增public修飾符,不加預設是包訪問許可權,介面的方法預設都是public
    • 因為Java介面沒有任何具體實現,即沒有任何與介面相關的儲存,因此可以定義一個Java類來implements多個介面,達到C++中多重繼承的效果。
    • Java可以定義一個介面去extends另外的一個或多個介面來實現介面的擴充套件
    • 因為Java介面中的域自動是final和static的,所以介面就成了一種便捷的建立常量組的工具。在Java SE5之前,用這種方式來產生enum的效果。Java SE5之後,Java有了enum關鍵字,因此使用介面來群組常量就沒意義了

第10章 內部類

10.1 連結到外部類(Java非static的普通內部類自動擁有對其外圍類所有成員的訪問權)。

  Java普通內部類能訪問其外圍物件(enclosing object)的所有成員,而不需要任何特殊條件 。C++巢狀類的設計只是單純的名字隱藏機制,與外圍物件沒有聯絡,也沒有隱含的訪問權。在Java中,當某個類建立一個內部類物件時,此內部類物件必定會祕密地捕獲一個指向那個外圍類的物件的引用。然後,在你訪問此外圍類的成員時,就是用那個引用來選擇外圍類的成員。這些細節是由編譯器處理的。Java的迭代器複用了這個特性。

// Sequence.java
interface Selector{
    boolean end();
    Object current();
    void next();
}
public class Sequence{
    private Object[] items;
    private int next = 0;
    public Sequence(int size){ items = new Object[size]; }
    public void add(Object x){
        if(next != items.length) items[next++] = x;
    }
    private class SequenceSelector implements Selector{
        private int i = 0;
        public boolean end(){ return i == items.length; }
        public Object current(){ return items[i]; }
        public void next(){ if (i < items.length) i++; }        
    }
    public Selector selector(){ return new SequenceSelector(); }
    public static void main(String[] args){
        Sequence sequence = new Sequence(10);
        for(int i = 0; i < 10; i++){
            sequence.add(Integer.toString(i));
        }
        Selector selector = sequence.selector();
        while(!selector.end()){
            System.out.print(selector.current() + " ");
            selector.next();
        }
        System.out.println();
    }
}
/**Output:
*  0 1 2 3 4 5 6 7 8 9
*/

10.2 .this.new

  • Java非static的普通內部類可應用.this返回其外圍物件的引用。
  • 外圍物件可應用.new來生成一個內部類物件。
// DotThis.java
// Qualifying access to the outer-class object.

public class DotThis{
    void f(){ System.out.println("DotThis.f()"); }
    public class Inner{
        public DotThis outer(){
            return DotThis.this; // a plain "this" would be Inner's "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();
    }
}
/*Output:
* DotThis.f()
*/

10.3 匿名內部類

  Anonymous Inner Class.

10.4 Java巢狀類

  內部類宣告為static時,不再包含外圍物件的引用.this,稱為巢狀類(與C++巢狀類大致相似,只不過在C++中那些類不能訪問私有成員,而在Java中可以訪問)。 - 建立巢狀類,不需要外圍物件。 - 不能從巢狀類的物件中訪問非靜態的外圍物件

10.4.1 介面內部的類

  巢狀類可以作為介面的一部分(正常情況下,介面內部不能放置任何程式碼)。放到介面中的任何類都自動是public和static的。因為類是static的,只是將巢狀類置於介面的名稱空間內,這並不違反介面的規則

10.4.2 從多層巢狀類中訪問外部類的成員

  一個內部類被巢狀多少層並不重要——它能透明地訪問它所嵌入的外圍類的所有成員,如下:

// MultiNestingAccess.java
class MNA{
    private void f(){}
    class A{
        private void g(){}
        public class B{
            void h(){
                g();
                f();
            }
        }
    }
}
public class MultiNestingAccess{
    public static void main(String[] args){
        MNA mna = new MNA();
        MNA.A mnaa = mna.new A();
        MNA.A.B mnaab = mnaa.new B();
        mnaab.h();
    }
}

10.5 為什麼需要內部類

  • 內部類繼承自某個類或實現某個介面,內部類的程式碼操作建立它的外圍類的物件。所以可以認為內部類提供了某種進入其外圍類的視窗。
  • 內部類實現一個介面與外圍類實現這個介面有什麼區別呢?答案是:後者不是總能享用到介面帶來的方便,有時需要用到介面的實現。所以,使用內部類最吸引人的原因是:   每個內部類才能獨立地繼承自一個(介面的)實現,所以無論外圍類是否已經繼承了某個(介面的)實現,對於內部類都沒有影響
  • 內部類使得多重繼承的解決方案變得完整。介面解決了部分問題,而內部類有效地實現了“多重繼承”。也就是說,內部類使得Java實現繼承多個非介面型別(類或抽象類)。
// MultiInterfaces.java
// two ways tha a clas can implement multiple interface.
interface A{}
interface B{}
class X implements A, B {}
class Y implements A {
    B makeB(){
        // Amonymous inner class
        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);
        takesB(x);
        takesA(y);
        takesB(y.makeB());
    }
}

10.6 閉包與回撥

  • 閉包(closure)是一個可呼叫的物件,它記錄了一些資訊,這些資訊來自於建立它的作用域。通過這個定義可以看出內部類是面向物件的閉包,因為它不僅包含外圍類物件(建立內部類的作用域)的資訊,還自動擁有一個指向此外圍類物件的引用(.this),在此作用域內,內部類有權操作所有的成員,包括private成員。
  • 回撥(callback),通過回撥,物件能夠攜帶一些資訊,這些資訊允許它在稍後的某個時刻呼叫初始的物件。Java中沒有指標,通過內部類提供的閉包功能可以實現回撥
// Callbacks.java
// using inner classes for callbacks

interface Incrementable{
    void increment();
}

// Very simple to just implement the interface:
class Callee1 implements Incrementable{
    private int i = 0;
    public void increment(){
        System.out.println(++i);
    }
}

class MyIncrement{
    public void increment(){ System.out.println("Other operation"); }
    static void f(MyIncrement mi) { mi.increment(); }
}

// If your class must implement increment() in some other way, you must use an inner class:
class Callee2 extends MyIncrement{
    private int i = 0;
    public void increment(){
        super.increment();
        System.out.println(++i);
    }
    private class Closure implements Incrementable{
        public void increment(){
            // Specify outer-class method, otherwise you'd get an infinite recursion:
            Callee2.this.increment();
        }
    }
    Incrementable getCallbackReference(){
        return new Closure();
    }
}

class Caller{
    private Incrementable callbackReference;
    Caller(Incrementable cbh){ callbackReference = cbh; }
    void go(){ callbackReference.increment(); }
}

public class Callbacks {
    public static void main(String[] args){
        Callee1 c1 = new Callee1();
        Callee2 c2 = new Callee2();
        MyIncrement.f(c2);
        Caller caller1 = new Caller(c1);
        Caller caller2 = new Caller(c2.getCallbackReference());
        caller1.go();
        caller1.go();
        caller2.go();
        caller2.go();
    }
}
/**Uoutput:
* Other operation
* 1
* 1
* 2
* Other operation
* 2
* Other operation
* 3
**/

10.7 Java介面和內部類總結

  Java的介面和內部類比其他面向物件的概念更深奧複雜,C++沒有這些,將兩者結合起來,同樣能夠解決C++中的用多重繼承所能解決的問題。

第11章 持有物件

image

11.1 迭代器(Iterator)

  • Iterator迭代器使得客戶端程式設計師不必知道或關心容器類的底層結構。
  • ListIterator只能用於各種List類的訪問。ListIterator可以雙向移動,而Iteraotr只能向前移動

11.2 ArrayList 和 LinkedList

  • 都可自動擴容。
  • ArrayList底層是陣列結構,即連續儲存空間,所以讀取元素快。因可自動擴容,所以可以把ArrayList當作“可自動擴充自身尺寸的陣列”看待。
  • LinkedList連結串列結構,所以插入元素快。
    • LinkedList具有能夠直接實現(Stack)的所有功能的方法,因此可以直接將LinkedList作為棧使用。
    • LinkdedList也提供了支援佇列(Queue)行為的方法,並且實現了Queue介面,所以也可以用作Queue。

11.3 Set 不儲存重複元素

11.4 Map 將物件對映到其他物件的能力是一種解決程式設計問題的殺手鐗

11.5 Collection 和 Iterator

  在Java中,Collection是描述所有序列容器的共性的根介面,它可能會被 認為是一個“附屬介面”,即因為要表示其他若干個介面的共性而出現的介面。而在標準C++類庫中並沒有其容器的任何公共基類——容器之間的所有共性都是通過迭代器達成的。Java將兩種方法繫結到了一起,因為實現Collection就意味著需要提供iterator()方法。

11.5 Foreach與迭代器

  foreach語法用於任何實現了Iterable介面的類。Collection介面擴充套件了Iterable介面,所以所有Collection物件都適用foreach語法。

11.6 容器的元素型別

  • 泛型之前的容器不能持有基本型別元素,顯然陣列是可以的。但是有了泛型,容器就可以指定並檢查它們所持有物件的型別,並且有了自動包裝機制,容器看起來還能夠持有基本型別
  • 在Java中,任何基本型別都不能作為型別引數。因此不能建立ArrayList<int>HashMap<int, int>之類的東西。但是可以利用自動包裝機制和基本型別的包裝器來解決,自動包裝機制將自動地實現intInteger的雙向轉換
// ListOfInt.java
import java.util.*;
public class ListOfInt{
    public static void main(String[] args){
        // 編譯錯誤:意外的型別
        // List<int> li = new ArrayList<int>();
        // Map<int, Interger> m = new HashMap<int, Integer>();
        List<Integer> li = new ArrayList<Integer>();
        for(int i = 0; i < 5; i++){
            li.add(i);      // int --> Integer
        }
        for(int i : li){    // Integer --> int
            System.out.print(i + " ");
        }
    }
}/* Output:
0 1 2 3 4
*/

第12章 通過異常處理錯誤

12.1 異常

  異常允許我們(如果沒有其他手段)強制程式停止執行,並告訴我們出現了什麼問題,或者(理想狀態下)強制程式處理問題,並返回到穩定狀態。

12.2 終止與恢復

  異常處理理論上有兩種基本模型。長久以來,儘管程式設計師們使用的作業系統支援恢復模型的異常處理,但他們最終還是轉向使用類似“終止模型”的程式碼,並且忽略恢復行為。

  • Java支援終止模型(它是Java和C++所支援的模型)。這種模型假設錯誤非常關鍵,以至於程式無法返回到異常發生的地方繼續執行。
  • 另一種模型稱為恢復模型。意思是異常處理程式的工作是修正錯誤,然後重新嘗試調用出問題的方法,並認為第二次能成功。

12.3 建立自定義異常

  所有標準異常都有兩個構造器:一個是預設構造器;另一個是接受字串作為引數,以便能把相關資訊放入異常物件的構造器。

// FullConstructors.java
class MyException extends Exception{
    public MyException(){}
    public MyException(String msg){ super(msg); }
}
public class FullConstructors{
    public static void f() throws MyException{
        System.out.println("Throwing MyException form f()");
        throw new MyException();
    }
    public static void g() throws MyException{
        System.out.println("Throwing MyException form g()");
        throw new MyException("Originated in g()");
    }
    public static void main(String[] args){
        try{
            f();
        }catch(MyException e){
            e.printStackTrace(System.out);
        }
        try{
            g();
        }catch(MyException e){
            e.printStackTrace(System.out);
        }
    }
}/*Output:
Throwing MyException form f()
MyException
        at FullConstructors.f(FullConstructors.java:11)
        at FullConstructors.main(FullConstructors.java:19)
Throwing MyException form g()
MyException: Originated in g()
        at FullConstructors.g(FullConstructors.java:15)
        at FullConstructors.main(FullConstructors.java:24)
*/

12.4 printStackTrace()

  Throwable類聲明瞭printStackTrace()方法,它將列印“從方法呼叫處直到異常丟擲處”的方法呼叫序列。printStackTrace()方法所提供的資訊可以通過getStackTrace()方法來直接訪問,這個方法將返回一個由棧軌跡中的元素所構成的陣列,其中每一個元素都表示棧中的一楨。元素0是棧頂元素,並且是呼叫序列中的最後一個方法呼叫(這個Throwable被建立和丟擲之處)。陣列中的最後一個元素和棧底是呼叫序列中的第一個方法呼叫。如下:

            
           

相關推薦

Java程式設計思想讀書筆記1~1316

目錄: 第1章 物件導論 1.1 伴隨多型的可互換物件   面向物件程式設計語言使用了後期繫結的概念。當向物件傳送訊息時,被呼叫的程式碼直到執行時才能確定。也叫動態繫結。   編譯器確保被呼叫方法的存在,並對呼叫引數和返回值執行型別檢查(Java是強型別的語言,無法

JAVA程式設計思想學習筆記

物件導論 1.1 抽象過程 Smalltalk的五個基本特性: 萬物皆為物件。 程式是物件的集合,它通過傳送訊息來告知彼此所要做的。 每個物件都有自己的由其他物件所構成的儲存。 每個物件都有其型別。 某一特定型別的所有物件都可以接受同樣的訊息。

java程式設計思想-程式碼賞析

package com.test.pet; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; public abstract clas

《JavaScript高階程式設計讀書筆記

JavaScript組成: 核心(ECMAScript) 文件物件模型(DOM) 瀏覽器物件模型(BOM) ECMAScript ,由ECMA-262標準定義,提供核心語言功能。JavaScript實現了ECMAScript,Adobe ActionScri

JAVA程式設計思想學習筆記操作符

操作符 別名問題 先來看段程式碼,猜猜最後輸出的是什麼: class Test{ int t; } public class A { public static void main(String[] args) { // TODO Auto-gener

JAVA程式設計思想學習筆記介面

介面 抽象類和抽象方法 抽象方法:這種方法不完整,僅有宣告而沒有方法體。所採用的語法如下: abstract void f(); 抽象類:包含抽象方法的類叫做抽象類,如果一個類包含一個或多個抽象方法,該類必須被限定為抽象的。 介面 關鍵字:interface 介面定

JAVA程式設計思想學習筆記多型

多型 繫結 繫結: 將一個方法呼叫同一個方法主體關聯起來被稱作繫結。 前期繫結: 若在程式執行前進行繫結,叫做前期繫結,它是面嚮物件語言不需要選擇就預設的繫結方式。 後期繫結: 它的含義就是在執行時根據物件的型別進行繫結,也叫做動態繫結或執行時繫結。java中除了static和fin

JAVA程式設計思想學習筆記複用類

複用類 組合語法 組合語法就是在類中用其他類的物件作為本類的成員變數。 編譯器不會為每一個引用都建立預設物件,想要初始化引用,可以在下列位置進行: 在定義物件的地方 在類的構造器中 在使用物件之前,惰性初始化 使用例項初始化 繼承語法 關鍵

JAVA程式設計思想學習筆記訪問許可權控制

訪問許可權控制 包:庫單元 打包關鍵字:package 匯入關鍵字:import package必須是除註釋以外的第一句程式程式碼。 java直譯器的執行過程: 找出環境變數CLASSPATH。 CLASSPATH包含一個或多個目錄,用作查詢.class檔案

JAVA程式設計思想學習筆記初始化與清理

初始化與清理 用構造器確保初始化 JAVA採用和C++相同的方案,即構造器採用與類相同的名稱,在建立物件時,將會為物件分配儲存空間,並呼叫同名的構造器,達到確保初始化的目的。 構造器是一種特殊的方法,它是沒有返回值的,與返回值為空(void)不同。 另外,如果自己沒有實現構造器,就會

JAVA程式設計思想學習筆記一切都是物件

一切都是物件 用引用操作物件 用一個類名,宣告一個變數,就是聲明瞭一個引用,比如類String String s; s就是類String的一個引用,引用並不是一個物件,但是它是可以控制相應的物件,相當於遙控器。引用不會分配儲存空間,new個物件之後才會分配空間。 必須由

java程式設計思想讀書筆記面向物件

面向物件 我們常見的程式設計正規化有指令式程式設計,函數語言程式設計,邏輯式程式設計,而面向物件程式設計也是一種指令式程式設計。 指令式程式設計式面向計算機硬體的一種抽象,有變數(儲存單元),賦值語句(獲取儲存指令),表示式(記憶體引用和算術運算)和控制語句(跳轉指令),命令式程

Java程式設計思想讀書筆記第一---

Java程式設計思想(第四版)學習筆記 第一章---第五章 第一章:物件導論 1.1抽象過程 1. 2訪問控制 第二章:一切都是物件 2. 1用引用操縱物件 2. 2基本型別 第三章:操作符

java程式設計思想讀書筆記物件的建立

java物件 物件的建立 java的物件是在執行時建立的,建立物件的的觸發條件有以下幾種: 用new語句建立物件,這是最常用的建立物件方法。 運用反射手段,呼叫java.lang.reflect.Constructor類的newInstance()例項方法。

java程式設計思想讀書筆記HashMap詳解

Map Map介面規定了一系列的操作,作為一個總規範它所定義的方法也是最基礎,最通用的。 AbstractMap AbstractMap是HashMap、TreeMap,、ConcurrentHashMap 等類的父類。當我們巨集觀去理解Map時會發現,其實Map就是一

Java併發程式設計讀書筆記

  前幾天整理電腦檔案的時候,突然發現了之前還在kindle儲存了關於併發程式設計的書,剛好自己在這方面挺薄弱的,故整理一波讀書筆記,繼續加強學習。   1.上下文切換 1.1 時間片分配演算法 時間片是CPU分配給各個執行緒的時間,CPU通過不停地切換執行緒執行,使各個執行緒彷彿是”同

java程式設計思想讀書筆記 十五 泛型 匿名內部類和擦除

1.匿名內部類 泛型還可以應用於內部類以及匿名內部類。下面的例子使用匿名內部類實現了Generator介面: public class Customer { private static long counter = 1; private f

Java併發程式設計的藝術——讀書筆記 併發程式設計的挑戰

第一章 併發程式設計的挑戰 因為最近找工作,準備筆試/面試,開始嘗試閱讀這本書,我不常寫部落格,距上一次寫已經過去大概一年時間了,連CSDN密碼都忘了/衰,所以這次新開一個賬號重新開始,希望我能堅持下去。 第一章沒什麼內容,我認為其目的主要是給出足夠多的閱讀這本書的理

JAVA程式設計思想學習筆記容器類List

容器類 在《java程式設計思想》一書中,容器類本是在持有物件那一章節裡面的,這裡我特意給提出來了,因為內容程式碼比較多,與其放一起顯得太臃腫,倒不如這樣來的清爽些。 List List承諾可以將元素維護在特定的序列中,List介面在Collection的基礎上添加了大量的方法,

python高階程式設計讀書筆記

python高階程式設計讀書筆記(一) python 高階程式設計讀書筆記,記錄一下基礎和高階用法 python2和python3相容處理 使用sys模組使程式python2和python3相容 import sysver=sys.version_info#(ma