IO流、字节流和字符流
一、IO流
1.1 概念
把这种数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为
输入input
和输出output
,即流向内存是输入流,流出内存的输出流。
Java中I/O操作主要是指使用
java.io
包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。
1.2 分类
根据数据的流向分为:输入流和输出流。
- 输入流 :把数据从
其他设备
上读取到内存
中的流。- 输出流 :把数据从
内存
中写出到其他设备
上的流。
格局数据的类型分为:字节流和字符流。
- 字节流 :以字节为单位,读写数据的流。
- 字符流 :以字符为单位,读写数据的流。
1.3 IO流的父类
名称 | 输入流 | 输出流 |
---|---|---|
字节流 | 字节输入流InputStream | 字节输出流OutputStream |
字符流 | 字符输入流Reader | 字符输出流Writer |
这些类都是抽象类,在实际的开发过程中使用的是相应的子类。
二、字节流
2.1 概念
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
2.2 OutputStream
java.io.OutputStream
抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
2.2.1 常用方法
方法名 | 说明 |
---|---|
public abstract void write(int b) | 将指定的字节输出流。 |
public void write(byte[] b) | 将 b.length字节从指定的字节数组写入此输出流。 |
public void write(byte[] b, int off, int len) | 从指定的字节数组写入 len字节,从偏移量 off开始输 |
public void close() | 关闭此输出流并释放与此流相关联的任何系统资源。 |
public void flush() | 刷新此输出流并强制任何缓冲的输出字节被写出。 |
- 注意事项
close
方法,当完成流的操作时,必须调用此方法,释放系统资源。
2.2.2 FileOutputStream类
java.io.FileOutputStream
类是文件输出流,用于将数据写出到文件。
2.2.2.1 构造方法
方法名 | 说明 |
---|---|
public FileOutputStream(File file) | 创建文件输出流以写入由指定的 File对象表示的文件。 |
public FileOutputStream(String name) | 创建文件输出流以指定的名称写入文件。 |
- 代码演示
File f=new File("E://test/a.txt");
//创建流文件的文件所在文件夹必须存在否则会报FileNotFoundException
//创建流文件的文件如果存在则会清空 不存在则会创建
FileOutputStream fos1=new FileOutputStream("E://test/b.txt");
FileOutputStream fos2=new FileOutputStream(f);
//fos1.write(int);每次可以写出一个字节数据
fos1.write(97);fos1.write(98);fos1.write(99);//abc
//fos1.write(byte[]);每次可以写出数组中的数据
byte []b={65,66,67,68,69,70,71,72,73,74};
fos1.write(b);//ABCDEFGHIJ
//fos1.write(byte[], startIndex, length);每次写出从startIndex索引开始,length个字节
fos1.write(b, 4, 4);//EFGH
fos1.flush();//刷新缓存
fos1.close();//关闭资源
- 注意事项
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。
2.2.2.2 输出字符串到文件
//将字符串输出至文件
//创建指定文件的输出流
FileOutputStream fos1=new FileOutputStream("E://test/b.txt");
//创建要输出的字符串
String str="这是一句话";
//调用字符串方法将字符串转换为字节数组
byte []b=str.getBytes();
//调用输出流对象写出字节数组的方法
//将字节数组内容输出至指定文件
fos1.write(b);
//刷新缓存
fos1.flush();
//关闭资源
fos1.close();
2.2.2.3 追加输入字符串
方法名 | 说明 |
---|---|
public FileOutputStream(File file,boolean append) | 创建文件输出流以写入由指定的 File对象表示的文件。 |
public FileOutputStream(String name, boolean append) | 创建文件输出流以指定的名称写入文件。 |
这两个构造方法,参数中都需要传入一个boolean类型的值,
true
表示追加数据,false
表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了
//创建流文件的文件所在文件夹必须存在否则会报FileNotFoundException
//创建流文件的文件如果 不存在则会创建
//boolean代表是否追加输入
//如果为false会清空文件内容
FileOutputStream fos1=new FileOutputStream("E://test/b.txt",true);
fos1.write("这又是一句话".getBytes());
fos1.flush();
fos1.close();
2.2.2.4 写出换行
如果写的数据比较多,那么就会在文件中在一行展示。会造成阅读不方便。我们现在的需求是,每写一次数据,都要从下行开始,这个时候就要涉及到换行的问题。
- 回车符与换行符
- 回车符
\r
和换行符\n
:
- 回车符:回到一行的开头(return)。
- 换行符:下一行(newline)。
- 系统中的换行:
- Windows系统里,每行结尾是
回车+换行
,即\r\n
;- Unix系统里,每行结尾只有
换行
,即\n
;- Mac系统里,每行结尾是
回车
,即\r
。从 Mac OS X开始与Linux统一。
- 代码演示
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();
}
}
2.3 InputStream
java.io.InputStream
抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
2.3.1 常用方法
方法名 | 说明 |
---|---|
public void close() | 关闭此输入流并释放与此流相关联的任何系统资源。 |
public abstract int read() | 从输入流读取数据的下一个字节。 |
public int read(byte[] b) | 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。 |
close方法,当完成流的操作时,必须调用此方法,释放系统资源。
2.3.2 FileInputStrean
2.3.2.1 构造方法
方法名 | 说明 |
---|---|
FileInputStream(File file) | 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。 |
FileInputStream(String name) | 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。 |
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出
FileNotFoundException
- 代码演示
//使用指定的文件对象或代表文件的字符串路径创建文件输入流
FileInputStream fis1 = new FileInputStream("E://test/a.txt");
FileInputStream fis2 = new FileInputStream(new File("E://test/b.txt"));
//fis1.read();
//读取一个字节并使用int返回
int read1 = fis1.read();
int read2 = fis1.read();
int read3 = fis1.read();
System.out.println((char)read1);
System.out.println((char)read2);
System.out.println((char)read3);
//fis1.read(byte[])
//使用指定数组获取输入内容
byte[] b=new byte[5];
int len = fis1.read(b);
System.out.println(len);
System.out.println(new String(b,0,len));
fis1.close();//关闭输入流
2.3.2.2 读取文件所有内容并输出至控制台
//读取文件所有内容并输出至控制台
//1获取文件字节后使用读取单个字节方法读取
//2使用字节数组一次读取多个字节
//创建读取指定文件的输入流对象
FileInputStream fis2 = new FileInputStream(new File("E://test/b.txt"));
//创建读取使用的字节数组
byte[] b=new byte[1024];
//创建变量保存每次读取的长度
int len=0;
//如果调用方法返回读取长度不为-1
while(len!=-1){
//将本次读取的内容存储至字节数组中
len=fis2.read(b);
//使用字节数组创建字符串对象
if(len!=-1){
String string = new String(b,0,len);
System.out.println(string);
}
}
// while((len=fis2.read(b))!=-1){//将本次读取的内容存储至字节数组中
// //使用字节数组创建字符串对象
// if(len!=-1){
// String string = new String(b,0,len);
// System.out.println(string);
// }
// }
//关闭输入流对象
fis2.close();
2.3.2.3 文件复制
所有文件本质都是字节文件(使用字节流可以复制所有类型的文件)
//文件复制
File f1=new File("E://test/e.zip");//源文件
File f2=new File("E://test/a/b/c/d/e.zip");//复制位置
//1、创建复制位置的文件夹
//获取复制文件的父文件夹对象
File parentFile = f2.getParentFile();
//调用创建方法创建多级目录
parentFile.mkdirs();
//2、创建源文件的文件字节输入流与赋值文件的文件字节输出流
FileInputStream fis=new FileInputStream(f1);
FileOutputStream fos=new FileOutputStream(f2);
//3、读取源文件向复制文件输出
//创建读取字节数组
byte []b=new byte[102400];
//创建读取长度
int len=0;
//读取并输出
while((len=fis.read(b))!=-1){
fos.write(b, 0, len);
}
//关闭流释放资源
fis.close();
fos.flush();
fos.close();
三、字符流
3.1 概念
字符流可以看做是特殊的字节流,在使用字节流读取文件夹时,如果读取的是文档文件,有时可能会因为字符编码导致读取出现问题
即使文件编码统一,在进行读取时,读取字节数与对应字符编码代表字节数读取不配,那么也会造成乱码问题.这个时候就需要是同字符流进行读取
3.2 Writer
java.io.Writer
抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字符输出流的基本共性功能方法。
3.2.1常用方法
方法名 | 说明 |
---|---|
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.2.2 FileWriter
java.io.FileWriter
类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
3.2.2.1 构造方法
方法名 | 说明 |
---|---|
FileWriter(File file) | 创建一个新的 FileWriter,给定要读取的File对象。 |
FileWriter(String fileName) | 创建一个新的 FileWriter,给定要读取的文件的名称。 |
FileWriter(File file) | 创建一个新的 FileWriter,给定要读取的File对象。 |
FileWriter(String fileName) | 创建一个新的 FileWriter,给定要读取的文件的名称。 |
当你创建一个流对象时,必须传入一个文件路径,类似于FileOutputStream。
- 代码演示
File file=new File("E://test/a.txt");
//创建以指定文档作为输出位置的写入流,如果不存在则创建
//如果存在则清空
//FileWriter fw1=new FileWriter(file);
//FileWriter fw2=new FileWriter("E://test/a.txt");
//true代表继续写入
FileWriter fw3=new FileWriter(file,true);
FileWriter fw4=new FileWriter("E://test/a.txt",true);
//将字符串指定索引起始长度写入文本文件
//fw3.write(String,off,len);
fw3.write("abcdefghijk",3,5);
//将字节数组指定索引起始长度写入文本文件
//fw3.write(char[], off, len);
char [] cbuf={'你','是','大','帅','哥','F'};
fw3.write(cbuf, 0, 5);
//刷新关闭释放资源
fw3.flush();
fw3.close();
3.2.2.2 读取文档输出控制台并复制
//读取文本文档并将内容输出至控制台
public static void main(String[] args) throws Exception {
//创建指定文本文档的文件对象
File file=new File("E://hlm.txt");
//创建文件复制输出对象
File file1=new File("E://hlm1.txt");
//创建对应文件的字符读取与写入流
FileReader fr=new FileReader(file);
FileWriter fw=new FileWriter(file1);
//创建读取字符数组
char [] c=new char[1000];
//创建读取长度
int len=0;
while((len=fr.read(c))!=-1){
String string = new String(c,0,len);
System.out.println(string);
fw.write(string);
}
//关闭释放资源
fr.close();
fw.flush();
fw.close();
}
3.3 Reader
java.io.Reader
抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
3.3.1 常用方法
方法名 | 说明 |
---|---|
public void close() | 关闭此流并释放与此流相关联的任何系统资源。 |
public int read() | 从输入流读取一个字符。 |
public int read(char[] cbuf) | 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 |
- 注意事项
- 字符编码: 字节与字符的对应规则。Windows系统的中文编码默认是GBK编码表。idea中UTF-8
- 字节缓冲区: 一个字节数组,用来临时存储字节数据。
3.3.2 FileReader
3.3.2.1 构造方法
方法名 | 说明 |
---|---|
FileReader(File file) | 创建一个新的 FileReader ,给定要读取的File对象。 |
FileReader(String fileName) | 创建一个新的 FileReader ,给定要读取的文件的名称。 |
当你创建一个流对象时,必须传入一个文件路径。类似于FileInputStream 。
- 代码演示
File file =new File("E://hlm.txt");
//使用指定文件获取字符串创建对应的字符输入流对象
FileReader fr1=new FileReader(file);
FileReader fr2=new FileReader("E://hlm.txt");
//读取一个字符返回对应字符的ASCII对应数值
//fr1.read();
System.out.print((char)fr1.read());
System.out.print((char)fr1.read());
System.out.print((char)fr1.read());
//使用char数组一次读取多个字符
//fr1.read(char[]);
char [] c=new char[1000];
int len = fr1.read(c);
System.out.print(new String(c,0,len));
3.3.2.2 读取文本文档内容并输出至控制台
//创建读取文本文档的文件对象
File file =new File("E://hlm.txt");
//使用文本文档的文件对象创建字符读取流
FileReader fr=new FileReader(file);
//创建每次取出数据的字符数组
char [] c=new char[1024];
//创建每次取出的字符数变量
int len=0;
//使用循环读取数据输出至控制台
while((len=fr.read(c))!=-1){
System.out.print(new String(c,0,len));
}
//关闭文件字符读取流
fr.close();
面试题
1、字节流与字符流的区别
- 字符流本质使用的就是字节流,只不过是根据当前编码进行字节流缓冲读取写入
- 字节流通常读取文件(可以对任意文件进行操作)
- 字符只能对文本文件进行操作(我们能看懂的)
当需要对文件内容进行操作时使用字符流,如果进行文件复制使用字节流2、
flush
与close
之间的区别
- 因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要
flush
方法了。
flush
:刷新缓冲区,流对象可以继续使用。close
:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
每日一点点进步
不进则退