什么是流:IO操作就是流。比如,标准输入输出,读写文件,内存赋值。 字节,字符区别:byte 1个字节,java char is 两个字节. c++ char is 1个字节 应用场景:字符流用于是文本,字节流用于所有场景。 常用字节流:ByteArrayInputStream,ObjectInputStream,FileInputStream, FilterInputStream(BufferedInputStream,DataInputStream)。output同样。 常用字符流:CharArrayReader,BufferedRead,FileReader .writer同样. 转换流:InputStreamReader,OutputStreamWriter. 关键字:Reader/Writer 是字符流,Input/output是字节流 。既有input(output)又有reader(writer)是转化;Buffer是对流的缓冲,增加效率.
字节流导图
注意:有IO buffer一定要用flush。所有IO流和所有文件句柄都要关闭. flush 和close的区分在于,flush之后buffer清空,继续使用;close之后buffer不再能用。 常见用法:BufferedReader in= new BufferedReader(new FileReader("Text.java"));
OutputStreamWriter 字符流转字节流
File f = new File ("D:\\output.txt"); // OutputStreamWriter 是字符流通向字节流的桥梁,创建了一个字符流通向字节流的对象 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(f),"UTF-8"); osw.write("我是字符流转换成字节流输出的");InputStreamReader 字节流转字符流。
File f = new File("D:\\output.txt"); //字节流转成字符流 InputStreamReader inr = new InputStreamReader(new FileInputStream(f),"UTF-8"); char[] buf = new char[1024]; int len = inr.read(buf);notes:转换流和字符流类似,按照字符读写。它是在字节流基础上二次读写流. InputStreamReader(FileInputStream(new file));InputStreamReader只是转存储方式,byte变成char(具体是StreamDecoder 实现) cout<<charbuffer ,应用层才是按照编码方式(unicode表,而不是ascii)读取和识别字符 or 字节.
字节流
try { //System.in is InputStream;System.in提供的 read方法每次只能读取一个字节的数据 //在控制台(console)每次只能输入一个字符,然后System.in按照字节读取 int read = System.in.read(); System.out.println(read);//输出ascii } catch(IOException e){ e.printStackTrace() ; }字符流
char cbuf[] = new char[1024]; //接收键盘录入,需要你在控制台输入数据后按回车键 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方式。后者比前者具有效率高等优点。
ObjectOutputStream的性能相对差,而且不能跨平台.现在常用protobuffer. 各种序列化性能比较https://colobu.com/2014/08/26/java-serializer-comparison/
ByteArrayOutputStream和BufferedOutputStream 非常相似. 那么 ByteArrayOutputStream 和BuffereOutputStream 区别是什么呢? StackOver 对两者区别的解释, 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 常用于内存操作. 常用用法:读写内存(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(); //开辟缓冲区 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 //网络通信 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"); }可以输出所有类型
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字节写入,都是写入的低位 dos.writeChars("世界"); // 按照Unicode写入 // 按照UTF-8写入(UTF8变长,开头2字节是由writeUTF函数写入的长度信息,方便readUTF函数读取) 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")));
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,所以直接读写会产生乱码。 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("我是字符流转换成字节流输出的123"); osw.close();tricky:c++,java都可以一次性读写文件。
因为字节流,1一个字节一个字节处理,编码的方式是ascii,范围是-127-127。比如:“我” 输出是-50,-46. 所以输出汉字只能用字符流 (字符流是双字节处理,加上UTF-8是三字节处理).
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")));
BufferedOutputStreamBufferedInputStream
Java的Stream对象分成字节流和字符流,而且需要自己缓冲. C++的steam对象统一,任何流都可以按字节、字符串、整形的方式读或者写,c++封装了缓冲。
就是在inputstream 之上wrap了一个8k的buffer。如果buffer空了(或者不够),再次调用fill函数将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).
Inputstream不是每次只能读写一个字节.底层实现两者都是本地方法readBytes(byte b[], int off, int len),这个方法底层是可以一次性拷贝多个字节的 BufferedInputStream和inputstream都可以一次读写多个字节。 BufferedInputStream 实现,详见BufferedInputStream 如果用Inputstream读取buffer array的方式,等于自己写了一个buffer 管理类,即BufferedInputStream。 BufferedInputStream还封装了readline,mark,reset 3个功能. stackover: BufferedOutputStream wrapper is mostly used to avoid frequent disk or network writes
inputstream 读写bytes >8k,inputstream和BufferedInputStream 效率差不多。(BufferedInputStream封装写的好一点,效率略高) inputstream 读写bytes <8k,inputstream和BufferedInputStream 效率差很多。 详见 FileInputStream 与 BufferedInputStream 效率对比 inputstream 读写小于8k(比如80bytes),造成多次读写硬盘。BufferedInputStream先放入buffer,累积够了8k再读写一次硬盘,效率高。
Decorator patternjava I/O库中设计模式的应用 具体分3步:1):将被装饰者通过装饰者的构造函数,传递给装饰者。2): 使用传入的被装饰者的属性 3):在2)的基础上加上装饰者的东东,两者合一形成新的结果。 br = BufferedInputStream(fileinputstream f) 将fileinputstream 传入构造函数, br.read base fileinputstream.read 接口基础上,wrap read,即br.read 调用fileinputstream readBytes(byte b[], int off, int len). notes:装饰者模式就是添加东东。
new BufferedInputStream(new InputStreamRead(new inputstream)); 一层一层对stream 添加修饰(即提高流的效率). Bufferinputstream实际作用就是调用了fileinputstream的带长度read,而不是缺省的一个一个read。 这篇文章的实例很说明问题: 学习、探究Java设计模式——装饰者模式 //下面,我们来自己实现自己的JavaIO的装饰者。要实现的功能是:把一段话里面的每个单词的首字母大写。我们先新建一个类:UpperFirstWordInputStream.java
public class UpperFirstWordInputStream extends FilterInputStream { private int cBefore = 32; protected UpperFirstWordInputStream(InputStream in) { //由于FilterInputStream已经保存了装饰对象的引用,这里直接调用super即可 super(in); } public int read() throws IOException{ //根据前一个字符是否是空格来判断是否要大写 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(); } }[适配器模式] (http://www.runoob.com/design-pattern/adapter-pattern.html)一个示例让你明白适配器模式 上面那个link的适配器非常好。 hotel只提供两口插座powerWithTwoRound,如何适配powerWithThreeRound呢? hotel是不能改变的,powerWithThreeRound是不能改变的。中间增加了一个转换器。 SocketAdapter implements DBSocketInterface 接口继承powerWithTwoRound。为了能传入hotel的接口(即构造函数) 实际内部实现不用powerWithTwoRound的实现,改成了powerWithThreeRound的实现。披了一层powerWithThreeRound的class的外衣,把里面的“同名”实现函数的具体内容换了。 这样就实现了调用powerWithThreeRound函数的目的。 表面是调用一个接口,实际执行的是另一个接口的内容。(类似,插座前面是三相的,尾部是二相的。只给外面看3相的接口) notes:适配器就是“旧瓶装新酒”,进去的时候和出去的时候不一样.
上面的适配器模式,是仅仅用了接口,直接调用了另一个接口的实现。这是最简单的adaptor模式。 adaptor模式也可以做内部转换。输入是字节流,经过内部adaptor转换,输出转换成了字符流。InputStreamReader和OutputStreamWriter源码分析StreamDecoder
灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
两者都是base传入的原型,内部处理。 装饰者没有改变原型性质,仅仅是优化。比如对字符流仅仅批处理。 adaptor模式:是改变性质。接口不变。字节流变成了字符流。
底层read/write 不是内部循环写,直到写完为止。是每次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调用几次调用write写