当前位置 : 主页 > 编程语言 > java >

Java中IO流解析及代码实例

来源:互联网 收集:自由互联 发布时间:2021-08-21
目录 I/O简介 字节流和字符流常用API 字节字符流相关操作 字节流读取文本内容 字符流读取文本内容 字节流读取文件到输出到指定位置 字符流读取文件到输出到指定位置 缓冲流 Buffer
目录
  • I/O简介
    • 字节流和字符流常用API
  • 字节字符流相关操作
    • 字节流读取文本内容
    • 字符流读取文本内容
    • 字节流读取文件到输出到指定位置
    • 字符流读取文件到输出到指定位置
    • 缓冲流
      • BufferedInputStream和BufferedOutputStream
      • BufferedReader和BufferedWriter
    • 数据流
      • 对象流
        • 序列化
        • 反序列化
    • 总结

      I/O简介

      I/O是Input/output的缩写,在java中,对于数据的输入和输出以流的方式进行。java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。 

      输入输出都是基于内存的角度来说的。输入:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。 输出:输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。

      流的分类

      操作数据单位不同分为:字节流(8 bit),字符流(16 bit)

      数据流的流向不同分为:输入流,输出流

      流的角色的不同分为:节点流,处理流  

      节点流:直接从数据源或目的地读写数据。  

      处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。 相当于是二次包装。

      抽象基类 字节流 字符流 输入流 InputStream Reader 输出流 OutputStream Writer

      Java的IO流共涉及40多个类,实际上非常规则,都是从这4个抽象基类派生的。由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

      字节流和字符流常用API

      InputStream

      • int read():从输入流中读取数据的下一个字节。返回0到255范围内的int字节值。如果读到流的末尾,则返回 -1。 此方法一次只读一个字节,效率太低,一般不用
      • int read(byte[] b):从此输入流中将最多b.length个字节的数据保存到一个byte数组中。返回实际读取字节长度,如果读到流的末尾,也返回 -1。
      • int read(byte[] b,int off,int len):将输入流中最多len个数据字节读入byte数组。尝试读取len个字节,但读取的字节也可能小于该值,实际读取个数以读取到的为准,比如长度为23个字节的内容,每次读取5个字节,则第五次读取流并没处于末尾,还剩长度是3,故返回3。第六次读取发现是末尾,则返回 -1 。一般都用此方法。
      • public void close() throws IOException:关闭此输入流并释放与该流关联的所有系统资源,一定要关闭。

      Reader

      • Reader和InputStream类似,就是将字节数组换成了字符数组
      • int read(); 效果与字节流一致
      • int read(char[] cbuf); 效果与字节流一致
      • int read(char[] cbuf,int off,int len); 效果与字节流一致
      • public void close() throws IOException;关闭此输入流并释放与该流关联的所有系统资源,字符流的必须要关闭,不然会出问题

      OutputStream

      • void write(int b) :将指定的字节写入此输出流。
      • void write( byte[] b) :将b.length个字节从指定的byte数组写入此输出流。
      • void write(byte[] b,int off,int len):将指定byte数组中从偏移量off开始的len个字节写入此输出流。
      • public void flush() throws IOException:刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立即写入它们预期的目标。就是将缓冲的字节全部写出到字节或字符数组中。
      • public void close() throws IOException:关闭此输入流并释放与该流关联的所有系统资源

      Writer

      • void write(int c):写入单个字符。
      • void write(char[] cbuf):写入字符数组。
      • void write(char[] cbuf,int off,int len) : 写入字符数组的某一部分。从off开始,写入len个字符
      • void write(String str):写入字符串。
      • void write(String str,int off,int len):写入字符串的某一部分。
      • void flush():刷新该流的缓冲,则立即将它们写入预期目标。
      • public void close() throws IOException:关闭此输入流并释放与该流关联的所有系统资源

      字节字符流相关操作

      字节流读取文本内容

      // 读取文本文件内容,用字节流去读,可能会乱码
          public void test1() {
              InputStream inputStream = null;
              try {
                  inputStream = new FileInputStream("hello.txt");//相对路径是工程路径下
                  int len;
                  byte[] bytes = new byte[5];// 定义5字节长度的byte数组
                  while ((len = inputStream.read(bytes)) != -1) {// 将数据读到byte数组中
                      System.out.println(new String(bytes, 0, len)); //打印到控制台
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      if (inputStream != null) {
                          inputStream.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
      	}
      

      字符流读取文本内容

      // 读取文本文件内容,用字符流去读,不会乱码,最多重复	
          public void test2() {
              Reader reader = null;
              try {
                  reader = new FileReader("hello.txt");
                  int len;
                  char[] charBuff = new char[5];
                  while ((len = reader.read(charBuff)) != -1) {
      //                System.out.println(new String(charBuff)); // 注意这两行的代码区别
                      System.out.println(new String(charBuff, 0, len));
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      if (reader != null) {
                          reader.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      

      字节流读取文件到输出到指定位置

      public void test3() {
              InputStream inputStream = null;
              OutputStream outputStream = null;
              try {
                  inputStream = new FileInputStream("hello.txt"); // 读取文件
                  outputStream = new FileOutputStream("hello2.txt"); // 相对路径,默认是工程路径下
                  int len;
                  byte[] bytes = new byte[5];
                  while ((len = inputStream.read(bytes)) != -1) {
                      outputStream.write(bytes, 0, len);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      if (inputStream != null) {
                          inputStream.close();
                      }
                      if (outputStream != null){
                          outputStream.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      

      字符流读取文件到输出到指定位置

      字符流读取文件到输出到指定位置时,如果没有手动关闭流,则不会输出到指定位置,需要手动flush。但是如果在finally块中关闭了流,则会自动flush。在close()操作中,会帮我们flush。

      public void test4() {
              Reader reader = null;
              Writer writer = null;
              try {
                  reader = new FileReader("hello2.txt");
                  writer = new FileWriter("hello3.txt");
                  int len;
                  char[] charBuff = new char[5];
                  while ((len = reader.read(charBuff)) != -1) {
                      writer.write(charBuff, 0, len);
      //                writer.flush();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      if (reader != null) {
                          reader.close();
                      }
                      if (writer != null) { // 一定要关闭字符流,否则要手动flush
                          writer.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      

      注意,字符流用来处理字符串很便捷,并且只能操作普通的文本文件,例如:.txt,.java,.c,.cpp 等语言的源代码。尤其注意.doc,excel,ppt这些不是文本文件。字节流既可以操做文本文件,也可以操作字节文件,比如.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt。如果用字符流来操作图片等字节文件,生成的文件是无法打开的!

      缓冲流

      为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用 8192个字节(8Kb) 的缓冲区。

      缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为BufferedInputStream和ufferedOutputStream以及BufferedReader和BufferedWriter。分别对应字节缓冲流和字符缓冲流。相当于在字节和字符流上包装了一下。

      BufferedInputStream和BufferedOutputStream

      public void test() {
              InputStream inputStream ;
              OutputStream outputStream ;
              BufferedInputStream bis = null;
              BufferedOutputStream bos = null;
              try {
                  inputStream = new FileInputStream("modify.txt"); //小于8KB
                  outputStream = new FileOutputStream("modify2.txt");
                  bis = new BufferedInputStream(inputStream);
                  bos = new BufferedOutputStream(outputStream);
                  int len;
                  byte[] bytes = new byte[5];
                  while ((len = bis.read(bytes)) != -1) {
                      bos.write(bytes, 0, len);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      if (bis != null) {
                          bis.close();
                      }
                      if (bos != null){
                          bos.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      

      BufferedReader和BufferedWriter

      public void test2() {
              Reader reader ;
              Writer writer ;
              BufferedReader br = null;
              BufferedWriter bw = null;
              try {
                  reader = new FileReader("modify.txt");
                  writer = new FileWriter("modify2.txt");
                  br = new BufferedReader(reader);
                  bw = new BufferedWriter(writer);
                  int len;
                  char[] bytes = new char[5];
                  while ((len = br.read(bytes)) != -1) {
                      bw.write(bytes, 0, len);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      if (br != null) {
                          br.close();
                      }
                      if (bw != null){
                          bw.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      

      **缓冲流如果没有手动关闭流,且读取的文件小于底层缓存大小8KB,是不会自动写到目标中去的,需要手动flush。**在关闭流时,只需关闭缓冲流即可,它会自动关闭它包装的底层节点流。

      数据流

      为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流。数据流有两个类:DataInputStream和DataOutputStream, 别“套接”在InputStream和OutputStream子类的流上。

      使用如下:

       public void test() {
              DataOutputStream dos = null;
              try {
                  OutputStream outputStream = new FileOutputStream("data.txt");
                  dos = new DataOutputStream(outputStream);
                  dos.writeUTF("热烈庆祝中国共产党成立一百周年");
                  dos.writeInt(100);
                  dos.writeBoolean(true);
                  System.out.println("成功!");
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  try {
                      if (dos != null) {
                          dos.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
          public void test2(){
              DataInputStream dis =  null;
              try {
                  InputStream inputStream = new FileInputStream("data.txt");
                  dis = new DataInputStream(inputStream);
                  System.out.println(dis.readUTF()); //读取时要按照写入顺序读取
                  System.out.println(dis.readInt());
                  System.out.println(dis.readBoolean());
                  System.out.println("成功!");
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  try {
                      if (dis != null) {
                          dis.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      

      对象流

      用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

      • 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
      • 反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
      • ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量

      序列化与反序列化的演示

      先定义一个Person类,该类必须实现Serializable接口,否则序列化时会报java.io.NotSerializableException 的错误

      package day07;
      import java.io.Serializable;
      import java.util.Date;
      public class Person implements Serializable {
          private static final long serialVersionUID = -5858950242987134591L;
          private String name;
          private Integer age;
          private Date date;
          public Person(){}
          public Person(String name, Integer age, Date date) {
              this.name = name;
              this.age = age;
              this.date = date;
          }
        // getter、setter略
          @Override
          public String toString() {
              return "Person{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      ", date=" + date +
                      '}';
          }
      }
      

      序列化

          public void test() {
              ObjectOutputStream oos = null;
              try {
                  OutputStream outputStream = new FileOutputStream("person.txt"); // 创建输出流对象,指定输出位置
                  oos = new ObjectOutputStream(outputStream); // 包装输出流
                  oos.writeObject(new Person("张三",22,new Date())); // 序列化对象
                  System.out.println("序列化成功!");
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  if (oos!=null){
                      try {
                          oos.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
      

      反序列化

      public void test2() {
              ObjectInputStream ois = null;
              try {
                  InputStream inputStream = new FileInputStream("person.txt");
                  ois = new ObjectInputStream(inputStream);
                  Person person = (Person) ois.readObject();
                  System.out.println("person姓名为:" + person.getName());
                  System.out.println("person年龄为:" + person.getAge());
                  SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh时mm分ss秒");
                  System.out.println("创建时间为:" + sdf.format(person.getDate()));
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              } finally {
                  if (ois != null) {
                      try {
                          ois.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
      

      在这里插入图片描述

      无论如何反序列化,时间都是不变的。

      总结

      本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注自由互联的更多内容!

      网友评论