JAVA—Detailed explanation of IO stream

1. Flow Overview

1.1. What is IO

IO:Input/Output is input & output, also known as flow (river, water flow), which refers to the process of data from one place to another; for computers, the copying process of files, the writing and saving of files, and the display functions are all IO is used; the data transmission process is regarded as an input and output. From a practical perspective, you can use the ears as the input stream and the mouth as the output stream.

  • Input and output media:
    • document
    • network
    • keyboard input)
    • Display (output)

1.2. IO classification

Java provides corresponding APIs for IO operations. Almost all IO operations in Java require the use of java.iopackages; the classification of streams in Java includes various methods:

  • Divided by flow direction (input and output processes are usually considered from a program perspective )
    • Input stream (Input)
    • Output
  • According to the type of stream processing
    • Byte stream (byte)
    • Character stream (char)
  • According to the function of flow
    • Node stream (interact directly with input and output sources)
    • Processing streams (streams that wrap other streams: wrapping streams)

Insert image description here

1.3. Top-level parent class

Although streams have a wide variety of contents, they are very regular. Almost all streams inherit from four basic streams:

input stream output stream
byte stream java.io.InputStream java.io.OutputStream
character stream java.io.Reader java.io.Writer

The above four streams are the top-level parent classes of all java streams and are all abstract classes ;

The stream type identification is very regular: generally, the Streamstream ending with is a byte stream; generally Reader/Writerthe stream ending with is a character stream.

1.4. Usage scenarios

  • File copy (File)
  • File upload and download
  • Excel import and export
  • Data transmission in network programs (chat tools)

2. Byte stream

2.1. Byte Overview

​ In a computer system, everything is a byte: the storage of various files (text documents, pictures, videos, audio) stored in the system is stored in the form of bytes at the lower level of the computer, so for any file operation They can all be operated on byte by byte (read, write); the top-level parent class of byte stream in java.io is: InputStream/OutputStream.

2.2. Byte input stream

The byte input streams in Java are all java.io.InputStreaminherited from. Since this class is an abstract class, it cannot be instantiated, so jdk provides some subclasses for byte input that can be used directly:

  • FileInputStream
  • ByteArrayInputStream
  • BufferedInputStream
  • ObjectInputStream

Common methods of InputStream

  • int available(): Get the number of readable bytes in the stream
  • int read():Read a byte from the stream and return the currently read byte data
  • int read(byte[] b): Store the read byte data into the byte buffer and return the actual total number of bytes read
  • skip(int b):Skip the specified bytes for the next read

2.2.1. FileInputStream

FileInputStreamIt is an implementation stream for byte input stream, mainly used for reading files. The internal methods are mainly for the implementation of the parent class.

  • Common constructors

    • FileInputStream(File file):An object built from the provided file
    • FileInputStream(String filePath): Build an object based on the provided file path
  • Use FileInputStreamto read files

    1. Basic read (read one byte at a time)

      //创建File对象
      File file = new File("C:\\Users\\Administrator\\Desktop\\新建文本文档.txt");
      //基于File创建字节输入流
      InputStream in = new FileInputStream(file);
      
      //读取一个字节
      int i = in.read();
      System.out.println((char)i);
      

      Java's IO can only read and write standard files, and does not allow you to directly create input or output streams for a directory (will cause java.io.IOException(拒绝访问))

    2. Read using byte buffer (buffer size is total readable bytes)

      Since the above reading method is to read one byte at a time, the reading efficiency is very low, so a byte buffer is generally used in actual development to improve the reading efficiency:

      //创建File对象
      File file = new File("C:\\Users\\Administrator\\Desktop\\新建文本文档.txt");
      //基于File创建字节输入流
      InputStream in = new FileInputStream(file);
      
      //创建字节缓冲区(大小为总可读字节数)
      byte[] arr = new byte[i];
      //将流中读取的字节内容存储到数组中
      int total = in.read(arr);
      //将字节数组转换为String字符串
      System.out.println(new String(arr));
      
    3. Read using appropriately sized byte buffer

      The above reading is to read the data in the file into the buffer at one time. Therefore, the capacity of the buffer may be large. If an excessively large buffer is used for reading a large file, it may be This causes space consumption and affects the execution of other programs. Therefore, a buffer of appropriate size is required for repeated reading.

      //创建文件输入流对象(文件:一缸水)
      InputStream in = new FileInputStream("C:\\Users\\Administrator\\Desktop\\Hero.java");
      
      //创建缓冲区(购买一个大小适中的水桶)
      byte[] b = new byte[1024];
      //声明临时变量表示每次读取的实际字节数
      int len = 0;
      while((len = in.read(b)) != -1){
              
              
          String s = new String(b,0,len);
          System.out.println(s);
      }
      

      The above reading method is a general solution for reading byte streams.

2.3. Byte output stream

According to the flow of data, in addition to reading (input) operations, data writing (output) operations are also very common. The byte output streams in java are all inherited java.io.OutputStreamfrom

Since OutputStreamit is an abstract class and cannot be instantiated, jdk also provides subclasses for this stream:

  • FileOutputStream
  • ByteArrayOutputStream
  • BufferedOutputStream
  • ObjectOutputStream
  • PrintStream

Common methods of the OutputStream class:

  • write(int b):Write a byte out through the output stream to the target output source
  • write(byte[] b): Write a byte array to the target output source through the output stream
  • write(byte[] b,int offset,int len)Write len bytes starting from the offset of an array to the target output source

2.3.1. FileOutputStream

FileOutputStreamIt is an implementation stream for byte output stream, mainly used for writing files. The internal methods are mainly for the implementation of the parent class.

  • Common constructors

    • FileOutputStream(String filePath):Creates an output stream for operations on a standard file based on its path
    • FileOutputStream(String filePath,boolean append): Create an output stream for operations on a standard file based on its path, using append mode.
    • FileOutputStream(File file):Creates an output stream for operations on a standard file based on its path
    • FileOutputStream(File file,boolean append)Creates an output stream for operations on a standard file object, using append mode
  • Use FileOutputStreamto write files

     //try...with语句:JDK1.7
            //针对所有的第三方资源不再需要手动回收(关闭)
            //因为从jdk1.7开始,很多资源类都实现过Closeable接口,但凡实现过该接口类
            //只要将其在try()中创建,不再手动关闭资源,会由JVM自动回收
            try(OutputStream os = new FileOutputStream("readme.txt",true);){
          
          
                //写出一个字节到文件中
    //            os.write(101);
    
                //荀子·劝学篇
                String msg = "不积小流无以成江海,不积跬步无以至千里";
                os.write(msg.getBytes());
    
            }catch (IOException e){
          
          
                e.printStackTrace();
            }
    

2.4. Comprehensive case-file copy

/**
     * 将一个源文件拷贝到一个目标目录中
     * @param src 源文件
     * @param targetDir 目标目录
     */
public static void copyFile(File src, File targetDir) throws IOException {
    
    
    InputStream is = null;
    OutputStream os = null;
    try {
    
    
        //获取源文件的输入流
        is = new FileInputStream(src);
        //获取目标文件的输出流(目标文件是由:目录+源文件名称构成):文件输出流可以创建文件(前提:父目录必须存在)
        os = new FileOutputStream(new File(targetDir,src.getName()));
        //声明字节缓冲区(缓冲区越大,拷贝效率越高,但是带来空间损耗也越大)
        byte[] b = new byte[1024*1024];
        //临时变量表示实际读取的字节数
        int len = 0;
        System.out.println("开始拷贝...");
        while((len = is.read(b)) != -1){
    
    
            //写出读取的内容到输出流
            os.write(b,0,len);
        }
        System.out.println("拷贝完成!");
    }  finally{
    
    
        if(os != null){
    
    
            os.close();
        }
        if(is != null){
    
    
            is.close();
        }
    }

}

//测试
public static void main(String[] args) throws IOException {
    
    
    //源文件
    File src = new File("D:\\素材\\视频\\短视频\\test.mp4");
    //目标目录
    File targetDir = new File("C:\\Users\\Administrator\\Desktop");
    //文件拷贝
    copyFile(src,targetDir);
}

Implement directory copy based on file copy:

public class FileCopy {
     
     

/**
   * 将一个源目录拷贝到另一个目标目录中
   * @param srcDir
   * @param targetDir
   */
  public static void copyDir(File srcDir,File targetDir) throws IOException {
     
     
      //获取新目录对象
      targetDir = new File(targetDir,srcDir.getName());
      //如果新目录不存在,则创建
      if(!targetDir.exists()){
     
     
          targetDir.mkdirs();
      }
      File[] files = srcDir.listFiles();
      if(Objects.nonNull(files)){
     
     
          for (File file : files) {
     
     
              if(file.isDirectory()){
     
     
                  //目录递归拷贝
                  copyDir(file,targetDir);
              }else{
     
     
                  //执行文件拷贝
                  copyFile(file,targetDir);
              }
          }
      }
  }

  /**
   * 将一个源文件拷贝到一个目标目录中
   * @param src 源文件
   * @param targetDir 目标目录
   */
  public static void copyFile(File src, File targetDir) throws IOException {
     
     
      InputStream is = null;
      OutputStream os = null;
      try {
     
     
          //获取源文件的输入流
          is = new FileInputStream(src);
          //获取目标文件的输出流(目标文件是由:目录+源文件名称构成)
          os = new FileOutputStream(new File(targetDir,src.getName()));
          //声明字节缓冲区(缓冲区越大,拷贝效率越高,但是带来空间损耗也越大)
          byte[] b = new byte[1024*1024];
          //临时变量表示实际读取的字节数
          int len = 0;
          System.out.println("开始拷贝...");
          while((len = is.read(b)) != -1){
     
     
              //写出读取的内容到输出流
             os.write(b,0,len);
          }
          System.out.println("拷贝完成!");
      }  finally{
     
     
          if(os != null){
     
     
              os.close();
          }
          if(is != null){
     
     
              is.close();
          }
      }

  }

  public static void main(String[] args) throws IOException {
     
     
      //源目录
      File srcDir = new File("D:\\素材\\视频");
      //目标目录
      File targetDir = new File("C:\\Users\\Administrator\\Desktop\\video");
      //目录拷贝
      copyDir(srcDir,targetDir);
  }
}

3. Character stream

3.1. Character overview

Usually 文本文件, the file content exists in the form of characters (a Chinese character, an English letter, and a symbol), usually in GBK encoding mode; character 1个字符=2个字节streams are generally suitable for processing text data, java Almost all character streams in Reader/Writer, and all inherit from two basic abstract classes:

  • java.io.Reader:Character input stream
  • java.io.Writer:Character output stream

3.2. Character input stream

Character input streams are generally used to read text data. Common subcategories:

  • InputStreamReader
  • FileReader
  • BufferedReader
  • CharArrayReader

3.2.1. FileReader

FileReader is a stream that reads file content in the form of a character stream. InputStreamReaderInherited from it, there are no redundant methods inside (consistent with the API of the parent class), and the following common construction methods are provided:

  • Commonly used construction methods

    • FileReader(File file): Get the file character input stream based on the provided file object
    • FileReader(String path):Get the file character input stream based on the provided file path
  • Specific use

    //创建文件字符输入流
    FileReader fr = new FileReader("D:\\文档资料\\电子书\\书籍推荐.txt");
    
    //获取当前流使用的默认编码模式(并非目标文件的编码)
    //        String encoding = fr.getEncoding();
    //        System.out.println(encoding);
    
    //读取一个字符(以int类型存在)
    //        int read = fr.read();
    //        System.out.println((char)read);
    
    //        int c = 0;
    //        while((c = fr.read()) != -1)
    //        {
          
          
    //            System.out.print((char)c);
    //        }
    
    //使用字符缓冲区
    char[] ch = new char[100];
    int len = 0;
    while((len = fr.read(ch)) != -1){
          
          
        String s = new String(ch,0,len);
        System.out.print(s);
    }
    

3.3. Character output stream

Character output streams are generally used to write text data. Common subcategories:

  • Reader`
  • OutputStreamWriter
  • BufferedWriter
  • CharArrayWriter
  • FileWriter
  • PrintWriter

3.3.1 FileWriter

FileWriter is a stream that writes file content in the form of a character stream. It is inherited from OutputStreamWriterFileWriter and has no redundant internal methods (consistent with the API of the parent class). It provides the following common construction methods:

  • Commonly used construction methods

    • FileWriter(File file): Get the file character input stream based on the provided file object
    • FileWriter(String path):Get the file character input stream based on the provided file path
    • FileWriter(File file,boolean append): Get the file character input stream based on the provided file object (using append mode)
    • FileWriter(String path,boolean append): Get the file character input stream based on the provided file path (using append mode)
  • Specific use

    //基于指定的文件创建字符输出流
    FileWriter fw = new FileWriter("readme.txt");
    fw.write("路漫漫其修远兮,吾将上下而求索");
    
    //允许在流未关闭之前,强制将字符缓冲区中的数据写出到目标输出源
    fw.flush();
    
    Thread.sleep(10000);
    fw.write("好好xio习,天天up!!!");
    fw.close();
    

    For the character output stream, one is used internally 字符缓冲区(字符数组). When writing data, the data to be written is usually cached in the character array, and then when the stream is closed (or when the buffer is full), the buffer is cached in one go. The data in the character buffer is written out to the target output source. If you need to force the data in the character buffer to be written out before the stream is closed (or when the buffer is not full), you can manually call flush()forced output.

4. Processing flow

The processing type (function) of the stream is divided into node stream and processing stream:

  • node flow

    Also called a low-level stream, a stream that communicates directly with the input and output source (for example: FileReader, FileWriter, FileInputStream, FileOutputStream)

  • processing flow

    Processing flow is also called advanced flow or packaging flow , which can be used to wrap other node flows to achieve flow 类型转换or efficiency improvement. The processing flow mainly consists of 缓冲流and转换流

Packaging usually uses a design pattern (decorator pattern)

4.1. Buffered streams

Buffered stream is a stream with its own buffer, which mainly consists of the following four types:

  • BufferedInputStream:Byte buffered input stream
  • BufferedOutputStream:byte buffered output stream
  • BufferedReader:Character buffer input stream
  • BufferedWriter:Character buffered output stream
long start = System.currentTimeMillis();
try(
    //获取输入流
    InputStream in = new FileInputStream("D:\\集团资料\\宣讲\\video\\云计算&大数据\\阿里云.mp4");
    //包装节点流
    BufferedInputStream bis = new BufferedInputStream(in);
    //获取输出流
    OutputStream os = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\阿里云.mp4");
    //包装节点流
    BufferedOutputStream bos = new BufferedOutputStream(os);
) {
    
    
    System.out.println("开始拷贝");
    byte[] b = new byte[1024*1024*8];
    int len = 0;
    while((len = bis.read(b)) != -1){
    
    
        bos.write(b,0,len);
    }
    System.out.println("拷贝结束");
}catch (IOException e){
    
    
    e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));

The internal implementation principle of the buffered stream uses a 8kbbyte buffer with a default size of . Each time the buffer space is filled, the data is read and written through the stream object.

4.2. Conversion flow

In some requirements, it is often necessary to convert the data of a byte stream into a character stream; or it is necessary to convert a character stream into a byte stream. In this case, it is necessary to use the conversion stream to complete the function. The conversion stream is generally used in: files Transcoding, when the data read from the network is a byte stream, but the stream contains characters, you can use the conversion stream to realize the conversion; the conversion stream in java.io is mainly composed of the following two classes:

  • InputStreamReader: Used to convert a byte input stream into a character input stream (bytes -> characters)
  • OutputStreamWriter: Used to convert character output stream to byte output stream (Character -> Byte)

Example of use:

//获取标准输入流
InputStream is = System.in;

//将字节流转换为字符流
Reader isr = new InputStreamReader(is);
//将字符节点流包装为缓冲流
BufferedReader br = new BufferedReader(isr);
//读取一行
String line = br.readLine();
System.out.println("输入的内容--->"+line);

Insert image description here

Converting a stream can only convert the type of the stream, not the direction of the stream.

4.3. Comprehensive case: file transcoding tool

In daily file copying, due to the differences in the encoding modes of multiple editors (systems), file garbled characters are very likely to occur (common for text files). The function of file transcoding can be realized by converting the stream:

public class FileCharacterConvert {
    
    

    /**
     * 将一个目标文件的编码转换为新的编码
     * @param file  原始文件
     * @param targetDir 目标目录
     * @param oldEncoding  原始编码
     * @param newEncoding   新编码
     */
    public static void convert(File file,File targetDir, String oldEncoding, String newEncoding) throws IOException {
    
    

        //使用特定的编码获取文件的输入流
        FileInputStream fis = new FileInputStream(file);
        Reader reader = new InputStreamReader(fis,oldEncoding);
        BufferedReader br = new BufferedReader(reader);

        //使用特定的编码获取文件的输出流
        FileOutputStream fow = new FileOutputStream(new File(targetDir,file.getName()));
        Writer writer = new OutputStreamWriter(fow,newEncoding);
        BufferedWriter bw = new BufferedWriter(writer);

        //开始读写
        String line = "";
        //循环读取每一行文本以换行符作为一行的结束标记(但是换行符不会被作为内容读取)
        while((line = br.readLine()) != null){
    
    
            //写入读取的一行文本()
            bw.write(line);
            //手动加入一个换行标记到文件,否则所有内容会在同一行显示
            bw.newLine();
            //将缓冲区的数据强制输出到目标输出源
            bw.flush();
        }
        bw.close();
        br.close();
    }

    public static void main(String[] args) throws IOException {
    
    
        //准备需要转换的文件
        File f = new File("C:\\Users\\Administrator\\Desktop\\GamePanel.java");
        convert(f,new File("C:\\Users\\Administrator\\Desktop\\temp"),"gbk","utf-8");
    }
}

5. Print stream

java.io provides two special streams for data output: print stream, print stream has only output but no input, and can be created directly for the output source, or it can wrap other output streams. The print stream mainly includes the following two streams :

  • PrintStream:byte print stream
  • PrintWriter:Character printing stream

5.1. PrintStream

PrintStreamIs inherited from java.io.OutputStream, is a stream used for byte data output, contains a large number of print/prinlnoverloaded methods; and System.outis actually aPrintStream

  • Common constructors

    • PrintStream(File file): Create a print stream based on the specified file
    • PrintStream(OutputStream os): Wrap other byte output streams
    • PrintStream(OutputStream os,boolean autoFlush): Wrap other byte output streams to automatically refresh the data in the stream
  • Common methods

    • print(...)
    • println(...)
  • basic use

    //        PrintStream ps = new PrintStream(new File("readme.txt"),true);
    
    OutputStream os = new FileOutputStream("readme.txt", true);
    PrintStream ps = new PrintStream(os,true);
    ps.println("你好中国");
    

5.2. PrintWriter

PrintWriterThe difference PrintStreamis that PrintWriterit is a character-based print stream (including a character buffer), and its API is PrintStreamvery similar to; remember to execute the method when using PrintWriterprint dataflush()

FileWriter fw = new FileWriter("readme.txt",true);

PrintWriter pw = new PrintWriter(fw);

pw.println("哪里有彩虹告诉过我!!!");

pw.flush();

pw.close();

6. Resource file reading (Properties class)

In the learning process of the later framework, the writing of some configuration files is often involved. Among them, property files are a very common file type. Java provides a class for reading and writing property files java.util.Properties:

InputStream in = PropertiesDemo.class.getResourceAsStream("/jdbc.properties");
//创建属性对象
Properties prop = new Properties();
//        Properties prop = System.getProperties();
//加载流到属性对象中
prop.load(in);

String user = prop.getProperty("user");
String url = prop.getProperty("url");
System.out.println(user);
System.out.println(url);

prop.setProperty("maxActive","10");

File f = new File("D:\\带班资料\\2021\\J2106\\课程资料\\code\\part1-javase\\java高级\\lesson_01_IO\\resources\\jdbc.properties");
FileWriter fw = new FileWriter(f);
prop.store(fw,"this is jdbc config file");
fw.close();

7. Summary of flow

  • All input stream names contain input,read
  • All output stream names contain output,write
  • All byte stream names streamend with
  • All character stream names end with readerorwriter
  • General binary type files (pictures, videos, audios, compressed files and other files that cannot be opened directly with text documents) mainly use byte stream operations
  • General text type files (txt, md, java and other files that can be opened directly using text documents) mainly use character stream operations

Guess you like

Origin blog.csdn.net/weixin_48006490/article/details/126649763