IO流复习
今天面试的时候问了一道IO流的题,鉴于之前在java基础篇的时候学过一遍,后再无使用和复习,就再跟着韩顺平老师的课件复习一遍。
IO流
IO流是什么?
当我们使用程序读取或修改本地文件时, 文件是以流的形式加载在内存中的。
- InputStream:输入流,从数据源加载至内存。
- OutputStream:输出流,从存在中加载至目的地。
IO流有哪些分类
输入流,输出流是最宽泛的概念,也是我们上面讲的概念。那为什么还需要分成字节流,字符流,节点流,处理流呢?
字节我们都知道:byte,是一个二进制单位,而我们的文件可以是txt(存储字符的),png(存储图片的),mp3(存储音频的),mp4(存储视频的),但传输的时候我们都将其转化为二进制,因为计算机只能识别二进制,也就是字节。所以字节流的意思就是说:将该文件以字节为单位进行读取,可以一个字节一个字节读取,或者一段字节数组进行重复读取。这样不管是什么文件都可以进行传输了。
那为啥还需要字符流呢?这是已因为字节流是直接作用于文件的,而字符流使用了缓冲区,先将数据写入缓冲区进行读写操作,当输出流close或者flush时,才会把内容写入目的地。一般我们在操作文本内容时,使用字符流会更好一些,而图片,视频如果使用字符流,可能导致文件损坏。
节点流代表的是我们实际操作数据的流,相比包装流会更贴近底层一些,也就是我们使用FileInputStream,FileOutputStream等,而包装流是封装在节点流之上,用来加入缓存,封装接口,进行性能优化等。相当于节点流是mybatis,包装流就是mybatis-plus。
具体使用(这里我只引用常用的):
FileInputStream(节点流,字节流)
请使用 FileInputStream 读取 hello.txt 文件,并将文件内容显示到控制台
public void fileInputStream() throws Exception{
FileInputStream fileInputStream = null;
int readLength = 0;
byte[] bytes = new byte[1024];
try{
fileInputStream = new FileInputStream("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello.txt");
while ((readLength = fileInputStream.read(bytes)) != -1) {
System.out.println(new String(bytes,0,readLength));
}
} catch (Exception e) {
System.out.println("哪里出现问题了");
} finally {
fileInputStream.close();
}
}
FileOutputStream(节点流,字节流)
请使用 FileOutputStream 在 hello.txt 文件,中写入 “hello,world”, 如果文件不存在,会创建
文件(注意:前提是目录已经存在.)
public void fileOutputStream() throws Exception{
File file = new File("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello.txt");
if (!file.exists()) {
file.createNewFile();
}
// 这里构造器如果加true,表示追加模式,如果不写,默认是覆盖原来内容
FileOutputStream fileOutputStream = new FileOutputStream(file,true);
try {
byte[] bytes = "hello,world---".getBytes();
fileOutputStream.write(bytes);
} finally {
fileOutputStream.close();
}
}
图片文件的拷贝
public void fileCopy() throws Exception{
FileInputStream fileInputStream = new FileInputStream("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/wallhaven.png");
FileOutputStream fileOutputStream = new FileOutputStream("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/wallhaven_.png");
int readLen = 0;
byte[] bytes = new byte[1024];
try {
while ((readLen = fileInputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes,0,readLen);
}
} finally {
fileInputStream.close();
fileOutputStream.close();
}
}
FileReader(节点流,字符流)
使用 FileReader 从 story.txt 读取内容,并显示
public void fileReader() throws IOException {
FileReader fileReader = new FileReader("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello.txt");
int readLen = 0;
char[] chars = new char[1024];
try {
while ((readLen = fileReader.read(chars)) != -1) {
System.out.println(new String(chars,0,readLen));
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
fileReader.close();
}
}
FileWriter(节点流,字符流)
使用 FileWriter 将 “风雨之后,定见彩虹” 写入到 note.txt 文件中, 注意细节.
@Test
public void fileWriter() throws IOException {
// 使用 FileWriter 将 “风雨之后,定见彩虹” 写入到 note.txt 文件中, 注意细节.
FileWriter fileWriter = new FileWriter("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello.txt", true);
try {
fileWriter.write("风雨之后,定见彩虹");
} finally {
// 一定要关闭流 ,数据才会写入到文件中,或者使用fileWriter.flush();
fileWriter.close();
}
}
BufferReader(包装流,字符流)
使用BufferReader读取文件,并打印到控制台
@Test
public void bufferedReader() throws IOException {
BufferedReader bufferedReader = new BufferedReader(new FileReader("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello.txt"));
String readLen;
try {
// readLine() 按照行读取,效率高
while ((readLen = bufferedReader.readLine()) != null) {
System.out.println(readLen);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
// 这里包装流关闭时,节点流底层自动关闭了
bufferedReader.close();
}
}
BufferWriter(包装流,字符流)
bufferWriter 写 文件
public void bufferWriter() throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello.txt"));
try {
bufferedWriter.write("hello");
// 换行
bufferedWriter.newLine();
bufferedWriter.write("world");
} finally {
bufferedWriter.close();
}
}
bufferReader,bufferedWriter 拷贝文件
public void fileCopyByBuffered() throws IOException {
BufferedReader bufferedReader = new BufferedReader(new FileReader("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello.txt"));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello_.txt"));
String readLen;
try {
// 注意这里的bufferedReader.readLine()为换行符时,默认返回的是空字符,所以导致原本换行的地方,现在直接拼接了
while ((readLen = bufferedReader.readLine()) != null) {
if ("".equals(readLen)) {
// 这样就能保证正常输出了,还有一点比方说:\n\n\n 实际上中间只空了两行
readLen+="\n";
}
bufferedWriter.write(readLen);
}
} finally {
bufferedWriter.close();
bufferedReader.close();
}
}
BufferInputStream(包装流,字节流)
BufferOutputStream(包装流,字节流)
BufferInputStream,BufferOutputStream拷贝文件
public void fileCopyByBufferedStream() throws IOException {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/wallhaven.png"));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/wallhaven__.png"));
byte[] bytes = new byte[1024];
int readLen = 0;
try {
while ((readLen = bufferedInputStream.read(bytes)) != -1) {
bufferedOutputStream.write(bytes,0,readLen);
}
} finally {
bufferedInputStream.close();
bufferedOutputStream.close();
}
}
ObjectInputStream(对象流,字节流)
对象流就是我们将java对象进行序列化,存储在文件中,并可以通过对象流反序列化回来,要序列化的对象一定需要实现Serializable
,其属性也需要实现,基本数据类型会自动变成包装类,而包装类是已经实现了的。
序列化一个dog对象到文件中,并再反序列化回来
public void objectInputStream() throws IOException, InterruptedException, ClassNotFoundException {
// FileOutputStream() 如果没有使用追加默认 会直接将文件清空
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/dog.dat"));
try {
objectOutputStream.writeObject(new dog("旺财",123));
objectOutputStream.writeObject(1);
System.out.println("输出成功");
} finally {
objectOutputStream.close();
}
Thread.sleep(500);
// ObjectInputStream 在读取数据文件时候会判断 magic value,version value,来判断文件是否正确,如果不正确会爆出EOP错误
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/dog.dat"));
try {
Object o = objectInputStream.readObject();
System.out.println((dog)o);
System.out.println(objectInputStream.readObject());
System.out.println("读取成功");
} finally {
objectInputStream.close();
}
}
ObjectOutputStream(对象流,字节流)
InputStreamReader (转化流,字节流)
为什么需要转化流?
因为字符编码问题,当我们使用BufferedReader读txt文件时,默认使用Utf-8编码,现在想用其他编码格式,就可以将字节流转为字符流,然后指定编码规则,其实字符流本身底层使用的还是字节流,只是针对字符内容做出了优化。
字节流FileInputStream 包装成字节流InputStreamReader,对文件进行读取(按照 utf-8/gbk 格式),进而在包装成BufferedReader。
public void InputStreamReader() throws IOException {
// 将字节流FileInputStream 包装成字符流 InputStreamReader,对文件进行读取(utf-8/gbk格式),进而包装成BufferedReader
FileInputStream fileInputStream = new FileInputStream("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello.txt");
// 底层默认使用的就是UTF-8 ,我们也可以使用gbk等
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
try {
String s = bufferedReader.readLine();
System.out.println("读取到的内容="+s);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
bufferedReader.close();
}
}
OutputStreamWriter(转化流,字节流)
使用gbk编码输出文本
public void OutputStreamWriter() throws Exception {
FileOutputStream fileOutputStream = new FileOutputStream("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello.txt");
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "gbk");
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
try {
bufferedWriter.write("hello Jvav 世界");
} finally {
bufferedWriter.close();
}
}
序列化需要注意的点
- 读写顺序一定一致
- 序列化,反序列化对象,需要实现Serializable
- 序列化的类中加入 SerialVersionUID,提高版本的兼容性
- 序列化对象时,其属性也都必须实现序列化接口
- 序列化具有继承性
课后练习
- 创建文件,并写入内容
- 利用转化流,改变字符编码
- 读取properties文件,创建对象
- 将对象序列化
@Test
public void finalTest1() throws Exception{
// 1. 判断磁盘是否有mytemp文件,如果没有就创建mytemp
// 创建文件前需要保证该路径是否存在,如果不存在应先创建对应的包
File file = new File("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/pack1/pack2/pack3/mytemp");
if (!file.getParentFile().exists()) {
boolean mkdirs = file.getParentFile().mkdirs();
System.out.println(mkdirs?"创建前提包":"创建失败");
}
if (!file.exists()) {
boolean newFile = file.createNewFile();
System.out.println(newFile?"成功创建":"创建失败");
} else {
// 2. 如果该文件已经存在 则输出文件已经存在,请不要重复创建了
System.out.println("文件已经存在,请不要重复创建了");
}
// 3. 在该文件中追加hello , world
FileOutputStream fileOutputStream = new FileOutputStream(file,true);
try {
fileOutputStream.write("hello,world".getBytes());
System.out.println("数据写入成功");
} finally {
fileOutputStream.close();
}
}
@Test
public void finalTest2() throws Exception{
// 1. 使用BufferedReader 读取一个文本文件 为每一行加一个句号,并输出到屏幕
FileReader fileReader = new FileReader("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String readLen;
try {
while ((readLen = bufferedReader.readLine()) != null) {
// 如果不加\n 默认换行只返回"",所有内容挤在一起
System.out.println(readLen+".\n");
}
System.out.println("输出结束");
} finally {
bufferedReader.close();
}
// 2.如果文本是gbk模式 出现了乱码
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/hello.txt"), "gbk");
BufferedReader bufferedReader1 = new BufferedReader(inputStreamReader);
char[] chars = new char[1024];
int readSize = 0;
try {
// while ((readSize = inputStreamReader.read(chars)) != -1) {
// System.out.println(new String(chars,0,readSize));
// }
while ((readLen=bufferedReader1.readLine()) != null) {
System.out.println(readLen);
}
} finally {
bufferedReader1.close();
}
}
@Test
public void finalTest3() throws IOException {
// 编写一个dog.properties 含有:name,age
// 编写一个dog类,创建一个dog对象,并读取dog.properties相应的属性,并完成初始化。
Properties properties = new Properties();
// 这里需要注意 IDEA的properties文件默认使用的编码ISO-8859-1,要么改为utf-8,要么读的时候改变字符编码格式
FileReader fileReader = new FileReader("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/dog.properties");
properties.load(fileReader);
properties.list(System.out);
// 实例化dog对象
dog dog = new dog(properties.getProperty("name"), Integer.parseInt(properties.getProperty("age")));
System.out.println(dog);
// 关闭流
fileReader.close();
// 将dog对象序列化到dog.dat文件
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("/Users/qinsicheng/IdeaProjects/IOStream/com/qinsicheng/dog.dat"));
objectOutputStream.writeObject(dog);
System.out.println("输出成功");
objectOutputStream.close();
}
我们上面列举到的实例,只是针对本地文件读取,这个最简单的,而真正需要我们去运用的是在网络IO中。不过基础还是得先打好。