第一次讀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多執行緒環境。
還沒看多少,持續更新中。。。。。。)