002java面试笔记——【java基础篇】从团800失败面试总结的java面试题
6、java io流
? ? ?1)java io流相關概念
輸出流:
?
輸入流:
因此輸入和輸出都是從程序的角度來說的。
字節流:一次讀入或讀出是8位二進制。
字符流:一次讀入或讀出是16位二進制。
字節流和字符流的原理是相同的,只不過處理的單位不同而已。后綴是Stream是字節流,而后綴是Reader,Writer是字符流。?
節點流:直接與數據源相連,讀入或讀出。
直接使用節點流,讀寫不方便,為了更快的讀寫文件,才有了處理流。
處理流:與節點流一塊使用,在節點流的基礎上,再套接一層,套接在節點流上的就是處理流。
? ? ?2)java io流的分類
按流向分:
輸入流: 程序可以從中讀取數據的流。
輸出流: 程序能向其中寫入數據的流。
按數據傳輸單位分:
字節流: 以字節為單位傳輸數據的流
字符流: 以字符為單位傳輸數據的流
按功能分:
節點流: 用于直接操作目標設備的流
過濾流: 是對一個已存在的流的鏈接和封裝,通過對數據進行處理為程序提供功能強大、靈活的讀寫功能。
? ? ?3)java io類結構圖
? ? ?流是一個很形象的概念,當程序需要讀取數據的時候,就會開啟一個通向數據源的流,這個數據源可以是文件,內存,或是網絡連接。類似的,當程序需要寫入數據的時候,就會開啟一個通向目的地的流。這時候你就可以想象數據好像在這其中“流”動一樣。Java把這些不同來源和目標的數據都統一抽象為數據流,在Java類庫中,IO部分的內容是很龐大的,因為它涉及的領域很廣泛:標準輸入輸出,文件的操作,網絡上的數據流,字符串流,對象流,zip文件流,java io 類結構如下:
[1]輸入字節流InputStream:InputStream 是所有的輸入字節流的父類,它是一個抽象類;ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三種基本的介質流,它們分別從Byte 數組、StringBuffer、和本地文件中讀取數據;PipedInputStream 是從與其它線程共用的管道中讀取數據;ObjectInputStream 和所有FilterInputStream 的子類都是裝飾流(裝飾器模式的主角)。
[2]輸出字節流OutputStream:OutputStream 是所有的輸出字節流的父類,它是一個抽象類。
ByteArrayOutputStream、FileOutputStream 是兩種基本的介質流,它們分別向Byte 數組、和本地文件中寫入數據。PipedOutputStream 是向與其它線程共用的管道中寫入數據,ObjectOutputStream 和所有FilterOutputStream 的子類都是裝飾流。
[3]字節流的輸入與輸出的對應
圖中藍色的為主要的對應部分,紅色的部分就是不對應部分。紫色的虛線部分代表這些流一般要搭配使用。從上面的圖中可以看出Java IO 中的字節流是極其對稱的。
不對稱的類介紹如下:
1>LineNumberInputStream 主要完成從流中讀取數據時,會得到相應的行號,至于什么時候分行、在哪里分行是由改類主動確定的,并不是在原始中有這樣一個行號。在輸出部分沒有對應的部分,我們完全可以自己建立一個LineNumberOutputStream,在最初寫入時會有一個基準的行號,以后每次遇到換行時會在下一行添加一個行號,看起來也是可以的。好像更不入流了。
2>PushbackInputStream 的功能是查看最后一個字節,不滿意就放入緩沖區。主要用在編譯器的語法、詞法分析部分。輸出部分的BufferedOutputStream 幾乎實現相近的功能。
3>StringBufferInputStream 已經被Deprecated,本身就不應該出現在InputStream 部分,主要因為String 應該屬于字符流的范圍。已經被廢棄了,當然輸出部分也沒有必要需要它了!還允許它存在只是為了保持版本的向下兼容而已。
4>SequenceInputStream 可以認為是一個工具類,將兩個或者多個輸入流當成一個輸入流依次讀取。完全可以從IO 包中去除,還完全不影響IO 包的結構,卻讓其更“純潔”――純潔的Decorator 模式。
5>PrintStream 也可以認為是一個輔助工具。主要可以向其他輸出流,或者FileInputStream 寫入數據,本身內部實現還是帶緩沖的。本質上是對其它流的綜合運用的一個工具而已。一樣可以踢出IO 包!System.out 和System.out 就是PrintStream 的實例!
[4]字符輸入流Reader:Reader 是所有的輸入字符流的父類,它是一個抽象類;CharReader、StringReader 是兩種基本的介質流,它們分別將Char 數組、String中讀取數據;PipedReader 是從與其它線程共用的管道中讀取數據;BufferedReader 很明顯就是一個裝飾器,它和其子類負責裝飾其它Reader 對象;FilterReader 是所有自定義具體裝飾流的父類,其子類PushbackReader 對Reader 對象進行裝飾,會增加一個行號;InputStreamReader 是一個連接字節流和字符流的橋梁,它將字節流轉變為字符流。FileReader 可以說是一個達到此功能、常用的工具類,在其源代碼中明顯使用了將FileInputStream 轉變為Reader 的方法。我們可以從這個類中得到一定的技巧。Reader 中各個類的用途和使用方法基本和InputStream 中的類使用一致。
[5]字符輸出流Writer:Writer 是所有的輸出字符流的父類,它是一個抽象類;CharArrayWriter、StringWriter 是兩種基本的介質流,它們分別向Char 數組、String 中寫入數據。PipedWriter 是向與其它線程共用的管道中寫入數據,BufferedWriter 是一個裝飾器為Writer 提供緩沖功能;PrintWriter 和PrintStream 極其類似,功能和使用也非常相似;
OutputStreamWriter 是OutputStream 到Writer 轉換的橋梁,它的子類FileWriter 其實就是一個實現此功能的具體類(具體可以研究一SourceCode)。
[6]字符流的輸入與輸出的對應
[8]字符流與字節流轉換
轉換流的特點:
其是字符流和字節流之間的橋梁
可對讀取到的字節數據經過指定編碼轉換成字符
可對讀取到的字符數據經過指定編碼轉換成字節
何時使用轉換流?
當字節和字符之間有轉換動作時;
流操作的數據需要編碼或解碼時。
具體的對象體現:
InputStreamReader:字節到字符的橋梁
OutputStreamWriter:字符到字節的橋梁
這兩個流對象是字符體系中的成員,它們有轉換作用,本身又是字符流,所以在構造的時候需要傳入字節流對象進來。
? ? ?4)java 管道通信
? ? ?Java提供管道功能,實現管道通信的類有兩組:PipedInputStream和PipedOutputStream或者是PipedReader和PipedWriter。管道通信主要用于不同線程間的通信。
一個PipedInputStream實例對象必須和一個PipedOutputStream實例對象進行連接而產生一個通信管道。PipedOutputStream向管道中寫入數據,PipedIntputStream讀取PipedOutputStream向管道中寫入的數據。一個線程的PipedInputStream對象能夠從另外一個線程的PipedOutputStream對象中讀取數據。
//Sender類 package pipeCommu; import java.io.PipedInputStream; import java.io.PipedOutputStream; public class Sender extendsThread{private PipedOutputStream out=new PipedOutputStream();//發送者創建PipedOutputStream向外寫數據; public PipedOutputStream getOut(){return out;}public void run(){String strInfo="hello,receiver";try{out.write(strInfo.getBytes());//寫入數據out.close();}catch(Exception e){e.printStackTrace();}} } //Reader類,負責接收數據 package pipeCommu; import java.io.PipedInputStream; public class Reader extends Thread{private PipedInputStream in=new PipedInputStream();//發送者創建PipedOutputStream向外寫數據public PipedInputStream getIn(){returnin;}public void run(){byte[] buf=new byte[1024];//聲明字節數組try{int len=in.read(buf);//讀取數據,并返回實際讀到的字節數System.out.println("receive from sender:"+newString(buf,0,len));in.close();}catch(Exception e){e.printStackTrace();}} } package pipeCommu; import java.io.*; public class PipedStream {public static void main(String[] args) throws Exception{Sender send=new Sender();Reader read=new Reader();PipedOutputStream out=send.getOut();PipedInputStream in=read.getIn();out.connect(in);//或者也可以用in.connect(out);send.start();read.start();} } package pipeCommu; import java.io.*; public class PipedCommu {public static void main(String[] args) {// TODOAuto-generatedmethodstubtry{PipedReader reader=new PipedReader();PipedWriter writer=new PipedWriter(reader);Thread a=new Send(writer);Thread b=new Read(reader);a.start();Thread.sleep(1000);b.start();}catch(IOException e){e.printStackTrace();}catch(InterruptedException e1){e1.printStackTrace(); }} }class Send extends Thread{PipedWriter writer;public Send(PipedWriter writer){this.writer=writer;}public void run(){try{writer.write("this is a.hello world".toCharArray());writer.close();}catch(IOException e){e.printStackTrace();} }}class Read extends Thread{PipedReader reader;public Read(PipedReader reader){this.reader=reader;}public void run(){System.out.println("this is B");try{char[] buf=new char[1000];reader.read(buf,0,100);System.out.println(new String(buf));}catch(Exception e){e.printStackTrace();}}}
? ? ?5)java 對象序列化
? ? ?對于一個存在Java虛擬機中的對象來說,其內部的狀態只是保存在內存中。JVM退出之后,內存資源也就被釋放,Java對象的內部狀態也就丟失了。而在很多情況下,對象內部狀態是需要被持久化的,將運行中的對象狀態保存下來(最直接的方式就是保存到文件系統中),在需要的時候可以還原,即使是在Java虛擬機退出的情況下。?
? ? ?對象序列化機制是Java內建的一種對象持久化方式,可以很容易實現在JVM中的活動對象與字節數組(流)之間進行轉換,使用得Java對象可以被存儲,可以被網絡傳輸,在網絡的一端將對象序列化成字節流,經過網絡傳輸到網絡的另一端,可以從字節流重新還原為Java虛擬機中的運行狀態中的對象。?
? ? ?對于任何需要被序列化的對象,都必須要實現接口Serializable,它只是一個標識接口,本身沒有任何成員,只是用來標識說明當前的實現類的對象可以被序列化。
? ? ?如果在類中的一些屬性,希望在對象序列化過程中不被序列化,使用關鍵字transient標注修飾就可以。當對象被序列化時,標注為transient的成員屬性將會自動跳過。
? ? ?注:
? ? ??[1].當一個對象被序列化時,只保存對象的非靜態成員變量,不能保存任何的成員方法,靜態的成員變量和transient標注的成員變量。?
?[2].如果一個對象的成員變量是一個對象,那么這個對象的數據成員也會被保存還原,而且會是遞歸的方式。
?[3].如果一個可序列化的對象包含對某個不可序列化的對象的引用,那么整個序列化操作將會失敗,并且會拋出一個NotSerializableException。可以將這個引用標記transient,那么對象仍然可以序列化。
? ? ? ?java對象序列化示例代碼:
實體類:
class Student implements Serializable{ private String name; private transient int age; private Course course; public void setCourse(Course course){ this.course = course; } public Course getCourse(){ return course; } public Student(String name, int age){ this.name = name; this.age = age; } public String toString(){ return "Student Object name:"+this.name+" age:"+this.age; } } class Course implements Serializable{ private static String courseName; private int credit; public Course(String courseName, int credit){ this.courseName = courseName; this.credit = credit; } public String toString(){ return "Course Object courseName:"+courseName +" credit:"+credit; } } 將對象寫入文件序列化
public class TestWriteObject{ public static void main(String[] args) { String filePath = "C://obj.txt"; ObjectOutputStream objOutput = null; Course c1 = new Course("C language", 3); Course c2 = new Course("OS", 4); Student s1 = new Student("king", 25); s1.setCourse(c1); Student s2 = new Student("jason", 23); s2.setCourse(c2); try { objOutput = new ObjectOutputStream(new FileOutputStream(filePath)); objOutput.writeObject(s1); objOutput.writeObject(s2); objOutput.writeInt(123); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ try { objOutput.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("Info:對象被寫入"+filePath); } 從文件中讀取對象,反序列化
public class TestReadObject { public static void main(String[] args) { String filePath = "C://obj.txt"; ObjectInputStream objInput = null; Student s1 = null,s2 = null; int intVal = 0; try { objInput = new ObjectInputStream(new FileInputStream(filePath)); s1 = (Student)objInput.readObject(); s2 = (Student)objInput.readObject(); intVal = objInput.readInt(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }catch(ClassNotFoundException cnfe){ cnfe.printStackTrace(); }finally{ try { objInput.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("Info:文件"+filePath+"中讀取對象"); System.out.println(s1); System.out.println(s1.getCourse()); System.out.println(s2); System.out.println(s2.getCourse()); System.out.println(intVal); } }
? ? ?7、java ?nio
? ? 1)java nio簡介
? ? ?nio 是 java New IO 的簡稱,在 jdk1.4 里提供的新 api 。 Sun 官方標榜的特性如有:為所有的原始類型提供 (Buffer) 緩存支持;字符集編碼解碼解決方案;Channel :一個新的原始 I/O 抽象;支持鎖和內存映射文件的文件訪問接口;提供多路 (non-bloking) 非阻塞式的高伸縮性網絡 I/O 。
? ? 2)java nio非阻塞原理
? ? ?一個常見的網絡 IO 通訊流程如下 :
? ? ?從該網絡通訊過程來理解一下何為阻塞 :在以上過程中若連接還沒到來,那么 accept 會阻塞 , 程序運行到這里不得不掛起, CPU 轉而執行其他線程。在以上過程中若數據還沒準備好, read 會一樣也會阻塞。阻塞式網絡 IO 的特點:多線程處理多個連接。每個線程擁有自己的棧空間并且占用一些 CPU 時間。每個線程遇到外部未準備好的時候,都會阻塞掉。阻塞的結果就是會帶來大量的進程上下文切換。且大部分進程上下文切換可能是無意義的。比如假設一個線程監聽一個端口,一天只會有幾次請求進來,但是該 cpu 不得不為該線程不斷做上下文切換嘗試,大部分的切換以阻塞告終。
? ? ?何為非阻塞?
? ? ?下面有個隱喻:
? ? ? 一輛從 A 開往 B 的公共汽車上,路上有很多點可能會有人下車。司機不知道哪些點會有哪些人會下車,對于需要下車的人,如何處理更好?
? ? ? 1. 司機過程中定時詢問每個乘客是否到達目的地,若有人說到了,那么司機停車,乘客下車。 ( 類似阻塞式 )
? ? ? 2. 每個人告訴售票員自己的目的地,然后睡覺,司機只和售票員交互,到了某個點由售票員通知乘客下車。 ( 類似非阻塞 )
? ? ?很顯然,每個人要到達某個目的地可以認為是一個線程,司機可以認為是 CPU 。在阻塞式里面,每個線程需要不斷的輪詢,上下文切換,以達到找到目的地的結果。而在非阻塞方式里,每個乘客 ( 線程 ) 都在睡覺 ( 休眠 ) ,只在真正外部環境準備好了才喚醒,這樣的喚醒肯定不會阻塞。
? ? ?非阻塞的原理
? ? ?把整個過程切換成小的任務,通過任務間協作完成。由一個專門的線程來處理所有的 IO 事件,并負責分發。事件驅動機制:事件到的時候觸發,而不是同步的去監視事件。線程通訊:線程之間通過 wait,notify 等方式通訊。保證每次上下文切換都是有意義的。減少無謂的進程切換。
? ? ?以下是異步 IO 的結構:
? ??Reactor 就是上面隱喻的售票員角色。每個線程的處理流程大概都是讀取數據、解碼、計算處理、編碼、發送響應。
? ? 異步 IO 核心 API:
? ? Selector:異步 IO 的核心類,它能檢測一個或多個通道 (channel) 上的事件,并將事件分發出去。使用一個 select 線程就能監聽多個通道上的事件,并基于事件驅動觸發相應的響應。而不需要為每個 channel 去分配一個線程。
? ? SelectionKey:包含了事件的狀態信息和時間對應的通道的綁定。
? ? 3)Buffer結構、主要方法
? ? ?Buffer內部結構如圖:
? ??一個 buffer 主要由 position,limit,capacity 三個變量來控制讀寫的過程。此三個變量的含義見如下表格:? ??
| 參數 | 寫模式???? | 讀模式 |
| position | 當前寫入的單位數據數量。 | 當前讀取的單位數據位置。 |
| limit | 代表最多能寫多少單位數據和容量是一樣的。 | 代表最多能讀多少單位數據,和之前寫入的單位數據量一致。 |
| capacity | buffer?容量 | buffer?容量 |
? ? flip(): 寫模式轉換成讀模式
? ? rewind() :將 position 重置為 0 ,一般用于重復讀。
? ? clear() :清空 buffer ,準備再次被寫入 (position 變成 0 , limit 變成 capacity) 。
? ? compact(): 將未讀取的數據拷貝到 buffer 的頭部位。
? ? mark() 、 reset():mark 可以標記一個位置, reset 可以重置到該位置。
? ? Buffer 常見類型: ByteBuffer 、 MappedByteBuffer 、 CharBuffer 、 DoubleBuffer 、 FloatBuffer 、 IntBuffer 、 LongBuffer 、ShortBuffer 。
? ? channel 常見類型 :FileChannel 、 DatagramChannel(UDP) 、 SocketChannel(TCP) 、 ServerSocketChannel(TCP)
? ? ?4)Buffer、Chanel
? ? ?Channel 和 buffer 是 NIO 是兩個最基本的數據類型抽象。Buffer:是一塊連續的內存塊,是 NIO 數據讀或寫的中轉地;Channel:數據的源頭或者數據的目的地,用于向 buffer 提供數據或者讀取 buffer 數據,buffer 對象的唯一接口,支持異步 I/O 。chanel與Buffer關系如下:
? ? Buffer、Chanel使用例子:CopyFile.java
package sample; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class CopyFile { public static void main(String[] args) throws Exception { String infile = "C:\\copy.sql"; String outfile = "C:\\copy.txt"; // 獲取源文件和目標文件的輸入輸出流 FileInputStream fin = new FileInputStream(infile); FileOutputStream fout = new FileOutputStream(outfile); // 獲取輸入輸出通道 FileChannel fcin = fin.getChannel(); FileChannel fcout = fout.getChannel(); // 創建緩沖區 ByteBuffer buffer = ByteBuffer.allocate(1024); while (true) { // clear方法重設緩沖區,使它可以接受讀入的數據 buffer.clear(); // 從輸入通道中將數據讀到緩沖區 int r = fcin.read(buffer); // read方法返回讀取的字節數,可能為零,如果該通道已到達流的末尾,則返回-1 if (r == -1) { break; } // flip方法讓緩沖區可以將新讀入的數據寫入另一個通道 buffer.flip(); // 從輸出通道中將數據寫入緩沖區 fcout.write(buffer); } } } ? ? ?5)nio.charset
? ? ?字符編碼解碼 : 字節碼本身只是一些數字,放到正確的上下文中被正確被解析。向 ByteBuffer 中存放數據時需要考慮字符集的編碼方式,讀取展示 ByteBuffer 數據時涉及對字符集解碼。Java.nio.charset 提供了編碼解碼一套解決方案。
? ? ?以我們最常見的 http 請求為例,在請求的時候必須對請求進行正確的編碼。在得到響應時必須對響應進行正確的解碼。
? ? ?以下代碼向 baidu 發一次請求,并獲取結果進行顯示。例子演示到了 charset 的使用。
注:本文java io流部分內容參考博客:《java io流分析整理》http://blog.csdn.net/llhhyy1989/article/details/7388059、《java Io流學習總結》http://blog.csdn.net/heliteng/article/details/12812715、《java Io簡介》http://sishuok.com/forum/blogPost/list/468.html、《java 對象序列化》http://yuyiming.iteye.com/blog/1277089
總結
以上是生活随笔為你收集整理的002java面试笔记——【java基础篇】从团800失败面试总结的java面试题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: glassfish插件_Maven嵌入式
- 下一篇: [Jetson TX2] NVIDIA