金九銀十準備季:Java異常+Java IO與NIO面試題 Java中的IO與NIO面試題

寫在前面:2020年面試必備的Java後端進階面試題總結了一份複習指南在Github上,內容詳細,圖文並茂,有需要學習的朋友可以Star一下!
GitHub地址:https://github.com/abel-max/Java-Study-Note/tree/master

1、Java中異常分爲哪兩種?

編譯時異常

運行時異常

2、異常的處理機制有幾種?

異常捕捉:try…catch…finally,異常拋出:throws。

3、如何自定義一個異常

繼承一個異常類,通常是RumtimeException或者Exception

4、try catch fifinally,try裏有return,finally還執行麼?

執行,並且finally的執行早於try裏面的return

結論:

(1)不管有木有出現異常,finally塊中代碼都會執行;

(2)當try和catch中有return時,finally仍然會執行;

(3)finally是在return後面的表達式運算後執行的(此時並沒有返回運算後的值,而是先把要返回的值保存起來,管finally中的代碼怎麼樣,返回的值都不會改變,任然是之前保存的值),所以函數返回值是在finally執行前確定的;

(4)finally中最好不要包含return,否則程序會提前退出,返回值不是try或catch中保存的返回值。

5、 Excption與Error包結構

Java可拋出(Throwable)的結構分爲三種類型:被檢查的異常(CheckedException),運行時異常(RuntimeException),錯誤(Error)。

(1)運行時異常

定義:RuntimeException及其子類都被稱爲運行時異常。

特點:Java編譯器不會檢查它。也就是說,當程序中可能出現這類異常時,倘若既"沒有通過throws聲明拋出它",也"沒有用try-catch語句捕獲它",還是會編譯通過。例如,除數爲零時產生的ArithmeticException異常,數組越界時產生的IndexOutOfBoundsException異常,fail-fast機制產生的ConcurrentModi?cationException異常(java.util包下面的所有的集合類都是快速失敗的,“快速失敗”也就是fail-fast,它是Java集合的一種錯誤檢測機制。當多個線程對集合進行結構上的改變的操作時,有可能會產生fail-fast機制。記住是有可能,而不是一定。

例如:假設存在兩個線程(線程1、線程2),線程1通過Iterator在遍歷集合A中的元素,在某個時候線程2修改了集合A的結構(是結構上面的修改,而不是簡單的修改集合元素的內容),那麼這個時候程序就會拋出ConcurrentModi?cationException 異常,從而產生fail-fast機制,這個錯叫併發修改異常。Fail-safe,java.util.concurrent包下面的所有的類都是安全失敗的,在遍歷過程中,如果已經遍歷的數組上的內容變化了,迭代器不會拋出ConcurrentModi?cationException異常。如果未遍歷的數組上的內容發生了變化,則有可能反映到迭代過程中。這就是ConcurrentHashMap迭代器弱一致的表現。ConcurrentHashMap的弱一致性主要是爲了提升效率,是一致性與效率之間的一種權衡。要成爲強一致性,就得到處使用鎖,甚至是全局鎖,這就與Hashtable和同步的HashMap一樣了。)等,都屬於運行時異常。

常見的五種運行時異常:

ClassCastException(類轉換異常)

IndexOutOfBoundsException(數組越界)

NullPointerException(空指針異常)

ArrayStoreException(數據存儲異常,操作數組是類型不一致)

Bu?erOver?owException

(2)被檢查異常

定義:Exception類本身,以及Exception的子類中除了"運行時異常"之外的其它子類都屬於被檢查異常。特點 : Java編譯器會檢查它。此類異常,要麼通過throws進行聲明拋出,要麼通過try-catch進行捕獲處理,否則不能通過編譯。例如,CloneNotSupportedException就屬於被檢查異常。當通過clone()接口去克隆一個對象,而該對象對應的類沒有實現Cloneable接口,就會拋出CloneNotSupportedException異常。被檢查異常通常都是可以恢復的。

如:

IOException

FileNotFoundException

SQLException

被檢查的異常適用於那些不是因程序引起的錯誤情況,比如:讀取文件時文件不存在引發的FileNotFoundException 。然而,不被檢查的異常通常都是由於糟糕的編程引起的,比如:在對象引用時沒有確保對象非空而引起的 NullPointerException 。

(3)錯誤

定義 : Error類及其子類。

特點 : 和運行時異常一樣,編譯器也不會對錯誤進行檢查。當資源不足、約束失敗、或是其它程序無法繼續運行的條件發生時,就產生錯誤。程序本身無法修復這些錯誤的。例如,VirtualMachineError就屬於錯誤。出現這種錯誤會導致程序終止運行。OutOfMemoryError、ThreadDeath。Java虛擬機規範規定JVM的內存分爲了好幾塊,比如堆,棧,程序計數器,方法區等

6、Thow與thorws區別

位置不同

1. throws 用在函數上,後面跟的是異常類,可以跟多個;而 throw 用在函數內,後面跟的是異常對象。

功能不同:

2. throws 用來聲明異常,讓調用者只知道該功能可能出現的問題,可以給出預先的處理方式;throw 拋出具體的問題對象,執行到 throw,功能就已經結束了,跳轉到調用者,並將具體的問題對象拋給調用者。也就是說 throw 語句獨立存在時,下面不要定義其他語句,因爲執行不到。

3. throws 表示出現異常的一種可能性,並不一定會發生這些異常;throw 則是拋出了異常,執行 throw 則一定拋出了某種異常對象。

4. 兩者都是消極處理異常的方式,只是拋出或者可能拋出異常,但是不會由函數去處理異常,真正的處理異常由函數的上層調用處理。

7、Error與Exception區別?

Error和Exception都是java錯誤處理機制的一部分,都繼承了Throwable類。Exception表示的異常,異常可以通過程序來捕捉,或者優化程序來避免。Error表示的是系統錯誤,不能通過程序來進行錯誤處理。

8、error和exception有什麼區別

error 表示恢復不是不可能但很困難的情況下的一種嚴重問題。比如說內存溢出。不可能指望程序能處理這樣的情況 exception 表示一種設計或實現問題。也就是說,它表示如果程序運行正常,從不會發生的情況。

Java中的IO與NIO面試題

1、Java 中 IO 流?

Java 中 IO 流分爲幾種?

1. 按照流的流向分,可以分爲輸入流和輸出流;

2. 按照操作單元劃分,可以劃分爲字節流和字符流;

3. 按照流的角色劃分爲節點流和處理流。

Java Io 流共涉及 40 多個類,這些類看上去很雜亂,但實際上很有規則,而且彼此之間存在非常緊密的聯繫, Java I0 流的 40 多個類都是從如下 4 個抽象類基類中派生出來的。

1. InputStream/Reader: 所有的輸入流的基類,前者是字節輸入流,後者是字符輸入流。

2. OutputStream/Writer: 所有輸出流的基類,前者是字節輸出流,後者是字符輸出流。

2、 Java IO與 NIO的區別

NIO即New IO,這個庫是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但實現方式不同,NIO 主要用到的是塊,所以NIO的效率要比IO高很多。在Java API中提供了兩套NIO,一套是針對標準輸入輸出NIO,另一套就是網絡編程NIO。

3、常用io類有那些

4、字節流與字符流的區別

以字節爲單位輸入輸出數據,字節流按照8位傳輸

以字符爲單位輸入輸出數據,字符流按照16位傳輸

5、阻塞 IO 模型

最傳統的一種 IO 模型,即在讀寫數據過程中會發生阻塞現象。當用戶線程發出 IO 請求之後,內核會去查看數據是否就緒,如果沒有就緒就會等待數據就緒,而用戶線程就會處於阻塞狀態,用戶線程交出 CPU。當數據就緒之後,內核會將數據拷貝到用戶線程,並返回結果給用戶線程,用 戶線程才解除 block 狀態。典型的阻塞 IO 模型的例子爲: data = socket.read();如果數據沒有就緒,就會一直阻塞在 read 方法

6、****非****阻塞 IO 模型

當用戶線程發起一個 read 操作後,並不需要等待,而是馬上就得到了一個結果。 如果結果是一個error 時,它就知道數據還沒有準備好,於是它可以再次發送 read 操作。一旦內核中的數據準備好了,並且又再次收到了用戶線程的請求,那麼它馬上就將數據拷貝到了用戶線程,然後返回。所以事實上,在非阻塞 IO 模型中,用戶線程需要不斷地詢問內核數據是否就緒,也就說非阻塞 IO不會交出 CPU,而會一直佔用 CPU。 典型的非阻塞 IO 模型一般如下:

但是對於非阻塞 IO 就有一個非常嚴重的問題, 在 while 循環中需要不斷地去詢問內核數據是否就緒,這樣會導致 CPU 佔用率非常高,因此一般情況下很少使用 while 循環這種方式來讀取數據。

7、多路複用 IO 模型

多路複用 IO 模型是目前使用得比較多的模型。 Java NIO 實際上就是多路複用 IO。在多路複用 IO模型中,會有一個線程不斷去輪詢多個socket 的狀態,只有當 socket 真正有讀寫事件時,才真正調用實際的 IO 讀寫操作。因爲在多路複用 IO 模型中,只需要使用一個線程就可以管理多個socket,系統不需要建立新的進程或者線程,也不必維護這些線程和進程,並且只有在真正有socket 讀寫事件進行時,纔會使用IO 資源,所以它大大減少了資源佔用。在 Java NIO 中,是通過 selector.select()去查詢每個通道是否有到達事件,如果沒有事件,則一直阻塞在那裏,因此這種方式會導致用戶線程的阻塞。多路複用 IO 模式,通過一個線程就可以管理多個 socket,只有當socket 真正有讀寫事件發生纔會佔用資源來進行實際的讀寫操作。因此,多路複用 IO 比較適合連接數比較多的情況。

另外多路複用 IO 爲何比非阻塞 IO 模型的效率高是因爲在非阻塞 IO 中,不斷地詢問 socket 狀態時通過用戶線程去進行的,而在多路複用IO 中,輪詢每個 socket 狀態是內核在進行的,這個效率要比用戶線程要高的多。

不過要注意的是,多路複用 IO 模型是通過輪詢的方式來檢測是否有事件到達,並且對到達的事件逐一進行響應。因此對於多路複用 IO 模型來說, 一旦事件響應體很大,那麼就會導致後續的事件遲遲得不到處理,並且會影響新的事件輪詢。

8、信號驅動 IO 模型

在信號驅動 IO 模型中,當用戶線程發起一個 IO 請求操作,會給對應的 socket 註冊一個信號函數,然後用戶線程會繼續執行,當內核數據就緒時會發送一個信號給用戶線程,用戶線程接收到信號之後,便在信號函數中調用 IO 讀寫操作來進行實際的 IO 請求操作。

9、異步 IO 模型

異步 IO 模型纔是最理想的 IO 模型,在異步 IO 模型中,當用戶線程發起 read 操作之後,立刻就可以開始去做其它的事。而另一方面,從內核的角度,當它受到一個 asynchronous read 之後,它會立刻返回,說明 read 請求已經成功發起了,因此不會對用戶線程產生任何block。然後,內核會等待數據準備完成,然後將數據拷貝到用戶線程,當這一切都完成之後,內核會給用戶線程發送一個信號,告訴它read 操作完成了。也就說用戶線程完全不需要實際的整個 IO 操作是如何進行的, 只需要先發起一個請求,當接收內核返回的成功信號時表示 IO 操作已經完成,可以直接去使用數據了。

也就說在異步 IO 模型中, IO 操作的兩個階段都不會阻塞用戶線程,這兩個階段都是由內核自動完成,然後發送一個信號告知用戶線程操作已完成。用戶線程中不需要再次調用 IO 函數進行具體的讀寫。這點是和信號驅動模型有所不同的,在信號驅動模型中,當用戶線程接收到信號表示數據已經就緒,然後需要用戶線程調用 IO 函數進行實際的讀寫操作;而在異步 IO 模型中,收到信號表示 IO 操作已經完成,不需要再在用戶線程中調用 IO 函數進行實際的讀寫操作。

注意,異步 IO 是需要操作系統的底層支持,在 Java 7 中,提供了 Asynchronous IO。

更多參考: http://www.importnew.com/19816.html

10、JAVA NIO

NIO 主要有三大核心部分: Channel(通道), Buffer(緩衝區), Selector。傳統 IO 基於字節流和字符流進行操作, 而 NIO 基於 Channel 和Buffer(緩衝區)進行操作,數據總是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中。 Selector(選擇區)用於監聽多個通道的事件(比如:連接打開,數據到達)。因此,單個線程可以監聽多個數據通道。 NIO 和傳統 IO 之間第一個最大的區別是, IO 是面向流的, NIO 是面向緩衝區的。

11、NIO 的緩衝區

Java IO 面向流意味着每次從流中讀一個或多個字節,直至讀取所有字節,它們沒有被緩存在任何地方。此外,它不能前後移動流中的數據。如果需要前後移動從流中讀取的數據, 需要先將它緩存到一個緩衝區。 NIO 的緩衝導向方法不同。數據讀取到一個它稍後處理的緩衝區,需要時可在緩衝區中前後移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩衝區中包含所有您需要處理的數據。而且,需確保當更多的數據讀入緩衝區時,不要覆蓋緩衝區裏尚未處理的數據。

12、NIO 的非阻塞

IO 的各種流是阻塞的。這意味着,當一個線程調用 read() 或 write()時,該線程被阻塞,直到有一些數據被讀取,或數據完全寫入。該線程在此期間不能再幹任何事情了。 NIO 的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什麼都不會獲取。而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。 線程通常將非阻塞 IO 的空閒時間用於在其它通道上執行 IO 操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel)。

13、Channel

首先說一下 Channel,國內大多翻譯成“通道”。 Channel 和 IO 中的 Stream(流)是差不多一個等級的。 只不過 Stream 是單向的,譬如:InputStream, OutputStream, 而 Channel 是雙向的,既可以用來進行讀操作,又可以用來進行寫操作。NIO 中的 Channel 的主要實現有:

1. FileChannel

2. DatagramChannel

3. SocketChannel

4. ServerSocketChannel

這裏看名字就可以猜出個所以然來:分別可以對應文件 IO、 UDP 和 TCP(Server 和 Client)。下面演示的案例基本上就是圍繞這 4 個類型的 Channel 進行陳述的。

14、Buffer

Buffer,故名思意, 緩衝區,實際上是一個容器,是一個連續數組。 Channel 提供從文件、網絡讀取數據的渠道,但是讀取或寫入的數據都必須經由 Buffer。

上面的圖描述了從一個客戶端向服務端發送數據,然後服務端接收數據的過程。客戶端發送數據時,必須先將數據存入 Buffer 中,然後將Buffer 中的內容寫入通道。服務端這邊接收數據必須通過 Channel 將數據讀入到 Buffer 中,然後再從 Buffer 中取出數據來處理。

在 NIO 中, Buffer 是一個頂層父類,它是一個抽象類,常用的 Buffer 的子類有:ByteBuffer、 IntBuffer、 CharBuffer、 LongBuffer、DoubleBuffer、 FloatBuffer、ShortBuffer

15、Selector

Selector 類是 NIO 的核心類, Selector 能夠檢測多個註冊的通道上是否有事件發生,如果有事件發生,便獲取事件然後針對每個事件進行相應的響應處理。這樣一來,只是用一個單線程就可以管理多個通道,也就是管理多個連接。這樣使得只有在連接真正有讀寫事件發生時,纔會調用函數來進行讀寫,就大大地減少了系統開銷,並且不必爲每個連接都創建一個線程,不用去維護多個線程,並且避免了多線程之間的上下文切換導致的開銷。

歡迎關注微信公衆號【慕容千語】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章