Java_ Detailed explanation of IO flow

Posted by ramas on Thu, 09 Dec 2021 15:52:18 +0100

Java_IO stream

The important thing in learning IO flow is to know when to use what flow

1, Disk operation

1.1 concept

What is a file

Files are not unfamiliar to us. Files are places to save data, such as word documents, txt files, excel files... All of which are files. It can save a picture, video, sound

File stream

Files are operated in the form of streams in programs

  • Stream: the path of data between the data source (file) and the program (memory)
  • Input stream: the path of data from data source (file) to program (memory)
  • Output stream: the path of data from program (memory) to data source (file)

1.2. Common file operations

Constructors and methods for creating file objects

new File(String pathname) //Build a File object based on the path
new File(File parent, String child) // Build according to parent directory file + child path
new File(String parent, String child)  // Build according to parent directory + child path
    
createNewFile() // create a new file  

public class FileCreate {
   public static void main(String[] args) {
   }

   // Mode 1 new File(String pathname)
   public void create01() {
      String filePtah = "G:\\news1.txt";
      File file = new File(filePtah);
      try {
         file.createNewFile();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   // Method 2: new file (file parent, string child) / / build according to the parent directory file + child path
   public void create02() {
      File parentFile = new File("G:\\");
      String fileName = "news2.txt";
      File file = new File(parentFile, fileName);
      //The file object here, in a java program, is just an object
      //Only when the createNewFile method is executed can the file be truly created on the disk
      try {
         file.createNewFile();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   //Method 3: new file (string parent, string child) / / build according to parent directory + child path
   public void create03() {
      String parentPath = "G:\\";
      String fileName = "news3.txt";
      File file = new File(parentPath, fileName);

      try {
         file.createNewFile();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

Get information about the file

getName,getAbsolutePath,getPatent,length,exists,isFile,isDirectory

public class FileInformation {
   public static void main(String[] args) {
   }
   
   // Get file information
   public void info() {
      // Create the file object first
      File file = new File("G:\\news1.txt");

      // Call the corresponding method to get the corresponding information
      System.out.println("File name=" + file.getName());

      System.out.println("Absolute path=" + file.getAbsolutePath());
      System.out.println("Parent directory=" + file.getParent());
       // In utf-8, an English character is 1 byte and a Chinese character is 3 bytes
      System.out.println("File size (bytes)=" + file.length());
      System.out.println("Does the file exist=" + file.exists());//T
      System.out.println("Is it a file=" + file.isFile());//T
      System.out.println("Is it a directory=" + file.isDirectory());//F
   }
}

Directory operation and file deletion

mkdir creates a first level directory, mkdirs creates a multi-level directory, delete deletes an empty directory or file

public class Directory {
   public static void main(String[] args) {
      m1();
      m2();
      m3();
   }

   //Judge G: \ News1 Txt exists. If it exists, delete it
   public static void m1() {
      String filePath = "G:\\news1.txt";
      File file = new File(filePath);
      if (file.exists()) {
         if (file.delete()) {
            System.out.println(filePath + "Delete succeeded");
         } else {
            System.out.println(filePath + "Deletion failed");
         }
      } else {
         System.out.println("The file does not exist...");
      }
   }

   //Judge whether D:\demo02 exists and delete it if it exists. Otherwise, it will prompt that it does not exist
   //Here we need to realize that in java programming, directories are also treated as files
   public static void m2() {
      String filePath = "D:\\demo02";
      File file = new File(filePath);
      if (file.exists()) {
         if (file.delete()) {
            System.out.println(filePath + "Delete succeeded");
         } else {
            System.out.println(filePath + "Deletion failed");
         }
      } else {
         System.out.println("The directory does not exist...");
      }

   }

   //Judge whether the D:\demo\a\b\c directory exists. If it exists, it will prompt that it already exists. Otherwise, it will be created
   public static void m3() {
      String directoryPath = "D:\\demo\\a\\b\\c";
      File file = new File(directoryPath);
      if (file.exists()) {
         System.out.println(directoryPath + "existence..");
      } else {
         if (file.mkdirs()) { //Create a first level directory using mkdir(), and create a multi-level directory using mkdirs()
            System.out.println(directoryPath + "Created successfully..");
         } else {
            System.out.println(directoryPath + "Creation failed...");
         }
      }
   }
}

2, IO flow principle and classification

2.1 Java IO flow principle

  • I/O is the abbreviation of Input/Output. I/O technology is a very practical technology for processing data transmission. Such as reading / writing documents, network communication, etc.

  • In Java programs, the input / output operation of data is carried out in the form of "Stream".

  • java. Various "streams" and interfaces are provided under the IO package to obtain different kinds of data and input or output data through methods

  • input: read external data (data from disk, optical disc and other storage devices) into the program (memory)

  • Output: output program (memory) data to disk, optical disc and other storage devices

2.2 classification of flow

According to different operation data units, it is divided into byte stream (8-bit) binary file and character stream (by character) text file

According to the flow direction of data flow, it is divided into input flow and output flow

According to the different roles of the stream, it is divided into byte stream, processing stream / wrapper stream

Java's IO stream involves more than 40 classes, which are actually very regular and derived from the above four abstract base classes.

The names of subclasses derived from these four classes are all suffixed with their parent class names

2.3 common classes

InputStream: byte input stream

The InputStream abstract class is a superclass of all class byte input streams

Common subclasses of InputStream

  • FileInputStream: file input stream
  • BufferedInputStream: buffered byte input stream
  • ObjectInputStream: object byte input stream

FileInputStream

public class FileInputStream01 {
   public static void main(String[] args) {
//    readFile01();
      readFile02();
   }

   /**
    * read file
    * Single byte reading, low efficiency
    * -> read(byte[] b)
    */
   public static void readFile01() {
      String filePath = "G:\\hello.txt";
      int readData = 0;
      FileInputStream fileInputStream = null;
      try {
         // Create a FileInputStream object to read files
         fileInputStream = new FileInputStream(filePath);
         // Read one byte of data from the input stream. If no input is available, this method blocks.
         // If - 1 is returned, the reading is completed
         while ((readData = fileInputStream.read()) != -1) {
            System.out.print((char) readData);
         }
      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         try {
            fileInputStream.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
   }

   public static void readFile02() {
      String filePath = "G:\\hello.txt";
      int readData = 0;
      // Byte array
      byte[] buf = new byte[8];  // Read 8 bytes at a time
      FileInputStream fileInputStream = null;
      try {
         // Create a FileInputStream object to read files
         fileInputStream = new FileInputStream(filePath);
         // Read up to b.length bytes of data into a byte array from this input stream.
         // If the reading is normal, the number of bytes actually read is returned
         while ((readData = fileInputStream.read(buf)) != -1) {
            System.out.print(new String(buf, 0, readData));
         }
      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         try {
            fileInputStream.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
   }
}

FileOutputStream

// Demonstrate the use of FileInputStream (byte input stream file -- > program)
public class FileOutputStream01 {
   public static void main(String[] args) {
      writeFile();
   }

   public static void writeFile() {
      // Create FileOutputStream object
      String filePath = "G:\\hello.txt";

      FileOutputStream fileOutputStream = null;

      try {
         /**
          * 1. new FileOutputStream(filePath) Creation method. When content is written, the original content will be overwritten
          * 2. new FileOutputStream(filePath, true) Creation method: when writing content, it is appended to the back of the file
          */
         fileOutputStream = new FileOutputStream(filePath, true);
         // Write a byte
//       fileOutputStream.write('a');
         // Write string
         String str = "hello,world";
         fileOutputStream.write(str.getBytes());
         /**
          * write(byte[] b, int off, int len)
          *           Writes len bytes from the offset off in the specified byte array to this file output stream.
          */
         fileOutputStream.write(str.getBytes(), 0, 0);

      } catch (Exception e) {
         e.printStackTrace();
      }finally {
         try {
            fileOutputStream.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
   }
}

File copy

public class FileCopy {
   public static void main(String[] args) throws Exception {
      // File copy, G:
      /*
         Train of thought analysis
         1. Create the input stream of the file and read the file into the program
         2. Create the output stream of the file and write the read file data to the specified file
       */
      String srcFilePath = "G:\\Koala.jpg";
      String destFilePath = "G:\\Koala2.jpg";

      FileInputStream fileInputStream = new FileInputStream(srcFilePath);
      FileOutputStream fileOutputStream = new FileOutputStream(destFilePath);

      // Define a byte array to improve the reading effect
      byte[] buf = new byte[1024];
      int readLen = 0;

      while ((readLen = fileInputStream.read(buf)) != -1) {
         // After reading, it is written to the file through fileOutputStream
         fileOutputStream.write(buf, 0, readLen); // Be sure to use this method
      }
      System.out.println("Copy ok~");

      fileInputStream.close();
      fileOutputStream.close();
   }
}

FileReader and FileWriter

FileReader and FileWriter are character streams, that is, IO is operated according to characters

FileReader related methods:

new FileReader(File/String)
read: Each time a single character is read, this character is returned. If it reaches the end of the file, it is returned-1
read(char[]): Batch read multiple characters into the array, return the number of characters read, if to the end of the file, return-1
    
relevant API
new String(char[]): take char[] Convert to String
new String(carr[],off,len): take char[]Convert the specified part of to String

FileWriter common methods

new FileWriter(File/String): Overlay mode, which is equivalent to the pointer of the stream at the head end
new FileWriter(File/String, true): Append mode, which is equivalent to the pointer of the stream at the end
writer(int): Write a single character
write(char[]): Writes the specified array
write(char[], oof, len): Writes the specified part of the specified array
write(string): Write a string
write(string, off, len): Writes the specified part of the string

relevant API: String Class: toCharArray: take String convert to char[]

be careful: FileWriter It must be closed after use( close)Or refresh( flush),Otherwise, the specified file cannot be written!

case

public class FileReader01 {
   public static void main(String[] args) {
//    readFile01();
      readFile02();
   }

   /**
    * Single character reading
    */
   public static void readFile01() {
      String filePath = "G:\\hello.txt";
      FileReader fileReader = null;
      int data = 0;
      try {
         fileReader = new FileReader(filePath);
         // Read is used for cyclic reading, and a single character is read
         while ((data = fileReader.read()) != -1) {
            System.out.print((char) data);
         }
      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         try {
            fileReader.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
   }

   /**
    * Character array reading
    */
   public static void readFile02() {
      String filePath = "G:\\hello.txt";
      FileReader fileReader = null;

      int readLen = 0;
      char[] buf = new char[1024];
      try {
         fileReader = new FileReader(filePath);
         // Cyclic reading uses read(buf) to read a single character
         while ((readLen = fileReader.read(buf)) != -1) {
            System.out.print(new String(buf, 0, readLen));
         }
      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         try {
            fileReader.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
   }

}
public class FileWriter01 {
   public static void main(String[] args) {
      String filePath = "G:\\hello.txt";
      FileWriter fileWriter = null;
      char[] chars = {'a', 'b', 'c'};
      try {
         fileWriter = new FileWriter(filePath);
         // Five ways of writing
         fileWriter.write("H");
         fileWriter.write(chars);
         fileWriter.write("hahahaha".toCharArray(), 0, 3);
         fileWriter.write("hello ah");
         fileWriter.write("Shanghai Tianjin", 0, 2);
      } catch (IOException e) {
         e.printStackTrace();
      } finally {
         try {
            // For FileWriter, you must close the stream or flush to write data to the file
            fileWriter.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
      System.out.println("The program ends....");
   }
}

Note: for FileWriter, you must close the stream or flush to write data to the file

3, Node flow and processing flow

3.1 concept

Node streams can read and write data from a specific data source, such as FileReader and FileWriter

Processing flow (also known as wrapper flow) is "connected" to the existing flow (node flow or processing flow) to provide more powerful reading and writing functions for programs, such as BufferedReader and BufferedWriter.

Node flow and processing flow diagram

Differences and relations between node flow and processing flow

  • Node flow is the underlying flow / low-level flow, which is directly connected to the data source.
  • Processing flow wrapping node flow can not only eliminate the implementation differences of different node flows, but also provide a more convenient method to complete input and output.
  • The processing flow (also known as wrapping flow) wraps the node flow, uses the decorator mode, and is not directly connected to the data source

The functions of processing flow are mainly reflected in two aspects:

  • Performance improvement: mainly increase the buffer to improve the efficiency of input and output.
  • Convenient operation: the processing flow may provide a series of convenient methods to input and output large quantities of data at one time, which is more flexible and convenient to use

Case: simulate BufferedReader

public abstract class Reader_ { //abstract class
    public void readFile() {
    }
    public void readString() {
    }
    
    //In Reader_ Abstract classes, using the read method for unified management
    //Later, when calling, it is conducive to the object dynamic binding mechanism, which can be bound to the corresponding implementation subclass
    //public abstract void read();
}

public class FileReader_ extends Reader_ {

        public void readFile() {
        System.out.println("Read file...");
    }
}

public class StringReader_ extends Reader_ {
    public void readString() {
        System.out.println("Read string..");
    }
}

public class BufferedReader_ extends Reader_{

    private Reader_ reader_; //Aggregate Reader_

    public BufferedReader_(Reader_ reader_) {
        this.reader_ = reader_;
    }

    public void readFile() { //Encapsulate one layer
        reader_.readFile();
    }

    //Make the method more flexible, read the file multiple times, or add buffer byte []
    public void readFiles(int num) {
        for(int i = 0; i < num; i++) {
            reader_.readFile();
        }
    }

    //Extend readString to batch process string data
    public void readStrings(int num) {
        for(int i = 0; i <num; i++) {
            reader_.readString();
        }
    }
}

public class Test_ {
    public static void main(String[] args) {
        BufferedReader_ bufferedReader_ = new BufferedReader_(new FileReader_());
        bufferedReader_.readFiles(10);
        //bufferedReader_.readFile();

        //This time, I hope to use BufferedReader_ Read string multiple times
        BufferedReader_ bufferedReader_2 = new BufferedReader_(new StringReader_());
        bufferedReader_2.readStrings(5);
    }
}

3.2,BufferedReader

BufferedWriter and BufferedWriter belong to character stream and read data according to characters

When off, you only need to turn off the outer flow

public class BufferedReader_ {
   public static void main(String[] args) throws Exception {
      String filePath = "G:\\a.java";

      BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));

      String line;
      while ((line = bufferedReader.readLine()) != null) {
         System.out.println(line);
      }

      // Close the flow. Note here that you only need to close the BufferedReader, because the magnetosphere will automatically close the node flow
      bufferedReader.close();
   }
}

debug found that the node stream new FileReader(filePath) we passed in is closed here

3.3,BufferedWriter

public class BufferedWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath = "G:\\ok.txt";
        //Create BufferedWriter
        //explain:
        //1. new FileWriter(filePath, true) means to write by appending
        //2. new FileWriter(filePath), which means to write in the way of overwrite
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
        bufferedWriter.write("hello, bufferedWriter1");
        bufferedWriter.newLine(); // Insert a system related line feed
        bufferedWriter.write("hello, bufferedWriter2");
        bufferedWriter.newLine();
        bufferedWriter.write("hello, bufferedWriter3");

        bufferedWriter.close();
    }
}

Case: using BufferedReader and BufferedReader to copy files

public class BufferedCopy_ {
   public static void main(String[] args) {
      String srcFilePath = "G:\\a.java";
      String destFilePath = "G:\\b.java";
      // Note: it is assumed that the Reader and Writer are read by characters, so binary files (byte files), such as pictures and music, cannot be operated, resulting in data reading errors
      BufferedReader br = null;
      BufferedWriter bw = null;
      String line;
      try {
         br = new BufferedReader(new FileReader(srcFilePath));
         bw = new BufferedWriter(new FileWriter(destFilePath));
         while ((line = br.readLine()) != null) {
            // Read and write one line
            bw.write(line);
            bw.newLine();
         }
         System.out.println("Copy complete");
      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         try {
            bw.close();
            br.close();
         } catch (IOException e) {
            e.printStackTrace();
         }
      }

   }
}

3.4,BufferedInputStream

BufferedInputStream is a byte stream. When creating BufferedInputStream, an internal buffer array will be created.

3.5,BufferedOutputStream

BufferedOutputStream is a byte stream that implements buffered output stream. Multiple bytes can be written to the underlying output stream without calling the underlying system for each byte write

Byte processing stream copy files BufferedInputStream and BufferedOutputStream

public class BufferedCopy02 {
    public static void main(String[] args) {
        String srcFilePath = "G:\\koala.jpg";
        String destFilePath = "G:\\koala.jpg";
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            // FileInputStream is a subclass of InputStream
            bis = new BufferedInputStream(new FileInputStream(srcFilePath));
            bos = new BufferedOutputStream(new FileOutputStream(destFilePath));

            // Loop through the file and write to destFilePath
            byte[] buff = new byte[1024];
            int readLen = 0;
            // Returning - 1 indicates that the file has been read
            while ((readLen = bis.read(buff)) != -1) {
                bos.write(buff, 0, readLen);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                bos.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Think: can byte stream manipulate binary files and text files? Certainly.

3.6 object flow

ObjectInputStream and ObjectOutputStream

Look at a demand

  1. Save the int data of int num=100 to the file. Note that it is not a 100 number, but int 100, and you can directly restore int 100 from the file
  2. Save the dog object dog = new dog ("Xiaohuang", 3) to a file and restore it from the file.
  3. The above requirement is to be able to serialize and deserialize basic data types or objects

serialization and deserialization

  • Serialization is to save the value and data type of data when saving data
  • Deserialization is to recover the value and data type of data when recovering data
  • If an object needs to support serialization mechanism, its class must be serializable. In order to make a class serializable, the class must implement one of the following two interfaces:
    • Serializable / / this is a tag interface
    • Externalizable

ObjectOutputStream provides serialization

ObjectInputStream provides deserialization

Use ObjectOutputStream to serialize the basic data type and a Dog object (name, age) and save it to data Dat file

public class ObjectOutputStream_ {
   public static void main(String[] args) throws Exception {
      // After serialization, the saved file format is not text, but saved according to its format
      String filePath = "G:\\data.dat";

      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));

      //Serialize data to e: \ data dat
      oos.writeInt(100);// Int - > integer (serializable implemented)
      oos.writeBoolean(true);// Boolean - > Boolean (serializable is implemented)
      oos.writeChar('a');// Char - > character (serializable is implemented)
      oos.writeDouble(9.5);// Double - > double (serializable is implemented)
      oos.writeUTF("Han Shunping Education");//String

      //Save a dog object
      oos.writeObject(new Dog("Wangcai", 10));
      oos.close();
      System.out.println("Data saved(serialized form )");
   }
}

public class ObjectInputStream_ {
   public static void main(String[] args) throws Exception {
      //Specifies the file to deserialize
      String filePath = "G:\\data.dat";
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
      //read
      // The order of reading (deserialization) needs to be consistent with the order in which you save data (serialization), otherwise an exception will occur
      System.out.println(ois.readInt());
      System.out.println(ois.readBoolean());
      System.out.println(ois.readChar());
      System.out.println(ois.readDouble());
      System.out.println(ois.readUTF());

      //The compilation type of Dog is object, and the running type of Dog is Dog
      Object dog = ois.readObject();
      System.out.println("Operation type=" + dog.getClass());
      System.out.println("dog information=" + dog);//Underlying object - > dog

      //Here are particularly important details:
      //1. If we want to call Dog's method, we need to transform down
      Dog dog2 = (Dog) dog;
      System.out.println(dog2.getName()); //Wangcai

      //Close the stream, just close the outer stream, and the bottom stream will close the FileInputStream stream stream
      ois.close();
   }
}

Notes and details

  • Read and write in the same order
  • Serializable interface is required to implement serialized or deserialized objects
  • SerialVersionUID is recommended to be added to serialized classes to improve version compatibility
  • When serializing an object, all the attributes in it are serialized by default, except for members decorated with static or transient
    • Members decorated with static or transient are not serialized
  • When serializing an object, it is required that the type of the attribute inside also implement the serialization interface
  • Serialization is inheritable, that is, if a class has implemented serialization, all its subclasses have implemented serialization by default
private static final long serialVersionUID = 1L;

3.7 standard input / output stream

  • Standard input -- > system In (type InputStream)
  • Standard output -- > system Out (type PrintStream)
public class InputAndOutput_ {
   public static void main(String[] args) {
      //Public final static InputStream in of System class = null;
      // System.in compilation type InputStream
      // System.in run type BufferedInputStream
      // Represents a standard input keyboard
      System.out.println(System.in.getClass());

      //1. System.out public final static PrintStream out = null;
      //2. Compile type PrintStream
      //3. Operation type: PrintStream
      //4. Indicates standard output display
      System.out.println(System.out.getClass());

      System.out.println("hello, ~");

      Scanner scanner = new Scanner(System.in);
      System.out.println("Input content");
      String next = scanner.next();
      System.out.println("next=" + next);
      scanner.close();
   }
}

3.8 conversion flow

Create a new txt file and save it in ANSI format

public class CodeQuestion {
   public static void main(String[] args) throws IOException {
      //Read the G:\a.txt file to the program
      //thinking
      //1. Create character input stream BufferedReader [processing stream]
      //2. Use BufferedReader object to read a.txt
      //3. By default, the read file is encoded according to utf-8
      String filePath = "G:\\a.txt";
      BufferedReader br = new BufferedReader(new FileReader(filePath));

      String s = br.readLine();
      System.out.println("Read content: " + s);
      br.close();
   }
}

As you can see, read the garbled code because the text file is not utf-8

The reason for the occurrence of garbled code is that the encoding method for reading is not specified. The conversion stream can convert the byte stream into a character stream, and the encoding method can be specified for the byte stream

InputStreamReader and OutputStreamWriter

  • InputStreamReader is a subclass of Reader, which can convert InputStream (byte stream) into Reader (character stream)
  • OutputStreamWriter is a subclass of Writer, which can convert OutputStream (byte stream) to Writer (character stream)
  • When processing plain text data, if using character stream is more efficient and can effectively solve the problem of Chinese garbled code, it is recommended to convert byte stream into character stream
  • You can specify the encoding format (such as utf-8, gbk, gb2312, ISO8859-1, etc.) when using

/**
 * Demonstrates how to use InputStreamReader to transform streams to solve the problem of Chinese garbled code
 * Convert byte stream FileInputStream to character stream InputStreamReader, and specify the encoding gbk/utf-8
 */
public class InputStreamReader_ {
   public static void main(String[] args) throws Exception {
      String filePath = "G:\\a.txt";

      //1. Convert FileInputStream to InputStreamReader
      //2. Specify the code gbk
      InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
      //3. Transfer InputStreamReader to BufferedReader
      BufferedReader br = new BufferedReader(isr);
      
      //4. Read
      String s = br.readLine();
      System.out.println("Read content=" + s);
      //5. Turn off the outer flow
      br.close();
   }
}
/**
 * Demonstrate the use of OutputStreamWriter
 * Convert FileOutputStream byte stream into character stream OutputStreamWriter
 * Specifies the encoding for processing gbk/utf-8/utf8
 */
public class OutputStreamWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath = "G:\\lsp.txt";
        Charset charset = Charset.forName("gbk");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charset);
        osw.write("hi, lsp");
        osw.close();

        System.out.println("According to:" + charset + " File saved successfully!");
    }
}

3.9 print stream

PrintStream,PrintWriter

public class PrintStream_ {
    public static void main(String[] args) throws IOException {
        PrintStream out = System.out;
        // By default, the location of PrintStream output data is standard output, that is, the display
        /*
		public void print(String var1) {
			if (var1 == null) {
				var1 = "null";
			}

			this.write(var1);
		}
		 */
        out.print("Hello");
        // Because the bottom layer of print uses write, we can directly call write to print / output
        out.write("hello, Hello".getBytes());
        out.close();

        //We can modify the location / device of print stream output
        //1. Modify the output to "G:\f1.txt"
        //2. "hello, Han Shunping education ~" will be output to e: \ F1 txt
        //3. 
        /*
			public static void setOut(PrintStream out) {
					checkIO();
				   setOut0(out); // native Method, modified out
			   }
			 */
        System.setOut(new PrintStream("G:\\f1.txt"));
        System.out.println("hell Ah, lsp");

    }
}

public class PrintWriter_ {
   public static void main(String[] args) throws IOException {

//    PrintWriter printWriter = new PrintWriter(System.out);
      PrintWriter printWriter = new PrintWriter(new FileWriter("G:\\f2.txt"));
      printWriter.print("hi,Hello, Beijing!");
      printWriter.close();
   }
}

3.10 Properties

The following configuration file is mysql properties

ip=192.168.100.100
user=root
pwd=123456

What are the programmed values of ip, user and pwd

analysis

  • Traditional methods
  • Using the Properties class
// Traditional methods
public class Properties01 {
   public static void main(String[] args) throws IOException {

      //Read mysql Properties file and get ip, user and pwd
      BufferedReader br = new BufferedReader(new FileReader("mysql.properties"));
      String line = "";
      while ((line = br.readLine()) != null) {
         String[] split = line.split("=");
         //If we require the specified ip value
         if ("ip".equals(split[0])) {
            System.out.println(split[0] + "Value is: " + split[1]);
         }
      }
      br.close();
   }
}

Properties class

  1. A collection class dedicated to reading and writing configuration files

    Format of configuration file:

    • Key = value
  2. Note: key value pairs do not need spaces, and values do not need to be enclosed in quotation marks. The default type is string

  3. Common methods for Properties

    - load: Load the key value pair of the configuration file to Properties object
    - list: Displays data to the specified device
    - getProperty(key) : Get value according to key
    - setProperty(key, value): Set key value to Properties object
    - store: take Properties The key value pairs in are stored in the configuration file idea In, save the information to the configuration file. If it contains Chinese, it will be stored as unicode code
    
public class Properties02 {
   public static void main(String[] args) throws IOException {
      //Use the properties class to read mysql Properties file
      
      //1. Create Properties object
      Properties properties = new Properties();
      //2. Load the specified configuration file
      properties.load(new FileReader("src\\mysql.properties"));
      //3. Display the k-v on the console
      properties.list(System.out);
      //4. Obtain the corresponding value according to the key
      String user = properties.getProperty("user");
      String pwd = properties.getProperty("pwd");
      System.out.println("user name=" + user);
      System.out.println("The password is=" + pwd);
   }
}
public class Properties03 {
    public static void main(String[] args) throws IOException {
        //Use the Properties class to create a configuration file and modify the content of the configuration file
        Properties properties = new Properties();
        //establish
        //1. If the file does not have a key, it is created
        //2. If the file has a key, it is modified
        /*
            Properties The parent class is Hashtable, and the bottom layer is the core method of Hashtable
            public synchronized V put(K key, V value) {
                // Make sure the value is not null
                if (value == null) {
                    throw new NullPointerException();
                }

                // Makes sure the key is not already in the hashtable.
                Entry<?,?> tab[] = table;
                int hash = key.hashCode();
                int index = (hash & 0x7FFFFFFF) % tab.length;
                @SuppressWarnings("unchecked")
                Entry<K,V> entry = (Entry<K,V>)tab[index];
                for(; entry != null ; entry = entry.next) {
                    if ((entry.hash == hash) && entry.key.equals(key)) {
                        V old = entry.value;
                        entry.value = value;//If the key exists, replace it
                        return old;
                    }
                }

                addEntry(hash, key, value, index);//If it is a new k, add entry
                return null;
            }

         */
        properties.setProperty("charset", "utf-8");
        properties.setProperty("user", "Tom");//Note that when saving, it is the unicode code value in Chinese
        properties.setProperty("pwd", "888888");

        //Store the k-v in a file
        properties.store(new FileOutputStream("src\\mysql2.properties"), null);
        System.out.println("Successfully saved the configuration file~");
    }
}

practice

/**
 * (1) Judge whether there is a folder mytemp under disk e. if not, create mytemp
 * (2) In the G:\mytemp directory, create the file hello txt
 * (3) If hello Txt already exists, indicating that the file already exists. Do not create it again
 * (4) And in hello Txt file, write hello,world~
 */
public class Homework01 {
   public static void main(String[] args) throws IOException {
      String directoryPath = "G:\\mytemp";
      File file = new File(directoryPath);
      if (!file.exists()) {
         //establish
         if (file.mkdirs()) {
            System.out.println("establish " + directoryPath + " Created successfully");
         } else {
            System.out.println("establish " + directoryPath + " Creation failed");
         }
      }

      String filePath = directoryPath + "\\hello.txt";// e:\mytemp\hello.txt
      file = new File(filePath);
      if (!file.exists()) {
         //create a file
         if (file.createNewFile()) {
            System.out.println(filePath + " Created successfully~");


         } else {
            System.out.println(filePath + " Creation failed~");
         }
      } else {
         //If the file already exists, a prompt message will be given
         System.out.println(filePath + " Already exists, not created repeatedly...");
      }
      //If the file exists, we use the BufferedWriter character input stream to write the content
      BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file));
      bufferedWriter.write("hello, world~~ Han Shunping Education");
      bufferedWriter.close();
   }
}
/**
* Requirements: use BufferedReader to read a text file and add line number to each line,
* Then output to the screen together with the content.
*/
public class Homework02 {
    public static void main(String[] args) throws IOException {
        String filePath = "G:\\mytemp\\hello.txt";

        BufferedReader br = new BufferedReader(new FileReader(filePath));
        String line = "";
        int lineNum = 0;
        while ((line = br.readLine()) != null) {
            System.out.println(++lineNum + " " + line);
        }
        br.close();
    }
}
/**
 * (1) To write a dog properties   name=tom age=5 color=red
 * (2) Write a dog class (name,age,color) to create a dog object and read the dog Properties complete the property initialization with the corresponding content and output
 * (3) Serialize the created Dog object to the file e: \ \ Dog Dat file
 */
public class Homework03 {
   public static void main(String[] args) throws Exception {

      String filePath = "dog.properties";

      Properties properties = new Properties();
      properties.load(new FileInputStream(filePath));

      String name = properties.get("name") + ""; //Object -> String
      int age = Integer.parseInt(properties.get("age") + "");// Object -> int
      String color = properties.get("color") + "";//Object -> String


      Dog dog = new Dog(name, age, color);
      System.out.println("===dog Object information====");
      System.out.println(dog);

      //Serialize the created Dog object to the file Dog Dat file
      String serFilePath = "G:\\dog.bat";
      ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(serFilePath));
      objectOutputStream.writeObject(dog);

      objectOutputStream.close();
      System.out.println("dog Object, serialization complete");

      m1();
   }

   // Deserialization
   public static void m1() throws Exception {
      String serFilePath = "G:\\dog.bat";
      ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(serFilePath));
      Dog dog = (Dog) objectInputStream.readObject();
      System.out.println("=======Deserialized dog========");
      System.out.println(dog);
   }
}

class Dog implements Serializable {
   private static final long serialVersionUID = 1306781508659844850L;
   private String name;
   private int age;
   private String color;

   public Dog(String name, int age, String color) {
      this.name = name;
      this.age = age;
      this.color = color;
   }

   @Override
   public String toString() {
      return "Dog{" +
            "name='" + name + '\'' +
            ", age=" + age +
            ", color='" + color + '\'' +
            '}';
   }
}

4, Operation Zip

ZipInputStream is a FilterInputStream, which can directly read the contents of the zip package:

The other JarInputStream is derived from ZipInputStream, and its main added function is to directly read the manifest in the jar file MF file. Because in essence, the jar package is a zip package, with some fixed description files attached.

Read zip package

Let's take a look at the basic usage of ZipInputStream.

We want to create a ZipInputStream, usually pass in a FileInputStream as the data source, and then call getNextEntry() repeatedly until null is returned, indicating the end of the zip stream.

A ZipEntry represents a compressed file or directory. If it is a compressed file, we use the read() method to read it continuously until - 1 is returned:

try (ZipInputStream zip = new ZipInputStream(new FileInputStream(...))) {
    ZipEntry entry = null;
    while ((entry = zip.getNextEntry()) != null) {
        String name = entry.getName();
        if (!entry.isDirectory()) {
            int n;
            while ((n = zip.read()) != -1) {
                ...
            }
        }
    }
}

Write zip package

Zipoutputstream is a FilterOutputStream that can write content directly to a zip package. We first create a ZipOutputStream, usually wrapping a FileOutputStream. Then, before writing a file, we call putNextEntry() first, then write byte[] data with write(), and finish the package after calling closeEntry().

try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(...))) {
    File[] files = ...
    for (File file : files) {
        zip.putNextEntry(new ZipEntry(file.getName()));
        zip.write(getFileDataAsBytes(file));
        zip.closeEntry();
    }
}

The above code does not consider the directory structure of the file. If you want to implement the directory hierarchy, the name passed in by new ZipEntry(name) should use a relative path.

Summary

ZipInputStream can read streams in zip format, and zipuutputstream can write multiple copies of data into zip packages;

With FileInputStream and FileOutputStream, you can read and write zip files.

5, Using Files

Starting from Java 7, two tool classes, Files and Paths, are provided, which can greatly facilitate us to read and write Files.

Although Files and Paths are Java NiO package, but they encapsulate many simple methods to read and write Files. For example, we need to read all the contents of a file as a byte [], which can be written as follows:

byte[] data = Files.readAllBytes(Paths.get("/path/to/file.txt"));

If it is a text file, you can read all the contents of a file as String:

// Read with UTF-8 encoding by default:
String content1 = Files.readString(Paths.get("/path/to/file.txt"));
// Code can be specified:
String content2 = Files.readString(Paths.get("/path/to/file.txt"), StandardCharsets.ISO_8859_1);
// Read and return each line by line:
List<String> lines = Files.readAllLines(Paths.get("/path/to/file.txt"));

Writing files is also very convenient:

// Write binary:
byte[] data = ...
Files.write(Paths.get("/path/to/file.txt"), data);
// Write text and specify encoding:
Files.writeString(Paths.get("/path/to/file.txt"), "Text content...", StandardCharsets.ISO_8859_1);
// Write text by line:
List<String> lines = ...
Files.write(Paths.get("/path/to/file.txt"), lines);

In addition, the Files tool class also has shortcut methods such as copy(), delete(), exists(), move(), etc. to operate Files and directories.

Finally, it should be noted that the reading and writing methods provided by Files are limited by memory. They can only read and write small Files, such as configuration Files, and cannot read several G large Files at a time. To read and write large Files, you still need to use file stream, and read and write part of the file content at a time.

Summary

For simple small file reading and writing operations, you can use the Files tool class to simplify the code.

6, Network operation

Network support in Java:

  • InetAddress: used to represent hardware resources on the network, i.e. IP address;
  • URL: uniform resource locator;
  • Sockets: use TCP protocol to realize network communication;
  • Datagram: use UDP protocol to realize network communication.

6.1,InetAddress

There is no public constructor. You can only create instances through static methods.

InetAddress.getByName(String host);
InetAddress.getByAddress(byte[] address);

6.2,URL

Byte stream data can be read directly from the URL.

public static void main(String[] args) throws IOException {
    URL url = new URL("http://www.baidu.com");

    /* Byte stream */
    InputStream is = url.openStream();

    /* Character stream */
    InputStreamReader isr = new InputStreamReader(is, "utf-8");

    /* Provide caching function */
    BufferedReader br = new BufferedReader(isr);

    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }

    br.close();
}

6.3,Sockets

  • ServerSocket: server-side class
  • Socket: client class
  • The server and client input and output through InputStream and OutputStream.

6.4,Datagram

  • Datagram socket: communication class
  • Datagram packet: packet class

7, NIO

New IO

The new input / output (NIO) library is introduced in JDK 1.4, which makes up for the shortcomings of the original I/O and provides high-speed, block oriented I/O.

7.1 flow and block

The most important difference between I/O and NIO is the way of data packaging and transmission. I/O processes data in stream, while NIO processes data in block.

Stream oriented I/O processes one byte of data at a time: an input stream generates one byte of data, and an output stream consumes one byte of data. It's easy to create filters for streaming data, linking several filters so that each filter is responsible for only part of a complex processing mechanism. The downside is that stream oriented I/O is usually quite slow.

Block oriented I/O processes one data block at a time, and processing data by block is much faster than processing data by stream. However, block oriented I/O lacks the elegance and simplicity of stream oriented I/O.

I/O package and NIO have been well integrated, Java io.* It has been re implemented based on NIO, so now it can take advantage of some features of NIO. For example, Java io.* Some classes in the package contain methods to read and write data in the form of blocks, which makes the processing speed faster even in stream oriented systems.

7.2 channels and buffers

passageway

Channel is a simulation of the stream in the original I/O package, through which data can be read and written.

The difference between channels and streams is that streams can only move in one direction (a stream must be a subclass of InputStream or OutputStream), while channels are bidirectional and can be used for reading, writing or both.

Channels include the following types:

  • FileChannel: read and write data from files;
  • Datagram channel: read and write data in the network through UDP;
  • SocketChannel: read and write data in the network through TCP;
  • ServerSocketChannel: it can listen to new TCP connections and create a SocketChannel for each new connection.

buffer

All data sent to a channel must be put into the buffer first. Similarly, any data read from the channel must be read into the buffer first. In other words, the channel will not be read or written directly, but will pass through the buffer first.

A buffer is essentially an array, but it is not just an array. Buffers provide structured access to data and can also track the read / write process of the system.

Buffers include the following types:

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

7.3 buffer state variables

  • Capacity: maximum capacity;
  • position: the number of bytes currently read and written;
  • limit: the number of bytes that can also be read and written.

Example of change process of state variable:

① Create a buffer with a size of 8 bytes. At this time, position is 0 and limit = capacity = 8. The capacity variable will not change and will be ignored in the following discussion.

② Read 5 bytes of data from the input channel and write it into the buffer. At this time, the position is 5 and the limit remains unchanged.

③ Before writing the buffer data to the output channel, you need to call the flip() method, which sets the limit to the current position and the position to 0.

④ Take 4 bytes from the buffer to the output buffer. At this time, position is set to 4.

⑤ Finally, you need to call the clear() method to empty the buffer. At this time, both position and limit are set to the original position.

7.4 document NIO example

The following shows an example of using NIO to quickly copy files:

public static void fastCopy(String src, String dist) throws IOException {
    /* Gets the input byte stream of the source file */
    FileInputStream fin = new FileInputStream(src);

    /* Gets the file channel of the input byte stream */
    FileChannel fcin = fin.getChannel();

    /* Gets the output byte stream of the target file */
    FileOutputStream fout = new FileOutputStream(dist);

    /* Gets the file channel of the output byte stream */
    FileChannel fcout = fout.getChannel();

    /* Allocate 1024 bytes for the buffer */
    ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

    while (true) {
        /* Read data from the input channel into the buffer */
        int r = fcin.read(buffer);

        /* read() Return - 1 for EOF */
        if (r == -1) {
            break;
        }

        /* Toggle read / write */
        buffer.flip();

        /* Writes the contents of the buffer to the output file */
        fcout.write(buffer);

        /* Empty buffer */
        buffer.clear();
    }
}

7.5 selector

NIO is often called non blocking IO, mainly because NIO is widely used in network communication.

NIO implements the Reactor model in IO multiplexing. A Thread uses a Selector selector to listen for events on multiple channels through polling, so that one Thread can handle multiple events.

By configuring the monitored Channel channel as non blocking, when the IO event on the Channel has not arrived, it will not enter the blocking state and wait all the time, but continue to poll other channels to find the Channel where the IO event has arrived for execution.

Because of the high overhead of creating and switching threads, using one thread to process multiple events instead of one thread to process one event has good performance for IO intensive applications.

It should be noted that only socket Channel can be configured as non blocking, while FileChannel cannot. It is meaningless to configure non blocking for FileChannel.

1. Create selector

Selector selector = Selector.open();

2. Register the channel on the selector

ServerSocketChannel ssChannel = ServerSocketChannel.open();
ssChannel.configureBlocking(false);
ssChannel.register(selector, SelectionKey.OP_ACCEPT);

The channel must be configured in the non blocking mode, otherwise using the selector is meaningless, because if the channel is blocked on an event, the server cannot respond to other events and must wait for the event to be processed before processing other events. Obviously, this runs counter to the role of the selector.

When registering the channel to the selector, you also need to specify the specific events to register, mainly including the following categories:

  • SelectionKey.OP_CONNECT
  • SelectionKey.OP_ACCEPT
  • SelectionKey.OP_READ
  • SelectionKey.OP_WRITE

Their definitions in SelectionKey are as follows:

public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;

It can be seen that each event can be regarded as a bit field to form an event set integer. For example:

int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

3. Listening events

int num = selector.select();

Use select() to listen for incoming events, which will block until at least one event arrives.

4. Get events that arrive

Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
 SelectionKey key = keyIterator.next();
 if (key.isAcceptable()) {
     // ...
 } else if (key.isReadable()) {
     // ...
 }
 keyIterator.remove();
}

5. Event cycle
Because a single select() call cannot handle all events, and the server may need to listen to events all the time, the code for processing events on the server will generally be placed in an endless loop.

while (true) {
    int num = selector.select();
    Set<SelectionKey> keys = selector.selectedKeys();
    Iterator<SelectionKey> keyIterator = keys.iterator();
    while (keyIterator.hasNext()) {
        SelectionKey key = keyIterator.next();
        if (key.isAcceptable()) {
            // ...
        } else if (key.isReadable()) {
            // ...
        }
        keyIterator.remove();
    }
}

7.6 socket NIO instance

public class NIOServer {
    public static void main(String[] args) throws IOException {

        Selector selector = Selector.open();

        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        ssChannel.configureBlocking(false);
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);

        ServerSocket serverSocket = ssChannel.socket();
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8888);
        serverSocket.bind(address);

        while (true) {

            selector.select();
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = keys.iterator();

            while (keyIterator.hasNext()) {

                SelectionKey key = keyIterator.next();

                if (key.isAcceptable()) {

                    ServerSocketChannel ssChannel1 = (ServerSocketChannel) key.channel();

                    // The server creates a SocketChannel for each new connection
                    SocketChannel sChannel = ssChannel1.accept();
                    sChannel.configureBlocking(false);

                    // This new connection is mainly used to read data from the client
                    sChannel.register(selector, SelectionKey.OP_READ);

                } else if (key.isReadable()) {

                    SocketChannel sChannel = (SocketChannel) key.channel();
                    System.out.println(readDataFromSocketChannel(sChannel));
                    sChannel.close();
                }

                keyIterator.remove();
            }
        }
    }

    private static String readDataFromSocketChannel(SocketChannel sChannel) throws IOException {

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        StringBuilder data = new StringBuilder();

        while (true) {

            buffer.clear();
            int n = sChannel.read(buffer);
            if (n == -1) {
                break;
            }
            buffer.flip();
            int limit = buffer.limit();
            char[] dst = new char[limit];
            for (int i = 0; i < limit; i++) {
                dst[i] = (char) buffer.get(i);
            }
            data.append(dst);
            buffer.clear();
        }
        return data.toString();
    }
}

public class NIOClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8888);
        OutputStream out = socket.getOutputStream();
        String s = "hello world";
        out.write(s.getBytes());
        out.close();
    }
}

7.7 memory mapping file

Memory mapped file I/O is a method to read and write file data. It can be much faster than conventional stream based or channel based I/O.

Writing to a memory mapped file can be dangerous. Simply changing a single element of an array may directly modify the file on disk. Modifying data is not separate from saving data to disk.

The following code line maps the first 1024 bytes of the file to memory, and the map() method returns a MappedByteBuffer, which is a subclass of ByteBuffer. Therefore, the newly mapped buffer can be used like any other ByteBuffer, and the operating system is responsible for performing the mapping when needed.

MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);

7.8 comparison

NIO differs from ordinary I/O mainly in the following two points:

  • NIO is non blocking;
  • NIO is block oriented and I/O is stream oriented.

Topics: Java Back-end NIO