三、IO流概述
- 流的分类
按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
按数据流的流向不同分为:输入流,输出流
按流的角色的不同分为:节点流,处理流
- 流的继承关系
4个抽象基类:
InputStream
OutputStream
Reader
Writer
说明:上图中蓝色背景的流是本章的重点。
四、节点流的使用
4.1 对指定磁盘下文件夹的访问
1.1.遍历本目录下所有的文件(不包括目录的目录里的文件)
import java.io.File;
public class main {
public static void main(String[] args) {
String path = "D:\\JAVA"; //要遍历的路径
File file = new File(path); //获取其file对象
File[] fs = file.listFiles(); //遍历path下的文件和目录,放在File数组中
for(File f:fs){ //遍历File[]数组
if(!f.isDirectory()) //若非目录(即文件),则打印
System.out.println(f);
}
}
}
2.遍历本目录下所有的文件(包括目录的目录里的文件)
import java.io.File;
import java.io.FileFilter;
public class FileText {
public static void main(String[] args) {
String path = "D:\\JAVA"; //要遍历的路径
File file = new File(path); //获取其file对象
func(file);
}
private static void func(File file){
File[] fs = file.listFiles();
for(File f:fs){
if(f.isDirectory()) //若是目录,则递归打印该目录下的文件
func(f);
if(f.isFile()) //若是文件,直接打印
System.out.println(f);
}
}
}
4.11 FileReader 和 FileWriter
//FileReader的基本使用
@Test
public void testFileReader() throws IOException {
//1.创建文件对象
File file1 = new File("hello.txt");
//2. 创建流对象
FileReader fr = new FileReader(file1);
//3. 读入数据的过程
int data = fr.read();
while (data != -1) {
System.out.print((char) data);
data = fr.read();
}
//4.对于流的关闭操作,必须手动实现
fr.close();
}
- 优化以后:
//对上述程序优化1:在io流中,凡是需要手动关闭流资源的程序中,都需要使用try-catch-finally处理异常
@Test
public void testFileReader1() {
FileReader fr = null;
try {
//1.创建文件对象
File file1 = new File("hello.txt");
//2. 创建流对象
fr = new FileReader(file1);
//3. 读入数据的过程
int data;//保存每次读取的字符数据
while ((data = fr.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.对于流的关闭操作,必须手动实现
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 进一步优化:
//对上述程序优化2:每次读取一个字符数组
@Test
public void testFileReader2() {
FileReader fr = null;
try {
//1.创建文件对象
File file1 = new File("hello.txt");
//2. 创建流对象
fr = new FileReader(file1);
//3. 读入数据的过程
char[] cbuf = new char[5];
int len;//记录每次读入到cbuf数组中的字符的个数
while((len = fr.read(cbuf)) != -1){
//错误的:
// for (int i = 0; i < cbuf.length; i++) {
// System.out.print(cbuf[i]);
//
// }
//错误的:
// String str = new String(cbuf);
// System.out.print(str);
//正确的:
// for (int i = 0; i < len; i++) {
// System.out.print(cbuf[i]);
// }
//正确的:
String str = new String(cbuf,0,len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.对于流的关闭操作,必须手动实现
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- FileWriter的使用
@Test
public void testFileWriter() {
FileWriter fw = null;
try {
//1. 创建输出到的File对象
File file1 = new File("info.txt");
//2. 创建输出流
// fw = new FileWriter(file1);
fw = new FileWriter(file1,true);
//3. 输出数据的过程
// fw.write('a');
// fw.write("I love You!");
fw.write("I love You!\n".toCharArray());
fw.write("You love him".toCharArray());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 手动的关闭资源
try {
if(fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 使用总结
结论:
1. 对于输入流来说,读取的文件一定要存在,否则会报FileNotFoundException
2. 对于输出流来说,要写出到的文件可以不存在。
如果不存在,则在执行过程中,会自动创建对应的文件
如果存在,1)使用FileWriter(File file) 或 FileWriter(File file,false):会对已有的文件进行覆盖
2)FileWriter(File file,true):在现有文件内容的末尾,追加内容
3. 流,因为都需要进行资源的关闭,所有本章所有的异常,只要涉及流资源的关闭,都必须使用try-catch-finally
- 综合使用FileReader和FileWriter实现文本文件的复制
/*
综合使用FileReader和FileWriter实现文本文件的复制
*/
@Test
public void testFileReaderWriter(){
FileReader fr = null;
FileWriter fw = null;
try {
//1.指明读取的File对象和写出到的File对象对应的文件
File srcFile = new File("hello.txt");
File destFile = new File("hello1.txt");
//不能处理非文本文件
// File srcFile = new File("baby.jpg");
// File destFile = new File("baby1.jpg");
//2. 创建对应的FileReader和FileWriter
fr = new FileReader(srcFile);
fw = new FileWriter(destFile);
//3. 读取并写出数据的过程
char[] cbuf = new char[5];
int len;//记录每次读入到char[]中字符的个数
while((len = fr.read(cbuf)) != -1){
// fw.write(cbuf);//错误的
fw.write(cbuf,0,len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
//方式一:
// try {
// if(fw != null)
// fw.close();
// } catch (IOException e) {
// e.printStackTrace();
// }finally{
// try {
// if(fr != null)
// fr.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
//
// }
//方式二;
try {
if(fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 思考
//FileReader和FileWriter:属于字符流,不能用来处理非文本文件的数据!
//如果想处理非文本文件的数据,就需要使用字节流:FileInputStream和FileOutputStream
4.2 FileInputStream和FileOutputStream
//复制一个文件
@Test
public void testFileInputOutputStream() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1. 造文件
// File srcFile = new File("baby.jpg");
// File destFile = new File("baby2.jpg");
File srcFile = new File("hello.txt");
File destFile = new File("hello2.txt");
//2. 造流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//3. 读取并写出的操作
byte[] buffer = new byte[5];
int len;//记录每次读取到buffer中的字节的个数
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
// String data = new String(buffer, 0, len);
// System.out.print(data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if (fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
结论:
* 通常,我们使用字节流来处理非文本文件(.jpg, .mp3,.mp4,.avi,.doc,.ppt,.xls)
* 使用字符流来处理文本文件(.txt,.java,.py)
五、处理流之一:缓冲流的使用
5.1 基本结构
* 抽象基类 文件流 缓冲流(处理流的一种):提高数据读写的效率
* InputStream FileInputStream BufferedInputStream
* OutputStream FileOutputStream BufferedOutputStream
* Reader FileReader BufferedReader
* Writer FileWriter BufferedWriter
5.2 BufferedInputStream和BufferedOutputStream的使用
@Test
public void testBufferedInputOutput() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1. 造文件
File srcFile = new File("baby.jpg");
File destFile = new File("baby3.jpg");
//2. 造流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3. 读写的细节操作
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源
//先关闭外面的流,再关闭内部的流
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bis != null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
//可以省略
// fos.close();
// fis.close();
}
}
5. 3 对比文件流与缓冲流的执行效率
/*
对比FileInputStream + FileOutputSteam 与 BufferedInputStream + BufferedOutputStream的读写效率
*/
public void copyFileWithFiled(String src,String dest){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1. 造文件
File srcFile = new File(src);
File destFile = new File(dest);
//2. 造流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//3. 读取并写出的操作
byte[] buffer = new byte[1024];
int len;//记录每次读取到buffer中的字节的个数
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
try {
if (fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void copyFileWithBuffered(String src,String dest){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1. 造文件
File srcFile = new File(src);
File destFile = new File(dest);
//2. 造流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3. 读写的细节操作
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源
//先关闭外面的流,再关闭内部的流
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bis != null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void testCopyTime(){
long start = System.currentTimeMillis();
String src = "G:\\教学视频\\01-Java基础\\尚硅谷_200213大数据Java基础_宋红康\\6-每日视频\\01-作业题.avi";
String dest = "G:\\教学视频\\01-Java基础\\尚硅谷_200213大数据Java基础_宋红康\\6-每日视频\\test02.avi";
// copyFileWithFiled(src,dest);
copyFileWithBuffered(src,dest);
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));//2028 - 507
}
5.4 BufferedReader和BufferedWriter的使用
@Test
public void testBufferedReaderWriter() {
BufferedReader br = null;
BufferedWriter bw = null;
try {
//1. 造文件、造流
br = new BufferedReader(new FileReader(new File("dbcp.txt")));
bw = new BufferedWriter(new FileWriter(new File("dbcp-1.txt")));
//2. 读写文件的细节
//写法一:
// char[] cbuf = new char[1024];
// int len;
// while ((len = br.read(cbuf)) != -1) {
// bw.write(cbuf, 0, len);
// }
//写法二:
String data;
while((data = br.readLine()) != null){
// bw.write(data + "\n");
bw.write(data);
bw.newLine();//换行
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
try {
if (bw != null)
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
七、处理流之三:对象流
7.1 举例说明序列化过程
//序列化过程:ObjectOutputStream:实现内存中的数据,写入到具体的文件中
@Test
public void testObjectOutputStream() throws Exception {
//1.创建文件和流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
//操作基本数据类型
// oos.writeByte();
// oos.writeBoolean();
//2.操作对象
oos.writeObject(new String("Tom"));
oos.flush();//刷新
oos.writeObject(new String("王辰硕"));
oos.flush();//刷新
//3.关闭资源
oos.close();
}
7.2 举例说明反序列化过程
//反序列化过程:ObjectInputStream:将磁盘文件中保存的对象,还原为内存中的对象
@Test
public void testObjectInputStream() throws IOException, ClassNotFoundException {
//1.
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.dat"));
//2.
String s1 = (String) ois.readObject();
System.out.println(s1);
String s2 = (String) ois.readObject();
System.out.println(s2);
//3.
ois.close();
}
7.3 小结:
对象流的使用:
* 1. ObjectInputStream 和 ObjectOutputStream
* 2. 作用:用于存储和读取基本数据类型数据或对象的处理流。
* 它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
*
*
* 面试题:你是如何理解对象的序列化机制的?
* 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,
* 或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
7.4 要想自定义的类可序列化,则需要满足
/**
* @author shkstart
* @create 2020 下午 4:56
* <p>
* 要想自定义的类可序列化,则需要满足:
* ① 实现接口:java.io.Serializable
* ② 显式声明全局常量:serialVersionUID,用于唯一标识当前类
* ③ 要想当前类的对象可序列化,必须其所有的属性也是可以序列化的
*
* 说明:1. 默认情况下,基本数据类型的变量都可以序列化
* 2. ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
*
*/
public class Person implements Serializable {
static final long serialVersionUID = 43453452L;
private int age;
private String name;
private Account acct;
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", acct=" + acct +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public Person() {
}
public Person(int age, String name, Account acct) {
this.age = age;
this.name = name;
this.acct = acct;
}
}
class Account implements Serializable {
private double balance;
static final long serialVersionUID = 43456453452L;
@Override
public String toString() {
return "Account{" +
"balance=" + balance +
'}';
}
public Account(double balance) {
this.balance = balance;
}
}