流分类
在java.io 包中,操作文件内容的主要有两大类:字节流、字符流,两类都分为输入和输出操作。
- 字节流:InputStream(字节输入流)、OutputStream(字节输出流);
- 字符流:Reader(字符输入流)、Writer(字符输出流);
字节流理解配图
字符流理解配图
字节流与字符流的区别
字节与字符的区别
如果要明白字节流与字符流的区别,首先需要明白字节和字符的区别
- 字节byte
它是计算机的数据存储单元,每个字节包括8个二进制“位-bit”,可以保存8位的二进制数。
由于受到电子设备的物理特定限制,例如磁性存储器只适合保存两种状态的信息—磁性的强弱、以电信号的方式也只适合传递两种状态的信息—电压的高低。
因此在计算机中最终结果时再将转换为人们熟悉的格式,如十进制数值、字符和图形等。
- 字符集Charset
为了实现对字符信息的存储,人们将可能用到的字符排成一个有序的字符队列,这种由多个有序字符组成的集合称为“字符集-Charset”,而在计算机中实际保存的是字符在字符集中的序号,即一个二进制形式的整数。而到底采用1个还是多个字节以及具体如何来存储一个字符集的字符,这种相关的规定被称为“编码-Encoding”。
常见的字符集有ISO-8859-1(一个字符占一个字节)、GB2312(一个字符占两个字节)、GBK(一个字符占两个字节)、UTF-8(变长编码,根据不同的Unicode字符,用1到6个字节编码)
- 字符char
(1) 用英文单引号括起来的单个字符,例如:'a'、'字'。这是最常见的声明字符形式。
(2) 用英文单引号括起来的十六进制字符代码值来表示单个字符。其格式为:'uXXXX',其中u是约定的前缀(u是unicode的第一个字母),而后面的XXXX位4位十六进制数,是该字符在unicode字符集中的序号。例如:'uFFFF'。
通过上面的字符与字节的区别,我们就可以知道,任何终端(网络、文件)读取或者输出的数据都一定是字节,字符是经过内存处理后的数据。
- 字符输入:字节(磁盘)–> 自动转换为 –>字符(内存);
- 字符输出:字符(内存)–> 自动转换为–>字节(磁盘);
字节流与字符流区别
- 字节流
字节流的处理单元为1个字节,操作字节和字节数组。字节流更加适用于音频文件、图片、歌曲,操作中文文本可能会出现乱码。字节流在操作过程中不会使用到缓冲区,直接对文件本身进行操作。
- 字符流
字节流读取文字字节数据后,不直接操作而是先查指定的编码表。获取对应的文字。在对这个文字进行操作。即:字节流+编码表,在操作的过程中不能只读取一个字节,否则不能将字节根据字符集转换成相应的字符,因此需要缓存区。 字符流在操作的时候是需要使用到缓冲区的,因此需要使用close方法才能输出内容,或调用flush方法强制刷新缓冲区。
流的操作
不管使用的是字节流还是字符流,其基本的操作流程几乎是一样的,以文件操作为例。
- 创建File类对象 ,主要是指明要操作的文件路径;
- 根据字节流或字符流的子类实例化父类对象 ;
- 进行数据的读取或写入操作;
- 关闭流(close());
对于IO操作属于资源处理,所有的资源处理操作(IO操作、数据库操作、网络)最后必须要进行关闭。
字节输入流InputStream
public abstract class InputStream implements Closeable
这个抽象类是表示输入字节流的所有类的超类。
Modifier and Type | Method and Description |
---|---|
void |
close() 关闭此输入流并释放与流相关联的任何系统资源。 |
abstract int |
该方法阻塞直到输入数据可用,检测到流的结尾,或抛出异常。 该方法是一个抽象方法,需要子类去根据自己的数据类型去实现,后面的 |
读取到缓冲区的总字节数, 或者如果没有更多的数据,因为已经到达流的末尾,则是 |
该方法阻塞直到输入数据可用,检测到文件结束或抛出异常。 如果 如果没有字节可用,因为流在文件末尾,则返回值 读取的字节数最多等于 如果k < b的长度,这些字节将存储在元素 如果k>= b的长度,那么最多读取d的长度的字节。 |
读取到缓冲区的总字节数,如果没有更多的数据,因为已经到达流的末尾,那么 |
从输入流读取最多 该方法阻塞直到输入数据可用,检测到流的结尾,或抛出异常。 如果 如果没有字节可用,因为流是文件的 第一个字节读取存储在元素 读取的字节数最多等于 这些字节将存储在元素 在每种情况下,元件 |
字节输出流OutputStream
public abstract class OutputStream implements Closeable, Flushable
这个抽象类是表示字节输出流的所有类的超类。 输出流接收输出字节并将其发送到某个接收器。
Modifier and Type | Method and Description |
---|---|
void |
close() 关闭此输出流并释放与此流相关联的任何系统资源。 |
void |
flush() 刷新此输出流并强制任何缓冲的输出字节被写出。 |
void |
write(byte[] b) 将 |
void |
write(byte[] b, int off, int len) 从指定的字节数组写入
元素 |
abstract void |
write(int b) 将指定的字节写入此输出流。 要写入的字节是参数 |
因为InputStream和OutputStream都是抽象类,在使用中用到的是他们的子类。
我们ByteArrayOutputStream和ByteArrayInputStream子类写了测试的例子
public class TestIO {
public static void main(String[] args) throws IOException {
byte[] bytes = "hello world".getBytes();
// 实例化输出流对象
OutputStream outputStream = new ByteArrayOutputStream(5);
// 写操作
outputStream.write(bytes);
// 关闭流
outputStream.close();
// 作为InputStream使用的缓冲区数组
byte[] inByteArray = new byte[20];
InputStream intputStream = new ByteArrayInputStream(bytes);
// 读取字节数据,将其放入到缓存区数组中,返回读取的长度
int len = intputStream.read(inByteArray);
System.out.println("length:" + len);
System.out.println("content:" + new String(inByteArray, 0, len));
// 关闭流
intputStream.close();
}
}
打印结果如下(可以通过调整缓存区字节数组的长度来测试读写的长度验证)
length:11
content:hello world
字符输入流Reader
public abstract class Reader implements Readable, Closeable
用于读取字符流的抽象类。 子类必须实现的唯一方法是read(char [],int,int)和close()。 然而,大多数子类将覆盖这里定义的一些方法,以便提供更高的效率,附加的功能或两者。
Modifier and Type | Method and Description |
---|---|
abstract void |
close() 关闭流并释放与之相关联的任何系统资源。 |
int |
read() 读一个字符 该方法将阻塞,直到某些输入可用,发生I / O错误或达到流的结尾。 |
int |
read(char[] cbuf) 将字符读入数组。 该方法将阻塞,直到某些输入可用,发生I / O错误或达到流的结尾。 |
abstract int |
read(char[] cbuf, int off, int len) 将字符读入数组的一部分。 该方法将阻塞,直到某些输入可用,发生I / O错误或达到流的结尾。 |
int |
read(CharBuffer target) 尝试将字符读入指定的字符缓冲区。 |
字符输出流Writer
public abstract class Writer implements Appendable, Closeable, Flushable
子类必须实现的唯一方法是write(char [],int,int),flush()和close()。 然而,大多数子类将覆盖这里定义的一些方法,以便提供更高的效率,附加的功能或两者。
Writer |
append(char c) 将指定的字符附加到此作者。 |
Writer |
append(CharSequence csq) 将指定的字符序列附加到此作者。 |
Writer |
append(CharSequence csq, int start, int end) 将指定字符序列的子序列附加到此作者。 |
abstract void |
close() 关闭流,先刷新。 |
abstract void |
flush() 刷新流。 |
void |
write(char[] cbuf) 写入一个字符数组。 |
abstract void |
write(char[] cbuf, int off, int len) 写入字符数组的一部分。 |
void |
write(int c) 写一个字符 |
void |
write(String str) 写一个字符串 |
void |
write(String str, int off, int len) 写一个字符串的一部分。 |
使用StringWriter和StringReader写的测试的例子
public static void main(String[] args) throws IOException {
String str = "hello world";
// 实例化输出流对象
StringWriter write = new StringWriter();
// 写操作
write.write(str);
// 刷新流
write.flush();
// 关闭流
write.close();
// 创建一个新的字符串阅读器
StringReader intputReader = new StringReader(str);
char[] c = new char[1024];
int temp = 0;
int len = 0;// 接收每一个读取进来的数据
while ((temp = intputReader.read()) != -1) {
// 如果文件还有内容
c[len] = (char) temp;
len++;
}
// 关闭流
intputReader.close();
System.out.println("length:" + len);
System.out.println("content:" + new String(c, 0, len));
}
字节流到字符流的桥InputStreamReader
Reader的一个子类
public class InputStreamReader extends Reader
InputStreamReader是从字节流到字符流的桥:它读取字节,并使用指定的charset
将其解码为字符 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
每个调用InputStreamReader的read()方法之一可能会导致从底层字节输入流读取一个或多个字节。 为了使字节有效地转换为字符,可以从底层流读取比满足当前读取操作所需的更多字节。
为了最大的效率,请考虑在BufferedReader中包装一个InputStreamReader。 例如:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
构造方法如下
Constructor and Description |
---|
InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader。 |
InputStreamReader(InputStream in, Charset cs) 创建一个使用给定字符集的InputStreamReader。 |
InputStreamReader(InputStream in, CharsetDecoder dec) 创建一个使用给定字符集解码器的InputStreamReader。 |
InputStreamReader(InputStream in, String charsetName) 创建一个使用命名字符集的InputStreamReader。 |
字符流到字节流的桥OutputStreamWriter
public class OutputStreamWriter extends Writer
OutputStreamWriter是字符流到字节流的桥梁:通过指定的字符集charset
把向其写入的字符编码成字节 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
每次调用write()方法都会使编码转换器在给定字符上被调用。 所得到的字节在写入底层输出流之前累积在缓冲区中。 可以指定此缓冲区的大小,但是默认情况下它大部分用于大多数目的。 请注意,传递给write()方法的字符不会缓冲。
为了最大的效率,请考虑在BufferedWriter中包装一个OutputStreamWriter,以避免频繁的转换器调用。 例如:
Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
构造方法如下
Constructor and Description |
---|
OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter。 |
OutputStreamWriter(OutputStream out, Charset cs) 创建一个使用给定字符集的OutputStreamWriter。 |
OutputStreamWriter(OutputStream out, CharsetEncoder enc) 创建一个使用给定字符集编码器的OutputStreamWriter。 |
OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用命名字符集的OutputStreamWriter。 |