记录Java中IO流相关知识,包括IO流基本概念,IO流中的相关类,FileInputStream类,缓冲流(处理流),文件分割与合并,FileReader与FileWriter,字符打印流与对象输入,对象输出流,包含文件的切割与合并完整代码
一、IO流
IO流(输入流/输出流)分为字节流与字符流。
输入流与输出流是相对于程序而言。输入流是将本地或者其他终端文件读入到程序中。输出流是指将程序中的数据写入到本地或者其他终端。
根据操作方式将流分为字节流与字符流。
字节流:按照字节的方式操作数据源。
字符流:按照字符方式操作数据源。
一般而言以stream结尾的类都是字节流。Readre结尾或者Writter结尾是字符流。
根据流里面的内容可以将流分为节点流与处理流。
节点流:流里面的内容是数据源。
处理流:流里面内容是节点流。
二、IO相关类
1.FileInputStream类
当我们对文件进行操作的时候,我们会使用字节流去处理。我们使用字节流处理的时候,在读入数据到程序时,会有两个read()方法,两个read方法返回值都是int,但表示的意义不同。并且读入的方式有区别。当我们对文件对象进行操作的时候,一定要记得关闭流对象。 代码如下(示例):
private static void save1(String src, String des) {
FileInputStream in = null;
FileOutputStream out = null;
//根据路径创建文件对象
File file = new File(src);
try {
//创建输入流对象,将文件读取到程序
in = new FileInputStream(file);
//创建输出流对象,将文件数据从程序中写入到指定路径下的文件
out = new FileOutputStream(des);
//此时的value表示从文件中读取的一个字节内容,当读取到结尾的的时候,read()将会返回-1
int value = 0;
while ((value = in.read()) != -1) {
out.write(value);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void save2(String src, String des) {
File file = new File(src);
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream(file);
out = new FileOutputStream(des);
byte[] bytes = new byte[10];
//此时的size表示的是我们的byte数组中实际存入数据的长度
int size = 0;
while ((size = in.read(bytes)) != -1) {
out.write(bytes, 0, size);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.缓冲流(处理流)
使用处理流提高效率。
代码如下:
private static void save3(String src, String des) {
//使用处理流提高效率
BufferedInputStream bin = null;
BufferedOutputStream bout = null;
try {
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(des);
//在输入缓冲流底层存在一个缓冲数组,大小是8192B(8KB),在构造方法里面我们也可以传入自定义的大小
//当我们使用缓冲流的时候,使用的byte数组大小小于8192时,我们每一次写入数据只会发生在填充满8192缓冲数组之后。
//当我们使用缓冲流的时候,使用的byte数据大小大于8192时,缓冲流里面的缓冲数组失效。
bin = new BufferedInputStream(in);
bout = new BufferedOutputStream(out);
int size = 0;
byte[] bytes = new byte[1024];
while ((size = bin.read(bytes)) != -1) {
bout.write(bytes,0,size);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (bin!=null){
try {
bin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
bout.flush();
} catch (IOException e) {
e.printStackTrace();
}
if (bout!=null){
try {
bout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.文件分割与合并
写一个方法,将文件分割为每份1MB大小的若干份,存储在一个temp的文件夹中(每份文件名自己定义), 如:1.temp 2.temp
然后再写一个方法,将这若干份合并为一个文件。
输入 | 输出 | |
---|---|---|
分割 | 一个文件 | 多个文件 |
合成 | 多个文件 | 一个文件 |
代码如下:
private static void cut(String path) throws IOException {
//传递的path参数是一个盘符的地址,是我们将要分割文件的所在路径。
// 如:F:/y2mate.com - ONCE UPON A TIME IN SHANGHAI OST_720p.mp4
//在F盘存在一个文件,将这个文件分割(按照大小为1M)分割成多个临时文件保存到F:/temp文件下
File file = new File(path);
FileInputStream in = new FileInputStream(file);
//创建文件夹temp,去存储我们分割的临时文件。
File temp = new File("F:/temp");
if (!temp.exists()) {
temp.mkdir();
}
int size = 0;
int name = 1;
byte[] bytes = new byte[1024 * 1024];
while ((size = in.read(bytes)) != -1) {
FileOutputStream out = new FileOutputStream("F:/temp/" + (name++) + ".temp");
out.write(bytes, 0, size);
out.close();
}
in.close();
}
private static void mix(String path) throws IOException {
//传入的参数是我们需要合并的临时文件所在的文件路径
//将盘符文件夹下面的所有文件合并成一个完整的文件。如:F:/temp
File file = new File(path);
File[] files = file.listFiles();
//在这个传递的参数是我们需要分割前文件的文件路径。
FileOutputStream out = new FileOutputStream(path + "/源文件.mp4");
int name = 1;
for (int i = 0; i < files.length - 1; i++) {
int size = 0;
byte[] bytes = new byte[1024];
//在这个地方记载一个问题(如果不使用文件名进行拼接,只是使用files数组传递,视屏文件将会出现播放问题)
FileInputStream in = new FileInputStream("F:/temp/" + (name++) + ".temp");
//下面这一步是不正确的,因为files数组返回的文件不是我们预期样子
//FileInputStream in = new FileInputStream(files[i]);
//预期的样子(数组下标索引为0对应的是1.temp,数组索引下标为1对应的是2.temp)
//实际样子(数组下标索引是递增的,但是每一个数组的元素名称不一定递增)
while ((size = in.read(bytes)) != -1) {
out.write(bytes, 0, size);
}
in.close();
}
out.close();
}
特别注意:File里面的listFiles()方法返回的是一个文件数组,这个文件数组里的文件不是按照我们预期的样子排列。数组的下标索引与文件的名称不是递增顺序排列。
files文件数组里面数组下标索引对应的文件如图所示:
temp文件夹文件按照数字递增排序,这个顺序与我们的分割顺序是一致的。合并时必须按照这个顺序。
导致的问题:文件合并有问题。如:我合并的视频文件,播放前六秒正常,之后的图像不动,音频静音,只有视频进度条走动。
解决方案:我们在合并文件的时候,必须按照文件的序号递增一个一个读取到程序,最后按照分割的先后顺序写入到文件中。
使用手动拼接文件名的方式,保证顺序一致。
4.FileReader与FileWriter
FileRead与FileWriter是字符流,通过InputStreamReader与InputStreamWriter处理流将内容转换成字符。
代码如下:
private static void read() throws IOException {
File file = new File("F:/demo.txt");
FileReader reader = new FileReader(file);
FileWriter writer = new FileWriter("F:/demo1.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
BufferedWriter bufferedWriter = new BufferedWriter(writer);
int size = 0;
char[] chars = new char[1024];
while ((size = bufferedReader.read(chars)) != -1) {
bufferedWriter.write(chars, 0, size);
}
bufferedReader.close();
bufferedWriter.flush();
bufferedWriter.close();
}
private static void readerLine() throws IOException {
File file = new File("F:/demo.txt");
FileReader reader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(reader);
FileWriter writer = new FileWriter("F:/demo1.txt");
BufferedWriter bufferedWriter = new BufferedWriter(writer);
String readLine;
while ((readLine = bufferedReader.readLine()) != null) {
bufferedWriter.write(readLine);
}
bufferedReader.close();
bufferedWriter.flush();
bufferedWriter.close();
}
5.字符打印流与对象输入,对象输出流
打印流分为字节打印流与字符打印流。从输入输出方面只存在输出打印流,不存在输入打印流。
当我们需要存储对象信息的时候(这个过程称为对象序列化),我们需要使用对象输出流;当我们需要把存储的对象信息读取到程序里面的时候(这个过程称为对反象序列化),我们需要使用对象输入流。
代码如下:
/* 打印流:分为字节打印流与字符打印流
按照输入与输出讲,打印流只存在输出流打印流,不存在输入打印流
* 演示字符打印流的操作
* */
private static void printWriter() throws FileNotFoundException {
File file = new File("F:/demo.html");
PrintWriter stream = new PrintWriter(file);
stream.print("<h1>Hello World!</h1>");
stream.close();
}
/**
* 对象输出流 与 对象输入流(两者属于处理流)
* 我们把对象信息打印到文件中称为对象的序列化,也就是对象输出流
* 把对象信息从文件中读取出来,称为对象的反序列化,也就是对象输入流
* 对象序列化时存在一个UID,这是类的唯一编号
* 如果我们需要将自定义的类进行序列化操作,我们应该实现Serializable,并显示将UID声明出来。
* 当我们不显示声明这个UID时,系统会默认生成一个UID
* 但是一旦我们的类发生改变,这个之前声明的UID将会失效(类发生改变后,我们之前序列化的对象,将找不到对应的类信息)
* 类的UID标识出类
*/
private static void objectStream() throws IOException, ClassNotFoundException {
FileOutputStream out = new FileOutputStream("F:/object.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
Student simon = new Student("Simon", 22);
objectOutputStream.writeObject(simon);
objectOutputStream.close();
File file = new File("F:/object.txt");
InputStream inputStream = new FileInputStream(file);
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Student stu = (Student)objectInputStream.readObject();
System.out.println(stu);
objectInputStream.close();
}
总结
IO流这块,首先要明确输入流与输出流的概念(把计算机程序作为参照物,将数据信息读取到程序中,我们称之为输入;我们将程序中的数据信息写入到文件或者其他媒介中,我们称之为输出)。
其次根据我们读取的数据内容分为字节流与字符流。
根据流里面的信息我们将流分为节点流(流里面的信息是数据内容)与处理流(流里面的信息是节点流)。
最后我们学习打印流与对象流。
打印流是将我们程序中的数据信息打印出来,可以使用文件存储打印的信息。
对象流是将程序中的对象信息存储起来(对象序列化),或者将存储的对象信息读取出来(对象反序列化)。
温故而知新,是一种理想状态。更多的是对已学习知识加深记忆与理解。多次的学习是提高自己编程技巧,查漏补缺的最佳方法。