java零碎知識點筆記

1.訪問權限

三個顯示關鍵字設置類中的訪問權限:public private protected

如果沒有使用上述三個關鍵字設置權限,默認爲default訪問權限,則稱爲包訪問,只有同一個包下的其他類成員可以訪問。

2.組合和聚合

組合:整體刪除,部件一定刪除

聚合:整體刪除,部件不會刪除

3.“是一個”與“像是一個”

是一個----純粹替代:子類繼承父類,不添加新方法,is-a

像是一個----導出類添加新方法:子類繼承父類後增加新的方法,添加新的接口,is-like-a

14.25

​ 95649.28

97522.78 97522.78

4.前期綁定,後期綁定

前期綁定:編譯器生成對特定函數名的調用,改調用會被解析爲將執行的代碼的絕對地址

後期綁定:程序運行到函數調用時才能確定代碼的地址,編譯器需要確保方法存在,並且對參數和返回值進行類型檢查,但不知要執行的確切代碼。(多態)

5.向上轉型

子類當做其父類來處理的過程叫向上轉型。

向下轉型:強制類型轉換(從父類到子類)

6.基本類型的存儲

new 出來的對象保存在堆內存中,不用new的變量直接存儲在棧內存中,更加高效。

7.finalize()

垃圾回收器只知道如何釋放new創建的對象的內存,不知道如何回收不是new分配的內存。

而在 Java 中,對象並非總是被垃圾回收,或者換句話說:

  1. 對象可能不被垃圾回收。
  2. 垃圾回收不等同於析構。

當垃圾回收器準備回收對象的內存時,會先調用其finalize()方法。

8.垃圾回收器如何工作

  • 引用計數:每個對象含有一個引用計數器,計數爲0則清除對象,在出現循環引用時其計數都不爲0,會出現無法回收的情況,因此這種工作方式未被使用

  • 自適應:

    • 停止-複製:這需要先暫停程序的運行(不屬於後臺回收模式),然後將所有存活的對象從當前堆複製到另一個堆,沒有複製的就是需要被垃圾回收的。另外,當對象被複制到新堆時,它們是一個挨着一個緊湊排列,然後就可以按照前面描述的那樣簡單、直接地分配新空間了。

      這種所謂的"複製回收器"效率低下主要因爲兩個原因。其一:得有兩個堆,然後在這兩個分離的堆之間來回折騰,得維護比實際需要多一倍的空間。某些 Java 虛擬機對此問題的處理方式是,按需從堆中分配幾塊較大的內存,複製動作發生在這些大塊內存之間。

      其二在於複製本身。一旦程序進入穩定狀態之後,可能只會產生少量垃圾,甚至沒有垃圾。儘管如此,複製回收器仍然會將所有內存從一處複製到另一處,這很浪費。爲了避免這種狀況,一些 Java 虛擬機會進行檢查:要是沒有新垃圾產生,就會轉換到另一種模式(即"自適應")。這種模式稱爲標記-清掃(mark-and-sweep),Sun 公司早期版本的 Java 虛擬機一直使用這種技術。對一般用途而言,"標記-清掃"方式速度相當慢,但是當你知道程序只會產生少量垃圾甚至不產生垃圾時,它的速度就很快了。

    • “標記-清掃”:從棧和靜態存儲區出發,遍歷所有的引用,找出所有存活的對象。但是,每當找到一個存活對象,就給對象設一個標記,並不回收它。只有當標記過程完成後,清理動作纔開始。在清理過程中,沒有標記的對象將被釋放,不會發生任何複製動作。"標記-清掃"後剩下的堆空間是不連續的,垃圾回收器要是希望得到連續空間的話,就需要重新整理剩下的對象。

9.可變參數列表

public class NewVarArgs {
    static void printArray(Object... args) {
        for (Object obj: args) {
            System.out.print(obj + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        // Can take individual elements:
        printArray(47, (float) 3.14, 11.11);
        printArray(47, 3.14F, 11.11);
        printArray("one", "two", "three");
        printArray(new A(), new A(), new A());
        // Or an array:
        printArray((Object[]) new Integer[] {1, 2, 3, 4});
        printArray(); // Empty list is OK
    }
}

有了可變參數,你就再也不用顯式地編寫數組語法了,當你指定參數時,編譯器實際上會爲你填充數組。你獲取的仍然是一個數組

10.final

類中所有的 private 方法都隱式地指定爲 final。因爲不能訪問 private 方法,所以不能覆寫它。

當說一個類是 finalfinal 關鍵字在類定義之前),就意味着它不能被繼承。

Java 中除了 staticfinal 方法(private 方法也是隱式的 final)外,其他所有方法都是後期綁定。這意味着通常情況下,我們不需要判斷後期綁定是否會發生——它自動發生。

11.類初始化和加載

每個類的編譯代碼都存在於它自己獨立的文件中。該文件只有在使用程序代碼時纔會被加載。一般可以說“類的代碼在首次使用時加載“。這通常是指創建類的第一個對象,或者是訪問了類的 static 屬性或方法。構造器也是一個 static 方法儘管它的 static 關鍵字是隱式的。因此,準確地說,一個類當它任意一個 static 成員被訪問時,就會被加載。

首次使用時就是 static 初始化發生時。所有的 static 對象和 static 代碼塊在加載時按照文本的順序(在類中定義的順序)依次初始化。static 變量只被初始化一次。

初始化過程:

初始化一個類時,如果他有繼承基類,不論是否創建了基類的對象,基類都會被加載,以此類推,當加載完成後,開始執行根基類的static,以此類推。

必要的類加載完成後,開始創建對象。對象中的所有基本類型變量都被置爲默認值,對象引用被設爲 null —— 這是通過將對象內存設爲二進制零值一舉生成的。接着會調用基類的構造器。

基類構造器和派生類構造器一樣以相同的順序經歷相同的過程。當基類構造器完成後,實例變量按文本順序初始化。最終,構造器的剩餘部分被執行。

12.多態

只有普通的方法調用可以是多態的

如果一個方法是靜態(static)的,它的行爲就不具有多態性

13.接口

關鍵字 default 允許在接口中提供方法實現——在 Java 8 之前被禁止。

// interfaces/InterfaceWithDefault.java
interface InterfaceWithDefault {
    void firstMethod();
    void secondMethod();

    default void newMethod() {
        System.out.println("newMethod");
    }
}

這樣可以不用實現newMethod方法而使用它。

允許在接口中添加靜態方法

特性 接口 抽象類
組合 新類可以組合多個接口 只能繼承單一抽象類
狀態 不能包含屬性(除了靜態屬性,不支持對象狀態) 可以包含屬性,非抽象方法可能引用這些屬性
默認方法 和 抽象方法 不需要在子類中實現默認方法。默認方法可以引用其他接口的方法 必須在子類中實現抽象方法
構造器 沒有構造器 可以有構造器
可見性 隱式 public 可以是 protected 或友元

接口中的字段都自動是 staticfinal

接口可以嵌套在類或其他接口中,像非嵌套接口一樣,它們具有 public 或包訪問權限的可見性。

14.內部類

當生成一個內部類的對象時,此對象與製造它的外圍對象(enclosing object)之間就有了一種聯繫,所以它能訪問其外圍對象的所有成員,而不需要任何特殊條件。此外,內部類還擁有其外圍類的所有元素的訪問權。

.new

// innerclasses/DotNew.java
// Creating an inner class directly using .new syntax
public class DotNew {
    public class Inner {}
    public static void main(String[] args) {
        DotNew dn = new DotNew();
        DotNew.Inner dni = dn.new Inner();
    }
}

要想直接創建內部類的對象,你不能按照你想象的方式,去引用外部類的名字 DotNew,而是必須使用外部類的對象來創建該內部類對象

在擁有外部類對象之前是不可能創建內部類對象的。這是因爲內部類對象會暗暗地連接到建它的外部類對象上。但是,如果你創建的是嵌套類(靜態內部類),那麼它就不需要對外部類對象的引用。

匿名內部類

// innerclasses/Parcel7.java
// Returning an instance of an anonymous inner class
public class Parcel7 {
    public Contents contents() {
        return new Contents() { // Insert class definition
            private int i = 11;

            @Override
            public int value() { return i; }
        }; // Semicolon required
    }

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

“創建一個繼承自 Contents 的匿名類的對象。”通過 new 表達式返回的引用被自動向上轉型爲對 Contents 的引用。

如果你的基類需要一個有參數的構造器:

// innerclasses/Parcel8.java
// Calling the base-class constructor
public class Parcel8 {
    public Wrapping wrapping(int x) {
        // Base constructor call:
        return new Wrapping(x) { // [1]
            @Override
            public int value() {
                return super.value() * 47;
            }
        }; // [2]
    }
    public static void main(String[] args) {
        Parcel8 p = new Parcel8();
        Wrapping w = p.wrapping(10);
    }
}
  • [1] 將合適的參數傳遞給基類的構造器。
  • [2] 在匿名內部類末尾的分號,並不是用來標記此內部類結束的。實際上,它標記的是表達式的結束,只不過這個表達式正巧包含了匿名內部類罷了。因此,這與別的地方使用的分號是一致的。

如果定義一個匿名內部類,並且希望它使用一個在其外部定義的對象,那麼編譯器會要求其參數引用是 final

15.嵌套類

如果不需要內部類對象與其外圍類對象之間有聯繫,那麼可以將內部類聲明爲 static,這通常稱爲嵌套類。想要理解 static 應用於內部類時的含義,就必須記住,普通的內部類對象隱式地保存了一個引用,指向創建它的外圍類對象。然而,當內部類是 static 的時,就不是這樣了。嵌套類意味着:

  1. 要創建嵌套類的對象,並不需要其外圍類的對象。
  2. 不能從嵌套類的對象中訪問非靜態的外圍類對象。

嵌套類與普通的內部類還有一個區別。普通內部類的字段與方法,只能放在類的外部層次上,所以普通的內部類不能有 static 數據和 static 字段,也不能包含嵌套類。但是嵌套類可以包含所有這些東西

嵌套類就沒有這個特殊的 this 引用,這使得它類似於一個 static 方法。

接口內部的類

嵌套類可以作爲接口的一部分。你放到接口中的任何類都自動地是 publicstatic 的。因爲類是 static 的,只是將嵌套類置於接口的命名空間內,這並不違反接口的規則。你甚至可以在內部類中實現其外圍接口,就像下面這樣:

// innerclasses/ClassInInterface.java
// {java ClassInInterface$Test}
public interface ClassInInterface {
    void howdy();
    class Test implements ClassInInterface {
        @Override
        public void howdy() {
            System.out.println("Howdy!");
        }
        public static void main(String[] args) {
            new Test().howdy();
        }
    }
}

如果你想要創建某些公共代碼,使得它們可以被某個接口的所有不同實現所共用,那麼使用接口內部的嵌套類會顯得很方便。

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

16.集合

ArrayListLinkedList 都是 List 的類型,從輸出中可以看出,它們都按插入順序保存元素。兩者之間的區別不僅在於執行某些類型的操作時的性能,而且 LinkedList 包含的操作多於 ArrayList 。本章後面將對這些內容進行更全面的探討。

HashSetTreeSetLinkedHashSetSet 的類型。從輸出中可以看到, Set 僅保存每個相同項中的一個,並且不同的 Set 實現存儲元素的方式也不同。 HashSet 使用相當複雜的方法存儲元素,這種技術是檢索元素的最快方法,因此,存儲順序看上去沒有什麼意義(通常只關心某事物是否是 Set 的成員,而存儲順序並不重要)。如果存儲順序很重要,則可以使用 TreeSet ,它將按比較結果的升序保存對象)或 LinkedHashSet ,它按照被添加的先後順序保存對象。

Map (也稱爲關聯數組)使用來查找對象,就像一個簡單的數據庫。所關聯的對象稱爲。 對於每個鍵, Map 只存儲一次。

鍵和值保存在 HashMap 中的順序不是插入順序,因爲 HashMap 實現使用了非常快速的算法來控制順序。 TreeMap 通過比較結果的升序來保存鍵, LinkedHashMap 在保持 HashMap 查找速度的同時按鍵的插入順序保存鍵。

List:

可以使用 contains() 方法確定對象是否在列表中。如果要刪除一個對象,可以將該對象的引用傳遞給 remove() 方法。同樣,如果有一個對象的引用,可以使用 indexOf()List 中找到該對象所在位置的下標號

當確定元素是否是屬於某個 List ,尋找某個元素的索引,以及通過引用從 List 中刪除元素時,都會用到 equals() 方法(根類 Object 的一個方法)

對於 LinkedList ,在列表中間插入和刪除都是廉價操作(在本例中,除了對列表中間進行的真正的隨機訪問),但對於 ArrayList ,這可是代價高昂的操作。

迭代器

迭代器(也是一種設計模式)的概念實現了這種抽象。迭代器是一個對象,它在一個序列中移動並選擇該序列中的每個對象,而客戶端程序員不知道或不關心該序列的底層結構。另外,迭代器通常被稱爲輕量級對象(lightweight object):創建它的代價小。因此,經常可以看到一些對迭代器有些奇怪的約束。例如,Java 的 Iterator 只能單向移動。這個 Iterator 只能用來:

  1. 使用 iterator() 方法要求集合返回一個 IteratorIterator 將準備好返回序列中的第一個元素。
  2. 使用 next() 方法獲得序列中的下一個元素。
  3. 使用 hasNext() 方法檢查序列中是否還有元素。
  4. 使用 remove() 方法將迭代器最近返回的那個元素刪除。

ListIterator 是一個更強大的 Iterator 子類型,它只能由各種 List 類生成。 Iterator 只能向前移動,而 ListIterator 可以雙向移動。它可以生成迭代器在列表中指向位置的後一個和前一個元素的索引,並且可以使用 set() 方法替換它訪問過的最近一個元素。可以通過調用 listIterator() 方法來生成指向 List 開頭處的 ListIterator ,還可以通過調用 listIterator(n) 創建一個一開始就指向列表索引號爲 n 的元素處的 ListIterator

17.Lambda表達式

Lambda 表達式是使用最小可能語法編寫的函數定義:

  1. Lambda 表達式產生函數,而不是類。 在 JVM(Java Virtual Machine,Java 虛擬機)上,一切都是一個類,因此在幕後執行各種操作使 Lambda 看起來像函數 —— 但作爲程序員,你可以高興地假裝它們“只是函數”。
  2. Lambda 語法儘可能少,這正是爲了使 Lambda 易於編寫和使用。

Lambda 表達式的基本語法是:

  1. 參數。

  2. 接着 ->,可視爲“產出”。

  3. -> 之後的內容都是方法體。

    • [1] 當只用一個參數,可以不需要括號 ()。 然而,這是一個特例。

    • [2] 正常情況使用括號 () 包裹參數。 爲了保持一致性,也可以使用括號 () 包裹單個參數,雖然這種情況並不常見。

    • [3] 如果沒有參數,則必須使用括號 () 表示空參數列表。

    • [4] 對於多個參數,將參數列表放在括號 () 中。

      到目前爲止,所有 Lambda 表達式方法體都是單行。 該表達式的結果自動成爲 Lambda 表達式的返回值,在此處使用 return 關鍵字是非法的。 這是 Lambda 表達式縮寫用於描述功能的語法的另一種方式。

    • [5] 如果在 Lambda 表達式中確實需要多行,則必須將這些行放在花括號中。 在這種情況下,就需要使用 return

Lambda 表達式通常比匿名內部類產生更易讀的代碼

// functional/LambdaExpressions.java

interface Description {
  String brief();
}

interface Body {
  String detailed(String head);
}

interface Multi {
  String twoArg(String head, Double d);
}

public class LambdaExpressions {

  static Body bod = h -> h + " No Parens!"; // [1]

  static Body bod2 = (h) -> h + " More details"; // [2]

  static Description desc = () -> "Short info"; // [3]

  static Multi mult = (h, n) -> h + n; // [4]

  static Description moreLines = () -> { // [5]
    System.out.println("moreLines()");
    return "from moreLines()";
  };

  public static void main(String[] args) {
    System.out.println(bod.detailed("Oh!"));
    System.out.println(bod2.detailed("Hi!"));
    System.out.println(desc.brief());
    System.out.println(mult.twoArg("Pi! ", 3.14159));
    System.out.println(moreLines.brief());
  }
}
interface IntCall {
  int call(int arg);
}
// functional/RecursiveFactorial.java
遞歸
public class RecursiveFactorial {
  static IntCall fact;
  public static void main(String[] args) {
    fact = n -> n == 0 ? 1 : n * fact.call(n - 1);
    for(int i = 0; i <= 10; i++)
      System.out.println(fact.call(i));
  }
}

方法引用:

方法引用組成:類名或對象名,後面跟 :: ,然後跟方法名稱。

18.異常

多重捕獲

通過 Java 7 的多重捕獲機制,你可以使用“或”將不同類型的異常組合起來,只需要一行 catch 語句:

// exceptions/MultiCatch.java
public class MultiCatch {
    void x() throws Except1, Except2, Except3, Except4 {}
    void process() {}
    void f() {
        try {
            x();

        } catch(Except1 | Except2 | Except3 | Except4 e) {
            process();
        }
    }
}複製ErrorOK!

或者以其他的組合方式:

// exceptions/MultiCatch2.java
public class MultiCatch2 {
    void x() throws Except1, Except2, Except3, Except4 {}
    void process1() {}
    void process2() {}
    void f() {
        try {
            x();
        } catch(Except1 | Except2 e) {
            process1();
        } catch(Except3 | Except4 e) {
            process2();
        }
    }
}

在return中使用finally

無論在try中的哪裏使用return,finally代碼塊都會執行。

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