简介
- IO流的输入与输出,都在站在内存的角度来看的,因为毕竟是和内促你打交道的嘛!
分类
IO流是可以根据方向,或者最小单位进行划分的
上述两两结合一下,就得到四种大的分类
IO流的继承体系
字节输入流InputStream
- 创建字节输入流对象
- 读取字节
创建流对象
// 创建一个文件输入流 用于链接文件 // File 对象的路径可以存在也可以不存在 // 但是文件输入流的对象是必须要存在的,所以这里需要抛出一个异常 // InputStream is = new FileInputStream( new File("D:\\新建 XLSX 工作表.txt")); InputStream is = new FileInputStream("D:\\新建 XLSX 工作表.txt");
使用循环一个个字节输入
- 效率低下
- 读到汉字必定乱码(一个个字节读取,但是汉字至少是三个字节)
// 读取字节 // 1. 读取一个字节 返回的是一个int类型的数据 // 2. 读取到文件的末尾 返回-1 int i ; while ( (i=is.read()) != -1) { System.out.print((char) i); }
关闭流
// 关闭流 释放资源 is.close();
读取多个字节
- 返回的事读取的字节数
- 最后一次读取肯定不能读满,一定要遵循一个原理:读取到多少个字节就输出多少个字节
- 效率得到提升,但是不可避免汉字乱码的问题
InputStream is = new FileInputStream("D:\\新建 XLSX 工作表.txt"); // 读取字节 一般大小都是1024的整数倍 byte[] buffer = new byte[1024 * 8]; int len; try { // read(buffer) 方法返回的是读取到的字节数 读取到文件末尾返回-1 while ((len = is.read(buffer)) != -1) { // 应该是读取到多少个字节就输出多少个字节 不然会造成数据错误 System.out.println(new String(buffer, 0, len)); } } catch (Exception e) { e.printStackTrace(); } finally { try { is.close(); } catch (Exception e) { e.printStackTrace(); } }
- 上述情况证明了:字节流的读取,只适合非字符的,适合非文本类型的
- 文本类型的得使用字符流,不要使用字节流
读取全部字节
- 一次读取全部字节能够解决中文乱码的问题
- 但是文件过大,就会有潜在问题
File file = new File("D:\\新建 XLSX 工作表.txt"); InputStream is = new FileInputStream(file); long length = file.length(); // 一次性读取所有的字节(这样就避免了乱码的问题) // byte[] 数组接受的长度是int类型的 但是文件的大小很有可能超过int的范围 byte[] buffer = new byte[(int) length ]; int read = is.read(buffer); // 读取到的字节数 一般情况下是等于文件的大小 System.out.println(new String(buffer, 0, read)); // 关闭流 释放资源 is.close();
Java提供了现成的方法
此方法是jdk17之后才有的,jdk8的项目不要这么写
InputStream is = new FileInputStream("D:\\新建 XLSX 工作表.txt"); byte[] buffer = is.readAllBytes(); System.out.println(new String(buffer));
文件字节输出流FileOutPutStream
创建API
- 若非是提前获取到File类型的对象,应该使用第2个和第4个方法
- 尽量使用第4个方法,告知使用追加数据
// 创建一个文件输出流 // 这个地方虽然会抛出文件找不到异常,但是如果文件不存在,会自动创建一个新文件 // 这里的true表示追加写入,如果不加true,会覆盖原文件 // 这里的是否追加是针对流的,而不是文件的(文件不存在可不可以追加这一说) OutputStream os = new FileOutputStream("test.txt",true);
输出API
// 写入数据 一个个字节写入的 // 可以写入ASCII,也可以写入字符 // 非ASCII的字符会导致乱码 os.write(97); os.write('b');
// 写入字节数组 一次写入多个字节 可以规定起始位置和长度 byte[] bytes = "hello我爱你中国".getBytes(); os.write(bytes); os.write(bytes, 0, 8);
特殊符号
换行符:
- \n :windows
- \r\n:所有平台的换行符
// 可以提前将换行符声明为一个字节数组,然后写入 byte[] huanHang = "\r\n".getBytes(); os.write(huanHang); os.write(99);
案例-文件复制
- 定义输入流-链接原文件
- 定义输出流-链接新文件
- 循环读取数据
- 将循环读取的数据写入到新文件中
// 创建一个文件输出流 写入文件 // 复制文件的时候不要定义append为true,否则会追加写入 OutputStream os = new FileOutputStream("test2.txt"); // 定义一个字节数组,用来存放读取到的数据 byte[] bytes = new byte[1024 * 8]; // 循环读取数据 try { int len = 0; // 读取数据到字节数组中,返回读取到的字节数 while ((len = is.read(bytes)) != -1) { // 循环写入数据 从0开始,写入len个字节 os.write(bytes, 0, len); } } catch (Exception e) { e.printStackTrace(); }finally { // 关闭流 try { is.close(); os.close(); } catch (IOException e) { e.printStackTrace(); } }
- 前面不是说文件字节流只适合非文本的文件吗,为什么文件复制的时候不会出问题呢?
- 因为每次复制的都是全部的内容,不管是ASCII的,还是非ASCII的,最后都会完完整整地复制过来
释放资源
try-catch-finally
- 常见的释放资源的方式是:在try中包裹对IO流的操作,在finally中释放资源(输入流和输出流)
- finally 非常强大,即使你已经return了,他还会坚持把finally中代码执行一遍
- 只有一种情况finally执行不到,就是JVM停了,例如调用System.exit(0)
InputStream is = null; OutputStream os = null; // 循环读取数据 try { // 创建一个文件输入流 读取文件 is = new FileInputStream("test.txt"); // 创建一个文件输出流 写入文件 // 复制文件的时候不要定义append为true,否则会追加写入 os = new FileOutputStream("test2.txt"); // 定义一个字节数组,用来存放读取到的数据 byte[] bytes = new byte[1024 * 8]; int len = 0; // 读取数据到字节数组中,返回读取到的字节数 while ((len = is.read(bytes)) != -1) { // 循环写入数据 从0开始,写入len个字节 os.write(bytes, 0, len); } } catch (Exception e) { e.printStackTrace(); }finally { // 关闭流 try { if (is != null) is.close(); if (os != null) os.close(); } catch (IOException e) { e.printStackTrace(); } }
try-with-resourses
- 上面的写法很专业,但是很复杂,很臃肿
- try-with-resourses 免去了手动书写finally的过程
- try的括号里面之定义需要关闭的资源对象,不要定义其他的东西
- 这里的资源基本指的是java官方定义的资源,数据流啊,数据库链接啊,不包括一些小众的资源。官方资源之所以能够自动关闭,是因为实现了接口 AutoClosable
- 如果自定义的资源也想使用上述语法,就必须是先实现接口AutoClosable
try (// 将资源放在try的括号中,try中的代码执行完毕之后,会自动关闭资源 InputStream is = new FileInputStream("test.txt"); OutputStream os = new FileOutputStream("test2.txt"); ) { // 定义一个字节数组,用来存放读取到的数据 byte[] bytes = new byte[1024 * 8]; int len = 0; // 读取数据到字节数组中,返回读取到的字节数 while ((len = is.read(bytes)) != -1) { // 循环写入数据 从0开始,写入len个字节 os.write(bytes, 0, len); } } catch (Exception e) { e.printStackTrace(); }
注意事项