概念
IO 流:按照流动的方向,以内存为基准,分为输入 input 和输出 output,即流向内存是输入流,流出内存是输出流
进行IO操作前需要明确四个关键点:
- 明确要操作的数据是数据源还是数据目的(读还是写)
- 明确要操作的设备上的数据是字节还是文本
- 明确数据所在的具体设备
- 明确是否需要额外功能(转换流、高效流等)
输入流(读数据)、输出流(写数据)
分类
| 类型 |
输入流 |
输出流 |
| 字节流 |
InputStream |
OutputStream |
| 字符流 |
Reader |
Writer |
字符流的由来:因为数据编码的不同,字节流直接读中文会乱码。字符流 = 字节流 + 编码表
1 个字符 = 2 个字节
- 字节流适合读取视频、音乐、图片等二进制文件
- 字符流适合读取纯文本文件
File 类
java.io.File 类是专门对文件进行操作的类,只能对文件本身进行操作,不能对文件进行读和写(输入和输出)
File 类构造方法不会检验文件或文件夹是否真实存在,无论该路径下是否存在文件或目录,都不影响 File 对象的创建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| package com.nwa;
import org.junit.Test; import java.io.*;
public class FileDemo { @Test public void fun1() throws IOException { File f = new File("C:\\Users\\FF\\Desktop\\1.txt"); if (!f.exists()) { System.out.println("是否创建:" + f.createNewFile()); } System.out.println("是否创建:" + f.createNewFile()); System.out.println("文件绝对路径:" + f.getAbsolutePath()); System.out.println("文件构造路径:" + f.getPath()); System.out.println("文件名称:" + f.getName()); System.out.println("文件长度:" + f.length() + "字节"); System.out.println("文件:" + f.isFile()); System.out.println("目录:" + f.isDirectory()); }
@Test public void testDemo2() { File f2 = new File("newDira"); System.out.println("是否存在:" + f2.exists()); System.out.println("是否创建:" + f2.mkdir()); System.out.println("是否存在:" + f2.exists());
File f3 = new File("newDira\\newDirb"); System.out.println(f3.mkdir()); System.out.println(f3.mkdirs());
System.out.println(f2.delete()); System.out.println(f3.delete()); }
@Test public void demo3() { File file = new File("E:\\other\\study\\学习sp\\实用篇"); printFile(file); }
public static void printFile(File file) { if (!file.isDirectory()) { return; } File[] files = file.listFiles(); for (File f : files) { if (f.isDirectory()) { printFile(f); } else { System.out.println(f.getName()); } } } }
|
IO 流详解
数据流向内存就是输入流,流出内存就是输出流
FileOutputStream
1
| FileOutputStream outputStream = new FileOutputStream("abc.txt");
|
创建输出流对象时做了三件事:
- 调用系统功能去创建文件(输出流对象才会自动创建)
- 创建 outputStream 对象
- 把 outputStream 对象指向这个文件
注意事项:
- 创建输出流对象时,如果没有这个文件,会创建该文件
- 如果有这个文件,会清空这个文件的数据
- 创建输入流对象时,文件不存在则会抛出 FileNotFoundException
追加数据构造方法:
public FileOutputStream(File file, boolean append)
public FileOutputStream(String name, boolean append) — true 表示追加数据
换行符说明:
- Windows 系统:回车+换行
\r\n
- Unix 系统:换行
\n
- Mac 系统(OS X 起):换行
\n
流进内存的,输入流
1 2 3 4 5 6 7 8 9 10 11 12
| int b; while ((b = fis.read()) != -1) { System.out.print((char) b); }
int len; byte[] bys = new byte[1024]; while ((len = fis.read(bys)) != -1) { System.out.println(new String(bys, 0, len)); }
|
FileWriter
写出字符到文件的便利类(输出流)
关闭 close 和刷新 flush 的区别:
- flush:刷新缓冲区,流对象可以继续使用
- close:先刷新缓冲区,然后通知系统释放资源,流对象不可再使用
如果不关闭输出流,数据只是保存到缓冲区,并未保存到文件。
缓冲流
缓冲流原理:
- 使用底层流对象从具体设备上获取数据,存储到缓冲区数组内
- 通过缓冲区的 read() 方法获取具体字符数据,提高效率
- readLine() 功能:读取到换行符时,将临时存储的数据转成字符串返回
在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统 IO 次数,从而提高读写效率。
| 类型 |
输入流 |
输出流 |
| 字节缓冲流 |
BufferedInputStream |
BufferedOutputStream |
| 字符缓冲流 |
BufferedReader |
BufferedWriter |
BufferedReader 特有方法:
public String readLine() — 读一行数据,读取到最后返回 null
BufferedWriter 特有方法:
public void newLine() — 换行,由系统属性定义符号
转换流
编码与解码:
- 编码:字符(能看懂的)→ 字节(看不懂的)
String → byte[]
- 解码:字节(看不懂的)→ 字符(能看懂的)
byte[] → String
1 2 3 4 5
| new String(byte[] bytes, String charsetName)
byte[] getBytes(String charsetName)
|
转换流 java.io.InputStreamReader 是 Reader 的子类,从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。
1 2 3 4 5
| InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt"), "GBK");
|
实战案例
字节流写入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| FileOutputStream fos = new FileOutputStream("a.txt", true); fos.write(97); fos.write("我要吃汉堡".getBytes()); fos.close();
FileOutputStream fos2 = new FileOutputStream("a2.txt"); byte[] b = "abcde".getBytes(); fos2.write(b, 2, 2); fos2.close();
FileOutputStream fos3 = new FileOutputStream("fos.txt"); byte[] words = {97, 98, 99, 100, 101}; for (int i = 0; i < words.length; i++) { fos3.write(words[i]); fos3.write("\r\n".getBytes()); } fos3.close();
|
图片复制
1 2 3 4 5 6 7 8 9
| FileInputStream fis = new FileInputStream("C:\\Users\\FF\\Desktop\\6.png"); FileOutputStream fos = new FileOutputStream("1.png"); byte[] b = new byte[1024]; int len; while ((len = fis.read(b)) != -1) { fos.write(b, 0, len); } fos.close(); fis.close();
|
字符流复制文本
1 2 3 4 5 6 7 8 9 10
| FileWriter fileWriter = new FileWriter("66.md"); FileReader fileReader = new FileReader("E:\\后端代码接收解析.md"); char[] c = new char[1024]; int len; while ((len = fileReader.read(c)) != -1) { fileWriter.write(c, 0, len); } fileWriter.flush(); fileWriter.close(); fileReader.close();
|
缓冲流效率对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| FileOutputStream fos = new FileOutputStream("ps2015.zip"); FileInputStream fis = new FileInputStream("E:\\other\\装机必备软件\\必备软件\\ps2015.zip"); byte[] bytes = new byte[1024]; int len; while ((len = fis.read(bytes)) != -1) { fos.write(bytes, 0, len); } fis.close(); fos.close();
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("ps2015.zip")); BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\other\\装机必备软件\\必备软件\\ps2015.zip")); byte[] bytes2 = new byte[1024]; int len2; while ((len2 = bis.read(bytes2)) != -1) { bos.write(bytes2, 0, len2); } bis.close(); bos.close();
|
BufferedReader 读取一行
1 2 3 4 5 6
| BufferedReader br = new BufferedReader(new FileReader("b.txt")); String len; while ((len = br.readLine()) != null) { System.out.println(len); } br.close();
|
转换流解决乱码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| String fileName = "C:\\Users\\FF\\Desktop\\1.txt";
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream(fileName), "GBK");
int read; while ((read = isr2.read()) != -1) { System.out.print((char) read); } isr2.close();
|