第八章 多態 多態的條件: 1. 要有繼承 2.父類對象引用子類對象 3. 要有方法的重寫 多態的作用:消除類型之間的耦合關係。 將一個方法調用與一個方法主體關聯起來稱作綁定。若在程式執行前進行綁定,叫做前期綁定;在運行時根據對象的類型進行綁定,叫做後期綁定,也叫動態綁定、運行時綁定。 Java中除 ...
第八章 多態
- 多態的條件:
1. 要有繼承
2.父類對象引用子類對象
3. 要有方法的重寫
- 多態的作用:消除類型之間的耦合關係。
- 將一個方法調用與一個方法主體關聯起來稱作綁定。若在程式執行前進行綁定,叫做前期綁定;在運行時根據對象的類型進行綁定,叫做後期綁定,也叫動態綁定、運行時綁定。
- Java中除了static方法和final方法之外,其他所有的方法都是後期綁定。所以final的作用還有關閉動態綁定。
- 只有非private方法才可以被覆蓋,但是還需要密切註意覆蓋private方法的現象,這時雖然編譯器不會報錯,但是也不會按照我們的預期的執行。
- 如果某個子對象要依賴於其他對象,銷毀的順序應該和初始化順序相反。
- 域是不具有多態性的,只有普通的方法調用是多態的。如果直接訪問某個域,這個訪問就將在編譯期進行解析,即域是靜態解析的。靜態方法也是不具有多態性的。
第九章 介面
- 抽象方法和抽象類:
抽象方法是不完整的,僅有聲明而沒有方法體abstract void f();
包含抽象方法的類叫做抽象類;如果一個類包含一個或多個抽象方法,該類必須被定義為抽象的
如果從一個抽象類繼承,並想創建該類的對象,那麼就必須為基類中的所有抽象方法提供方法定義;如果不這樣做,那麼導出類也是抽象類,且編譯器會強制我們用abstract關鍵字來限制這個類。 -
interface這個關鍵字產生一個完全抽象的類,它根本就沒有提供任何具體實現
要讓一個類遵循某個特定介面(或一組介面),需要使用implements關鍵字
- 如果從一個非介面的類繼承,那麼只能從一個類繼承;可以繼承多個介面
- 介面可以多繼承介面來擴展,介面還可以嵌套。
- 介面是實現多重繼承的途徑,而生成遵循某個介面的對象的典型方式就是工廠方法。在工廠對象上調用的是創建方法,而該工廠對象將生成介面的某個實現的對象。
- 介面可以包含域,且隱式地是
static
和final
的。 - 介面關鍵字
interface
前可以添加public
修飾符,不加預設是包訪問許可權,介面的方法預設都是public
的。
第十章 內部類
- 將一個類的定義放在另一個類的定義內部,這就是內部類。
- 從外部類的非靜態方法之外的任意位置創建某個內部類的對象,那麼必須具體地指明這個對象的類型OuterClassName.InnerClassName。
-
非static的普通內部類自動擁有對其外圍類所有成員的訪問權(包括private)
- 如果需要生成對外部類對象的引用,可以使用外部類的名字後面緊跟.this
public class DotThis { void f() { System.out.println("DotThis.f()"); } public class Inner { public DotThis outer() { return DotThis.this;//通過.this返回外部類對象 } } public Inner inner() {return new Inner();} public static void main(String[] args) { DotThis dotThis = new DotThis(); DotThis.Inner dtInner = dotThis.inner(); dtInner.outer().f(); } }
如果要創建內部類對象,必須使用外部類對象和.new
public class DotNew { public class Inner {} public static void main(String[] args) { DotNew dn = new DotNew(); DotNew.Inner dni = dn.new Inner(); } }
- 如果擁有的是抽象類或者具體類,而不是介面,那麼就只能使用內部類實現多重繼承
public class Example1 { public String getName() { return "llb"; } } public class Example2 { public int getAge() { return 25; } } public class MultiImplementation { private class test1 extends Example1 { public String getName() { return super.getName(); } } private class test2 extends Example2 { public int getAge() { return super.getAge(); } } public String getName() { return new test1.getName(); } public int getAge() { return new test2.getAge(); } public static void main(String[] args) { MultiImplementation my = new MultiImplementation(); System.out.println("姓名: " + my.getName()); System.out.println("年齡: " + my.getAge()); } }
-
局部內部類是指內部類定義在方法或作用於內
- 局部內部類不能有訪問說明符
- 局部內部類可以訪問當前代碼塊內的常量以及此外圍類的所有成員
局部內部類會跟著其他類一起通過編譯,但是在定義該局部內部類的方法或作用域之外,該局部內部類是不可用的
- 內部類聲明為
static
時,不再包含外圍對象的引用.this
,稱為嵌套類(與C++嵌套類大致相似,只不過在C++中那些類不能訪問私有成員,而在Java中可以訪問)。
- 創建嵌套類,不需要外圍對象。
- 不能從嵌套類的對象中訪問非靜態的外圍對象。
public class OuterClass { private static String address = "Shanghai"; public static class StaticInnerClass { public String getAddress() { return address; } } public static void main(String[] args) { OuterClass.StaticInnerClass sic = new OuterClass.StaticInnerClass(); String address = sic.getAddress(); System.out.println(address); } }
-
匿名內部類創建方式為:
new 外部類構造器(參數列表)或介面 {}
- 在定義類的同時就生成了該內部類的一個實例,隨後該類的定義會消失,所以匿名內部類不能被重覆使用
- 匿名內部類必須繼承一個父類或者實現一個介面,但是也只能繼承一個父類或實現一個介面
- 匿名內部類中不能定義構造方法(沒有類名),不能存在任何的靜態成員變數和靜態方法
- 匿名內部類不能是抽象的,它必須實現所繼承的類或者實現的介面中的全部抽象方法
public interface Contents { int value(); } public class Parcel7 { public Contents contents() { return new Contents() { private int i = 1; public int value() { return i; } }; } //等價於 /* class MyContents implements Contents { private int i = 1; public int value() { return i; } } public Contents contents() { return new MyContents(); } */ public static void main(String[] args) { Parcel7 parcel7 = new Parcel7(); Contents c = parcel7.contents(); } }
給匿名內部類傳遞參數時,若該形參在內部類被使用,那麼該形參必須被聲明為final
public class Parcel9 { //dest是一個在外部定義的對象,必須將其定義為final參數引用 public Destination destination(final String dest) { return new Destination() { private String label = dest; @Override public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel9 parcel9 = new Parcel9(); Destination destination = parcel9.destination("Shanghai"); System.out.println(destination.readLabel()); } }
- 為什麼要是final?內部類並不是直接調用方法傳遞的參數,而是利用自身的構造器對傳入的參數進行備份,自己內部方法調用的實際上是自己的屬性而不是外部方法傳遞進來的參數,在內部類中的屬性和外部方法的參數兩者看似是同一個東西,但實際上卻不是,也就是說在內部類中對屬性的修改並不會影響到外部的形參,如果內部類中的屬性改變了,而外部方法的形參卻沒有改變,這是難以接受的,為了保證參數的一致性,就規定使用final來避免兩者不同時變化的情況發生。
-
每個類都會產生一個.class文件,其中包含瞭如何創建該類型的對象的全部信息;內部類也必鬚生成有個.class文件以包含它們的class對象信息,其命名規則是:
外圍類的名字,加上”$“,再加上內部類的名字,如果時匿名內部類,編譯器會簡單地產生一個數字作為其標識符,例如:
Outer$Inner.class Outer$1.class
第十一章 持有對象
- Collection是一個集合介面,提供了對集合對象進行操作的通用介面方法
Collections是一個包裝類,包含有各種集合操作的靜態方法,此類不能實例化,就像一個工具類,服務於Collection框架
Collection介面是最基本的集合介面,一個Collection代表一組Object,即Collection的元素 -
List,Set,Queue介面都是Collection介面的實現
-
- List:必須按照插入的順序保存元素
- Set:不能有重覆的元素
- Queue:按照隊列的規則來確定對象產生的順序(通常與元素插入的順序相同)
- 在Java.util包中的Arrays和Collections類中都有很多實用方法,可以在Collection中添加一組元素;Arrays.asList()方法接受一個數組或是一個用逗號分割的元素列表(使用可變參數),並將其轉換為一個List對象;Collections.addAll()方法接受一個Collection對象,以及一個數組或是一個用逗號分割的列表,將元素添加到Collection中
import java.util.*; public class AddingGroups { public static void main(String[] args) { Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5)); Integer[] moreInts = {6, 7, 8, 9, 10}; collection.addAll(Arrays.asList(moreInts));//更快,但不夠靈活 Collections.addAll(collection, 11, 12, 13, 14, 15); Collections.addAll(collection, moreInts);//更加靈活 List<Integer> list = Arrays.asList(16, 17, 18, 19, 20); list.set(1, 99); } }
-
Arrays類似於Collections,是一個工具類
Arrays.asList()返回一個受指定數組支持的固定大小的列表,可以用來將數組轉換成List
反過來,利用List的toArray()方法,可以將List轉換成數組
-
容器的列印
必須使用Arrays.toString()來產生數組的可列印表示
-
List
ArrayList:隨機訪問,但是在List的中間插入或移除元素時較慢
LinkedList:通過代價較低的在List中間進行的插入和刪除操作,提供了優化的順序訪問;隨機訪問較慢
ArrayList常見方法
contains(Object o):確定某個對象是否在列表中
remove(int index):移除指定位置上的元素
indexOf(Object o):返回列表中首次出現指定元素的索引,如果不包含該元素,返回-1
add(E e):將制定元素添加到此列表的尾部
add(int index, E e):將指定元素插入到指定位置
-
迭代器是一個對象,它的工作是遍歷並選擇序列中的對象
Java中的Iterator只能單向移動,只能用來:
- 使用方法iterator()要求容器返回一個Iterator;Iterator準備好返回序列的第一個元素
- 使用next()獲得序列中的下一個元素
- 使用hasNext()檢查序列中是否還有元素
- 使用remove()將迭代器新近返回的元素刪除
-
LinkedList常見方法
addFirst(E e)/addLast(E e):將元素添加到列表的開頭/結尾
getFirst()/element():返回列表的第一個元素
peek()/peekFirst():獲取但不移除列表的第一個元素
offer(E e)/offerLast(E e):將元素插入到列表末尾
-
Queue
隊列時一個典型的先進先出(FIFO)的容器,即從容器的一端放入事物,從另一端取出,並且事物放入容器的順序與取出的順序是一樣的
LinkedList提供了方法以支持隊列的行為,並且它實現了Queue介面,因此LinkedList可以用作Queue的一種實現,也可以將LinkedList向上轉型為Queue
-
Set
Set不保存重覆的元素;Set最常被使用的是測試歸屬性,我們可以很容易地詢問某個對象是否在某個Set中
存儲元素的方式:
HashSet:使用散列函數
LinkedHashSet:使用散列,但是看起來使用了鏈表來維護元素的插入順序
TreeSet:將元素存儲在紅-黑樹結構中
-
Map:一組成對的“鍵值對”對象,允許使用鍵來查找值;映射表允許我們使用另一個對象來查找某個對象,它被稱為“關聯數組”,因為它將某些對象與另外一些對象關聯在了一起,或者被稱為“字典”
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
更複雜的形式
Map<Integer, List<String>> map = new HashMap<Integer, List<String>>(); map.put(1, rrays.asList("lv", "long", "bao"));
map的鍵是一個Set,值是一個Collection
Map常見方法
get(Object o):返回指定鍵所映射的值,如果不包含該鍵的映射關係,返回null
put(K key, V value):將指定的值與此映射中的指定鍵關聯,如果已經存在映射關係,更新值
hashCode():返回此映射的哈希碼值
Map的三種實現
HashMap:基於“拉鏈法”實現的散列表,一般用於單線程中,不是線程安全的
HashTable:基於“拉鏈法”實現的散列表,一般用於多線程中,是線程安全的
TreeMap:有序的散列表,通過紅黑樹實現的,一般用於單線程中存儲有序的映射
- 總結:
- 數組將數字與對象聯繫起來:它保存類型明確的對象,查詢對象時,不需要對結果做類型轉換;它可以時多維的,可以保存基本類型的數據;但是,數組一旦生成,其容量不能改變
- Collection保存單一的元素,而Map保存關聯的鍵值對:有了Java泛型,你就可以指定容器中存放的對象類型,因此你就不會將錯誤類型的對象放置到容器中,並且在從容器中獲取元素時,不必進行類型轉換;各種Collection和Map都可以在你向其中添加更多的元素時,自動調整其尺寸;容器不能持有基本類型,但是自動包裝機制會仔細地執行基本類型到容器中所持有包裝器類型之間的雙向裝換
- 像數組一樣,List也建立數字索引與對象的關聯,因此,數組和List都是排好序的容器;List能夠自動擴充容量
- 如果要進行大量的隨機訪問,就使用ArrayList;如果要經常從表中間插入或刪除元素,則應該使用LinkedList
- 各種Queue以及棧的行為,由LinkedList提供支持
- Map是一種將對象(而非數字)與對象相關聯的設計:HashMap設計用來快速訪問;TreeMap保持“鍵”始終處於排序狀態,所以沒有HashMap快;LinkedHashMap保持元素插入的順序,但是也通過散列提供了快速訪問能力
- Set不接受重覆元素:HashSet提供了最快的查詢速度;TreeSet保持元素處於排序狀態;LinkedHashSet以插入順序保存元素
- 新程式中不應該使用過時的Vector,Hashtable和Stack(原因參考:https://www.cnblogs.com/fudashi/p/7214609.html)