異常
1.異常的基本概念
- 異常是導致程式中斷執行的一種指令流
- 基本的異常處理格式:try...catch,try中捕獲異常,出現異常之後的程式碼將不再被執行,而是中轉到相應的catch語句中執行,用於處理異常
- 對於異常也可以設定其統一的出口,使用finally完成
- 在整個JAVA的異常結構中,實際上有兩個最常用的類:Exception、Error,這兩個類全都是Throwable的子類
- Exception:一般表示的是程式中出現的問題,可以直接使用try...catch處理
- Error:一般指的是JVM錯誤,程式中無法處理
- 【注意】一般在輸出異常資訊的時候,可以直接使用System.out.println()列印異常物件,也可以通過Exception提供的一個方法:public void printStackTrace()
- Java的異常處理機制處理步驟
- 一旦產生異常,則首先會產生一個異常類的例項化物件
- 在try語句中對此異常物件進行捕捉
- 產生的異常物件與catch語句中的各個異常型別進行匹配,如果匹配成功,則執行catch語句中的程式碼
- 如果有一些其它的異常無法知道的話,可以最後使用Exception進行捕獲
- 【注意】在異常處理中,捕獲更粗的異常要放在捕獲更細的異常之後
- 當所有的異常處理的方式是一樣的就可以使用直接對Exception進行捕獲,當然,在一個比較細緻的開發中是不建議這樣使用的,所有的異常最好分別捕獲
- 小結
- 異常出現之後,如果沒有合理的處理的話,則會讓整個程式中斷執行
- 使用try...catch和try...catch...finally可以處理異常,finally將作為異常的統一出口,不管是否有異常都會執行此語句
- 一個異常處理中可以同時出現多個catch,但是捕獲更粗的異常要放在捕獲更細的異常之後,否則程式編譯的時候將出現錯誤
- 在異常中最大的類是Throwable,分為兩個子類:Exception、Error
- Exception:是程式可以自己處理的異常
- Error:表示JVM錯誤,一般程式無法處理
- 捕獲的時候可以直接捕獲Exception,但是最好分開捕獲,如果所有的異常處理操作是一樣的話,則也可以直接捕獲Exception
- 每當異常產生之後,會在程式中產生一個異常類的例項化物件,之後使用此物件與catch中的異常型別相匹配,如果匹配成功,則執行catch語句中的內容,如果匹配不成功,則向下繼續匹配,如果都無法成功,程式中出現中斷執行的問題
2.異常中的其它概念
- 在定義一個方法的時候可以使用throws關鍵字宣告,使用throws宣告的方法表示此方法不處理異常,而交給方法處進行處理
- JAVA中最大的頭就是JVM,所以如果在主方法中使用了throws關鍵字,則表示一切的異常交給JVM進行處理,預設的處理方法也是使用JVM完成的
- throw可以直接丟擲一個異常,丟擲的時候直接丟擲異常類的例項化物件即可
- 在Java的異常處理機制中
- 如果丟擲的是Exception的,則必須使用try...catch進行處理
- 如果丟擲的是RuntimeException的型別,則不是必須使用try...catch處理,一旦發生異常之後將由JVM進行處理,但是為了保證程式的健康性,建議在有可能出現異常的地方還是老實的使用try...catch進行處理
- 自定義異常類只需要繼承Exception不可以了
- 在JDK 1.4之後,系統增加了斷言的功能,就是斷定某一個操作的結果肯定是正確的,如果程式執行到出現斷言語句的時候發現結果不正確了,則會出現錯誤的資訊
- 斷言就是肯定某一個結果的返回值是正確的,如果最終結果的返回值是錯誤的,則通過斷言檢查會為使用者提示錯誤資訊
- 斷言本身不會影響程式的執行,但是如果要想讓一個斷言起作用,則必須對斷言進行驗證
- 小結
- 斷言在實際開發的使用中並不是很常見
- throw和throws關鍵字聯合使用問題
- throw:丟擲異常物件
- throws:在方法宣告處使用,表示此方法不處理異常
- Exception的異常是必須處理的,而RuntimeException的異常是可以不處理的,但是為了保證程式的執行,只要有異常是好全部處理
- 如果需要自定義異常,則直接繼承Exception類即可
包及訪問控制許可權
1.包的定義及匯入
- 多人開發:在JAVA中,可以將一個大型專案中的類分別獨立出來,分門別類地存到檔案裡,再將這些檔案一起編譯執行,如此的程式程式碼將更易於維護
- package是在使用多個類或介面時,為了避免名稱重複而採用的一種措施,直接在程式中加入package關鍵字即可
- 定義包之後,實際上類的名稱就是:包.類名稱
- import語法
- import 包名稱.子包名稱.類名稱:手工匯入所需要的類
- import 包名稱.子包名稱.*:由JVM自動載入所需要的類
- 實際上來說這兩種效能都是一樣的,因為如果使用後者,是由JVM幫助使用者決定需要匯入的類,不需要的類,是不會被載入進來的
- 如果一個類只在本包中訪問,不需要被外包訪問,則直接宣告成class即可,而如果一個類需要被外包訪問,則必須宣告為public class
- JAVA新特性:靜態匯入
- 在JDK 1.5之後提供了靜態匯入功能
- 如果一個類中的方法全部是使用static宣告的靜態方法,則在匯入的時候就可以直接使用"import static"的方式匯入
- 匯入格式:import static 包.類.*;
- JAR命令中的主要引數
- “C”:建立新的文件
- “V”:生成詳細的輸出資訊
- “F”:指定存檔的檔名
- 在實際的Java開發中往往把一些實用的工具類打成jar包交給使用者使用
- 小結
- 包可以將很多的class檔案分類的存放好,這樣可以避免多人開發時,類檔案重名的情況
- 在實際的開發中,沒有包的類基本上不存在的,完整的類名稱“包.類名稱”
- 如果匯入了不同包的同名類的時候,可以直接通過完整的包.類名稱避免重複
- JDK 1.5之後提供了靜態匯入功能,可以直接使用一個類的靜態方法
- 如果一個包中的全部類要交付使用者使用,則要將其打成一個jar包
2.訪問控制許可權入命名規範
- 訪問控制許可權
- private:可以定義方法,屬性,定義的方法和屬性不能被類的外部所看到
- default:可以在本包中的任意地方訪問
- protected:保護,不同包的非子類不可以訪問
- public:公共的,都可以訪問,不受任何的限制
- 回顧
- 當產生了一個類之後,為了保證類中的內容不被外部直接看到,則使用pribate關鍵字
- 但是,如果現在兩個有關係的類要進行屬性互相訪問的話就比較麻煩,之前只能使用getter/setter取得設定,所以為了減少私有屬性的訪問的麻煩,使用了內部類,但是內部類本身會破壞程式的結構
- 為了讓一個類的內容可以斷續方便的使用,使用了繼承的概念,但是在繼承中private屬性也是無法被子類看到的,所以此時,為了方便子類的操作,可以將屬性使用protected進行封裝,這樣一來外部也無法直接看到(不同包)
- 之後有了繼承之後,既然有了父子的關係,所以就可以使用向上或向下的轉型操作,以完成多型性,但是在開發中類與類之間的直接繼承並不多見,而往往繼承抽象類或實現介面,當若干個操作間需要解耦合的時候就可以使用介面完成
- 既然有內部類,則如果一個介面或抽象類的子類只使用一次,則可以將其定義成匿名內部類
- 開發中沒有包的類是絕對不存在的
- 封裝->繼承->多型
多執行緒
1.認識多執行緒
- 在JAVA中如果要想實現多執行緒可以採用兩種方式
- 繼承Thread類
- 實現Runnable介面
- 一個類繼承了Thread類之後,那麼此類就具備了多執行緒的操作功能
- 【問題】為什麼不直接呼叫 run()方法,而是通過 start()呼叫呢
- 查閱JAVA原始碼
- start()方法有可能丟擲異常
- stopBeforeStart是一個boolean型別的變數
- native關鍵字表示的是一個由Java呼叫本機作業系統函式的一個關鍵字。
- 在Java中,執行JAVA程式呼叫本機的作業系統的函式以完成特定的功能
- 證明:如果現在要想實現多執行緒的話,則肯定需要作業系統的支援,因為多執行緒操作中牽扯到一個搶佔CPU的情況,要等待CPU進行高度,那麼這一點肯定需要作業系統的底層支援,所以使用了 native呼叫本機的系統函式,而且在各個作業系統中多執行緒的實現底層程式碼肯定是不同的,所以使用 native關鍵字也可以讓 JVM自動去調整不同的 JVM實現
- threadStatus也表示一種狀態,如果執行緒已經啟動了再呼叫 start()方法的時候就有可能產生異常
- Thread類與Runnable介面的聯絡
- Thread類也是Runnable介面的子類
- 從Thread類與Runnable類的聯絡上來看,做法非常類似於代理設計模式,Thread類完成比執行緒主體更多的操作,例如:分配CPU資源,判斷是否已經啟動等等
- 使用Thread類在操作多執行緒的時候無法達到資源共享的目的,而使用Runnable介面實現的多執行緒操作可以實現資源共享
- Thread類與Runnable介面的使用結論
- 實現Runnable介面比繼承Thread類有如下的明顯優點
- 適合多個相同程式程式碼的執行緒去處理同一個資源
- 可以避免由於單繼承侷限所帶來的影響
- 增強了程式的健壯性,程式碼能夠被多個執行緒共享,程式碼與資料是獨立的
- 綜合以上來看,開發中使用Runnable介面是最合適的
- 執行緒的狀態
- 多執行緒在操作中也是有一個固定的操作狀態的
- 建立狀態:準備好了一個多執行緒的物件:Thread t = new Thread()
- 就緒狀態:呼叫了 start()方法,等待 CPU進行高度
- 執行狀態:執行 run()方法
- 阻塞狀態:暫停止執行,可能將資源交給其他執行緒使用
- 終止狀態(死亡狀態):執行緒執行完畢了,不再使用了
- 實際上,執行緒呼叫 start()方法的時候不是立刻啟動的,而是等待 CPU進行高度
- 小結
- 程式與結程式的區別,關係
- 執行緒是在程式的基礎之上劃分的
- 執行緒消失了程式不會消失,程式如果消失了,則執行緒肯定會消失
- Java程式實現的兩種方式
- 繼承Thread類
- 實現Runnable介面
- 執行緒的啟動:通過 start()方法完成,需要進行 CPU高度,呼叫 start()實際上呼叫的就是 run()方法
- Thread類也是Runnable的子類,使用了代理的機制完成
- 在使用多執行緒的實現中建議通過Runnable介面實現,這樣可以避免由於單繼承所帶來的開發局限,而且通過使用Runnable介面也可以達到資源共享的目的
- 執行緒的狀態
2.執行緒常用操作方法
- 在多執行緒中所有的操作方法實際上都是從Thread類開始的,所有的操作基本上都在Thread類之中
- 取得當前結程式:通過 currentThread()方法取得當前正在執行的程式物件
- 【問題】主方法是以執行緒的形式出現的,那麼JAVA執行時到底啟動了多少呢
- 每當java程式執行的時候,實際上都會啟動一個JVM,每一個JVM實際上就是在作業系統中啟動了一個程式,Java中本身具備了垃圾收集機制,所以java執行時至少啟動兩個執行緒:主執行緒、GC
- 判斷執行緒是否啟動:isAlive()
- 執行緒的強制執行:線上程操作中,可以使用join()方法讓一個執行緒強制執行,執行緒強制執行期間,其他執行緒無法執行,必須等此執行緒完成之後才可以繼續執行
- 執行緒休眠:在程式中允許一個執行緒進行暫的休眠,直接使用 Thread.sleep()方法即可
- 執行緒的中斷:一個執行緒可以被另外一個執行緒中斷其操作的狀態,使用 interrupt()方法完成
- 執行緒的禮讓:線上程操作中,也可以使用 yield()方法將一個執行緒的操作暫讓給其他執行緒執行
3.同步與死鎖
- 所謂的同步就是指多個操作在同一個時間段內只能有一個執行緒進行,其他執行緒要等待此執行緒完成之後才可以繼續執行
- 要想解決資源共享的同步操作問題,可以使用同步程式碼塊及同步方法兩種方式完成
- 【回顧】程式碼塊分為四種
- 普通程式碼塊:是直接定義在方法之中的
- 構造塊:是直接定義在類中的,優先於構造方法執行,重複呼叫
- 靜態塊:是使用 static關鍵字宣告的,優先於構造塊執行,只執行一次
- 同步程式碼塊:使用 synchronized關鍵字宣告的程式碼塊,稱為同步程式碼塊
- 寫同步程式碼塊的時候,必須指明同步的物件,一般情況下會將當前物件作為同步的物件,使用this表示,格式:synchronized(同步物件){需要同步的程式碼;}
- 同步方法:synchronized 方法返回值 方法名稱(引數列表){}
- 死鎖:同步可以保證資源共享操作的正確性,但是過多同步也會產生問題,死鎖一般情況下就是表示在互相等待
- Object類對執行緒的支援
- 等待:wait()方法
- 喚醒:notify()、notifyAll()方法
- 小結
- 多個執行緒在訪問同一資源的時候需要進行同步操作
- 同步使用 synchronized關鍵字完成,分為同步程式碼塊及同步方法
- 過多的同步有可能造成死鎖的產生,死鎖是在程式執行時的一種表現狀態
4.執行緒的生命週期
- 一個新的執行緒建立之後通過 start()方法進入到執行狀態,在執行狀態中可以使用 yield()進行禮讓,但是仍然可以進行
- 如果現在一個執行緒需要暫停的話,可以使用 suspend()、sleep()、wait()
- 如果現線上程不需要再執行,則可以通過 stop結束(如果 run()方法執行完畢也表示結束),或者一個新的執行緒直接呼叫 stop()方法也可以進行結束
- 以上有如下的幾個方法
- suspend():暫時掛起執行緒
- resume():恢復掛起的執行緒
- stop():停止執行緒
- 因為以上三個方法都會產生死鎖的問題,所以現在已經不建議使用了
- 如果現在要想停止一個執行緒的執行,可以通過設定標誌位,讓執行緒停止執行
泛型
1.泛型入門
- 泛型是在 JDK 1.5之後增加的新功能,泛型(Generic)
- 泛型可以解決資料型別的安全性問題,它主要的原理,是在類宣告的時候通過一個標識表示類中某個屬性的型別或者是某個方法的返回值及引數型別。這樣在類宣告或例項化的時候只要指定好需要的型別即可
- 泛型也可以在構造方法中使用,一般有可能使用構造方法為類中的屬性賦值
- 構造方法可以為類中的屬性初始化,那麼如果類中的屬性通過泛型指定,而又需要通過構造設定屬性內容的時候,那麼構造方法的定義與之前並無不同,不需要像宣告類那樣指定泛型
- 在泛型中也可以同時指定多個泛型型別
- 在泛型應用中最好在宣告類物件的時候指定好其內部的資料型別
- 如果在泛型類例項化時沒有指定泛型的型別,則在java中為了保證程式依然可以使用,會將 T設定成 Object型別,這樣一來,就可以接收任意的資料型別,也就是說此時的型別就是 Object,所有的泛型資訊將被擦除
- 小結
- 泛型的產生意義:為了保證資料的安全性
- 泛型的基本使用:由外部指定其具體的操作型別
2.萬用字元
- 匹配任意型別的萬用字元:在開發中物件的引用傳遞是最常見的,但是如果在泛型類的操作中,在進行引用傳遞的時候泛型型別必須匹配才可以傳遞,否則是無法傳遞的
- 在使用 <?>時只能接收,但是不能修改
- 受限泛型
- 在引用傳遞中,泛型操作中也可以設定一個泛型物件的範圍上限和範圍下限
- 範圍上限使用 extends關鍵字宣告,表示引數化的型別可能是所指定的型別,或者是此型別的子類
- 範圍下限使用 super進行宣告,表示引數化的型別可能是所指定的型別,或者是此型別的父型別,直到 Object類
- 【解釋】泛型與子類繼承的限制:一個類的子類可以通過物件多型性,為其父類例項化,但在泛型操作中,子類的泛型型別是無法使用父類的泛型型別接收的,例如:Info<String>不能使用 Info<Object>接收
- 小結
- 使用 ?可以接收任意的泛型物件
- 泛型的上限:? extends 型別
- 泛型的下限相對理解一些就可以了
- 瞭解為什麼泛型子類之間的繼承無法直接轉換的原因
3.泛型的其它應用
- 定義泛型介面:在 JDK1.5之後,不僅僅可以宣告泛型類,也可以宣告泛型介面,宣告泛型介面和宣告泛型類的語法類似,也是在介面名稱後面加上 <T>,格式:[訪問許可權]interface 介面名稱<泛型標識>{}
- 如果現在實現介面的子類不想使用泛型宣告,則在實現介面的時候直接指定好其具體的操作型別即可
- 泛型除了可以為類中的屬性指定型別之外,也可以定義方法,泛型方法所在的類中是否是泛型本身是沒有任何關係的
- 定義泛型方法
- 泛型方法中可以定義泛型引數,此時,引數的型別就是傳入資料的型別
- 泛型方法的簡單定義:[訪問許可權]<泛型標識>泛型標識 方法名稱([泛型標識 引數名稱])
- 使用泛型方法的時候,也可以傳遞或返回一個泛型陣列
- 在設定泛型的時候也可以巢狀的設定
- 小結
- 泛型在介面上可以定義,及其實現的方式
- 泛型在使用的時候可以進行巢狀的操作,只要根據其操作語法即可
- 泛型方法上使用泛型標記的時候需要先宣告,同樣可以指定其操作的上限和下限