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