一、IO流概述

1.1 流的简介
  1. IO Input Output
  2. 通过IO可以完成硬盘文件的读和写
  • 往内存中去,叫做输入(Input)。或者叫做读(Read)
  • 从内存中出来,叫做输出(Output)。或者叫做写(Write)

二、流的分类

2.1 字符流和字节流
  1. 字符流(读取普通文本,读不了音频、视频、图片)
  2. 字节流(读取任意文件类型file)
  • byte字节表示计量单位,我们可以设置流对象一次性可以读取多少个字节
  • 字节流读取数据的方式是读取字符对应的asclI码值,每一个字符都有对应的ascll;

    如:一个文件中有字符串’ABCDEFG’ 字符流按照一个字符一个字符的读取; 字节流是一次性读取一个字节,但是读取的是每个字符对应的ascll码

    1. 有的流是按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。
    2. 按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的。

2.2 输入流和输出流
  1. 定义

    文件 <—> 内存
    输入流 :就是将文件中的数据读取到内存中
    输出流: 将内存中的数据写到文件中

  2. 按操作分类:

  • 字节输入流
  • 字节输出流
  • 字符输入流
  • 字符输出流
  1. 以stream结尾的就是字节流,以reader/writer结尾的就是字符流
  • java.io.InputStream字节输入流
  • java.io.OutputStream字节输出流
  • java.io. Reader字符输入流
  • java.io. Writer字符输出流
  1. 所有的流都实现了java.io.Closeable接口
  • 都是可关闭的,都有close()方法。
  • 所有的输出流都实现了java.io. Flushable接口,都是可刷新的,都有flush()方法。防止写入管道存在残留数据。
  1. 文件专属流
  • java.io.FileInputStream
  • java.io. FileOutputStream
  • java.io. FileReader
  • java.io. FileWriter

    三、文件流读写操作

    ```java FileInputStream fileInputStream = null;

//创建流对象 try {

  1. fileInputStream = new FileInputStream("D:\\temp\\IOTest01.java");
  2. //创建一个字节数组 一次性可以读取多个字节
  3. //存储数据采取覆盖机制。
  4. byte[] bytes = new byte[500];
  5. //read用来存储读取到的字节数;
  6. int read = 0;
  7. //遍历完了会返回-1
  8. while ((read = fileInputStream.read(bytes)) != -1){
  9. //将字节数组中的ascll转成字符串
  10. //参数:数组,起始位置,转换个数;
  11. String str = new String(bytes,0, read);
  12. System.out.print(str);
  13. }

} catch (FileNotFoundException fileNotFoundException) {

  1. fileNotFoundException.printStackTrace();

} catch (IOException ioException) {

  1. ioException.printStackTrace();

} finally {

  1. //关闭流对象
  2. try {
  3. fileInputStream.close();
  4. } catch (IOException ioException) {
  5. ioException.printStackTrace();
  6. }

}

  1. ```java
  2. FileOutputStream fileOutputStream = null;
  3. //创建流对象
  4. try {
  5. fileOutputStream = new FileOutputStream("D:\\temp\\aaa.txt");
  6. //写数据到文件中
  7. fileOutputStream.write(65);
  8. //创建一个字节数组
  9. byte[] bytes = {66,67,68,69,70,71};
  10. fileOutputStream.write(bytes);
  11. //将字符串写入文件中
  12. String str = "\nhello world";
  13. //将字符串转成字节数组
  14. byte[] strBytes = str.getBytes();
  15. fileOutputStream.write(strBytes,0,3);
  16. //刷新
  17. fileOutputStream.flush();
  18. } catch (FileNotFoundException fileNotFoundException) {
  19. fileNotFoundException.printStackTrace();
  20. } catch (IOException ioException) {
  21. ioException.printStackTrace();
  22. } finally {
  23. //关闭流对象
  24. if (fileOutputStream != null){
  25. try {
  26. fileOutputStream.close();
  27. } catch (IOException ioException) {
  28. ioException.printStackTrace();
  29. }
  30. }
  31. }

案例,拷贝文件
  1. package com.eaglslab.copy;
  2. import java.io.FileInputStream;
  3. import java.io.FileNotFoundException;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. public class FileCopyUtils {
  7. private static FileInputStream fileInputStream = null;
  8. private static FileOutputStream fileOutputStream= null;
  9. private FileCopyUtils(){
  10. }
  11. /**
  12. *
  13. * @param inputPath 输入路径
  14. * @param outputPath 输出路径
  15. */
  16. public static void FileCopy(String inputPath,String outputPath){
  17. try {
  18. //输入对象
  19. fileInputStream = new FileInputStream(inputPath);
  20. //输出对象
  21. fileOutputStream = new FileOutputStream(outputPath);
  22. int read = 0;
  23. //创建字节数组
  24. byte[] bytes = new byte[1024 * 1024];
  25. while ((read = fileInputStream.read(bytes)) != -1){
  26. //一边读一边写,读取到多少个就写多少个
  27. fileOutputStream.write(bytes,0,read);
  28. }
  29. //刷新
  30. fileOutputStream.flush();
  31. } catch (Exception e) {
  32. e.printStackTrace();
  33. }finally {
  34. //关闭流
  35. if (fileInputStream != null){
  36. try {
  37. fileInputStream.close();
  38. } catch (IOException ioException) {
  39. ioException.printStackTrace();
  40. }
  41. }
  42. if (fileOutputStream != null){
  43. try {
  44. fileOutputStream.close();
  45. } catch (IOException ioException) {
  46. ioException.printStackTrace();
  47. }
  48. }
  49. }
  50. }
  51. }

四、文件缓冲流操作BufferedReader

关于缓存流的使用:自带缓存区的流,在一次性读取数据的时候,可以不用创建数组来进行操作
可以通过缓冲流来提升效率,不必创建字符数组和字节数组;

  1. public static void main(String[] args) throws Exception {
  2. //缓存流对象 缓冲流(字节流转字符流)
  3. BufferedReader bufferedReader = new BufferedReader(new FileReader("D:\\temp\\IOTest01.java"));
  4. String readLine = null;
  5. while ((readLine = bufferedReader.readLine()) != null){
  6. System.out.println(readLine);
  7. }
  8. //关闭流
  9. bufferedReader.close();
  10. }

五、数据流使用DataOutputStream

  1. 数据流的使用:会将数据按照特定的数据类型写入文件中,然后在将数据按照数据类型进行取出。

    写入的文件是加密的,必须按写入顺序读取。

    1. public static void main(String[] args) throws Exception {
    2. //创建数据输出流对象,会新建一个data.txt文件
    3. DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("data.txt"));
    4. boolean flag = true;
    5. float f = 10f;
    6. int i = 100;
    7. double d = 3.14;
    8. //写入数据
    9. dataOutputStream.writeBoolean(flag);
    10. dataOutputStream.writeFloat(f);
    11. dataOutputStream.writeInt(i);
    12. dataOutputStream.writeDouble(d);
    13. //刷新
    14. dataOutputStream.flush();
    15. //关闭
    16. dataOutputStream.close();
    17. }
  2. 在进行数据读取时,我们根据我们写入的顺序进行读取,不然会导致数据错乱。

    1. //创建数据输出流对象
    2. DataInputStream dataInputStream = new DataInputStream(new FileInputStream("data.txt"));
    3. boolean aBoolean = dataInputStream.readBoolean();
    4. float readFloat = dataInputStream.readFloat();
    5. int i = dataInputStream.readInt();
    6. double v = dataInputStream.readDouble();
    7. System.out.println(aBoolean);
    8. System.out.println(readFloat);
    9. System.out.println(i);
    10. System.out.println(v);
    11. //关闭
    12. dataInputStream.close();

六、标准输出流PrintStream

  1. public static void main(String[] args) throws Exception {
  2. //创建标准输出流对象
  3. PrintStream printStream = new PrintStream(new FileOutputStream("write.txt"));
  4. //设置输出路径
  5. System.setOut(printStream);
  6. //默认情况下输出路径是控制台,没有设置输出路径就会默认打印在控制台
  7. System.out.println("hello java");
  8. printStream.flush();
  9. printStream.close();
  10. }

案例 ;完成一个日志收集工具。
  1. 收集日志对应的事件和信息,通过标准输出流的方式写入文件内。<br />日期格式化对象:
  1. //获取当前系统的时间
  2. Date date = new Date();
  3. //创建日期格式化对象
  4. SimpleDateFormat simpleDateFormat = new SimpleDateFormat( pattern: "yyy-MM-dd hh :mm:ss");
  5. //格式化日期
  6. String dateTime = simpleDateFormat . format (date);
  7. System. out. println(dateTime);
  1. public static void logger(String message){
  2. PrintStream printStream = null;
  3. try {
  4. //创建标准输出流对象 添加true就是开始追加,避免覆盖机制
  5. printStream = new PrintStream(new FileOutputStream("logger.txt",true));
  6. //设置输出路径
  7. System.setOut(printStream);
  8. //创建日期格式化对象
  9. String dateTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
  10. System.out.println(dateTime+" : "+message);
  11. //刷新
  12. printStream.flush();
  13. } catch (FileNotFoundException fileNotFoundException) {
  14. fileNotFoundException.printStackTrace();
  15. }finally {
  16. if (printStream != null){
  17. printStream.close();
  18. }
  19. }
  20. }

七、对象专属流ObjectOutputStream

  1. 对象专属流:专门用于操作对象,可以将javaa对象序列化到一个文件夹中,在通过序列化的方式取出来。
  • 序列化
  • 反序列化

反序列化取出文件中的java对象

  1. public static void main(String[] args) throws Exception {
  2. //创建序列化对象
  3. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("object.txt"));
  4. //创建用户对象
  5. User user1 = new User("zs001", "q123456");
  6. User user2 = new User("zs002", "a123456");
  7. User user3 = new User("zs003", "s123456");
  8. User user4 = new User("zs004", "d123456");
  9. User user5 = new User("zs005", "f123456");
  10. //创建集合对象
  11. ArrayList<User> userArrayList = new ArrayList<>();
  12. userArrayList.add(user1);
  13. userArrayList.add(user2);
  14. userArrayList.add(user3);
  15. userArrayList.add(user4);
  16. userArrayList.add(user5);
  17. //将java对象写到文件中
  18. //list已经继承了Serializable接口
  19. objectOutputStream.writeObject(userArrayList);
  20. //如果直接写入对象,自定义的对象需要先继承一个接口(Serializable)序列化接口
  21. objectOutputStream.writeObject(user1);
  22. }
  1. 序列化相关操作:
  • 如果直接使用对象专属流写入对象,自定义的对象需要先继承一个接口(Serializable)序列化接口。
  • 取消序列化操作,transient关键字 修饰的属性不再参与序列化。

    1. private transient String name
  • 只有序列化的数据才能通过反序列化操作取出。

  • 对类继承接口后,进行序列化操作后,不能再修改该类的数据,会导致序列化版本号不同,导致报错。需要写死一个序列化版本号。
  • 写死序列化版本号之后再添加属性数据,就不会出现版本号冲突。

    操作如下 private static final long serialVersionUID = 版本号;

  1. 案例

    描述: 购物车案例:完成对购物车商品的增删改查。将商品的数据通过序列化的方式放到文件中。

  • 编辑商品类,并继承Serializable。
  • 通过序列化读取 objectOutputStream.writeObject写入对象。
  • 编辑商品管理类;