使用场景
按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流两类。
节点流
可以从或向一个特定的地方(节点)读写数据。如 FileReader
- 文件:
FileInputStream
FileOutputStrean
FileReader
FileWriter
文件进行处理的节点流 - 数组:
ByteArrayInputStream
ByteArrayOutputStream
CharArrayReader
CharArrayWriter
对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组) - 字符:
StringReader
StringWriter
对字符串进行处理的节点流 - 管道:
PipedInputStream
PipedOutputStream
PipedReader
PipedWriter
对管道进行处理的节点流
处理流
是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如 BufferedReader
。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
- 缓冲流:
BufferedImputStream
BufferedOutputStream
BufferedReader
BufferedWriter
需要父类作为参数构造,增加缓冲功能,避免频繁读写硬盘,可以初始化缓冲数据的大小,由于带了缓冲功能,所以就写数据的时候需要使用 flush 方法咯 - 转换流:
InputStreamReader
OutputStreamWriter
要InputStream
或OutputStream
作为参数,实现从字节流到字符流的转换 - 数据流:
DataInputStream
DataOutputStream
提供将基础数据类型写入到文件中,或者读取出来,为什么要有这个流呢?看这样的分析,如果没有这种流的话,有一个 long,本身只占 8 个字节,如果我要写入到文件,需要转成字符串,然后在转成字符数组,那空间会占用很多,但是有了这种流之后就很方便了,直接将这 8 个字节写到文件就完了。是不是既节约了内存空间有让程序写起来更加方便简单了呐。写倒是很简单,但是读取的时候就注意了,根据读取的数据类型,指针会往下移,所以你写的顺序必须要和读的顺序一致才能完成你正确的需求
IO流概述和分类
按照数据的流向分为:
输入流:读数据
输出流:写数据
按照数据类型来分:
字节流:字节输入流,字节输出流
字符流:字符输入流,字符输出流
那么这两种流都在什么情况下使用呢?
如果数据通过window自带的记事本软件打开,我们还可以读懂里面的内容,就使用字符流,否则使用字节流。如果你不知道该使用哪种流,就使用字节流。
字节流
字节流写入的方式:
一次写一个字节
int b;
while((b=fis.read())!=-1)
{
fos.write(b);
}
效率太慢,改为字节数组方式,
byte[] bytes=new byte[1024];
int len;
while((len=fis.read(bytes))!=-1)
{
fos.write(bytes,0,len);
}
字节流写入的两个问题:
1、字节流写数据如何实现换行呢?
写完数据后,加换行符:
windows:\r\n
linux:\n
mac:\r
2、字节流写数据如何实现追加写入呢?
public FileOutputStream(String name,boolean append)
创建文件输出流以指定的名称写入文件,如果第二个参数为true,则字节将写入文件的末尾而不是开头。
字节流写数据加异常处理
FileOutStream fos=null;
try{
fos=new FileOutputStream("t.txt");
fos.write("hello".getBytes());
}catch(IOException e){
e.printStackTrace();
}finally{
if(fos!=null){
fos.close();
}
}
字节缓冲流
BufferOutputStream:字节缓冲输出流
BufferInputStream:字节缓冲输入流
构造方法 BufferOutputStream(OutputStream out) BufferInputStream(InputStream in)
字节缓冲流仅仅提供的是缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作
一次读一个字节原理:一次读8192个字节的数据放入缓冲输入流的数组中,然后一个字节一个字节的输出到缓冲输出流的数组中。然后通过write方法将数组中的数据一个字节一个字节写道文件中。
字符流
字符流出现的原因:
由于字节流操作中文不是特别方便,字节流读中文,每次只能读一部分,容易出现乱码,所以java提供字符流
字符流=字节流+编码表
字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行
字节拼接成中文,如何识别中文的呢?汉字在存储时,无论选择哪种编码存储,第一个字节都是负数。
一个汉字存储,如果是gbk编码,占用2个字节
如果是utf-8编码,占用3个字节
写数据方法 写一个字符write(int c), 写出一个字符数组write(char[] buff),写一个字符串 write(String str),写一个字符数组的一部分 write(char[] buff,int off,int len),写字符串的一部分 write(String str,int off,int len)
flush()和close()的区别,flush是刷新流,执行完后还能继续执行写操作。close()执行后也会执行刷新流,但是不能继续执行写操作。
读方法 一次读一个字符
int ch;
while((ch=fr.read())!=-1)
{
System.out.println((char)ch);
}
一次读多个字符
char[] chars=new char[1024];
int len;
while((len=fr.read(chars))!=-1)
{
System.out.println(new String(chars,0,len));
}
编码表
基础知识:计算机中存储的信息都是用二进制数表示的,我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
按照某种规则,把字符存储到计算机中,称之为编码,反之将存储在计算机中的二进制数按照某种规则解析显示出来,称之为解码。这里强调一下:按照A编码存储,必须按照A编码解析出来,这样才能显示正确的文本符号。否则就会导致乱码的出现。字符编码:就是一套自然语言的字符与二进制数之间的对应规则
转换流
InputStreamReader是从字节流到字符流的桥梁,它读取字节,并使用指定的charset将其解码为字符。
OutputStreamWriter是从字符流到字节流的桥梁,使用指定的charset将写入的字符编码为字节。
对象序列化流
对象序列化就是将对象保存到磁盘中,或者在网络中传输对象,这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息。反之,该字节序列还可以从文件中读取出来,重构对象,对他进行反序列化。
对象序列化流:ObjectOutputStream,对象需要实现Serializable,它是一个标记接口,实现该接口不需要重写任何方法。
对象反序列化流:ObjectInputStream
标准输入流 System.in,通常该流对应于键盘输入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in))
写起来太麻烦,于是换SCanner sc=new SCanner(System.in)
标准输出流 System.out,通常该流对应于显示输出