1. 程式人生 > >第一次讀Think In Java收穫

第一次讀Think In Java收穫

        新進公司的初級程式設計師,看了公司的專案,目前沒啥事做就在看Think In Java,然後想把自己的收穫記錄起來。這是本人第一次寫部落格。

 2018年5月25日

        1.建立一個新物件之後,直接輸出該物件,會預設呼叫Object(所有類都預設繼承Object類)的toString方法,也就是輸出了該物件的地址(“@”符號加上一堆沒有意義的數字和字母),如果該物件重寫了toString方法,則會打印出重寫方法中定義的內容。

例子:

(1)沒有重寫toString()時:

public class User {

}

public class test {
    public static void main(String[] args) {
        User user = new User();
        System.out.println(user);
    }
}

  輸出結果為:[email protected]

(2)重寫了toString()方法

    

public class User {
    @Override
    public String toString
() { return "123"; } }
public class test {
    public static void main(String[] args) {
        User user = new User();
        System.out.println(user);
    }
}
輸出結果:123

        2.多型的缺陷,“覆蓋”私有方法

例子:

class A{
    private void f(){
        System.out.println("a"
); } public static void main(String[] args) { A a = new Test(); a.f(); } } public class Test extends A{ public void f(){ System.out.println("test"); } }

輸出結果為:a

        我們期待的是輸出test,但是由於private修飾符修飾的方法是被自動認為是final的方法,而且對子類是隱蔽的。因此,這種情況下,子類中的f()方法就是一個全新的方法;既然父類中的方法f()方法在子類中不可見,因此甚至也不能被過載。

        結論:只有非private方法才可以被覆蓋;但是還需要密切注意覆蓋private方法的現象,這是雖然編譯器不會報錯,但是也不會按照我們所期望的來執行。所以在子類中,對於父類中的private的方法最好採用不同的名字。


3.使用繼承和組合的場景

        在使用繼承的時候只要考慮是否需要向上轉型,如果需要則使用繼承,不需要使用組合效率更高。


4.代理類

程式碼如下:

class A{
    public String a(){
        return "a";
    }
    public String b(){
        return "b";
    }
}

class Agent{
    private A a = new A();
    public void getA(){
        System.out.println(a.a());
    }
    public void getB(){
        System.out.println(a.b());
    }
}

public class Test{
    public static void main(String[] args) {
        Agent agent = new Agent();
        agent.getA();
        agent.getB();
    }
}


5.組合、繼承以及多型在構建順序上的作用(構造器、成員變數載入的順序)

例子:

class A{
    A(){
        System.out.println("A");
    }
}

class B{
    B(){
        System.out.println("B");
    }
}

class C{
    C(){
        System.out.println("C");
    }
}

class C1 extends C{
    C1(){
        System.out.println("C1");
    }
}

public class Test extends C1{
    private A a = new A();
    private B b = new B();
    public Test(){
         System.out.println("test");
     }
    public static void main(String[] args) {
        new Test();
    }
}

結果:

C
C1
A
B

test

結論:

        構建順序首先是找到該物件的最上層的基類,然後是下一級子類直到載入到當前類,載入到本類的物件時,會先按成員宣告的順序載入成員變數,最後在呼叫該類的構造器(同一類中構造器會在成員變數初始化之後被呼叫)。


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

class A{
   void a(){
        System.out.println("A.a()");
    }
    A(){
        System.out.println("A() before a()");
        a();
        System.out.println("A() after a()");
    }
}

class B extends A{
    private int radius = 1;
    B(int r){
        radius = r;
        System.out.println("B.B().redius = " + radius);
    }
    void a() {
        System.out.println("B.a().radius = " + radius);
    }
}

public class Test{
    public static void main(String[] args) {
        new B(5);
    }
}

結果:

        A() before a()
        B.a().radius = 0
        A() after a()
        B.B().redius = 5

結論:

        從結果可以看出,在基類的構造器中呼叫了被子類重寫的方法,會執行子類中重寫的方法,而不是執行基類中的方法。而在呼叫到子類中重寫的方法的時候,子類(B)還沒有被構造,也就導致了radius並沒有被初始化(輸出結果為0)。因此,編寫構造器時有一條有效的準則:“用盡可能簡單的方法使物件進入正常狀態;如果可以的話,避免呼叫其他方法”。


7、Collenction集合

ArrayList和LinkedList都是List型別,他們都按照插入的順序儲存元素。兩者的區別主要是在執行某些型別的操作時的效能,ArrayList遍歷只花費常量時間,LinkedList在插入刪除元素是花費常量時間。

HashSet、TreeSet和LinkedHashSet都是Set型別,Set中的元素不可重複,HashSet(使用了雜湊)是快速獲取元素的方法,因此不考慮儲存順序,TreeSet(使用紅-黑樹)會按照比較結果升序儲存物件, LinkedHashSet會按新增的順序儲存物件。

Map不用考慮尺寸問題,因為它自己會自動地調整尺寸。HashMap和HashSet一樣提供了最快的查詢技術,也沒有按照任何明顯的順序進行儲存元素,TreeMap會按照比較結果升序儲存物件,LinkedHashMap會按新增的順序儲存鍵,同時保留了HashMap的查詢速度。


8、List list = list1.subList(index1, index2)方法從一個List中擷取一部分內容,然後將這個結果賦給另一個list之後,使用list1.containsAll(list)會顯示true,就算我們使用了Collection.sort()和Collection.shuffle()之後,再呼叫list1.containsAll(list)仍返回true,由此看出順序並不重要,因為subList()所產生的列表的幕後是初始列表,因此,對所返回的列表的修改都會反映到初始列表中。測試程式碼如下:

public class Test{
    public static void main(String[] args) {
        List list1 = new ArrayList();
        list1.add(1);
        list1.add(3);
        list1.add(2);
        list1.add(9);
        list1.add(4);

        System.out.println("list1" + list1.toString());
        List list2 = list1.subList(0,3);
        Collections.sort(list2);
        System.out.println("sort list2" + list2.toString());
        System.out.println("list1" + list1.toString());
        System.out.println(list1.containsAll(list2));
        Collections.shuffle(list2);
        System.out.println("shuffle list2" + list2.toString());
        System.out.println(list1.toString());
        System.out.println("list1" + list1.containsAll(list2));
    }
}

結果為:


還有幾個個常用方法,retainAll()取交集,removeAll()移除所有元素,需要注意的是這兩個方法都是基於equals()方法的。

set(index, obj),替換指定位置的元素。addAll()可以追加元素到列表中,也可以從指定位置追加,isEmpty()用來判斷是否為空,clear()清除列表中的內容,toArray()將集合轉換為陣列型別等。


9、我們都知道queue佇列是先進先出的模式(通過等待時長來判斷是否為先進的元素),但有些時候需要在佇列中基於優先順序處理物件,於是就出現了PriorityQueue。

        PriorityQueue類在Java1.5中引入並作為 Java Collections Framework 的一部分。PriorityQueue是基於優先堆的一個無界佇列,這個優先佇列中的元素可以預設自然排序或者通過提供的Comparator(比較器)在佇列例項化的時排序。

        優先佇列不允許空值,而且不支援non-comparable(不可比較)的物件,比如使用者自定義的類。優先佇列要求使用Java Comparable和Comparator介面給物件排序,並且在排序時會按照優先順序處理其中的元素。
        優先佇列的頭是基於自然排序或者Comparator排序的最小元素。如果有多個物件擁有同樣的排序,那麼就可能隨機地取其中任意一個。當我們獲取佇列時,返回佇列的頭物件。
        優先佇列的大小是不受限制的,但在建立時可以指定初始大小。當我們向優先佇列增加元素的時候,佇列大小會自動增加。

PriorityQueue是非執行緒安全的,所以Java提供了PriorityBlockingQueue(實現BlockingQueue介面)用於Java多執行緒環境。



還沒看多少,持續更新中。。。。。。)