天天看點

卷妹帶你回顧Java基礎(一)每日更新Day3

卷妹帶你回顧Java基礎(一)每日更新Day3

👩‍💻部落格首頁:京與舊鋪的部落格首頁

✨歡迎關注🖱點贊🎀收藏⭐留言✒

🔮本文由京與舊鋪原創

😘系列專欄:java學習

👕參考網站:牛客網

💻首發時間:🎞2022年8月6日🎠

🎨你做三四月的事,八九月就會有答案,一起加油吧

🀄如果覺得部落客的文章還不錯的話,請三連支援一下部落客哦

🎧最後的話,作者是一個新人,在很多方面還做的不好,歡迎大佬指正,一起學習哦,沖沖沖

💬推薦一款模拟面試、刷題神器👉​​點選進入網站​​

🛒導航小助手🎪

文章目錄

  • ​​卷妹帶你回顧Java基礎(一)每日更新Day3​​
  • ​​🛒導航小助手🎪​​
  • ​​@[toc]​​
  • ​​1.36 遇到過異常嗎,如何處理?​​
  • ​​1.37 說一說Java的異常機制​​
  • ​​1.38 請介紹Java的異常接口​​
  • ​​1.44 說一說你對泛型的了解​​
  • ​​1.45 介紹一下泛型擦除​​
  • ​​1.46 List<? super T>和List<? extends T>有什麼差別?​​
  • ​​1.47 說一說你對Java反射機制的了解​​

1.36 遇到過異常嗎,如何處理?

參考答案

在Java中,可以按照如下三個步驟處理異常:

  1. 捕獲異常

    将業務代碼包裹在try塊内部,當業務代碼中發生任何異常時,系統都會為此異常建立一個異常對象。建立異常對象之後,JVM會在try塊之後尋找可以處理它的catch塊,并将異常對象交給這個catch塊處理。

  2. 處理異常

    在catch塊中處理異常時,應該先記錄日志,便于以後追溯這個異常。然後根據異常的類型、結合目前的業務情況,進行相應的處理。比如,給變量賦予一個預設值、直接傳回空值、向外抛出一個新的業務異常交給調用者處理,等等。

  3. 回收資源

    如果業務代碼打開了某個資源,比如資料庫連接配接、網絡連接配接、磁盤檔案等,則需要在這段業務代碼執行完畢後關閉這項資源。并且,無論是否發生異常,都要嘗試關閉這項資源。将關閉資源的代碼寫在finally塊内,可以滿足這種需求,即無論是否發生異常,finally塊内的代碼總會被執行。

1.37 說一說Java的異常機制

參考答案

關于異常處理:

在Java中,處理異常的語句由try、catch、finally三部分組成。其中,try塊用于包裹業務代碼,catch塊用于捕獲并處理某個類型的異常,finally塊則用于回收資源。當業務代碼發生異常時,系統會建立一個異常對象,然後由JVM尋找可以處理這個異常的catch塊,并将異常對象交給這個catch塊處理。若業務代碼打開了某項資源,則可以在finally塊中關閉這項資源,因為無論是否發生異常,finally塊一定會執行。

關于抛出異常:

當程式出現錯誤時,系統會自動抛出異常。除此以外,Java也允許程式主動抛出異常。當業務代碼中,判斷某項錯誤的條件成立時,可以使用throw關鍵字向外抛出異常。在這種情況下,如果目前方法不知道該如何處理這個異常,可以在方法簽名上通過throws關鍵字聲明抛出異常,則該異常将交給JVM處理。

關于異常跟蹤棧:

程式運作時,經常會發生一系列方法調用,進而形成方法調用棧。異常機制會導緻異常在這些方法之間傳播,而異常傳播的順序與方法的調用相反。異常從發生異常的方法向外傳播,首先傳給該方法的調用者,再傳給上層調用者,以此類推。最終會傳到main方法,若依然沒有得到處理,則JVM會終止程式,并列印異常跟蹤棧的資訊

1.38 請介紹Java的異常接口

參考答案

Throwable是異常的頂層父類,代表所有的非正常情況。它有兩個直接子類,分别是Error、Exception。

Error是錯誤,一般是指與虛拟機相關的問題,如系統崩潰、虛拟機錯誤、動态連結失敗等,這種錯誤無法恢複或不可能捕獲,将導緻應用程式中斷。通常應用程式無法處理這些錯誤,是以應用程式不應該試圖使用catch塊來捕獲Error對象。在定義方法時,也無須在其throws子句中聲明該方法可能抛出Error及其任何子類。

Exception是異常,它被分為兩大類,分别是Checked異常和Runtime異常。所有的RuntimeException類及其子類的執行個體被稱為Runtime異常;不是RuntimeException類及其子類的異常執行個體則被稱為Checked異常。Java認為Checked異常都是可以被處理(修複)的異常,是以Java程式必須顯式處理Checked異常。如果程式沒有處理Checked異常,該程式在編譯時就會發生錯誤,無法通過編譯。Runtime異常則更加靈活,Runtime異常無須顯式聲明抛出,如果程式需要捕獲Runtime異常,也可以使用try…catch塊來實作。

1.44 說一說你對泛型的了解

參考答案

Java集合有個缺點—把一個對象“丢進”集合裡之後,集合就會“忘記”這個對象的資料類型,當再次取出該對象時,該對象的編譯類型就變成了Object類型(其運作時類型沒變)。

Java集合之是以被設計成這樣,是因為集合的設計者不知道我們會用集合來儲存什麼類型的對象,是以他們把集合設計成能儲存任何類型的對象,隻要求具有很好的通用性。但這樣做帶來如下兩個問題:

  • 集合對元素類型沒有任何限制,這樣可能引發一些問題。例如,想建立一個隻能儲存Dog對象的集合,但程式也可以輕易地将Cat對象“丢”進去,是以可能引發異常。
  • 由于把對象“丢進”集合時,集合丢失了對象的狀态資訊,隻知道它盛裝的是Object,是以取出集合元素後通常還需要進行強制類型轉換。這種強制類型轉換既增加了程式設計的複雜度,也可能引發ClassCastException異常。

從Java 5開始,Java引入了“參數化類型”的概念,允許程式在建立集合時指定集合元素的類型,Java的參數化類型被稱為泛型(Generic)。例如 List,表明該List隻能儲存字元串類型的對象。

有了泛型以後,程式再也不能“不小心”地把其他對象“丢進”集合中。而且程式更加簡潔,集合自動記住所有集合元素的資料類型,進而無須對集合元素進行強制類型轉換。

1.45 介紹一下泛型擦除

參考答案

在嚴格的泛型代碼裡,帶泛型聲明的類總應該帶着類型參數。但為了與老的Java代碼保持一緻,也允許在使用帶泛型聲明的類時不指定實際的類型。如果沒有為這個泛型類指定實際的類型,此時被稱作raw type(原始類型),預設是聲明該泛型形參時指定的第一個上限類型。

當把一個具有泛型資訊的對象賦給另一個沒有泛型資訊的變量時,所有在尖括号之間的類型資訊都将被扔掉。比如一個 List 類型被轉換為List,則該List對集合元素的類型檢查變成了泛型參數的上限(即Object)。

上述規則即為泛型擦除,可以通過下面代碼進一步了解泛型擦除:

List<String> list1 = ...; List list2 = list1; // list2将元素當做Object處理      

擴充閱讀

從邏輯上來看,List 是List的子類,如果直接把一個List對象賦給一個List對象應該引起編譯錯誤,但實際上不會。對泛型而言,可以直接把一個List對象賦給一個 List 對象,編譯器僅僅提示“未經檢查的轉換”。

上述規則叫做泛型轉換,可以通過下面代碼進一步了解泛型轉換:

List list1 = ...; List<String> list2 = list1; // 編譯時警告“未經檢查的轉換”      

1.46 List<? super T>和List<? extends T>有什麼差別?

參考答案

  • ? 是類型通配符,List<?> 可以表示各種泛型List的父類,意思是元素類型未知的List;
  • List<? super T> 用于設定類型通配符的下限,此處 ? 代表一個未知的類型,但它必須是T的父類型;
  • List<? extends T> 用于設定類型通配符的上限,此處 ? 代表一個未知的類型,但它必須是T的子類型。

擴充閱讀

在Java的早期設計中,允許把Integer[]數組指派給Number[]變量,此時如果試圖把一個Double對象儲存到該Number[]數組中,編譯可以通過,但在運作時抛出ArrayStoreException異常。這顯然是一種不安全的設計,是以Java在泛型設計時進行了改進,它不再允許把 List 對象指派給 List 變量。

數組和泛型有所不同,假設Foo是Bar的一個子類型(子類或者子接口),那麼Foo[]依然是Bar[]的子類型,但G 不是 G 的子類型。Foo[]自動向上轉型為Bar[]的方式被稱為型變,也就是說,Java的數組支援型變,但Java集合并不支援型變。Java泛型的設計原則是,隻要代碼在編譯時沒有出現警告,就不會遇到運作時ClassCastException異常。

1.47 說一說你對Java反射機制的了解

參考答案

Java程式中的對象在運作時可以表現為兩種類型,即編譯時類型和運作時類型。例如 Person p = new Student(); ,這行代碼将會生成一個p變量,該變量的編譯時類型為Person,運作時類型為Student。

  • 第一種做法是假設在編譯時和運作時都完全知道類型的具體資訊,在這種情況下,可以先使用instanceof運算符進行判斷,再利用強制類型轉換将其轉換成其運作時類型的變量即可。
  • 第二種做法是編譯時根本無法預知該對象和類可能屬于哪些類,程式隻依靠運作時資訊來發現該對象和類的真實資訊,這就必須使用反射。
  • 程式運作時,可以通過反射獲得任意一個類的Class對象,并通過這個對象檢視這個類的資訊;
  • 程式運作時,可以通過反射建立任意一個類的執行個體,并通路該執行個體的成員;
  • 程式運作時,可以通過反射機制生成一個類的動态代理類或動态代理對象。