I/O流(A)
IO流这部分,算上学习File文件类的时间,总共花了三天时间学习,知识庞杂,种类繁多。但是说到底,各种各样的IO流,核心的方法无外乎构造()、read()、write()三种,根据文件类型的不同,选取不同的IO流。
东西太多,一次整理不完,因此分成几篇文章来整理,这里是IO概述、文件字节流、文件字符流、转换流四部分
I/O流概述
输入输出的概念
- 输入输出指的是系统内存与外部设备,网络等进行交互的过程。
|
I/O概念
流是一个抽象的概念。当Java程序需要从数据源读取数据时,会开启一个到数据源的流,这个流就是输出流,数据源可以是文件,内存或者网络等。同样,当程序需要输出数据到目的地时也一样会开启一个流,这个流就是输入流,数据目的地也可以是文件、内存或者网络等。流的创建是为了更方便地处理数据的输入输出。
流对象的操作依赖于操作系统,流通道本身并没有作用。
流分类
- 四大父类(抽象基类)
类名 | 类名 | 操作属性 | 操作对象 |
---|---|---|---|
字节输入流 | InputStream | 读入 | 字节 |
字符输入流 | Reader | 读入 | 字符 |
字节输出流 | OutputStream | 写出 | 字节 |
字符输出流 | Writer | 写出 | 字符 |
* 根据是否具有额外功能:
类名 | 类名 | 操作属性 | 操作对象 | 额外功能 |
---|---|---|---|---|
转换流 | InputStreamReader OutputStreamWriter | 输入转换流 输出转换流 | 字节和字符 | 转换字节到字符 |
缓冲流 | BufferedXXX | XXX的功能 | XXX的操作对象 | 可以通过缓冲原理实现加速并且实现一些新功能 |
流对象操作步骤
- 创建流子类对象,绑定数据目的(数据源和目标文件)
- 调用流方法进行读写操作
- .close();释放流对象(Java在程序结束时会自动关闭所有打开的流,但即便如此,显式的关闭任何打开的流仍是一个良好的习惯)。
I/O流注意事项
- I/O流只能对整个文件进行操作,对文件中个别数据进行操作只能用数据库才能实现。
- 流对象进行操作的时候经常会抛出IOException异常,所以需要用try{}catch{}语句来写。
文件字节流和文件字符流
主要区别
- 文件字节流可以读写任意文件(不包括文件夹),每次只操作文件中的一个字节。利用字节流创建的新文件可以保证不出错。
- 文件字符流只能读写文本文件(用.txt格式打开之后能读懂的文件就是文本文件),用字符流创建除文本文件以外任意文件都会造成文件损坏。
- 字节流不能直接操纵unicode字符,这时字符流的必要性就体现出来,因为一次操纵一个字符(即两个字节),这样就避免了数据传输中,汉字出现乱码等问题。
文件字节流
类名 | 类名 | 操作属性 | 操作对象 |
---|---|---|---|
文件字节输入流 | FileInputStream | 从文件中读入 | 字节 |
文件字节输出流 | FileOutputStream | 写出到文件中 | 字节 |
文件字节输入流
构造器
- 作用是绑定输入数据源
文件字节输入流有两种构造方法:
——————————
|FileInputStream(String filePath);|
|FileInputStream(File file); |
——————————示例代码
/*
* 这里是在方法内对异常进行处理的完整步骤,比较麻烦,不如throws之后在外边处理好用
* 这里只举例输入流的例子,输出流大同小异,定义时候的区别在其他代码里体现。
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class MyInputStream {
public static void main(String[] args) {
File file = new File("d:\\java\\全职高手.txt");
FileInputStream in = null;//try外定义方便显式关闭输入流;
try {
in = new FileInputStream(file);
} catch (IOException ea) {
ea.getMessage();//打印错误信息;
throw new RuntimeException("\n运行发生错误,请尝试重新运行:");//关闭程序;
} finally {
try {
if (in != null)//判断输入流是否存在且创建成功;
in.close();
} catch (IOException eb) {
eb.getMessage();
throw new RuntimeException("\n输入流关闭失败,请尝试重新运行程序");
}
}
}
}
常用方法
方法表达式 | 返回值 | 注意事项 |
---|---|---|
read(); | int(字节值) | 每次只读取一个字节,方法返回字节值,到达文件末尾返回-1 |
read(byte b[]) | int(实际读取的字节个数) | 每次读取最多b空间大小的数量的字节,到达末尾返回-1 b的大小太大会浪费空间,太小又影响速度,所以一般定义大小为1024 |
read(byte b[], int off, int len) | int(实际读取的字节个数) | off指定数据存放在数组中的位置,len指定方法读取的最大字节数 |
close(); | void | 显式关闭输入流 |
文件字节输出流
构造器
- 作用是绑定输出的输出目的
文件字节输出流有四种构造方法:
———————————————-
|FileOutputStream(String filePath); |
|FileOutputStream(File file); |
|FileOutputStream(String filePath, boolean append); |
|FileOutputStream(File file, boolean append); |
———————————————-输出流对象的构造方法可以创建对象,但是如果源文件存在,默认会覆盖原来的文件。
所有在构造方法的参数后边常常可以添加另外一个参数boolean append来决定如果文件存在是否覆盖,当apped**值为true**时,输出流会创建新文件覆盖目标文件,否则不覆盖,在目标文件中进行续写。
常用方法
方法表达式 | 返回值 | 注意事项 |
---|---|---|
write(int b); | void | 参数虽然是int类型,但是实际传参必须是byte类型 |
write(byte b[]) | void | 写入b空间大小的数量的字节 b的大小太大会浪费空间,太小又影响速度,所以一般定义大小为1024 |
write(byte b[], int off, int len) | void | off指定数据存放在数组中的位置,即偏移量,len指定方法写入的最大字节数 |
close(); | void | 显式关闭输出流 |
文件字符流
类名 | 类名 | 操作属性 | 操作对象 |
---|---|---|---|
文件字节输入流 | FileReader | 从文件中读入 | 字节 |
文件字节输出流 | FileWriter | 写出到文件中 | 字节 |
* 转换流失文件字符流的父类,继承关系:
* java.io.FileReader extends java.io.inputStreamReader;
* java.io.inputStreamReader extends java.io.Reader;
* java.io.Reader extends java.lang.Object;
* 文件字符输入流和文件字符输出流都采用系统默认的编码表,中文版Windows系统默认系统编码表为GBK;
文件字符输入流
构造器
- 作用是绑定输入数据源
- 文件字符输入流有两种构造方法:
————————–
|FileReader(String filePath);|
|FileReader(File file); |
————————–
常用方法
方法表达式 | 返回值 | 注意事项 |
---|---|---|
read(); | int(字符值) | 每次只读取一个字节,方法返回字节值,到达文件末尾返回-1 |
read(byte b[]) | int(实际读取的字符个数) | 每次读取最多b空间大小的数量的字节,到达末尾返回-1 b的大小太大会浪费空间,太小又影响速度,所以一般定义大小为1024 |
read(byte b[], int off, int len) | int(实际读取的字符个数) | off指定数据存放在数组中的位置,len指定方法读取的最大字节数 |
close(); | void | 显式关闭输入流 |
文件字节输出流
构造器
- 作用是绑定输出的输出目的
- 文件字符输出流有四种构造方法:
—————————————-
|FileWriter(String filePath); |
|FileWriter(File file); |
|FileWriter(String filePath, boolean append); |
|FileWriter(File file, boolean append); |
————————————– - 输出流对象的构造方法可以创建对象,但是如果源文件存在,默认会覆盖原来的文件。
- 所有在构造方法的参数后边常常可以添加另外一个参数boolean append来决定如果文件存在是否覆盖,当apped**值为true**时,输出流会创建新文件覆盖目标文件,否则不覆盖,在目标文件中进行续写。
- 与文件字节输出流不同,文件字符输出流只能创建有效的文本文件。
常用方法
方法表达式 | 返回值 | 注意事项 |
---|---|---|
write(int c); | void | 参数虽然是int,但是实际传参要传char类型 |
write(char b[]) | void | |
write(byte b[], int off, int len) | void | off指定数据存放在数组中的位置,即偏移量,len指定方法写入的最大字符数 |
write(String str) | 把字符串中所有字符写到文件中 | |
write(String str, int off, int len) | void | |
flush() | void | 冲流,将数据写入文件(不进行冲流则数据无法写入文件) 每次写入数据后进行一次冲流是一个好习惯 |
close(); | void | 显式关闭输出流,再关闭流的同时进行一次冲流操作 |
文件字节流复制文件代码举例
import java.io.*;
class ClsByte {
private String originalpath, finalpath;
private long starttime, endtime;
public ClsByte(String a, String b) {
this.originalpath = a;
this.finalpath = b;
}
public void function() {
System.out.println("字节流复制文件,开始执行:");
starttime = System.currentTimeMillis();
File orifile = new File(originalpath);
File tarfile = new File(finalpath);
FileOutputStream out = null;
FileInputStream in = null;
byte[] data = new byte[1024];
try {
in = new FileInputStream(orifile);
out = new FileOutputStream(tarfile, false);
int len = 0;
while ((len = in.read(data)) != -1) {
out.write(data, 0, len);
}
} catch (IOException e) {
e.getStackTrace();
throw new RuntimeException("\n文件传输中发生错误,请重试");
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
throw new RuntimeException("\n输入流关闭失败,请重试");
}
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
throw new RuntimeException("\n输出流关闭失败,请重试");
}
}
endtime = System.currentTimeMillis();
System.out.println("文件复制成功\n源文件" + orifile.getAbsolutePath() + "源文件大小" + orifile.getTotalSpace() + "B");
System.out.println("目标文件" + tarfile.getAbsolutePath() + "目标文件大小" + tarfile.getTotalSpace() + "B");
System.out.println("消耗时间" + (endtime - starttime) + "millseconds");
}
}
文件字符流复制文件代码举例
import java.io.*;
import java.util.*;
class ClsChar {
private String originalpath, finalpath;
private long starttime, endtime;
public ClsChar(String a, String b) {
this.originalpath = a;
this.finalpath = b;
}
public void function() {
System.out.println("\n字符流复制文件,开始执行:");
starttime = System.currentTimeMillis();
File orifile = new File(originalpath);
File tarfile = new File(finalpath);
FileReader in = null;
FileWriter out = null;
char [] data = new char [1024];
try {
in = new FileReader(orifile);
out = new FileWriter(tarfile, false);
int len = 0;
while ((len = in.read(data)) != -1) {
out.write(new String(data, 0, len));
out.flush();
}
} catch (IOException e) {
e.getStackTrace();
throw new RuntimeException("\n文件传输中发生错误,请重试");
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
throw new RuntimeException("\n输入流关闭失败,请重试");
}
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
throw new RuntimeException("\n输出流关闭失败,请重试");
}
}
endtime = System.currentTimeMillis();
System.out.println("文件复制成功\n源文件" + orifile.getAbsolutePath() + "源文件大小" + orifile.getTotalSpace() + "B");
System.out.println("目标文件" + tarfile.getAbsolutePath() + "目标文件大小" + tarfile.getTotalSpace() + "B");
System.out.println("消耗时间" + (endtime - starttime) + "millseconds");
}
}
转换流
转换流和文件字符流的区别
- 文件字符流是转换流的子类
- 文件字符流的编码表已指定为系统默认编码表,转换流则可以指定编码表
FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。
- *
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
FileReader fr = new FileReader("a.txt");
- 这三句代码的功能是一样的,其中第三句最为便捷。
- 注意:一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。什么时候用子类呢?
- 条件:
- 操作的是文件。
- 使用默认编码。
- 条件:
构造器
- InputStreamReader(InputStream in)
- 创建的转换流使用默认字符集
- InputStreamReader(InputStream in, Charset cs)
- 创建的转换流使用指定的字符集”cs”
- OutputStreamWriter(OutputStream out)
- 创建的转换流使用默认字符集
- OutputStreamWriter(OutputStream out, Charset cs)
- 创建的转换流使用指定的字符集”cs”