• java中的重要IO流


    IO

    四大顶流
    java四大流
    字节流
    字符流
    InputStream
    OutputStream
    Reader
    Writer
    Closeable
    Flushable
    1. 所有以Reader结尾的都是输入字符流;所有以Writer结尾的都是输出字符流;
    2. 四大流都实现了Closeable接口,说明都可以关闭,都有close()方法。流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭。不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭.
    3. 只有输出流实现了Flushable接口。养成一个好习惯,输出流在最终输出之后,一定要记得flush()
      刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道!)刷新的作用就是清空管道。注意:如果没有flush()可能会导致丢失数据.
    文件专属流

    FileInputStream FileOutputStream FileReader FileWriter

    1. FileInputStream 万能流, 任何类型的文件都可以以字节读入到内存,但是一次读取一个字节byte ,这样内存和硬盘交互太频繁,基本上时间资源都耗费在交互上面了。标准模版:
    FileInputStream fis = null;
    try {
       fis = new FileInputStream("src/Test.java");
       // 这种available()方法开辟空间不适合大文件
       byte[] bytes = new byte[fis.available()];
       int readcount;
       // read方法可以接收一个byte数组,每次读取byte数组长度的字节,并返回实际读到的字节数,因为有可能
       // 文件最后一次读取时读不满 byte.length 个字节。当 readcount=-1时,表示读不到数据了。
       while((readcount = fis.read(bytes)) != -1) {
          // String又一个构造方法是可以传入一个byte数组,并设置从哪个索引开始,将多少个字节数组元素转换为
          // String
          System.out.println(new String(bytes, 0, readcount));
       }
    } catch (IOException e) {
       e.printStackTrace();
    } finally {
       try {
          if (fis != null)
             fis.close();
       } catch (IOException e) {
          e.printStackTrace();
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    FileInputStream 其他常用方法:

    • available() 返回当前流中文件剩余没有读到的字节数量
    • skip(n) 跳过n个字节不读
    1. FileOutputStream
    FileOutputStream fos =null;
    try {
       // 如果该文件不存在则创建文件,如果存在则会覆盖原有内容
       fos = new FileOutputStream("/Users/ymy/test.txt");
       // 加上true后保证是在原有内容上追加
       fos = new FileOutputStream("/Users/ymy/test.txt", true);
       byte[] bytes = {101, 102, 23, 33};
       // 将整个字节数组写入
       fos.write(bytes);
       // 从0索引开始写入2个元素
       fos.write(bytes, 0, 2);
       // 写完之后一定要刷新
       fos.flush();
    } catch (IOException e) {
       e.printStackTrace();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. FileReader 只能读取文本文件
    FileReader fr = null;
    try {
       fr = new FileReader("/Users/ymy/test.txt");
       char[] chars = new char[6];
       int readCount = 0;
       while ((readCount = fr.read(chars)) != -1)
          System.out.println(new String(chars, 0, readCount));
    } catch (IOException e) {
       e.printStackTrace();
    } finally {
       if (fr != null) {
          try {
             fr.close();
          } catch (IOException e) {
             e.printStackTrace();
          }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    1. FileWriter 只能输出普通文本
    FileWriter fw = null;
    try {
       // 逻辑和FileOutputStream一样
       fw = new FileWriter("/Users/ymy/test.txt", true);
       // FileWriter 支持4种写入方式
       char[] chars = {'我', '是', '中', '国', '人'};
       fw.write(chars);
       fw.write(chars, 0, 3);
       fw.write(new String("哈哈哈哈"));
       fw.write(new String("一二三四"), 0, 3);
       fw.write("aaa");
       // 输出流一定要记得刷新
       fw.flush();
    } catch (IOException e) {
       throw new RuntimeException(e);
    } finally {
       if (fw != null) {
          try {
             fw.close();
          } catch (IOException e) {
             throw new RuntimeException(e);
          }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    FileReader 和 FileWriter 只能处理普通文本文件

    转换流

    InputStreamReader OutputStreamWriter

    缓冲流

    BufferedReader BufferedWriter BufferedInputStream BufferedOutputStream

    1. BufferedReader 带有缓冲区的字符输入流。使用这个流的时候不需要自定Xchar数组,或者说不需要自定byte数组。自带缓冲。比较方便写代码。
    BufferedReader br = null;
    try {
       // 当一个流的构造方法中需要- -个流的时候,这个被传进来的流叫做:节点流。
       // 外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流。
       // 像当前这个程序来说: FileReader就是-一个节点流。BufferedReader 就是包装流/处理流。
       br = new BufferedReader(new FileReader("/Users/ymy/test.java"));
    
       String s = null;
       // readline()方法读取一个文本行,但不会读取换行符。
       while ((s = br.readLine()) != null)
          System.out.println(s);
    } catch (FileNotFoundException e) {
       e.printStackTrace();
    } catch (IOException e) {
       throw new RuntimeException(e);
    } finally {
       if (br != null) {
          try {
             // 关闭流。对于包装流来说,只需要关闭最外层流就行,里面的结点流会自动关闭
             br.close();
          } catch (IOException e) {
             e.printStackTrace();
          }
       }
    }
    
    • 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
    1. BufferedWriter 一样,构造器接收一个Writer 类型的对象。其他操作和FileWriter一样
    数据流

    DataInputStream DataOutputStream

    1. DataOutputStream :数据专属的流。这个流可以将数据连同数据的类型一并写入文件。
      注意:这个文件不是普通文本文档。 (这个文件使用记事本打不开。 )
    标准输出流

    PrintWriter PrintStream

    1. PrintStream 标准的字节输出流。默认输出到控制台。并且该流不需要手动关闭close() 。我们最经常用的方法System.out.println()中,System.out 返回的就是一个 PrintStream 类的对象。
    PrintStream out = System.out;
    out.println("hahah");
    
    • 1
    • 2

    ​ 还可以将标准输出流的输出方向修改为某个文件。不过setOut方法要接收一个PrintStream类对象。

    这个就是实现日志记录的方法。

    try {
       PrintStream printStream = new PrintStream(new FileOutputStream("log.txt"));
       System.setOut(printStream);
       printStream.println("调用了方法");
    } catch (FileNotFoundException e) {
       throw new RuntimeException(e);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    对象流

    ObjectInputStream ObjectOutputStream

    序列化: Serialize java对象存储到文件中。 将java对象的状态保存下来的过程。
    反序列化: DeSerialize将硬盘上的数据重新恢复到内存当中,恢复成java对象。

    1. 如果要让自定义类可序列化和反序列化,一定要实现 Serializable 接口
    2. 通过源代码发现, Serializable接口只是一个标志接口:这个接口当中什么代码都没有。那么它起到一个什么作用呢?起到标识的作用,标志的作用, jqva虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。
    3. Serial izable这个标志接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类自动生成
      一个序列化版本号。

    序列化单一对象

    ObjectOutputStream oos = null;
    try {
       oos = new ObjectOutputStream(new FileOutputStream("students"));
       Student s1 = new Student("abc");
       oos.writeObject(s1);
       oos.flush();
    } catch (IOException e) {
       throw new RuntimeException(e);
    } finally {
       if (oos != null) {
          try {
             oos.close();
          } catch (IOException e) {
             e.printStackTrace();
          }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    反序列化单一对象

    ObjectInputStream ois = null;
    try {
       ois = new ObjectInputStream(new FileInputStream("students"));
       Object student = ois.readObject();
       System.out.println(student);
    } catch (IOException | ClassNotFoundException e) {
       e.printStackTrace();
    } finally {
       if (ois != null) {
          try {
             ois.close();
          } catch (IOException e) {
             e.printStackTrace();
          }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    序列化多个对象 此时要将多个对象放在集合中,并且该集合也需要实现 Serializable 接口

    List<Student> list = new ArrayList<>();
    list.add(new Student("qwe"));
    list.add(new Student("xyz"));
    list.add(new Student("jkl"));
    ObjectOutputStream oos = null;
    try {
       oos = new ObjectOutputStream(new FileOutputStream("objects"));
       oos.writeObject(list);
       oos.flush();
    } catch (IOException e) {
       e.printStackTrace();
    } finally {
       if (oos != null) {
          try {
             oos.close();
          } catch (IOException e) {
             e.printStackTrace();
          }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    反序列化多个对象 反序列化的结果也是放在一个集合中

    ObjectInputStream ois = null;
    try {
       ois = new ObjectInputStream(new FileInputStream("objects"));
       List<Student> list = (List<Student>) ois.readObject();
       System.out.println(list);
    } catch (IOException | ClassNotFoundException e) {
       e.printStackTrace();
    } finally {
       if (ois != null) {
          try {
             ois.close();
          } catch (IOException e) {
             e.printStackTrace();
          }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    transient 修饰的属性表示不参与序列化

    序列化版本号

    java语言中是采用什么机制来区分类的?

    1. 首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
    2. 如果类名一样,再怎么进行类的区别?靠序列化版本号进行区分。

    Java,虚拟机看到serializable接口之后,会自动生成一个序列化版本号。这里没有手动写出来, java虚拟机会默认提供这个序列化版本号。建议将序列化版本号手动的写出来。不建议自动生成。

    A编写了一个类: com.abc.java.bean.Student implements Serializable
    B编写了一个类: com.abc.java.bean.Student implements Serializable
    不同的人编写了同一个类,但“这两个类确实不是同-一个类”。这个时候序列化版本就起上作用了。对于java,虚拟机来说, java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口,都有默认的序列化版本号,他们的序列化版本号不-样。所以区分开了。( 这是自动生成序列化版本号的好处)

    这种自动生成序列化版本号有什么缺陷?
    这种自动生成的序列化版本号缺点是:一旦代码确定之后 ,不能进行后续的修改,因为只要修改,必然会重新编译,此时会生成全新的序列化版本号。这个时候java虚拟机会认为这是一个全新的类。( 这样就不好了! )

    最终结论:凡是一个类实现了Serial izable接口,建议给该类提供一个固定不变的序列化版本号。这样,以后这个类即使代码修改了,但是版本号不变, java虚拟机会认为是同-一个类。

    文件类File

    一个File对象有可能对应的是目录,也可能是文件。File只是一个路径名的抽象表示形式。File类中有很多常用方法。

  • 相关阅读:
    微信小程序配置企业微信的在线客服
    C/C++标准库梳理
    第四节唯物辩证法(两大总特征)
    达梦(DM)数据库管理表
    Scala 05 —— 函数式编程底层逻辑
    网络安全笔记-Web架构
    网页开发从无到有——html前端学习(四)
    高通WLAN框架学习(31)-- Power save
    前后端分离前端请求后端接口的方式
    Code Anatomy - 编写高性能 Python 代码
  • 原文地址:https://blog.csdn.net/D_Low/article/details/126556161