1 Java 字節(jié)流(byte)悠就,字符流(char,string)區(qū)別?
什么是流:IO操作就是流蚤吹。比如例诀,標準輸入輸出,讀寫文件裁着,內(nèi)存賦值繁涂。
字節(jié),字符區(qū)別:byte 1個字節(jié)二驰,java char is 兩個字節(jié). c++ char is 1個字節(jié)
應用場景:字符流用于是文本扔罪,字節(jié)流用于所有場景。
常用字節(jié)流:ByteArrayInputStream,ObjectInputStream,FileInputStream,
FilterInputStream(BufferedInputStream,DataInputStream)桶雀。output同樣矿酵。
常用字符流:BufferedRead,FileReader. Writer同樣.
轉換流:InputStreamReader,OutputStreamWriter.
關鍵字:Reader/Writer 是字符流,Input/output是字節(jié)流 矗积。既有input(output)又有reader(writer)是轉化全肮;Buffer是對流的緩沖,增加效率.
2 Java IO 導圖
3 Java 各種場景使用實例 (讀String,Socket棘捣,讀文件辜腺,標準IO)
3.1 文件
3.1.1 字節(jié)流(字節(jié)緩沖流)
public static void copyFile(File sourceFile, File targetFile)
throws IOException {
// 新建文件輸入流并對它進行緩沖
FileInputStream input = new FileInputStream(sourceFile);
//注意這僅僅是打開流,不是讀寫流
BufferedInputStream inBuff = new BufferedInputStream(input);
// 新建文件輸出流并對它進行緩沖
FileOutputStream output = new FileOutputStream(targetFile);
BufferedOutputStream outBuff = new BufferedOutputStream(output);
// 緩沖數(shù)組
byte[] b = new byte[1024 * 5];
int len;
while ((len = inBuff.read(b)) != -1) {
outBuff.write(b, 0, len);
}
// 刷新此緩沖的輸出流
outBuff.flush();
//關閉流乍恐;輸入流和輸出流都需要close评疗。注意順序,先開的最后close
inBuff.close();
outBuff.close();
output.close();
input.close();
}
notes: //注意下面這句僅僅是打開流禁熏,不是讀寫流
BufferedInputStream inBuff = new BufferedInputStream(input);
3.1.2 字符流
public class FileReaderDemo {
public static void main(String[] args) throws IOException {
// 定義源文件
File file = new File("E:\\test.txt");
Reader reader = new FileReader(file);
// 獲取文件名
String fileName = file.getName();
// 定義寫文件路徑
String aimPath = fileName+".out";
Writer writer = new FileWriter(aimPath);
// 定義字符數(shù)組壤巷,每次一個數(shù)組一個數(shù)組讀
char[] chars = new char[1024];
while (reader.read(chars) != -1) {
writer.write(chars);
}
//每次一個char一個char讀寫
// char[] c=new char[1024];
// int temp=0 ,len=0;
// while((temp=input.read())!=-1){
// c[len]=(char) temp;
// len++;
// }
writer.flush();
writer.close();
reader.close();
}
注意:有IO buffer一定要用flush。所有IO流和所有文件句柄都要關閉.
flush 和close的區(qū)分在于瞧毙,flush之后buffer清空胧华,繼續(xù)使用寄症;close之后buffer不再能用。
常見用法:BufferedReader in= new BufferedReader(new FileReader("Text.java"));
3.2 轉換流
OutputStreamWriter 字符流轉字節(jié)流
File f = new File ("D:\\output.txt");
// OutputStreamWriter 是字符流通向字節(jié)流的橋梁,創(chuàng)建了一個字符流通向字節(jié)流的對象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(f),"UTF-8");
osw.write("我是字符流轉換成字節(jié)流輸出的");
InputStreamReader 字節(jié)流轉字符流矩动。
File f = new File("D:\\output.txt");
//字節(jié)流轉成字符流
InputStreamReader inr = new InputStreamReader(new FileInputStream(f),"UTF-8");
char[] buf = new char[1024];
int len = inr.read(buf);
notes:轉換流和字符流類似有巧,按照字符讀寫。它是在字節(jié)流基礎上二次讀寫流.
InputStreamReader(FileInputStream(new file));InputStreamReader只是轉存儲方式悲没,byte變成char(具體是StreamDecoder 實現(xiàn))
cout<<charbuffer ,應用層才是按照編碼方式(unicode表篮迎,而不是ascii)讀取和識別字符
or 字節(jié).
3.3 標準IO
字節(jié)流
try {
//System.in is InputStream;System.in提供的 read方法每次只能讀取一個字節(jié)的數(shù)據(jù)
//在控制臺(console)每次只能輸入一個字符,然后System.in按照字節(jié)讀取
int read = System.in.read();
System.out.println(read);//輸出ascii
} catch(IOException e){
e.printStackTrace() ;
}
字符流
char cbuf[] = new char[1024];
//接收鍵盤錄入,需要你在控制臺輸入數(shù)據(jù)后按回車鍵
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
int a = read.read(cbuf);
System.out.println(cbuf);
常見用法:BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
notes:scanner class也可以用于read 標準IO和file,但是通常使用BufferReader方式示姿。后者比前者具有效率高等優(yōu)點甜橱。
3.4 讀寫socket
Socket client = new Socket(host, port);
//socket和system.in一樣當成字節(jié)流. 先轉成字符流讀寫
Writer writer = new OutputStreamWriter(client.getOutputStream());
writer.write("Hello From Client");
3.5 序列化和反序列化ObjectOutputStream
objectwriter=new ObjectOutputStream(new FileOutputStream("C:/student.txt"));
objectwriter.writeObject(new Student("gg", 22));
class Student implements Serializable{
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
ObjectOutputStream的性能相對差,而且不能跨平臺.現(xiàn)在常用protobuffer.
各種序列化性能比較
https://colobu.com/2014/08/26/java-serializer-comparison/
3.6 ByteArrayOutputStream
ByteArrayOutputStream和BufferedOutputStream 非常相似.
那么 ByteArrayOutputStream 和BuffereOutputStream 區(qū)別是什么呢栈戳?
StackOver 對兩者區(qū)別的解釋岂傲,
Generally BufferedOutputStream wrapper is mostly used to avoid frequent disk or network writes. It can be much more expensive to separately write a lot of small pieces than make several rather large operations. The ByteArrayOutputStream operates in memory, so I think the wrapping is pointless.
BufferedInputStream 那些文件,socket操作子檀,ByteArrayOutputStream也能做镊掖。但是沒有BufferedInputStream好用,所以通常不用。ByteArrayOutputStream 常用于內(nèi)存操作.
常用用法:讀寫內(nèi)存(string)
eg1:
public static void main(String[] args) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
String name = "xxy";
int age = 84;
dout.writeUTF(name);
dout.writeInt(age);
byte[] buff = bout.toByteArray();
//開辟緩沖區(qū)
ByteArrayInputStream bin = new ByteArrayInputStream(buff);
DataInputStream dis = new DataInputStream(bin);
String newName = dis.readUTF();
int newAge = dis.readInt();
System.out.println(newName + ":" + newAge);
}
eg2: ByteArrayOutputStream //網(wǎng)絡通信
public static void main(String[] args) throws IOException {
private OutputStream toAgent = null;
toAgent = localSocket.getOutputStream();
ByteArrayOutputStream msgByteStream = new ByteArrayOutputStream();
//先寫buffer褂痰,然后寫socket
msgByteStream.write("hello world");
msgByteStream.writeTo(toAgent);
//如果換成BufferedOutputStream
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(toAgent);
bufferedOutputStream.write("hello world");
}
3.7 "格式化"輸入/輸出DataOutputStream/DataInputStream
可以輸出所有類型
dos = new DataOutputStream(new FileOutputStream("d://dataTest.txt"));
dos.writeInt(18888);
dos.writeByte(123);
dos.writeFloat(1.344f);
dos.writeBoolean(true);
dos.writeChar(49);
dos.writeBytes("世界"); //按2字節(jié)寫入亩进,都是寫入的低位
dos.writeChars("世界"); // 按照Unicode寫入
// 按照UTF-8寫入(UTF8變長,開頭2字節(jié)是由writeUTF函數(shù)寫入的長度信息缩歪,方便readUTF函數(shù)讀取)
dos.writeUTF("世界");
常見用法:
DataInputStream in=new DataInputStream(new ByteArrayInputStream(str.getBytes()));
DataInputStream in=new DataInputStream(new BufferedInputStream(new FileInputStream("Data.txt")));
DataOutputStream dos= new DataOutputStream(new BufferedOutputStream(new FileOutputStream("Data.txt")));
3.8 Java,c++ 讀寫中文字符 (兩種方法writer和DataOutputStream)(漢字需要指定UTF-8讀寫)
3.8.1 c++直接讀寫归薛,java轉成Reader/Writer+UTF-8 讀寫
c++
// 以寫模式打開文件
string love_cpp = "我愛你中國123";
ofstream outfile;
outfile.open("afile.dat");
outfile << love_cpp.c_str() << endl;
outfile.close();
Java
FileWrite和OutputStreamWriter
兩者效果相同。但是FileWriter默認編碼不是UTF-8匪蝙,所以直接讀寫會產(chǎn)生亂碼苟翻。
FileWriter fw=new FileWriter(file); fw.write(..) ; 錯誤
FileWriter fw=new FileWriter(file)骗污; fw.write(..,"UTF-8") ; 正確
//讀取漢字需要添加編碼類型
OutputStreamWriter osw = new OutputStreamWriter(new
FileOutputStream(f),"UTF-8");
osw.write("我是字符流轉換成字節(jié)流輸出的123");
osw.close();
tricky:c++崇猫,java都可以一次性讀寫文件。
3.8.2 為什么字節(jié)流不能輸出漢字需忿?
輸入比較簡單诅炉,讀取流不需要考慮格式。printf屬于應用層屋厘,所以需要考慮漢字涕烧,英文,數(shù)字的編碼格式汗洒,按照編碼方式輸出才能顯示對應的漢字or英文.如果輸出編碼方式不對议纯,顯示亂碼. 字節(jié)流,1一個字節(jié)一個字節(jié)處理溢谤,編碼的方式是ascii瞻凤,范圍是-127-127憨攒。比如:“我” 輸出是-50,-46.所以輸出漢字只能用字符流 (字符流是雙字節(jié)處理阀参,加上UTF-8是三字節(jié)處理).
3.9 其他常見用法
PrintWriter pw=new PrintWriter(new BufferedWriter("text.out"));
PrintWriter pw=new PrintWriter(System.out,true);
PrintStream ps= new PrintStream(new BufferedOutputStream(new FileOutputStream("text.out")));
3.10 Examples link
BufferedOutputStream
BufferedInputStream
4 Java 和 c++ 流區(qū)別肝集?
Java的Stream對象分成字節(jié)流和字符流,沒有自適應性(英文or中文),字節(jié)流換成字符流才能讀出漢字蛛壳,同時英文or數(shù)字也按照兩個byte讀取. 而且需要自己緩沖.
C++的steam對象統(tǒng)一,任何流都可以按字節(jié)杏瞻、字符串、或者整形的方式讀或者寫,c++封裝了緩沖衙荐。
5 BufferedInputStream 底層實現(xiàn)和應用場景
5.1 BufferedInputStream 源碼實現(xiàn)
就是在inputstream 之上wrap了一個8k的buffer捞挥。如果buffer空了(或者不夠),再次調(diào)用fill函數(shù)將buffer讀滿忧吟。stackover的解釋:For example, your file is 32768 bytes long.
To get all the bytes in memory with a FileInputStream, you will require 32768 native calls to the OS.
With a BufferedInputStream, you will only require 4, regardless of the number of read() calls you will do (still 32768).
5.2 BufferedInputStream和 inputstream
Inputstream不是每次只能讀寫一個字節(jié).底層實現(xiàn)兩者都是本地方法readBytes(byte b[], int off, int len)树肃,這個方法底層是可以一次性拷貝多個字節(jié)的
BufferedInputStream和inputstream都可以一次讀寫多個字節(jié)。
BufferedInputStream 實現(xiàn)瀑罗,詳見BufferedInputStream
如果用Inputstream讀取buffer array的方式,等于自己寫了一個buffer 管理類雏掠,即BufferedInputStream斩祭。
BufferedInputStream還封裝了readline,mark乡话,reset 3個功能.
stackover: BufferedOutputStream wrapper is mostly used to avoid frequent disk or network writes
5.3 buffer >8k,buffer <8k
inputstream 讀寫bytes >8k,inputstream和BufferedInputStream 效率差不多摧玫。(BufferedInputStream封裝寫的好一點,效率略高)
inputstream 讀寫bytes <8k,inputstream和BufferedInputStream 效率差很多绑青。
詳見 FileInputStream 與 BufferedInputStream 效率對比
inputstream 讀寫小于8k(比如80bytes)诬像,造成多次讀寫硬盤。BufferedInputStream先放入buffer闸婴,累積夠了8k再讀寫一次硬盤坏挠,效率高。
6 裝飾者模式與io關系
6.1 什么是裝飾者模式
Decorator pattern
java I/O庫中設計模式的應用
具體分3步:1):將被裝飾者通過裝飾者的構造函數(shù)邪乍,傳遞給裝飾者降狠。2): 使用傳入的被裝飾者的屬性 3):在2)的基礎上加上裝飾者的東東,兩者合一形成新的結果庇楞。
br = BufferedInputStream(fileinputstream f) 將fileinputstream 傳入構造函數(shù)榜配,
br.read base fileinputstream.read 接口基礎上,wrap read吕晌,即br.read 調(diào)用fileinputstream readBytes(byte b[], int off, int len).
notes:裝飾者模式就是添加東東蛋褥。
6.2 裝飾者模式與io關系
new BufferedInputStream(new InputStreamRead(new inputstream)); 一層一層對stream 添加修飾(即提高流的效率).
Bufferinputstream實際作用就是調(diào)用了fileinputstream的帶長度read,而不是缺省的一個一個read睛驳。
這篇文章的實例很說明問題: 學習烙心、探究Java設計模式——裝飾者模式
//下面膜廊,我們來自己實現(xiàn)自己的JavaIO的裝飾者。要實現(xiàn)的功能是:把一段話里面的每個單詞的首字母大寫弃理。我們先新建一個類:UpperFirstWordInputStream.java
public class UpperFirstWordInputStream extends FilterInputStream {
private int cBefore = 32;
protected UpperFirstWordInputStream(InputStream in) {
//由于FilterInputStream已經(jīng)保存了裝飾對象的引用溃论,這里直接調(diào)用super即可
super(in);
}
public int read() throws IOException{
//根據(jù)前一個字符是否是空格來判斷是否要大寫
int c = super.read();
if(cBefore == 32)
{
cBefore = c;
return (c == -1 ? c: Character.toUpperCase((char) c));
}else{
cBefore = c;
return c;
}
}
}
//接著編寫一個測試類:InputTest.java
public class InputTest {
public static void main(String[] args) throws IOException {
int c;
StringBuffer sb = new StringBuffer();
try {
//這里用了兩個裝飾者,分別是BufferedInputStream和我們的UpperFirstWordInputStream
InputStream in = new UpperFirstWordInputStream(new BufferedInputStream(new FileInputStream("test.txt")));
while((c = in.read()) >= 0)
{
sb.append((char) c);
}
System.out.println(sb);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
7 適配器模式與IO
7.1 什么是適配器模式
[適配器模式]
(http://www.runoob.com/design-pattern/adapter-pattern.html)
一個示例讓你明白適配器模式
上面那個link的適配器非常好痘昌。
hotel只提供兩口插座powerWithTwoRound钥勋,如何適配powerWithThreeRound呢?
hotel是不能改變的辆苔,powerWithThreeRound是不能改變的算灸。中間增加了一個轉換器。
SocketAdapter implements DBSocketInterface 接口繼承powerWithTwoRound驻啤。為了能傳入hotel的接口(即構造函數(shù))
實際內(nèi)部實現(xiàn)不用powerWithTwoRound的實現(xiàn)菲驴,改成了powerWithThreeRound的實現(xiàn)。披了一層powerWithThreeRound的class的外衣骑冗,把里面的“同名”實現(xiàn)函數(shù)的具體內(nèi)容換了赊瞬。
這樣就實現(xiàn)了調(diào)用powerWithThreeRound函數(shù)的目的。
表面是調(diào)用一個接口贼涩,實際執(zhí)行的是另一個接口的內(nèi)容巧涧。(類似,插座前面是三相的遥倦,尾部是二相的谤绳。只給外面看3相的接口)
notes:適配器就是“舊瓶裝新酒”,進去的時候和出去的時候不一樣.
7.2 適配器模式與java IO
上面的適配器模式袒哥,是僅僅用了接口缩筛,直接調(diào)用了另一個接口的實現(xiàn)。這是最簡單的adaptor模式堡称。
adaptor模式也可以做內(nèi)部轉換瞎抛。輸入是字節(jié)流,經(jīng)過內(nèi)部adaptor轉換却紧,輸出轉換成了字符流婿失。
InputStreamReader和OutputStreamWriter源碼分析
StreamDecoder
7.3 適配器模式優(yōu)點:接口不變
靈活性和擴展性都非常好,通過使用配置文件啄寡,可以很方便地更換適配器豪硅,也可以在不修改原有代碼的基礎上增加新的適配器類,完全符合“開閉原則”挺物。
8 裝飾者模式和適配器模式的區(qū)別
兩者都是base傳入的原型懒浮,內(nèi)部處理。
裝飾者沒有改變原型性質,僅僅是優(yōu)化砚著。比如對字符流僅僅批處理次伶。
adaptor模式:是改變性質。接口不變稽穆。字節(jié)流變成了字符流冠王。
備注1:c,c++ 讀寫文件和漢字
c 讀寫文件(eg: copy 文件)
pf1 = fopen("1.mp3", "rb")
while(fread(buf,1,256,pf1), !feof(pf1))
{
fwrite(buf,1,256,pf2);
}
c++ 讀寫文件
fstream fin("1.mp3",ios::in|ios::binary);
fout<<fin.rdbuf();
https://blog.csdn.net/bookish_2010_prj/article/details/5454771
備注2:c++ 缺省采用系統(tǒng)自動緩沖舌镶。自定義緩沖使用setbuf柱彻。
備注3:Java Read/Write實現(xiàn)
底層read/write 不是內(nèi)部循環(huán)寫,直到寫完為止。是每次read/write不能超過IO buffer(通常4k). 超過IO buffer餐胀,write會寫錯,write return -1.
while((n = read(infd, buf, 1024)) > 0 ){
write(outfd, buf, n);
}
所以BufferedInputStream 8k的buffer哟楷,一定要while調(diào)用幾次調(diào)用write寫.