Java核心技術(卷I)讀書筆記 第四~六章

Java核心技術(卷I)第四~六章

面向對象設計部分 OOP

第四章 對象與類

  1. 類之間的關係有
    uses-a: 依賴,一個類的方法操縱另一個類的對象。
    has-a:聚合,一個類的對象包含另一個類的對象。
    is-a:繼承,用於表示特殊和一般關係。
  2. 構造器( constructor ):構造新實例,應和類名相同,在構造器前面加上 new 操作符用於構造對象。
    Date birthday = new Date()
    注意:Date deadline 只是定義了一個對象變量,她可以引用Date類型的對象,但deadline本事不是對象。在Java中,任何對象變量的值都是對存儲在另一個地方的一個對象的引用,new 操作符的返回值也是一個引用。可以顯示地將對象變量設置爲 null 表明這個對象變量目前沒有引用任何值。
    另外,局部變量不會自動初始化爲null。不能對已經存在的對象調用構造器達到重新設置實例域的目的。不要在構造器中定義與實例域重名的局部變量。
    如果類中提供了至少一個構造器,但是沒有提供無參數的構造器,則在構造對象的時候,如果沒有提供參數就被視爲不合法。
    調用另一個構造器:如果構造器的第一個語句行如this(…),那麼這個構造器將調用同類中的另一個構造器。
    public Employee{
    this('Employee #' + nextId, s);
    nextId++;
    }
  3. 更改器( mutator method )和訪問器(accessor method)方法:對實例做出修改的叫做更改器方法,僅訪問實例域而不進行修改的叫做訪問器方法。通常在訪問器前面加 get, 在更改器前面加get
  4. 隱式( implicit )參數和顯式( explicit )參數:
    public void raiseSalary(double byPercent){
    salary += salary*byPercent/100; }

    第一個參數稱爲隱式參數,是出現在方法名之前的類對象,關鍵字 this 表示隱式參數;第二個參數是方法名後面括號中的數值,顯式參數。
  5. 在封裝時,不要編寫返回引用可變對象的訪問器方法。如果要返回一個可變對象的引用,應先對其 克隆( clone ), 對象的克隆是指存放在另一個位置上的對象副本。
  6. final實例域:final修飾基本( primitive )類型域,或不可變( immutable )類型域。構建對象時必須在每一個構造器執行之後初始化這樣的域。
  7. 靜態域和靜態方法:如果將域定義爲static, 那麼每個類中只有一個這樣的域,每一個對象對於所有的實例域都有一份自己的拷貝。即使沒有對象,靜態域也存在,它屬於類,而不屬於任何對象。
    靜態方法是一種不能向對象操作的方法,如Math.pow(x, a),沒有隱式參數。因爲靜態方法不能操作對象,所以不能再靜態方法中訪問實例域,但可以訪問自身類中的靜態域。我們建議使用類名來調用靜態方法
    使用靜態方法的兩種情況:
    • 一個方法不需要訪問對象狀態,其所需參數都是通過顯示參數提供。
    • 一個方法只需要訪問類的靜態域。
  8. 工廠( Factory )方法:
  9. 方法參數:Java總是採用按值調用( call by value )。方法參數共有兩種類型:基本數據類型和對象引用。方法得到的是對象引用的拷貝,對象引用及其拷貝同時引用同一個對象。
    Java 中方法參數的使用情況:
    • 一個方法不能修改一個基本數據類型的參數。
    • 一個方法可以改變一個對象的參數。
    • 一個方法不能讓對象參數引用一個新的對象。
  10. 顯式域初始化:可以在類定義中直接將一個值付給任何域,也可以調方法對域進行初始化。
  11. 初始化塊( initialization block ):在一個類的聲明中,可以包含多個代碼塊,只要構造類的對象,這些塊就會自動執行,建議將初始化塊放在域定義之後。
    靜態的初始化塊:將代碼放在一個塊中,並標記關鍵字static。
  12. 可以爲一個類添加 finalize 方法,這個方法將在垃圾回收器清除對象之前調用。但在實際應用中,不要依賴使用這個方法回收任何短缺資源,因爲很難知道這個方法什麼時候被調用。
  13. 包 (package):使用包的主要原因是確保類名的唯一性。
  14. 類的導入:
    • 第一種方式是在每個類名前添加完整的包名。
      java.util.Date today = new java.util.Date();
    • 使用import語句導入一個特定的類或者整個包。
      import java.util.*; 只能使用星號導入一個包。
      import還增加了導入靜態方法和靜態域的功能。
      `import static java.lang.System.*;
  15. 要想將一個類放到包中,就要將包的名字放在源文件的開頭。
    package com.horstmann.corejava
    如果沒有在源文件中放置package中,這個源文件的類就被放置到一個默認的包中( default package ),沒有名字。
  16. 文檔註釋( javadoc ):可以由源文件生成一個HTML文檔。在源代碼中添加以專用的定界符/* 開始的註釋。每個/*…*/文檔註釋在標記後緊跟自由格式文本( free-form text ),標記由 @ 開始, 自由文本的第一句應該是一個概要性的句子。
    方法註釋:
    • @param:變量描述。可以佔據多行並用HTML描述,一個方法的所有@param標記必須放在一起。
    • @return:可以佔據多行並用HTML描述
    • @ throws:添加一個註釋,表示這個方法有可能拋出異常。
      通用註釋:
    • @author 姓名:產生一個作者條目
    • @version 文本:產生一個版本條目
    • @since 文本:產生一個始於條目,可以是對引入特性的描述
    • @deprecated 文本:對類、方法或變量添加一個不再使用的註釋,文本中給出取代的建議。
    • @see 引用:這個標記將在”see also”部分添加一個超級鏈接。可用於類或方法中。
      @see com.horstmann.corejava.Employee#raiseSalary(double)
      一定要使用要使用#分割類名與方法名,或類名與變量名。
      如果@see之後有一個 < 字符就要制定一個超鏈接,可以鏈接到任何URL。
      @see <a href="www.horstmann.com/corejava.html">The Core Java home page </a>
      如果@see之後有一個雙引號(“)字符,文本就會顯示在see also 之後。
    • @link:可以在註釋的任何位置放置指向其他類或方法的超級鏈接,以及插入一個專用的標記。
      {@link package class#feature label}
  17. 類註釋:必須放在import語句之後,類定義之前。
    域註釋:只需要對公有域( 通常指的是靜態常量 )進行註釋。
  18. 產生包註釋:
    • 提供一個以package.html命名的HTML文件,在標記…之間的所有文本都會被抽取出來。
    • 提供一個以package-info.java命名的java文件。

第五章 繼承

  1. 使用關鍵字 extends 表示繼承,所有的繼承都是共有繼承
    class Manager extends Employee{}
  2. 使用關鍵字 super 指示編譯器調用超類方法。
    double baseSalary = super.getSalary();
    super在構造器中使用。使用super調用構造器的語句必須是子類構造器的第一條語句。如果子類沒有顯式調用超類構造器,則將自動調用超類默認構造器。
    super(n, s, year, month, day);
  3. 一個變量對象可以指示多種實際類型的現象被稱作多態( polymorphism ), 在運行司能夠自動地選擇調用那個方法的現象叫做動態綁定( dynamic binding ).
  4. 繼承層次( inheritance hierarchy ):有一個公共超類派生出來的所有兩類的集合。在繼承層次中,從某個特定的類到其祖先的路徑被稱爲該類的繼承鏈。
  5. “is-a”是一種置換法則,表示程序中出現超類對象的任何地方都可以用子類對象置換
    Employee e;
    e = new Employee();
    e = new Manager();
    這個例子:
    Manager boss = new Manager()
    Employee[] staff = new Employee[3];
    staff[0] = boss;
    在這個例子中,變量staff[0]與boss引用同一個對象,但編譯器將staff[0]看做Employee。
  6. 在覆蓋方法時,如果子類中定義了一個與超類簽名相同的方法,那麼子類中的這個方法就覆蓋了超類中的相同簽名方法,允許子類將覆蓋方法的返回類型定義爲原返回類型的子類型。此外,子類方法不能低於超類方法的可見性,特別是,如果超類方法時 public,子類方法一定要聲明爲 public。
    public Employee getBuddy(){...}
    public Manager getBuddy(){...}
  7. 方法表( method table ):虛擬機預先爲每個類創建了一個方法表,其中列出了所有方法的簽名和實際調用的方法。這樣在調用方法時只需查表即可。
  8. 對象的強制類型轉換:僅需要用一對圓括號將目標類名括起來,並放置在需要轉換的對象引用之前即可。只能在繼承層次內進行類型轉換。在將超類轉換成子類之前,應該使用 instanceof 進行檢查。:一般情況下應儘量少使用類型轉換和instanceof運算符。
    Manager boss = (Manager) staff[0];
  9. 抽象類:使用 abstract 關鍵字。
    public abstract String getDescription(); #不需要實現
    包含一個或者多個抽象方法的類本身必須被聲明爲抽象類。即使類不含有抽象方法,也可以將類聲明爲抽象類。
    abstract class Person{ public abstract String getDescription(); }
    除了抽象方法外,抽象類還可以包含具體數據和方法。
    抽象類不能被實例化,但可以定義一個抽象類的對象變量,但它只能引用非抽象子類的對象。
  10. protected:對本包和子類可見。
    private:僅對本類可見。
    public:對所有類可見。
    默認:對本包可見,不需要修飾符。
  11. Object:所有類的超類。如果沒有明確指出超類,Object類就被當做是這個類的超類。可以私用Object類型變量引用任何類型的對象。
    Object obj = new Employee("Harry Hacker", 35000);
  12. 完美 equals 的建議:
    • 顯式參數命名爲otherObject,稍後需要將它轉換成叫做other的變量。
    • 檢測this和other是否引用用一個對象
      if(this == otherObject) return true;
    • 檢測otherObject是否爲null,是的話返回false
      if(otherObject == null) return false;
    • 比較this和otherObject是否屬於同一個類,如果equals的語句在每個子類中有所改變,就使用getClass檢測;或者說子類擁有自己的相等概念,則對稱性強制採用getClass進行檢測。
      if(getClass() != otherObject.getClass()) return false;
      如果所有子類都有統一的語義,使用instanceof檢查。或者說如果由超類決定相等的概念,就使用instanceof進行檢測。
      if(!(otherObject instanceof ClassName)) return false;
    • 將otherObject轉換爲相應的類類型變量。
      ClassName other = (ClassName)otherObject;
    • 現在開始對所有需要比較的域進行比較,使用==比較基本數據類型,使用equals比較對象域。
      return field1 == other.field1 && Object.equals(field2, other.field2);
  13. Object.hashCode 方法:散列碼是由對象導出的一個整數值(可以使負數)。在Java 7中,最好使用null安全的Object.hashCode,如果參數爲null,這個方法返回0。該方法還可以提供多個參數,對各個參數調用Object.hashCode,並組合這些散列值。
    return Object.hashCode(name, salary, hireDay);
  14. o.toString方法:絕大多數的toString方法都遵循這樣的格式:類的名字,隨後是一對方括號括起來的值域。
  15. 泛型數組列表ArrayList這個採用參數類型( type parameter )的泛型類( generic class )。
    ArrayList<Employee> staff = new ArrayList<>();
    ArrayList<Integer> a = new ArrayList<>();!!!#這裏要使用基本數據類型的包裝器,Integer,而不是int
    在Java 7 中可以省去菱形語法右邊尖括號裏的參數。
    • 使用, add 方法將語速添加到數組列表中。
      staff.add(new Employee("Tony Tester", 30000));
    • 使用, ensureCapacity方法在填充數組前分配容量。
      staff.ensureCapacity(4);
    • 使用, size()方法返回數組列表中包含的實際元素數目。
    • 使用, trimToSize()方法將存儲區域的大小調整到當前元素所需要的存儲空間數目。
    • 使用, set(int index, E element)方法設置第i個元素。
    • 使用, get(int index)方法返回此列表中指定位置上的元素。
    • 使用, remove(int index):移除此列表中首次出現的指定元素(如果存在)。
      將原始的ArrayList賦值給類型化的ArrayList會得到一個警告。使用類型轉換並不能避免出現警告。
  16. 對象包裝器和自動裝箱:所有的基本烈性都有一個與之對應的類,這些類稱爲 包裝器(wrapper),當Integer類型的數組列表獲得或添加值的時候,使用list.add(Integer.valueOf(3));這稱爲自動裝箱(autoboxing)。==運算符也可以應用於對象包裝器對象,只不過檢測的是對象是否指向同一個存儲區域。
  17. 參數數量可變的方法:
    public PrintStream printf(String fmt, Object...args){}
    這裏…表示這個方法可以接受任意數量的對象。實際上,printf方法接受兩個參數,一個是格式字符串,另一個是Object[]數組,其中保存着所有參數(如果提供的是整形數組或者其他基本類型的值,自動裝箱功能將把它們轉換成對象。)
  18. 枚舉類:所有沒枚舉類都是Enume類的子類。
    public enum Size{
    SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
    private String abbreviation;
    }
    • toString():返回枚舉類的常量名。
      Size.SMALL.toString();
    • valueOf:toString的逆方法,靜態方法
      Size s = Enum.valueOf(Size.class, "SMALL");
    • values:靜態方法,返回一個包含全部枚舉值的數組。
      Size[] values = Size.values();
    • ordinal:返回enum聲明中枚舉常量的位置,從0開始計數。

第六章 接口與內部類

  1. 接口( interface ):接口不是類,而死對類需求的一組描述。接口中所有方法都自動屬於 pubic,因此不必提供關鍵字public, 但在接口的實現中必須將其聲明爲public。接口中的域都將自動被設置爲 public static final。接口中可以定義常量,但接口中決不能含有實例域,也不能在接口中實現方法.
    爲了讓類實現接口,通常需要:
    • 讓類聲明爲實現給定的接口。使用implements關鍵字
      class Employee implements Comparable
    • 對接口中的所有方法進行定義。
      特性:
    • 接口不是類,不能使用new運算符。
    • 但可以聲明接口的變量,接口變量必須引用實現了接口的類對象。
      Comparable x = new Employee();
    • 可以使用instanceof檢查一個對象是否實現了某個特定的接口。
      if(anObject instanceof Comparable) {}
    • 接口允許被拓展
      public interface Powered extends Comparable{}
      使用 , 將實現的各個藉口分隔開
      class Empliyee implements Cloneable, Comparable
  2. 對象克隆:默認的克隆操作是淺拷貝,會拷貝隊形中所有屬於數值或者基本類型的與。如果對象包含了子對象的引用,拷貝的結果就會使兩個對象共享這部分信息。如果子對象是可變的,那就要重新定義clone方法以便實現深拷貝。
    對每一個類判斷:
    • 默認的clone是否滿足要求
    • 默認的clone是否能夠條用哪個可變子對象的clone得到修補
    • 是否不應該使用clone
      要實現上述前兩點,必須
    • 實現Cloneable接口
    • 使用public訪問修飾符重新定義clone方法。
      即使clone默認操作滿足要求,也要實現Cloneable接口,並將其重新定義爲public,並調用super.clone();
      class Employee implements Cloneable{
      public Employee clone() throws CloneNotSsupportException{
      return (Employee)super.clone()
      }
      }
  3. 接口與回調( callback ):回調是一種常見的程序設計模式,在這種模式中,可以指出某個特定事件發生時應該採取什麼動作。
  4. 內部類(inner class):是定義在另一個類中的類。使用內部類的原因
    • 內部類可以訪問該類定義所在的作用域中的數據,包括私有數據
    • 內部類可以對同一個包中的其他類隱藏起來
    • 當想要定義一個毀掉函數且不想編寫大量代碼時,使用匿名( anonymous )內部類比較便捷。
      儘管內部類位於類中,但不意味着每個外部類都有一個內部類實例域。
      內部類對象總有一個隱式引用,指向創建它的外部類對象。
  5. 內部類的特殊語法規則:
    • OuterClassName.this表示外圍類的引用。
    • OuterObject.new InnerClass(construction parameters):更加明確的編寫內部對象的構造器。
    • OuterClass.InnerClass:這樣引用內部類
  6. 局部內部類:是定義在類方法中的類,不能使用public或者private訪問說明符進行聲明。可以對外界世界完全隱藏,即使外部類的其他代碼也不能訪問。局部類不僅可以訪問他們的外部類,還可以訪問被聲明爲final的局部變量。
public void start(int interval, final boolean beep){
    class TimePrinter implements ActionListener{
        public void actionPerformed(ActionEvent event){
            if(beep) {...}
        }
    }

    ActionListener listener = new ActionListener();
}
  1. 匿名內部類( anonymous inner class ):如果只想穿件這個類的一個對象,就不用命名了。
public static void start(int interval, final boolean final){
    ActionListener listener = new ActionListener()
    {
        public void actionPerformed(ActionEvent event)
        {
            Date now = new Date();
            ...
        }
    }
}

通用語法格式爲
new SuperType(construction parameters){
inner class methods and data
}
由於構造器必須和類名相同,而匿名類沒有類名,這樣,匿名類就不能有構造器。取而代之的是將構造器參數傳遞給超類( superclass )的構造器。尤其在內部類實現接口的時候,不能有任何構造參數。
8. 靜態內部類:使用內部類僅僅爲了把一個類隱藏在另一個類的內部,而不需要內部類引用外圍類對象,爲此,可以將內部類聲明爲 static。當然只有內部類可以聲明爲static。

發佈了38 篇原創文章 · 獲贊 11 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章