IO流的介绍
文章目录
一、简述IO
1.1 什么是IO
IO流,即input(输入)和output(输出)。
主要是用来进行数据的传输,比如我们日常下载东西和上传文件等,都是IO的表现。
我们可以把这种数据的传输看成是一种流向,以内存为基准,流向内存的叫做input(输入流)
,从内存流出的叫做output(输出流)
。
1.2 IO的分类
根据数据的流向可以分为输入流和输出流
输入流:把数据从其他设备读取到内存中的流。
输出流:把数据从内存的流中写入到其他设备。
根据数据的类型又可以分为字节流和字符流
字节流:以字节为单位,读写数据的流。
字符流:以字符为单位,读写数据的流。
一张简单的图片来说明一下
1.3 IO的顶级父类们
字节输入流: java.io.InputStream
字节输出流: java.io.OutputStream
字符输入流: java.io.Reader
字符输出流: java.io.Writer
二、输入流
2.1 字节输入流
java.io.InputStream
是一个抽象类,是所有字节输入流的父类。它定义了一些字节输入流的共性方法
public void close()
关闭此输入流并且释放掉所有与此流相关的系统资源
public abstract int read()
从此输入流读取数据的下一个字节
public int read(byte[] b)
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
close 方法,当完成流的操作以后,必须调用此方法释放系统资源,否则系统资源会一直被占用
2.2 FileInputStream
java.io.FileInputStream
类是文件输入流,其作用是从文件中读取字节。
常用构造方法
FileInputStream(File file)
:通过打开和实际文件的连接来创建一个 FileInputStream ,该文件通过文件系统中的FIle对象 file 指定。
FileInputStream(String name)
:通过打开和实际文件的连接来创建一个 FileInputStream,该文件由文件系统中的路径名 name 来指定。
当指定的 FIle 文件找不到或者传入的name路径中的文件找不到的时候,系统会抛出 FileNotFoundException 异常
代码演示
读取字节
public class FISRead {
public static void main(String[] args) throws IOException{
// 使用文件名称创建流对象
FileInputStream fis = new FileInputStream("read.txt");
// 读取数据,返回一个字节
int read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
// 读取到末尾,返回-1
read = fis.read();
System.out.println( read);
// 关闭资源
fis.close();
}
}
输出结果:
a
b
c
d
e
-1
// 优化代码
public class FISRead {
public static void main(String[] args) throws IOException{
// 使用文件名称创建流对象
FileInputStream fis = new FileInputStream("read.txt");
// 定义变量,保存数据
int b ;
// 循环读取
while ((b = fis.read())!=-1) {
System.out.println((char)b);
}
// 关闭资源
fis.close();
}
}
输出结果:
a
b
c
d
e
使用字节数组读取
public class FisRead{
public static void main(String[] args){
FileInputStream fis = new FileInputStream("read.txt"); // 该文件中放了abcde
int len;
byte[] b = new byte[2];
while((len=fis.read(b)) != -1){
System.out.println(new String(b));
}
fis.close();
}
}
输出结果:
ab
cd
ed
我们会发现多输出了一个d,这是因为最后一次读取的时候只读取到了一个字节e,而数组中上次读取的数据没有被完全替换,所以要通过len,获取有效字节
public class FISRead {
public static void main(String[] args) throws IOException{
// 使用文件名称创建流对象.
FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde
// 定义变量,作为有效个数
int len ;
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b))!=-1) {
// 每次读取后,把数组的有效字节部分,变成字符串打印
System.out.println(new String(b,0,len));// len 每次读取的有效字节个数
}
// 关闭资源
fis.close();
}
}
输出结果:
ab
cd
e
2.3 字符输入流
在我们使用字节输出流的时候会遇到一个尴尬的问题,那就是遇到中文字符的时候,可能会导致不能显示完整的字符,因为一个中文字符占了好几个字节存储。所以Java中提供了一些字符流类,以字符为单位读写数据专门来处理文本文件。
java.io.Reader
抽象类是所有字符输入流的父类,可以去读取字符信息到内存中。它定义了一些字符输入流的共性方法。
public void close()
关闭此流并释放所有与此流相关的系统资源。
public int read()
从输入流读取一个字符。
public int read(char[] cbuf)
从输入流中读取一些字符,并将他们存储到字符数组 cbuf 中。
2.4 FileReader
java.io.FileReader
类是读取字符文件的类。构造时使用系统默认的字符编码和默认字节缓冲区。
字符编码:字节和字符对应的规则。Window系统的中文编码默认是GBK编码表。在IDEA开发工具中默认编码是UTF-8。
字节缓冲区:一个字节数组,用来临时存储字节数据。
常用构造方法
FileReader(File file)
:创建一个新的 FileReader ,给定要读取的File对象。
FileReader(String fileName)
:创建一个新的 FileReader ,给定要读取的文件的名称。
使用方法和步骤和上面演示代码基本一致,这里不再进行演示
三、输出流
3.1 字节输出流
java.io.OutputStream
抽象类是所有字节输出流的父类,它定义了一些基本的共性方法。
public void close()
:关闭此输入流并且释放所有与此流相关的系统资源。
public void flush()
:刷新此输入流并强制任何缓冲的输入字节被写出。
public void writer(byte[] b)
:将 b.length长度的字节从指定的字节数组写入此输出流。
public void writer(byte[] b,int off, int len)
:从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
3.2 FileOutputStream
java.io.FileOutputStream
类是文件输出流,主要是用来将数据写出到文件。
常用构造方法
public FileOutputStream(File file)
创建文件输出流以写入由指定的 File对象表示的文件。
public FileOutputStream(String name)
创建文件输出流以指定的名称写入文件。
public FileOutputStream(File file, boolean append)
: 创建文件输出流以写入由指定的 File对象表示的文件。
public FileOutputStream(String name, boolean append)
: 创建文件输出流以指定的名称写入文件。
当我们创建一个输出流的时候,必须传入一个文件路径。与输入流不同的是,如果我们传入的路径下没有这个文件,那么则会创建该文件,如果有该文件,则会清空该文件的数据。
代码如下
public class FileOutputStreamConstructor throws IOException {
public static void main(String[] args) {
// 使用File对象创建流对象
File file = new File("a.txt");
FileOutputStream fos = new FileOutputStream(file);
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("b.txt");
}
}
代码演示
写出字节
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 写出数据
fos.write(97); // 写出第1个字节
fos.write(98); // 写出第2个字节
fos.write(99); // 写出第3个字节
// 关闭资源
fos.close();
}
}
输出结果:
abc
// 优化代码
写出字节数组
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字符串转换为字节数组
byte[] b = "我爱写代码".getBytes();
// 写出字节数组数据
fos.write(b);
// 关闭资源
fos.close();
}
}
输出结果:
我爱写代码
写出指定长度的字节数组
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字符串转换为字节数组
byte[] b = "abcde".getBytes();
// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
fos.write(b,2,2);
// 关闭资源
fos.close();
}
}
输出结果:
cd
数据追加续写
在我们上面的代码演示中,我们每次程序运行创建输出流对象,都会清空目标文件,那么我们怎么在保留原有文件数据的情况下,继续添加新数据呢?
代码演示
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
// 参数中需要传入一个boolean类型的值,true表示为追加数据,false表示情况原有数据
FileOutputStream fos = new FileOutputStream("fos.txt",true);
// 字符串转换为字节数组
byte[] b = "abcde".getBytes();
// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
fos.write(b);
// 关闭资源
fos.close();
}
}
文件操作前:cd
文件操作后:cdabcde
写出换行
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 定义字节数组
byte[] words = {97,98,99,100,101};
// 遍历数组
for (int i = 0; i < words.length; i++) {
// 写出一个字节
fos.write(words[i]);
// 写出一个换行, 换行符号转成数组写出
fos.write("\r\n".getBytes());
}
// 关闭资源
fos.close();
}
}
输出结果:
a
b
c
d
e
- 回车符
\r
和换行符\n
:
- 回车符:回到一行的开头(return)。
- 换行符:下一行(newline)。
- 系统中的换行:
- Windows系统里,每行结尾是
回车+换行
,即\r\n
;- Unix系统里,每行结尾只有
换行
,即\n
;- Mac系统里,每行结尾是
回车
,即\r
。从 Mac OS X开始与Linux统一。
3.3 字符输出流
java.io.Writer
抽象类是所有字符输出流的父类。它定义了字符输出流的基本共性方法。
void write(int c)
写入单个字符。
void write(char[] cbuf)
写入字符数组。
abstract void write(char[] cbuf, int off, int len)
写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
void write(String str)
写入字符串。
void write(String str, int off, int len)
写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
void flush()
刷新该流的缓冲。
void close()
关闭此流,但要先刷新它。
3.4 FileWriter
java.io.FileWriter
类是写出字符到文件的类。构造时使用系统默认的字符编码和默认字节缓存区。
构造方法
FileWriter(File file)
: 创建一个新的 FileWriter,给定要读取的File对象。
FileWriter(String fileName)
: 创建一个新的 FileWriter,给定要读取的文件的名称。
使用方法和步骤和上面演示代码基本一致,这里不再进行演示
关闭和刷新
因为内置缓冲区的原因,如果不关闭输出流则无法写出数据到文件中。但是关闭的流对象是无法再次写出数据的。我们如果既想要写出数据,又想继续使用流,这时候就需要用到 flush
方法了。
flush
:刷新缓冲区,流对象可以继续使用。
close
:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileWriter fw = new FileWriter("fw.txt");
// 写出数据,通过flush
fw.write('刷'); // 写出第1个字符
fw.flush();
fw.write('新'); // 继续写出第2个字符,写出成功
fw.flush();
// 写出数据,通过close
fw.write('关'); // 写出第1个字符
fw.close();
fw.write('闭'); // 继续写出第2个字符,【报错】java.io.IOException: Stream closed
fw.close();
}
}
四、IO的异常处理
JDK7之前的处理
我们之前使用的方法,都是把异常抛出,但是实际开发中并不能这样去处理,所以这里建议使用try...catch...finally
代码块,处理异常部分。
public class HandleException1 {
public static void main(String[] args) {
// 声明变量
FileWriter fw = null;
try {
//创建流对象
fw = new FileWriter("fw.txt");
// 写出数据
fw.write("我爱写代码"); //我爱写代码
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
总结
IO流是我们在日常开发中使用非常频繁的类,虽然它的分类有点多,但是都有异曲同工之妙,我们只需要多多练习就一定能够将其掌握~