이전에 우리는 IO API (Input/Output Application Programming Interface)와 java.io 패키지에 대해 알게 되었습니다 . 이 패키지의 클래스는 주로 Java에서 스트림 작업을 위한 것입니다. 여기서 핵심은 스트림 의 개념입니다 .

오늘 우리는 NIO API (New Input/Output) 를 고려하기 시작할 것입니다 .

I/O에 대한 두 가지 접근 방식의 주요 차이점은 IO API는 스트림 지향적인 반면 NIO API는 버퍼 지향적이라는 것입니다. 따라서 이해해야 할 주요 개념은 버퍼채널 입니다 .

버퍼는 무엇이며 채널은 무엇입니까?

채널 데이터가 들어오고 나가는 논리적 포털인 반면 버퍼는 이 전송된 데이터의 소스 또는 대상입니다. 출력하는 동안 보내려는 데이터는 버퍼에 넣고 버퍼는 데이터를 채널로 전달합니다. 입력하는 동안 채널의 데이터가 버퍼에 들어갑니다.

다시 말해서:

  • 버퍼 단순히 정보를 쓰고 정보를 읽을 수 있는 메모리 블록입니다.
  • 채널 파일이나 소켓과 같은 I/O 장치에 대한 액세스를 제공하는 게이트웨이입니다.

채널은 java.io 패키지의 스트림과 매우 유사합니다. 어디로든 가는(또는 어디에서 오는) 모든 데이터는 채널 객체를 통과해야 합니다. 일반적으로 NIO 시스템을 사용하려면 I/O 엔터티에 대한 채널과 데이터를 저장하기 위한 버퍼를 얻습니다. 그런 다음 버퍼로 작업하여 필요에 따라 데이터를 입력하거나 출력합니다.

버퍼에서 앞뒤로 이동할 수 있습니다. 즉, 스트림에서 할 수 없는 버퍼를 "걷기"할 수 있습니다. 이는 데이터를 처리할 때 더 많은 유연성을 제공합니다. 표준 라이브러리에서 버퍼는 추상 Buffer 클래스와 여러 하위 항목으로 표시됩니다.

  • 바이트버퍼
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • 부동 버퍼
  • 더블버퍼
  • 롱버퍼

하위 클래스 간의 주요 차이점은 그들이 저장하는 데이터 유형 — bytes , ints , longs 및 기타 기본 데이터 유형입니다.

버퍼 속성

버퍼에는 네 가지 주요 속성이 있습니다. 용량, 한계, 위치 및 표시입니다.

용량은 버퍼에 저장할 수 있는 최대 데이터/바이트 양입니다. 버퍼의 용량은 변경할 수 없습니다 . 버퍼가 가득 차면 더 쓰기 전에 지워야 합니다.

쓰기 모드에서 버퍼의 한계는 용량과 동일하며 버퍼에 쓸 수 있는 최대 데이터 양을 나타냅니다. 읽기 모드에서 버퍼의 한계는 버퍼에서 읽을 수 있는 최대 데이터 양을 나타냅니다.

위치 버퍼에서 커서의 현재 위치를 나타냅니다. 처음에는 버퍼가 생성될 때 0으로 설정됩니다. 즉, 읽거나 쓸 다음 요소의 인덱스입니다.

마크 커서 위치를 저장하는 데 사용됩니다. 버퍼를 조작할 때 커서 위치는 계속 변경되지만 항상 이전에 표시된 위치로 되돌릴 수 있습니다.

버퍼 작업 방법

이제 채널에서 데이터를 읽고 쓰기 위해 버퍼(메모리 블록)로 작업할 수 있게 해주는 주요 메서드 집합을 살펴보겠습니다.

  1. allocate(int capacity) — 이 메서드는 지정된 용량으로 새 버퍼를 할당하는 데 사용됩니다. 전달된 용량이 음의 정수인 경우 allocate () 메서드는 IllegalArgumentException 을 발생시킵니다 .

  2. capacity()는 현재 버퍼의 용량을 반환합니다 .

  3. position()은 현재 커서 위치를 반환합니다. 읽기 및 쓰기 작업은 커서를 버퍼의 끝으로 이동합니다. 반환 값은 항상 제한보다 작거나 같습니다.

  4. limit()는 현재 버퍼의 한계를 반환합니다.

  5. mark()는 현재 커서 위치를 표시(저장)하는 데 사용됩니다.

  6. reset()은 커서를 이전에 표시된(저장된) 위치로 되돌립니다.

  7. clear()는 위치를 0으로 설정하고 제한을 용량으로 설정합니다. 이 메서드는 버퍼의 데이터를 지우지 않습니다 . 위치, 한계 및 마크만 다시 초기화합니다.

  8. flip()은 버퍼를 쓰기 모드에서 읽기 모드로 전환합니다. 또한 한계를 현재 위치로 설정한 다음 위치를 다시 0으로 설정합니다.

  9. read() — 채널의 read 메서드는 채널에서 버퍼로 데이터를 쓰는 데 사용되는 반면 버퍼의 put() 메서드는 데이터를 버퍼에 쓰는 데 사용됩니다.

  10. write() — 채널의 write 메서드는 버퍼에서 채널로 데이터를 쓰는 데 사용되는 반면 버퍼의 get() 메서드는 버퍼에서 데이터를 읽는 데 사용됩니다.

  11. rewind()는 버퍼를 되감습니다. 이 방법은 버퍼를 다시 읽어야 할 때 사용되며 위치를 0으로 설정하고 제한을 변경하지 않습니다.

이제 채널에 대한 몇 마디.

Java NIO에서 가장 중요한 채널 구현은 다음 클래스입니다.

  1. FileChannel — 파일에서 데이터를 읽고 쓰기 위한 채널입니다.

  2. DatagramChannel — 이 클래스는 UDP(사용자 데이터그램 프로토콜)를 통해 네트워크에서 데이터를 읽고 씁니다.

  3. SocketChannel — TCP(전송 제어 프로토콜)를 통해 네트워크에서 데이터를 읽고 쓰기 위한 채널입니다.

  4. ServerSocketChannel — 웹 서버와 마찬가지로 TCP 연결을 통해 데이터를 읽고 쓰기 위한 채널입니다. 들어오는 각 연결에 대해 SocketChannel 생성됩니다.

관행

몇 줄의 코드를 작성할 때입니다. 먼저 파일을 읽고 콘솔에 내용을 표시한 다음 파일에 문자열을 씁니다.

코드에는 많은 주석이 포함되어 있습니다. 모든 것이 어떻게 작동하는지 이해하는 데 도움이 되기를 바랍니다.


// Create a RandomAccessFile object, passing in the file path
// and a string that says the file will be opened for reading and writing
try (RandomAccessFile randomAccessFile = new RandomAccessFile("text.txt", "rw");
    // Get an instance of the FileChannel class
    FileChannel channel = randomAccessFile.getChannel();
) {
// Our file is small, so we'll read it in one go   
// Create a buffer of the required size based on the size of our channel
   ByteBuffer byteBuffer = ByteBuffer.allocate((int) channel.size());
   // Read data will be put into a StringBuilder
   StringBuilder builder = new StringBuilder();
   // Write data from the channel to the buffer
   channel.read(byteBuffer);
   // Switch the buffer from write mode to read mode
   byteBuffer.flip();
   // In a loop, write data from the buffer to the StringBuilder
   while (byteBuffer.hasRemaining()) {
       builder.append((char) byteBuffer.get());
   }
   // Display the contents of the StringBuilder on the console
   System.out.println(builder);
 
   // Now let's continue our program and write data from a string to the file
   // Create a string with arbitrary text
   String someText = "Hello, Amigo!!!!!";
   // Create a new buffer for writing,
   // but let the channel remain the same, because we're going to the same file
   // In other words, we can use one channel for both reading and writing to a file
   // Create a buffer specifically for our string — convert the string into an array and get its length
   ByteBuffer byteBuffer2 = ByteBuffer.allocate(someText.getBytes().length);
   // Write our string to the buffer
   byteBuffer2.put(someText.getBytes());
   // Switch the buffer from write mode to read mode
   // so that the channel can read from the buffer and write our string to the file
   byteBuffer2.flip();
   // The channel reads the information from the buffer and writes it to our file
   channel.write(byteBuffer2);
} catch (FileNotFoundException e) {
   e.printStackTrace();
} catch (IOException e) {
   e.printStackTrace();
}

NIO API를 사용해 보세요 . 마음에 드실 겁니다!