Java NIO - Guida rapida

Il pacchetto Java.nio è stato introdotto in java 1.4. Al contrario di java I / O in java NIO, viene introdotto il flusso di dati orientato al buffer e al canale per le operazioni di I / O che di conseguenza forniscono un'esecuzione più rapida e prestazioni migliori.

Anche le API NIO offrono selettori che introducono la funzionalità di ascoltare più canali per eventi IO in modo asincrono o non bloccante.In NIO le attività di I / O più dispendiose in termini di tempo, incluso il riempimento e lo svuotamento dei buffer del sistema operativo che aumenta di velocità.

Le astrazioni centrali delle API NIO sono le seguenti:

  • Buffer, che sono contenitori per dati, set di caratteri e decoder e codificatori associati, che traducono tra byte e caratteri Unicode.

  • Canali di vario tipo, che rappresentano connessioni a entità in grado di eseguire operazioni di I / O

  • Selettori e tasti di selezione, che insieme ai canali selezionabili definiscono una funzione di I / O multiplex e non bloccante.

Questa sezione ti guida su come scaricare e configurare Java sulla tua macchina. Seguire i passaggi seguenti per configurare l'ambiente.

Java SE è disponibile gratuitamente dal collegamento Scarica Java . Quindi scarichi una versione basata sul tuo sistema operativo.

Segui le istruzioni per scaricare java ed eseguire il file .exeper installare Java sulla tua macchina. Una volta installato Java sulla macchina, sarà necessario impostare le variabili di ambiente in modo che puntino alle directory di installazione corrette -

Impostazione del percorso per Windows 2000 / XP

Supponendo che tu abbia installato Java nella directory c: \ Program Files \ java \ jdk -

  • Fare clic con il pulsante destro del mouse su "Risorse del computer" e selezionare "Proprietà".

  • Fare clic sul pulsante "Variabili d'ambiente" nella scheda "Avanzate".

  • Ora modifica la variabile "Path" in modo che contenga anche il percorso dell'eseguibile Java. Ad esempio, se il percorso è attualmente impostato su "C: \ WINDOWS \ SYSTEM32", modificare il percorso in "C: \ WINDOWS \ SYSTEM32; c: \ Program Files \ java \ jdk \ bin".

Impostazione del percorso per Windows 95/98 / ME

Supponendo che tu abbia installato Java nella directory c: \ Program Files \ java \ jdk -

  • Modifica il file "C: \ autoexec.bat" e aggiungi la seguente riga alla fine:
    "SET PATH =% PATH%; C: \ Program Files \ java \ jdk \ bin"

Configurazione del percorso per Linux, UNIX, Solaris, FreeBSD

La variabile d'ambiente PATH dovrebbe essere impostata in modo che punti a dove sono stati installati i binari java. Fare riferimento alla documentazione della shell se si hanno problemi a farlo.

Esempio, se usi bash come shell, aggiungi la seguente riga alla fine di '.bashrc: export PATH = / path / to / java: $ PATH'

Editor Java popolari

Per scrivere i tuoi programmi java avrai bisogno di un editor di testo. Sul mercato sono disponibili IDE ancora più sofisticati. Ma per ora, puoi considerare uno dei seguenti:

  • Notepad - Su una macchina Windows puoi usare qualsiasi semplice editor di testo come Blocco note (consigliato per questo tutorial), TextPad.

  • Netbeans - è un IDE Java open source e gratuito che può essere scaricato da http://www.netbeans.org/index.html.

  • Eclipse - è anche un IDE java sviluppato dalla comunità open source di eclipse e può essere scaricato da https://www.eclipse.org/.

Come sappiamo, java NIO è stato introdotto per il progresso dell'API java IO convenzionale. I principali miglioramenti che rendono NIO più efficiente di IO sono il modello di flusso di dati del canale utilizzato in NIO e l'uso del sistema operativo per le attività IO convenzionali.

La differenza tra Java NIO e Java IO può essere spiegata come segue:

  • Come accennato nel post precedente nel buffer NIO e nel flusso di dati orientato al canale per le operazioni di I / O che forniscono un'esecuzione più rapida e prestazioni migliori rispetto all'IO. Anche NIO utilizza il sistema operativo per le attività di I / O convenzionali, il che lo rende ancora più efficiente.

  • Un altro aspetto della differenza tra NIO e IO è che questo IO utilizza il flusso di dati della linea di flusso, ovvero un byte in più alla volta e si basa sulla conversione degli oggetti di dati in byte e viceversa, mentre NIO si occupa dei blocchi di dati che sono blocchi di byte.

  • In java IO stream gli oggetti sono unidirezionali mentre in NIO i canali sono bidirezionali, il che significa che un canale può essere utilizzato sia per leggere che per scrivere dati.

  • Lo snellimento del flusso di dati in IO non consente di spostarsi avanti e indietro nei dati.Se il caso è necessario spostarsi avanti e indietro nei dati letti da un flusso, è necessario prima memorizzarli nella cache in un buffer.Mentre in caso di NIO usiamo il buffer oriented che consente di accedere ai dati avanti e indietro senza bisogno di cache.

  • L'API NIO supporta anche il multithreading in modo che i dati possano essere letti e scritti in modo asincrono in modo tale che durante l'esecuzione delle operazioni di I / O il thread corrente non venga bloccato, rendendolo ancora una volta più efficiente dell'API java IO convenzionale.

  • Il concetto di multi threading viene introdotto con l'introduzione di Selectors in java NIO che consentono di ascoltare più canali per eventi IO in modo asincrono o non bloccante.

  • Il multi threading in NIO lo rende Non bloccante, il che significa che al thread viene richiesto di leggere o scrivere solo quando i dati sono disponibili, altrimenti il ​​thread può essere utilizzato in altre attività per il tempo medio.Ma questo non è possibile in caso di java IO convenzionale come nessun multi threading è supportato in esso che lo rende come Blocco.

  • NIO consente di gestire più canali utilizzando un solo thread, ma il costo è che l'analisi dei dati potrebbe essere un po 'più complicata rispetto alla lettura dei dati da un flusso di blocco in caso di java IO, quindi nel caso in cui siano necessarie meno connessioni con larghezza di banda molto elevata con l'invio di molti dati alla volta, che in questo caso Java IO API potrebbe essere la soluzione migliore.

Descrizione

Come suggerisce il nome, il canale viene utilizzato come mezzo del flusso di dati da un'estremità all'altra.Qui in java il canale NIO agisce allo stesso modo tra il buffer e un'entità all'altra estremità, in altre parole il canale viene utilizzato per leggere i dati nel buffer e anche per scrivere i dati dal buffer.

A differenza dei flussi che vengono utilizzati nei convenzionali canali Java IO sono a due vie, cioè possono leggere e scrivere. Il canale Java NIO supporta il flusso asincrono di dati sia in modalità di blocco che non di blocco.

Implementazioni di Channel

Il canale Java NIO è implementato principalmente nelle seguenti classi:

  • FileChannel- Per leggere i dati dal file usiamo il canale file. L'oggetto del canale file può essere creato solo chiamando il metodo getChannel () sull'oggetto file poiché non è possibile creare direttamente l'oggetto file.

  • DatagramChannel - Il canale del datagramma può leggere e scrivere i dati sulla rete tramite UDP (User Datagram Protocol). L'oggetto di DataGramchannel può essere creato utilizzando metodi di fabbrica.

  • SocketChannel- Il canale SocketChannel può leggere e scrivere i dati sulla rete tramite TCP (Transmission Control Protocol). Utilizza anche i metodi di fabbrica per creare il nuovo oggetto.

  • ServerSocketChannel- Il ServerSocketChannel legge e scrive i dati su connessioni TCP, come un server web. Per ogni connessione in entrata viene creato un SocketChannel.

Esempio

L'esempio seguente legge da un file di testo da C:/Test/temp.txt e stampa il contenuto sulla console.

temp.txt

Hello World!

ChannelDemo.java

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelDemo {
   public static void main(String args[]) throws IOException {
      RandomAccessFile file = new RandomAccessFile("C:/Test/temp.txt", "r");
      FileChannel fileChannel = file.getChannel();
      ByteBuffer byteBuffer = ByteBuffer.allocate(512);
      while (fileChannel.read(byteBuffer) > 0) {
         // flip the buffer to prepare for get operation
         byteBuffer.flip();
         while (byteBuffer.hasRemaining()) {
            System.out.print((char) byteBuffer.get());
         }
      }
      file.close();
   }
}

Produzione

Hello World!

Descrizione

Come già accennato, l'implementazione FileChannel del canale Java NIO è stata introdotta per accedere alle proprietà dei metadati del file, inclusa la creazione, la modifica, la dimensione, ecc. Insieme a questo File, i canali sono multi thread, il che rende Java NIO più efficiente di Java IO.

In generale possiamo dire che FileChannel è un canale che è collegato a un file tramite il quale è possibile leggere dati da un file e scrivere dati su un file. Altra caratteristica importante di FileChannel è che non può essere impostato in modalità non bloccante e funziona sempre in modalità di blocco.

Non possiamo ottenere direttamente l'oggetto canale file, l'oggetto del canale file è ottenuto da -

  • getChannel() - metodo su qualsiasi FileInputStream, FileOutputStream o RandomAccessFile.

  • open() - metodo di File channel che di default apre il canale.

Il tipo di oggetto del canale File dipende dal tipo di classe chiamata dalla creazione dell'oggetto, cioè se l'oggetto viene creato chiamando il metodo getchannel di FileInputStream, il canale File viene aperto per la lettura e lancerà NonWritableChannelException nel caso in cui si tenti di scrivere su di esso.

Esempio

Il seguente esempio mostra come leggere e scrivere dati da Java NIO FileChannel.

L'esempio seguente legge da un file di testo da C:/Test/temp.txt e stampa il contenuto sulla console.

temp.txt

Hello World!

FileChannelDemo.java

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashSet;
import java.util.Set;

public class FileChannelDemo {
   public static void main(String args[]) throws IOException {
      //append the content to existing file 
      writeFileChannel(ByteBuffer.wrap("Welcome to TutorialsPoint".getBytes()));
      //read the file
      readFileChannel();
   }
   public static void readFileChannel() throws IOException {
      RandomAccessFile randomAccessFile = new RandomAccessFile("C:/Test/temp.txt",
      "rw");
      FileChannel fileChannel = randomAccessFile.getChannel();
      ByteBuffer byteBuffer = ByteBuffer.allocate(512);
      Charset charset = Charset.forName("US-ASCII");
      while (fileChannel.read(byteBuffer) > 0) {
         byteBuffer.rewind();
         System.out.print(charset.decode(byteBuffer));
         byteBuffer.flip();
      }
      fileChannel.close();
      randomAccessFile.close();
   }
   public static void writeFileChannel(ByteBuffer byteBuffer)throws IOException {
      Set<StandardOpenOption> options = new HashSet<>();
      options.add(StandardOpenOption.CREATE);
      options.add(StandardOpenOption.APPEND);
      Path path = Paths.get("C:/Test/temp.txt");
      FileChannel fileChannel = FileChannel.open(path, options);
      fileChannel.write(byteBuffer);
      fileChannel.close();
   }
}

Produzione

Hello World! Welcome to TutorialsPoint

Java NIO Datagram è usato come canale che può inviare e ricevere pacchetti UDP su un protocollo senza connessione.Per impostazione predefinita il canale del datagramma è bloccante mentre può essere utilizzato in modalità non bloccante.Per renderlo non bloccante possiamo usare il configureBlocking ( false) Il canale DataGram può essere aperto chiamando il suo metodo statico denominato come open() che può anche prendere l'indirizzo IP come parametro in modo che possa essere utilizzato per il multi casting.

Il canale del datagramma come FileChannel non è connesso di default per renderlo connesso dobbiamo chiamare esplicitamente il suo metodo connect () Tuttavia il canale del datagramma non ha bisogno di essere connesso per poter usare i metodi di invio e ricezione mentre deve essere connesso per poter utilizzare i metodi di lettura e scrittura, poiché questi metodi non accettano né restituiscono indirizzi socket.

Possiamo controllare lo stato della connessione del canale del datagramma chiamando il suo isConnected() Una volta connesso, un canale del datagramma rimane connesso fino a quando non viene disconnesso o chiuso. I canali del datagramma sono thread-safe e supportano simultaneamente il multi-threading e la concorrenza.

Metodi importanti per il canale del datagramma

  • bind(SocketAddress local) - Questo metodo viene utilizzato per associare il socket del canale del datagramma all'indirizzo locale fornito come parametro a questo metodo.

  • connect(SocketAddress remote) - Questo metodo viene utilizzato per collegare il socket all'indirizzo remoto.

  • disconnect() - Questo metodo viene utilizzato per scollegare il socket all'indirizzo remoto.

  • getRemoteAddress() - Questo metodo restituisce l'indirizzo della posizione remota a cui è connesso il socket del canale.

  • isConnected() - Come già accennato questo metodo restituisce lo stato di connessione del canale del datagramma, cioè se è connesso o meno.

  • open() and open(ProtocolFamily family) - Il metodo aperto viene utilizzato per aprire un canale datagramma per un singolo indirizzo, mentre il metodo aperto parametrizzato apre il canale per più indirizzi rappresentati come famiglia di protocolli.

  • read(ByteBuffer dst) - Questo metodo viene utilizzato per leggere i dati dal buffer specificato attraverso il canale del datagramma.

  • receive(ByteBuffer dst) - Questo metodo viene utilizzato per ricevere il datagramma tramite questo canale.

  • send(ByteBuffer src, SocketAddress target) - Questo metodo viene utilizzato per inviare il datagramma tramite questo canale.

Esempio

L'esempio seguente mostra come inviare dati da Java NIO DataGramChannel.

Server: DatagramChannelServer.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;

public class DatagramChannelServer {
   public static void main(String[] args) throws IOException {
      DatagramChannel server = DatagramChannel.open();
      InetSocketAddress iAdd = new InetSocketAddress("localhost", 8989);
      server.bind(iAdd);
      System.out.println("Server Started: " + iAdd);
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      //receive buffer from client.
      SocketAddress remoteAdd = server.receive(buffer);
      //change mode of buffer
      buffer.flip();
      int limits = buffer.limit();
      byte bytes[] = new byte[limits];
      buffer.get(bytes, 0, limits);
      String msg = new String(bytes);
      System.out.println("Client at " + remoteAdd + "  sent: " + msg);
      server.send(buffer,remoteAdd);
      server.close();
   }
}

Produzione

Server Started: localhost/127.0.0.1:8989

Cliente: DatagramChannelClient.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;

public class DatagramChannelClient {
   public static void main(String[] args) throws IOException {
      DatagramChannel client = null;
      client = DatagramChannel.open();

      client.bind(null);

      String msg = "Hello World!";
      ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
      InetSocketAddress serverAddress = new InetSocketAddress("localhost",
        8989);

      client.send(buffer, serverAddress);
      buffer.clear();
      client.receive(buffer);
      buffer.flip();
    
      client.close();
   }
}

Produzione

L'esecuzione del client stamperà il seguente output sul server.

Server Started: localhost/127.0.0.1:8989
Client at /127.0.0.1:64857  sent: Hello World!

Il canale socket Java NIO è un canale di tipo selezionabile, il che significa che può essere multiplexato utilizzando il selettore, utilizzato per il flusso di dati orientato al flusso di connessione dei socket.Il canale socket può essere creato invocando il suo open() metodo, a condizione che non sia già presente un socket preesistente.Il canale socket viene creato invocando il metodo aperto ma non ancora connesso.Per connettere il canale socket connect() Un punto da menzionare qui è che se il canale non è connesso e si tenta di tentare qualsiasi operazione di I / O, NotYetConnectedException viene lanciata da questo canale.Quindi bisogna assicurarsi che il canale sia connesso prima di eseguire qualsiasi IO Una volta connesso, il canale rimane connesso fino a quando non viene chiuso. Lo stato del canale socket può essere determinato invocando il suo isConnected metodo.

La connessione del canale socket può essere terminata invocando il suo finishConnect() È possibile determinare se un'operazione di connessione è in corso o meno richiamando il metodo isConnectionPending. Per impostazione predefinita, il canale socket supporta la connessione non bloccante. Supporta inoltre l'arresto asincrono, che è simile all'operazione di chiusura asincrona specificata nella classe Channel.

I canali socket possono essere utilizzati da più thread simultanei. Supportano la lettura e la scrittura simultanee, sebbene al massimo un thread possa leggere e al massimo un thread possa scrivere in un dato momento. I metodi connect e finishConnect vengono sincronizzati reciprocamente e un tentativo di avviare un'operazione di lettura o scrittura mentre è in corso una chiamata di uno di questi metodi si bloccherà fino al completamento di tale chiamata.

Metodi importanti del canale Socket

  • bind(SocketAddress local) - Questo metodo viene utilizzato per associare il canale socket all'indirizzo locale fornito come parametro a questo metodo.

  • connect(SocketAddress remote) - Questo metodo viene utilizzato per collegare il socket all'indirizzo remoto.

  • finishConnect() - Questo metodo viene utilizzato per completare il processo di connessione di un canale socket.

  • getRemoteAddress() - Questo metodo restituisce l'indirizzo della posizione remota a cui è connesso il socket del canale.

  • isConnected() - Come già accennato questo metodo restituisce lo stato di connessione del canale socket cioè se è connesso o meno.

  • open() and open((SocketAddress remote) - Il metodo Open viene utilizzato per aprire un canale socket per nessun indirizzo specificato mentre il metodo open parametrizzato apre il canale per l'indirizzo remoto specificato e si connette anche ad esso.Questo metodo di convenienza funziona come se invocasse il metodo open (), invocando il metodo di connessione al risultato canale socket, passandolo remoto e quindi restituendo quel canale.

  • read(ByteBuffer dst) - Questo metodo viene utilizzato per leggere i dati dal buffer specificato attraverso il canale socket.

  • isConnectionPending() - Questo metodo indica se è in corso un'operazione di connessione su questo canale.

Esempio

L'esempio seguente mostra come inviare dati da Java NIO SocketChannel.

C: /Test/temp.txt

Hello World!

Cliente: SocketChannelClient.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;

public class SocketChannelClient {
   public static void main(String[] args) throws IOException {
      ServerSocketChannel serverSocket = null;
      SocketChannel client = null;
      serverSocket = ServerSocketChannel.open();
      serverSocket.socket().bind(new InetSocketAddress(9000));
      client = serverSocket.accept();
      System.out.println("Connection Set:  " + client.getRemoteAddress());
      Path path = Paths.get("C:/Test/temp1.txt");
      FileChannel fileChannel = FileChannel.open(path, 
         EnumSet.of(StandardOpenOption.CREATE, 
            StandardOpenOption.TRUNCATE_EXISTING,
            StandardOpenOption.WRITE)
         );      
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while(client.read(buffer) > 0) {
         buffer.flip();
         fileChannel.write(buffer);
         buffer.clear();
      }
      fileChannel.close();
      System.out.println("File Received");
      client.close();
   }
}

Produzione

L'esecuzione del client non stamperà nulla fino all'avvio del server.

Server: SocketChannelServer.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;

public class SocketChannelServer {
   public static void main(String[] args) throws IOException {
      SocketChannel server = SocketChannel.open();
      SocketAddress socketAddr = new InetSocketAddress("localhost", 9000);
      server.connect(socketAddr);

      Path path = Paths.get("C:/Test/temp.txt");
      FileChannel fileChannel = FileChannel.open(path);
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while(fileChannel.read(buffer) > 0) {
         buffer.flip();
         server.write(buffer);
         buffer.clear();
      }
      fileChannel.close();
      System.out.println("File Sent");
      server.close();
   }
}

Produzione

L'esecuzione del server stamperà quanto segue.

Connection Set:  /127.0.0.1:49558
File Received

Il canale socket del server Java NIO è di nuovo un canale di tipo selezionabile utilizzato per il flusso di dati orientato al flusso che collega i socket.Il canale Socket del server può essere creato invocando il suo open() , a condizione che non sia già presente un socket preesistente.Il canale Socket del server viene creato invocando il metodo aperto ma non ancora associato. bind() metodo deve essere chiamato.

Un punto da menzionare qui è che se il canale non è associato e si tenta di tentare qualsiasi operazione di I / O, NotYetBoundException viene lanciata da questo canale, quindi è necessario assicurarsi che il canale sia limitato prima di eseguire qualsiasi operazione di I / O.

Le connessioni in entrata per il canale socket del server vengono ascoltate chiamando il metodo ServerSocketChannel.accept (). Quando il metodo accept () restituisce, restituisce un SocketChannel con una connessione in entrata. Pertanto, il metodo accept () si blocca fino all'arrivo di una connessione in entrata. Se il canale è in modalità non bloccante, il metodo accept restituirà immediatamente null se non ci sono connessioni in sospeso. Altrimenti si bloccherà indefinitamente fino a quando non sarà disponibile una nuova connessione o si verificherà un errore di I / O.

Il socket del nuovo canale è inizialmente non associato; deve essere associato a un indirizzo specifico tramite uno dei metodi di bind del suo socket prima che le connessioni possano essere accettate. Inoltre, il nuovo canale viene creato invocando il metodo openServerSocketChannel dell'oggetto SelectorProvider predefinito a livello di sistema.

Come il canale socket, il canale socket del server potrebbe leggere i dati utilizzando read()Per prima cosa viene allocato il buffer. I dati letti da un ServerSocketChannel vengono memorizzati nel buffer. In secondo luogo chiamiamo il metodo ServerSocketChannel.read () e leggiamo i dati da un ServerSocketChannel in un buffer. Il valore intero del metodo read () restituisce quanti byte sono stati scritti nel buffer

Allo stesso modo i dati potrebbero essere scritti sul canale socket del server utilizzando write() metodo utilizzando buffer come parametro.Commonly utilizza il metodo write in un ciclo while in quanto è necessario ripetere il metodo write () finché il buffer non ha ulteriori byte disponibili per la scrittura.

Metodi importanti del canale Socket

  • bind(SocketAddress local) - Questo metodo viene utilizzato per associare il canale socket all'indirizzo locale fornito come parametro a questo metodo.

  • accept() - Questo metodo viene utilizzato per accettare una connessione effettuata al socket di questo canale.

  • connect(SocketAddress remote) - Questo metodo viene utilizzato per collegare il socket all'indirizzo remoto.

  • finishConnect() - Questo metodo viene utilizzato per completare il processo di connessione di un canale socket.

  • getRemoteAddress() - Questo metodo restituisce l'indirizzo della posizione remota a cui è connesso il socket del canale.

  • isConnected() - Come già accennato questo metodo restituisce lo stato di connessione del canale socket cioè se è connesso o meno.

  • open() - Il metodo Open viene utilizzato per aprire un canale socket per nessun indirizzo specificato.Questo metodo comodo funziona come se invocasse il metodo open (), invocando il metodo connect sul canale socket del server risultante, passandolo remoto e quindi restituendo quel canale.

  • read(ByteBuffer dst) - Questo metodo viene utilizzato per leggere i dati dal buffer specificato attraverso il canale socket.

  • setOption(SocketOption<T> name, T value) - Questo metodo imposta il valore di un'opzione socket.

  • socket() - Questo metodo recupera un socket del server associato a questo canale.

  • validOps() - Questo metodo restituisce un set di operazioni che identifica le operazioni supportate da questo canale. I canali server-socket supportano solo l'accettazione di nuove connessioni, quindi questo metodo restituisce SelectionKey.OP_ACCEPT.

Esempio

L'esempio seguente mostra come inviare dati da Java NIO ServerSocketChannel.

C: /Test/temp.txt

Hello World!

Cliente: SocketChannelClient.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;

public class SocketChannelClient {
   public static void main(String[] args) throws IOException {
      ServerSocketChannel serverSocket = null;
      SocketChannel client = null;
      serverSocket = ServerSocketChannel.open();
      serverSocket.socket().bind(new InetSocketAddress(9000));
      client = serverSocket.accept();
      System.out.println("Connection Set:  " + client.getRemoteAddress());
      Path path = Paths.get("C:/Test/temp1.txt");
      FileChannel fileChannel = FileChannel.open(path, 
         EnumSet.of(StandardOpenOption.CREATE, 
            StandardOpenOption.TRUNCATE_EXISTING,
            StandardOpenOption.WRITE)
         );      
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while(client.read(buffer) > 0) {
         buffer.flip();
         fileChannel.write(buffer);
         buffer.clear();
      }
      fileChannel.close();
      System.out.println("File Received");
      client.close();
   }
}

Produzione

L'esecuzione del client non stamperà nulla fino all'avvio del server.

Server: SocketChannelServer.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;

public class SocketChannelServer {
   public static void main(String[] args) throws IOException {
      SocketChannel server = SocketChannel.open();
      SocketAddress socketAddr = new InetSocketAddress("localhost", 9000);
      server.connect(socketAddr);
      Path path = Paths.get("C:/Test/temp.txt");
      FileChannel fileChannel = FileChannel.open(path);
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while(fileChannel.read(buffer) > 0) {
         buffer.flip();
         server.write(buffer);
         buffer.clear();
      }
      fileChannel.close();
      System.out.println("File Sent");
      server.close();
   }
}

Produzione

L'esecuzione del server stamperà quanto segue.

Connection Set:  /127.0.0.1:49558
File Received

Come sappiamo, Java NIO è un'API più ottimizzata per le operazioni di I / O dati rispetto all'API IO convenzionale di Java.Un ulteriore supporto aggiuntivo fornito da Java NIO è la lettura / scrittura di dati da / su più buffer sul canale. e il supporto in scrittura è definito come Scatter and Gather in cui i dati vengono sparsi su più buffer da un singolo canale in caso di dati letti mentre i dati vengono raccolti da più buffer a un singolo canale in caso di dati di scrittura.

Per ottenere questa lettura e scrittura multipla dal canale ci sono ScatteringByteChannel e GatheringByteChannel API che Java NIO fornisce per leggere e scrivere i dati come illustrato nell'esempio seguente.

ScatteringByteChannel

Read from multiple channels - In questo abbiamo fatto di leggere i dati da un singolo canale in più buffer, per questo più buffer vengono allocati e aggiunti a un array di tipo buffer, quindi questo array viene passato come parametro al metodo read () ScatteringByteChannel che quindi scrive i dati da il canale nella sequenza in cui si trovano i buffer nell'array. Una volta che un buffer è pieno, il canale si sposta per riempire il buffer successivo.

Il seguente esempio mostra come viene eseguita la dispersione dei dati in Java NIO

C: /Test/temp.txt

Hello World!
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ScatteringByteChannel;

public class ScatterExample {	
   private static String FILENAME = "C:/Test/temp.txt";
   public static void main(String[] args) {
      ByteBuffer bLen1 = ByteBuffer.allocate(1024);
      ByteBuffer bLen2 = ByteBuffer.allocate(1024);
      FileInputStream in;
      try {
         in = new FileInputStream(FILENAME);
         ScatteringByteChannel scatter = in.getChannel();
         scatter.read(new ByteBuffer[] {bLen1, bLen2});
         bLen1.position(0);
         bLen2.position(0);
         int len1 = bLen1.asIntBuffer().get();
         int len2 = bLen2.asIntBuffer().get();
         System.out.println("Scattering : Len1 = " + len1);
         System.out.println("Scattering : Len2 = " + len2);
      } 
      catch (FileNotFoundException exObj) {
         exObj.printStackTrace();
      }
      catch (IOException ioObj) {
         ioObj.printStackTrace();
      }
   }
}

Produzione

Scattering : Len1 = 1214606444
Scattering : Len2 = 0

Infine si può concludere che l'approccio scatter / gather in Java NIO è stato introdotto come ottimizzato e multitasking se usato correttamente, consentendo di delegare al sistema operativo il duro lavoro di separare i dati letti in più bucket o di assemblarli pezzi disparati di dati in un insieme. Senza dubbio questo fa risparmiare tempo e utilizza il sistema operativo in modo più efficiente evitando le copie del buffer e riduce la quantità di codice necessario per la scrittura e il debug.

Come sappiamo, Java NIO è un'API più ottimizzata per le operazioni di I / O dati rispetto all'API IO convenzionale di Java.Un ulteriore supporto aggiuntivo fornito da Java NIO è la lettura / scrittura di dati da / su più buffer sul canale. e il supporto in scrittura è definito come Scatter and Gather in cui i dati vengono sparsi su più buffer da un singolo canale in caso di dati letti mentre i dati vengono raccolti da più buffer a un singolo canale in caso di dati di scrittura.

Per ottenere questa lettura e scrittura multipla dal canale ci sono ScatteringByteChannel e GatheringByteChannel API che Java NIO fornisce per leggere e scrivere i dati come illustrato nell'esempio seguente.

GatheringByteChannel

write to multiple channels - In questo abbiamo fatto scrivere i dati da più buffer in un unico canale.Per questo ancora più buffer vengono allocati e aggiunti a un array di tipo buffer, quindi questo array viene passato come parametro al metodo GatheringByteChannel write () che quindi scrive i dati dai buffer multipli nella sequenza i buffer si verificano nell'array. Un punto da ricordare qui è che vengono scritti solo i dati tra la posizione e il limite dei buffer.

L'esempio seguente mostra come viene eseguita la raccolta dei dati in Java NIO

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.GatheringByteChannel;

public class GatherExample {
   private static String FILENAME = "C:/Test/temp.txt";
   public static void main(String[] args) {
      String stream1 = "Gather data stream first";
      String stream2 = "Gather data stream second";
      ByteBuffer bLen1 = ByteBuffer.allocate(1024);
      ByteBuffer bLen2 = ByteBuffer.allocate(1024);
      // Next two buffer hold the data we want to write
      ByteBuffer bstream1 = ByteBuffer.wrap(stream1.getBytes());
      ByteBuffer bstream2 = ByteBuffer.wrap(stream2.getBytes());
      int len1 = stream1.length();
      int len2 = stream2.length();
      // Writing length(data) to the Buffer
      bLen1.asIntBuffer().put(len1);
      bLen2.asIntBuffer().put(len2);
      System.out.println("Gathering : Len1 = " + len1);
      System.out.println("Gathering : Len2 = " + len2);
      // Write data to the file
      try { 
         FileOutputStream out = new FileOutputStream(FILENAME);
         GatheringByteChannel gather = out.getChannel();						
         gather.write(new ByteBuffer[] {bLen1, bLen2, bstream1, bstream2});
         out.close();
         gather.close();
      }
      catch (FileNotFoundException exObj) {
         exObj.printStackTrace();
      }
      catch(IOException ioObj) {
         ioObj.printStackTrace();
      }
   }
}

Produzione

Gathering : Len1 = 24
Gathering : Len2 = 25

Infine si può concludere che l'approccio scatter / gather in Java NIO è stato introdotto come ottimizzato e multitasking se usato correttamente, consentendo di delegare al sistema operativo il duro lavoro di separare i dati letti in più bucket o di assemblarli pezzi disparati di dati in un insieme. Senza dubbio questo fa risparmiare tempo e utilizza il sistema operativo in modo più efficiente evitando le copie del buffer e riduce la quantità di codice necessario per la scrittura e il debug.

I buffer in Java NIO possono essere trattati come un oggetto semplice che agisce come un contenitore di dimensioni fisse di blocchi di dati che possono essere utilizzati per scrivere dati sul canale o leggere dati dal canale in modo che i buffer agiscano come endpoint per i canali.

Fornisce una serie di metodi che rendono più conveniente gestire il blocco di memoria per leggere e scrivere dati da e verso i canali.

I buffer rendono il pacchetto NIO più efficiente e veloce rispetto al classico IO poiché nel caso dei dati IO vengono trattati sotto forma di flussi che non supportano il flusso di dati asincrono e simultaneo. Inoltre IO non consente l'esecuzione dei dati in blocchi o gruppi di byte .

I parametri primari che definiscono il buffer Java NIO possono essere definiti come:

  • Capacity - Quantità massima di dati / byte che può essere memorizzata nel Buffer La capacità di un buffer non può essere modificata. Una volta che il buffer è pieno, dovrebbe essere cancellato prima di scrivere su di esso.

  • Limit - Il limite ha un significato in base alla modalità del buffer, ovvero in modalità di scrittura del buffer, il limite è uguale alla capacità, il che significa che i dati massimi che potrebbero essere scritti nel buffer mentre in modalità di lettura del buffer, il limite indica il limite di quanti dati possono essere letto dal Buffer.

  • Position - Punta alla posizione corrente del cursore nel buffer Inizialmente impostato a 0 al momento della creazione del buffer o in altre parole è l'indice del prossimo elemento da leggere o scrivere che viene aggiornato automaticamente da get () e put ( ) metodi.

  • Mark - Contrassegna un segnalibro della posizione in un buffer. Quando viene chiamato il metodo mark (), la posizione corrente viene registrata e quando viene chiamato reset () la posizione contrassegnata viene ripristinata.

Tipo di buffer

I buffer Java NIO possono essere classificati nelle seguenti varianti sulla base dei tipi di dati trattati dal buffer:

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

Metodi importanti di Buffer

Come già accennato, Buffer agisce come oggetto di memoria che fornisce una serie di metodi che rendono più conveniente trattare con il blocco di memoria. Di seguito sono riportati i metodi importanti di Buffer -

  • allocate(int capacity) - Questo metodo viene utilizzato per allocare un nuovo buffer con capacità come parametro. Il metodo Allocate genera IllegalArgumentException nel caso in cui la capacità passata sia un numero intero negativo.

  • read() and put() - il metodo di lettura del canale viene utilizzato per scrivere i dati dal canale al buffer mentre put è un metodo di buffer che viene utilizzato per scrivere i dati nel buffer.

  • flip() - Il metodo flip cambia la modalità del Buffer dalla modalità di scrittura a quella di lettura, inoltre riporta la posizione a 0 e imposta il limite a dove si trovava la posizione al momento della scrittura.

  • write() and get() - il metodo di scrittura del canale viene utilizzato per scrivere i dati dal buffer al canale mentre get è un metodo del buffer che viene utilizzato per leggere i dati dal buffer.

  • rewind() - il metodo rewind viene utilizzato quando è richiesta la rilettura in quanto riporta la posizione a zero e non altera il valore di limite.

  • clear() and compact() - chiari e compatti entrambi i metodi sono usati per rendere il buffer dalla modalità di lettura a quella di scrittura.clear() metodo rende la posizione a zero e il limite è uguale alla capacità, in questo metodo i dati nel buffer non vengono cancellati solo i marker vengono reinizializzati.

    D'altra parte compact() viene utilizzato quando sono rimasti alcuni dati non letti e ancora si usa la modalità di scrittura del buffer in questo caso il metodo compatto copia tutti i dati non letti all'inizio del buffer e imposta la posizione subito dopo l'ultimo elemento non letto. impostato sulla capacità.

  • mark() and reset() - Come suggerisce il nome, il metodo mark viene utilizzato per contrassegnare una posizione particolare in un buffer mentre il reset riporta la posizione alla posizione contrassegnata.

Esempio

L'esempio seguente mostra l'implementazione dei metodi sopra definiti.

import java.nio.ByteBuffer;
import java.nio.CharBuffer;

public class BufferDemo {
   public static void main (String [] args) {
      //allocate a character type buffer.
      CharBuffer buffer = CharBuffer.allocate(10);
      String text = "bufferDemo";
      System.out.println("Input text: " + text);
      for (int i = 0; i < text.length(); i++) {
         char c = text.charAt(i);
         //put character in buffer.
		 buffer.put(c);
      }
      int buffPos = buffer.position();
      System.out.println("Position after data is written into buffer: " + buffPos);
      buffer.flip();
      System.out.println("Reading buffer contents:");
      while (buffer.hasRemaining()) {
         System.out.println(buffer.get());                   
      }
      //set the position of buffer to 5.
      buffer.position(5);
      //sets this buffer's mark at its position
      buffer.mark();
      //try to change the position
      buffer.position(6);
      //calling reset method to restore to the position we marked.
      //reset() raise InvalidMarkException if either the new position is less
      //than the position marked or merk has not been setted.
      buffer.reset();
      System.out.println("Restored buffer position : " + buffer.position());
   }
}

Produzione

Input text: bufferDemo
Position after data is written into buffer: 10
Reading buffer contents:
b
u
f
f
e
r
D
e
m
o
Restored buffer position : 5

Come sappiamo, Java NIO supporta più transazioni da e verso canali e buffer. Quindi, per esaminare uno o più canali NIO e determinare quali canali sono pronti per la transazione dati, ad esempio leggere o scrivere Java NIO fornire Selector.

Con Selector possiamo creare un thread per sapere quale canale è pronto per la scrittura e la lettura dei dati e potrebbe trattare quel particolare canale.

Possiamo ottenere l'istanza del selettore chiamando il suo metodo statico open()Dopo aver aperto il selettore dobbiamo registrare un canale in modalità non bloccante con esso che restituisce un'istanza di SelectionKey.

SelectionKey è fondamentalmente una raccolta di operazioni che possono essere eseguite con il canale oppure possiamo dire che potremmo conoscere lo stato del canale con l'aiuto della chiave di selezione.

Le operazioni principali o lo stato del canale rappresentato dal tasto di selezione sono:

  • SelectionKey.OP_CONNECT - Canale pronto per la connessione al server.

  • SelectionKey.OP_ACCEPT - Canale pronto ad accettare le connessioni in entrata.

  • SelectionKey.OP_READ - Canale pronto per la lettura dei dati.

  • SelectionKey.OP_WRITE - Canale pronto per la scrittura dei dati.

La chiave di selezione ottenuta dopo la registrazione ha alcuni metodi importanti come indicato di seguito:

  • attach() - Questo metodo viene utilizzato per collegare un oggetto con la chiave. Lo scopo principale del collegamento di un oggetto a un canale è riconoscere lo stesso canale.

  • attachment() - Questo metodo viene utilizzato per mantenere l'oggetto collegato dal canale.

  • channel() - Questo metodo viene utilizzato per ottenere il canale per il quale viene creata la chiave specifica.

  • selector() - Questo metodo viene utilizzato per ottenere il selettore per il quale viene creata la chiave specifica.

  • isValid() - Questo metodo restituisce se la chiave è valida o meno.

  • isReadable() - Questo metodo afferma che il canale della chiave meteo è pronto per la lettura o meno.

  • isWritable() - Questo metodo afferma che il canale della chiave meteo è pronto per la scrittura o meno.

  • isAcceptable() - Questo metodo afferma che il canale della chiave meteo è pronto per accettare o meno la connessione in entrata.

  • isConnectable() - Questo metodo verifica se il canale di questa chiave ha terminato o non è riuscito a completare l'operazione di connessione socket.

  • isAcceptable() - Questo metodo verifica se il canale di questa chiave è pronto per accettare una nuova connessione socket.

  • interestOps() - Questo metodo recupera l'insieme di interessi di questa chiave.

  • readyOps() - Questo metodo recupera il set pronto che è il set di operazioni per cui il canale è pronto.

Possiamo selezionare un canale dal selettore chiamando il suo metodo statico select()Il metodo di selezione del selettore è sovraccarico come -

  • select() - Questo metodo blocca il thread corrente finché almeno un canale è pronto per gli eventi per i quali è registrato.

  • select(long timeout) - Questo metodo fa la stessa cosa di select () tranne che blocca il thread per un massimo di timeout millisecondi (il parametro).

  • selectNow() - Questo metodo non si blocca affatto, ma ritorna immediatamente con i canali pronti.

Anche per lasciare un thread bloccato che richiama il metodo di selezione,wakeup() metodo può essere chiamato dall'istanza del selettore dopo di che il thread in attesa all'interno di select () tornerà immediatamente.

Infine possiamo chiudere il selettore chiamando close() che invalida anche tutte le istanze di SelectionKey registrate con questo selettore insieme alla chiusura del selettore.

Esempio

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class SelectorDemo {
   public static void main(String[] args) throws IOException {
      String demo_text = "This is a demo String";	
      Selector selector = Selector.open();
      ServerSocketChannel serverSocket = ServerSocketChannel.open();
      serverSocket.bind(new InetSocketAddress("localhost", 5454));
      serverSocket.configureBlocking(false);
      serverSocket.register(selector, SelectionKey.OP_ACCEPT);
      ByteBuffer buffer = ByteBuffer.allocate(256);
      while (true) {
         selector.select();
         Set<SelectionKey> selectedKeys = selector.selectedKeys();
         Iterator<SelectionKey> iter = selectedKeys.iterator();
         while (iter.hasNext()) {
            SelectionKey key = iter.next();
            int interestOps = key.interestOps();
            System.out.println(interestOps);
            if (key.isAcceptable()) {
               SocketChannel client = serverSocket.accept();
               client.configureBlocking(false);
               client.register(selector, SelectionKey.OP_READ);
            }
            if (key.isReadable()) {
               SocketChannel client = (SocketChannel) key.channel();
               client.read(buffer);
               if (new String(buffer.array()).trim().equals(demo_text)) {
                  client.close();
                  System.out.println("Not accepting client messages anymore");
               }
               buffer.flip();
               client.write(buffer);
               buffer.clear();
            }
            iter.remove();
         }
      }
   }
}

In Java, NIO pipe è un componente che viene utilizzato per scrivere e leggere dati tra due thread. Il pipe consiste principalmente di due canali responsabili della propagazione dei dati.

Tra i due canali costituenti uno è chiamato canale Sink che è principalmente per la scrittura di dati e l'altro è il canale Source il cui scopo principale è leggere i dati dal canale Sink.

La sincronizzazione dei dati viene mantenuta in ordine durante la scrittura e la lettura dei dati in quanto è necessario garantire che i dati vengano letti nello stesso ordine in cui vengono scritti sul tubo.

Si deve tenere presente che si tratta di un flusso unidirezionale di dati in Pipe, ovvero i dati vengono scritti solo nel canale Sink e possono essere letti solo dal canale Sorgente.

In Java, NIO pipe è definita come una classe astratta con principalmente tre metodi di cui due astratti.

Metodi della classe Pipe

  • open() - Questo metodo viene utilizzato per ottenere un'istanza di Pipe oppure possiamo dire che pipe viene creato chiamando questo metodo.

  • sink() - Questo metodo restituisce il canale sink del pipe che viene utilizzato per scrivere dati chiamando il relativo metodo di scrittura.

  • source() - Questo metodo restituisce il canale di origine della pipe che viene utilizzato per leggere i dati chiamando il relativo metodo di lettura.

Esempio

Il seguente esempio mostra l'implementazione di Java NIO pipe.

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe;

public class PipeDemo {
   public static void main(String[] args) throws IOException {
      //An instance of Pipe is created
      Pipe pipe = Pipe.open();
      // gets the pipe's sink channel
      Pipe.SinkChannel skChannel = pipe.sink();
      String testData = "Test Data to Check java NIO Channels Pipe.";
      ByteBuffer buffer = ByteBuffer.allocate(512);
      buffer.clear();
      buffer.put(testData.getBytes());
      buffer.flip();
      //write data into sink channel.
      while(buffer.hasRemaining()) {
         skChannel.write(buffer);
      }
      //gets  pipe's source channel
      Pipe.SourceChannel sourceChannel = pipe.source();
      buffer = ByteBuffer.allocate(512);
      //write data into console     
      while(sourceChannel.read(buffer) > 0){
         //limit is set to current position and position is set to zero
         buffer.flip();
         while(buffer.hasRemaining()){
            char ch = (char) buffer.get();
            System.out.print(ch);
         }
         //position is set to zero and limit is set to capacity to clear the buffer.
         buffer.clear();
      }
   }
}

Produzione

Test Data to Check java NIO Channels Pipe.

Supponendo di avere un file di testo c:/test.txt, che ha il seguente contenuto. Questo file verrà utilizzato come input per il nostro programma di esempio.

Come suggerisce il nome, Path è la posizione particolare di un'entità come un file o una directory in un file system in modo che si possa cercarla e accedervi in ​​quella particolare posizione.

Tecnicamente in termini di Java, Path è un'interfaccia introdotta nel pacchetto di file Java NIO durante la versione 7 di Java ed è la rappresentazione della posizione in un particolare file system. Poiché l'interfaccia del percorso è nel pacchetto Java NIO, ottiene il suo nome qualificato come java .nio.file.Path.

In generale, il percorso di un'entità potrebbe essere di due tipi: uno è il percorso assoluto e l'altro è il percorso relativo.Il nome di entrambi i percorsi suggerisce che il percorso assoluto è l'indirizzo della posizione dalla radice all'entità in cui si trova mentre il percorso relativo è l'indirizzo della posizione che è relativo a qualche altro path.Path utilizza delimitatori nella sua definizione come "\" per Windows e "/" per sistemi operativi unix.

Per ottenere l'istanza di Path possiamo utilizzare il metodo statico della classe java.nio.file.Paths get()Questo metodo converte una stringa di percorso o una sequenza di stringhe che, quando unite formano una stringa di percorso, in un'istanza di Path. Questo metodo genera anche un'eccezione InvalidPathException di runtime se gli argomenti passati contengono caratteri non validi.

Come accennato in precedenza, il percorso assoluto viene recuperato passando l'elemento radice e l'elenco completo delle directory richiesto per individuare il file.Mentre il percorso relativo potrebbe essere recuperato combinando il percorso di base con il percorso relativo.Il recupero di entrambi i percorsi sarebbe illustrato nel seguente esempio

Esempio

package com.java.nio;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.file.FileSystem;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathDemo {
   public static void main(String[] args) throws IOException {
      Path relative = Paths.get("file2.txt");
      System.out.println("Relative path: " + relative);
      Path absolute = relative.toAbsolutePath();
      System.out.println("Absolute path: " + absolute);
   }
}

Finora sappiamo che cos'è l'interfaccia del percorso, perché ne abbiamo bisogno e come potremmo accedervi. Ora sapremmo quali sono i metodi importanti che l'interfaccia del percorso ci fornisce.

Metodi importanti di Path Interface

  • getFileName() - Restituisce il file system che ha creato questo oggetto.

  • getName() - Restituisce un elemento nome di questo percorso come oggetto Path.

  • getNameCount() - Restituisce il numero di elementi del nome nel percorso.

  • subpath() - Restituisce un percorso relativo che è una sottosequenza degli elementi del nome di questo percorso.

  • getParent() - Restituisce il percorso padre o null se questo percorso non ha un percorso padre.

  • getRoot() - Restituisce il componente radice di questo percorso come oggetto Path o null se questo percorso non ha un componente radice.

  • toAbsolutePath() - Restituisce un oggetto Path che rappresenta il percorso assoluto di questo percorso.

  • toRealPath() - Restituisce il percorso reale di un file esistente.

  • toFile() - Restituisce un oggetto File che rappresenta questo percorso.

  • normalize() - Restituisce un percorso che è questo percorso con elementi di nome ridondanti eliminati.

  • compareTo(Path other) - Confronta due percorsi astratti lessicograficamente.Questo metodo restituisce zero se l'argomento è uguale a questo percorso, un valore minore di zero se questo percorso è lessicograficamente minore dell'argomento, o un valore maggiore di zero se questo percorso è lessicograficamente maggiore dell'argomento .

  • endsWith(Path other) - Verifica se questo percorso termina con il percorso specificato.Se il percorso specificato ha N elementi e nessun componente radice, e questo percorso ha N o più elementi, allora questo percorso termina con il percorso dato se gli ultimi N elementi di ogni percorso, a partire dall'elemento più lontano dalla radice, sono uguali.

  • endsWith(String other) - Verifica se questo percorso termina con un percorso, costruito convertendo la stringa del percorso data, esattamente nel modo specificato dal metodoendsWith (Path).

Esempio

L'esempio seguente illustra i diversi metodi dell'interfaccia Path menzionati sopra:

package com.java.nio;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.file.FileSystem;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathDemo {
   public static void main(String[] args) throws IOException {
      Path path = Paths.get("D:/workspace/ContentW/Saurav_CV.docx");
      FileSystem fs =  path.getFileSystem();
      System.out.println(fs.toString());
      System.out.println(path.isAbsolute());
      System.out.println(path.getFileName());
      System.out.println(path.toAbsolutePath().toString());
      System.out.println(path.getRoot());
      System.out.println(path.getParent());
      System.out.println(path.getNameCount());
      System.out.println(path.getName(0));
      System.out.println(path.subpath(0, 2));
      System.out.println(path.toString());
      System.out.println(path.getNameCount());
      Path realPath = path.toRealPath(LinkOption.NOFOLLOW_LINKS);
      System.out.println(realPath.toString());
      String originalPath = "d:\\data\\projects\\a-project\\..\\another-project";
      Path path1 = Paths.get(originalPath);
      Path path2 = path1.normalize();
      System.out.println("path2 = " + path2);
   }
}

Il pacchetto Java NIO fornisce un'altra API di utilità denominata File che viene fondamentalmente utilizzata per manipolare file e directory utilizzando i suoi metodi statici che funzionano principalmente sull'oggetto Path.

Come menzionato nel tutorial Path, l'interfaccia Path è stata introdotta nel pacchetto Java NIO durante la versione Java 7 nel pacchetto file, quindi questo tutorial è per lo stesso pacchetto file.

Questa classe è costituita esclusivamente da metodi statici che operano su file, directory o altri tipi di file. Nella maggior parte dei casi, i metodi qui definiti delegheranno al provider del file system associato l'esecuzione delle operazioni sui file.

Ci sono molti metodi definiti nella classe Files che possono essere letti anche da documenti Java. In questo tutorial abbiamo cercato di coprire alcuni dei metodi importanti tra tutti i metodi della classe Java NIO Files.

Metodi importanti della classe Files.

Di seguito sono riportati i metodi importanti definiti nella classe Java NIO Files.

  • createFile(Path filePath, FileAttribute attrs) - La classe Files fornisce questo metodo per creare file utilizzando Path specificato.

Esempio

package com.java.nio;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class CreateFile {
   public static void main(String[] args) {
      //initialize Path object
      Path path = Paths.get("D:file.txt");
      //create file
      try {
         Path createdFilePath = Files.createFile(path);
         System.out.println("Created a file at : "+createdFilePath);
      } 
      catch (IOException e) {
         e.printStackTrace();
      }
   }
}

Produzione

Created a file at : D:\data\file.txt
  • copy(InputStream in, Path target, CopyOption… options) - Questo metodo viene utilizzato per copiare tutti i byte dal flusso di input specificato al file di destinazione specificato e restituisce il numero di byte letti o scritti come valore lungo LinkOption per questo parametro con i seguenti valori:

    • COPY_ATTRIBUTES - copia gli attributi nel nuovo file, ad esempio l'attributo dell'ora dell'ultima modifica.

    • REPLACE_EXISTING - sostituire un file esistente se esiste.

    • NOFOLLOW_LINKS - Se un file è un collegamento simbolico, viene copiato il collegamento stesso, non la destinazione del collegamento.

Esempio

package com.java.nio;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
public class WriteFile {
   public static void main(String[] args) {
      Path sourceFile = Paths.get("D:file.txt");
      Path targetFile = Paths.get("D:fileCopy.txt");
      try {
         Files.copy(sourceFile, targetFile,
         StandardCopyOption.REPLACE_EXISTING);
      }
      catch (IOException ex) {
         System.err.format("I/O Error when copying file");
      }
      Path wiki_path = Paths.get("D:fileCopy.txt");
      Charset charset = Charset.forName("ISO-8859-1");
      try {
         List<String> lines = Files.readAllLines(wiki_path, charset);
         for (String line : lines) {
            System.out.println(line);
         }
      } 
      catch (IOException e) {
         System.out.println(e);
      }
   }	
}

Produzione

To be or not to be?
  • createDirectories(Path dir, FileAttribute<?>...attrs) - Questo metodo viene utilizzato per creare directory utilizzando un determinato percorso creando tutte le directory padre inesistenti.

  • delete(Path path) - Questo metodo viene utilizzato per eliminare il file dal percorso specificato e genera NoSuchFileException se il file non esiste nel percorso specificato o se il file è una directory e potrebbe non essere vuoto e non può essere eliminato.

  • exists(Path path) - Questo metodo viene utilizzato per verificare se il file esiste nel percorso specificato e se il file esiste restituirà true oppure restituirà false.

  • readAllBytes(Path path) - Questo metodo viene utilizzato per leggere tutti i byte dal file in un determinato percorso e restituisce la matrice di byte contenente i byte letti dal file.

Esempio

package com.java.nio;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public class ReadFile {
   public static void main(String[] args) {
      Path wiki_path = Paths.get("D:file.txt");
      Charset charset = Charset.forName("ISO-8859-1");
      try {
         List<String> lines = Files.readAllLines(wiki_path, charset);
         for (String line : lines) {
            System.out.println(line);
         }
      } 
      catch (IOException e) {
         System.out.println(e);
      }
   }	
}

Produzione

Welcome to file.
  • size(Path path) - Questo metodo viene utilizzato per ottenere la dimensione del file nel percorso specificato in byte.

  • write(Path path, byte[] bytes, OpenOption… options) - Questo metodo viene utilizzato per scrivere byte su un file nel percorso specificato.

Esempio

package com.java.nio;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
public class WriteFile {
   public static void main(String[] args) {
      Path path = Paths.get("D:file.txt");
      String question = "To be or not to be?";
      Charset charset = Charset.forName("ISO-8859-1");
      try {
         Files.write(path, question.getBytes());
         List<String> lines = Files.readAllLines(path, charset);
         for (String line : lines) {
            System.out.println(line);
         }
      } 
      catch (IOException e) {
         System.out.println(e);
      }
   }
}

Produzione

To be or not to be?

Come sappiamo che Java NIO supporta la concorrenza e il multi-threading che ci consente di gestire diversi canali contemporaneamente allo stesso tempo, quindi l'API che è responsabile di questo nel pacchetto Java NIO è AsynchronousFileChannel che è definito nel pacchetto canali NIO. per AsynchronousFileChannel è java.nio.channels.AsynchronousFileChannel.

AsynchronousFileChannel è simile a quello del FileChannel di NIO, tranne per il fatto che questo canale consente alle operazioni sui file di essere eseguite in modo asincrono a differenza dell'operazione di I / O sincrona in cui un thread entra in un'azione e attende fino al completamento della richiesta.Quindi i canali asincroni sono sicuri per l'uso da più thread simultanei.

In modalità asincrona la richiesta viene passata dal thread al kernel del sistema operativo per eseguirla mentre il thread continua a elaborare un altro lavoro.Una volta terminato il lavoro del kernel, segnala il thread, quindi il thread ha riconosciuto il segnale e interrompe il lavoro corrente ed elabora il Lavoro di I / O secondo necessità.

Per ottenere la concorrenza, questo canale fornisce due approcci che includono uno come restituzione di un file java.util.concurrent.Future object e altro è Passare all'operazione un oggetto di tipo java.nio.channels.CompletionHandler.

Comprenderemo entrambi gli approcci con l'aiuto di esempi uno per uno.

  • Future Object - In questo viene restituita un'istanza di Future Interface dal canale, mentre in Future Interface c'è get() metodo che restituisce lo stato dell'operazione che viene gestito in modo asincrono in base al quale potrebbe essere decisa l'ulteriore esecuzione di altre attività.Possiamo anche verificare se l'attività è completata o meno chiamando il suo isDone metodo.

Esempio

L'esempio seguente mostra come utilizzare l'oggetto Future e eseguire le attività in modo asincrono.

package com.java.nio;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class FutureObject {
   public static void main(String[] args) throws Exception {
      readFile();
   }
   private static void readFile() throws IOException, InterruptedException, ExecutionException {
      String filePath = "D:fileCopy.txt";
      printFileContents(filePath);
      Path path = Paths.get(filePath);		
      AsynchronousFileChannel channel =AsynchronousFileChannel.open(path, StandardOpenOption.READ);
      ByteBuffer buffer = ByteBuffer.allocate(400);
      Future<Integer> result = channel.read(buffer, 0); // position = 0
      while (! result.isDone()) {
         System.out.println("Task of reading file is in progress asynchronously.");
      }
      System.out.println("Reading done: " + result.isDone());
      System.out.println("Bytes read from file: " + result.get()); 
      buffer.flip();
      System.out.print("Buffer contents: ");
      while (buffer.hasRemaining()) {
         System.out.print((char) buffer.get());                
      }
      System.out.println(" ");
      buffer.clear();
      channel.close();
   }
   private static void printFileContents(String path) throws IOException {
      FileReader fr = new FileReader(path);
      BufferedReader br = new BufferedReader(fr);
      String textRead = br.readLine();
      System.out.println("File contents: ");
      while (textRead != null) {
         System.out.println("     " + textRead);
         textRead = br.readLine();
      }
   fr.close();
   br.close();
   }
}

Produzione

File contents: 
   To be or not to be?
   Task of reading file is in progress asynchronously.
   Task of reading file is in progress asynchronously.
   Reading done: true
   Bytes read from file: 19
   Buffer contents: To be or not to be?
  • Completion Handler -

    Questo approccio è piuttosto semplice poiché in questo usiamo l'interfaccia CompletionHandler e sovrascrive i suoi due metodi uno è completed() metodo che viene richiamato quando l'operazione di I / O viene completata correttamente e l'altro è failed() metodo che viene richiamato se le operazioni di I / O falliscono. In questo viene creato un gestore per consumare il risultato di un'operazione di I / O asincrona poiché una volta completata un'attività, solo il gestore ha funzioni che vengono eseguite.

Esempio

L'esempio seguente mostra come utilizzare CompletionHandler per eseguire attività in modo asincrono.

package com.java.nio;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class CompletionHandlerDemo {
   public static void main (String [] args) throws Exception {
      writeFile();
   }
   private static void writeFile() throws IOException {
      String input = "Content to be written to the file.";
      System.out.println("Input string: " + input);
      byte [] byteArray = input.getBytes();
      ByteBuffer buffer = ByteBuffer.wrap(byteArray);
      Path path = Paths.get("D:fileCopy.txt");
      AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
      CompletionHandler handler = new CompletionHandler() {
         @Override
         public void completed(Object result, Object attachment) {
            System.out.println(attachment + " completed and " + result + " bytes are written.");
         }
         @Override
         public void failed(Throwable exc, Object attachment) {
            System.out.println(attachment + " failed with exception:");
            exc.printStackTrace();
         }
      };
      channel.write(buffer, 0, "Async Task", handler);
      channel.close();
      printFileContents(path.toString());
   }
   private static void printFileContents(String path) throws IOException {
      FileReader fr = new FileReader(path);
      BufferedReader br = new BufferedReader(fr);
      String textRead = br.readLine();
      System.out.println("File contents: ");
      while (textRead != null) {
         System.out.println("     " + textRead);
         textRead = br.readLine();
      }
      fr.close();
      br.close();
   }
}

Produzione

Input string: Content to be written to the file.
Async Task completed and 34 bytes are written.
File contents: 
Content to be written to the file.

In Java per ogni carattere esiste un'unità di codice unicode ben definita che è gestita internamente da JVM, quindi il pacchetto Java NIO definisce una classe astratta denominata Charset che viene utilizzata principalmente per la codifica e la decodifica di charset e UNICODE.

Set di caratteri standard

I set di caratteri supportati in java sono riportati di seguito.

  • US-ASCII - Caratteri ASCII a sette bit.

  • ISO-8859-1 - Alfabeto latino ISO.

  • UTF-8 - Questo è il formato di trasformazione UCS a 8 bit.

  • UTF-16BE - Questo è il formato di trasformazione UCS a 16 bit con ordine di byte big endian.

  • UTF-16LE - Questa è una trasformazione UCS a 16 bit con ordine di byte little endian.

  • UTF-16 - Formato di trasformazione UCS a 16 bit.

Metodi importanti della classe Charset

  • forName() - Questo metodo crea un oggetto charset per il nome charset specificato. Il nome può essere canonico o un alias.

  • displayName() - Questo metodo restituisce il nome canonico di un determinato set di caratteri.

  • canEncode() - Questo metodo controlla se il set di caratteri specificato supporta la codifica o meno.

  • decode() - Questo metodo decodifica la stringa di un determinato set di caratteri nel buffer di caratteri Unicode.

  • encode() - Questo metodo codifica il buffer di caratteri del set di caratteri unicode nel buffer di byte di un determinato set di caratteri.

Esempio

L'esempio seguente illustra i metodi importanti della classe Charset.

package com.java.nio;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
public class CharsetExample {
   public static void main(String[] args) {
      Charset charset = Charset.forName("US-ASCII");
      System.out.println(charset.displayName());
      System.out.println(charset.canEncode());
      String str = "Demo text for conversion.";
      //convert byte buffer in given charset to char buffer in unicode
      ByteBuffer byteBuffer = ByteBuffer.wrap(str.getBytes());
      CharBuffer charBuffer = charset.decode(byteBuffer);
      //convert char buffer in unicode to byte buffer in given charset
      ByteBuffer newByteBuffer = charset.encode(charBuffer);
      while(newbb.hasRemaining()){
         char ch = (char) newByteBuffer.get();
         System.out.print(ch);
      }
      newByteBuffer.clear();
   }
}

Produzione

US-ASCII
Demo text for conversion.

Come sappiamo, Java NIO supporta la concorrenza e il multi threading che gli consente di gestire più thread che operano su più file contemporaneamente, ma in alcuni casi richiediamo che il nostro file non venga condiviso da nessuno dei thread e non sia accessibile.

Per tale requisito, NIO fornisce nuovamente un'API nota come FileLock che viene utilizzata per fornire il blocco su tutto il file o su una parte del file, in modo che quel file o la sua parte non venga condiviso o accessibile.

per fornire o applicare tale blocco dobbiamo utilizzare FileChannel o AsynchronousFileChannel, che fornisce due metodi lock() e tryLock()a tale scopo La serratura fornita può essere di due tipi:

  • Exclusive Lock - Un blocco esclusivo impedisce ad altri programmi di acquisire un blocco sovrapposto di entrambi i tipi.

  • Shared Lock - Un blocco condiviso impedisce ad altri programmi in esecuzione simultanea di acquisire un blocco esclusivo sovrapposto, ma consente loro di acquisire blocchi condivisi sovrapposti.

Metodi utilizzati per ottenere il blocco sul file -

  • lock() - Questo metodo di FileChannel o AsynchronousFileChannel acquisisce un blocco esclusivo su un file associato al canale specificato. Il tipo di ritorno di questo metodo è FileLock che viene ulteriormente utilizzato per monitorare il blocco ottenuto.

  • lock(long position, long size, boolean shared) - Questo metodo è di nuovo il metodo sovraccarico del metodo di blocco e viene utilizzato per bloccare una parte particolare di un file.

  • tryLock() - Questo metodo restituisce un FileLock o un null se non è stato possibile acquisire il blocco e tenta di acquisire un blocco esplicitamente esclusivo sul file di questo canale.

  • tryLock(long position, long size, boolean shared) - Questo metodo tenta di acquisire un blocco su una determinata regione del file di questo canale che può essere di tipo esclusivo o condiviso.

Metodi della classe FileLock

  • acquiredBy() - Questo metodo restituisce il canale su cui è stato acquisito il blocco del file.

  • position() - Questo metodo restituisce la posizione all'interno del file del primo byte della regione bloccata. Una regione bloccata non deve essere contenuta o addirittura sovrapposta al file sottostante effettivo, quindi il valore restituito da questo metodo potrebbe superare la dimensione corrente del file.

  • size() - Questo metodo restituisce la dimensione della regione bloccata in byte. Una regione bloccata non deve essere contenuta o addirittura sovrapposta al file sottostante effettivo, quindi il valore restituito da questo metodo potrebbe superare la dimensione corrente del file.

  • isShared() - Questo metodo viene utilizzato per determinare se il blocco è condiviso o meno.

  • overlaps(long position,long size) - Questo metodo indica se questo blocco si sovrappone o meno all'intervallo di blocco specificato.

  • isValid() - Questo metodo indica se il blocco ottenuto è valido o meno. Un oggetto blocco rimane valido fino a quando non viene rilasciato o il canale file associato viene chiuso, a seconda di quale evento si verifica per primo.

  • release()- Rilascia il blocco ottenuto. Se l'oggetto blocco è valido, l'invocazione di questo metodo rilascia il blocco e rende l'oggetto non valido. Se questo oggetto di blocco non è valido, l'invocazione di questo metodo non ha alcun effetto.

  • close()- Questo metodo richiama il metodo release (). È stato aggiunto alla classe in modo che potesse essere utilizzato insieme al costrutto del blocco di gestione automatica delle risorse.

Esempio per dimostrare il blocco dei file.

L'esempio seguente crea un blocco su un file e scrivi il contenuto su di esso

package com.java.nio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class FileLockExample {
   public static void main(String[] args) throws IOException {
      String input = "Demo text to be written in locked mode.";  
      System.out.println("Input string to the test file is: " + input);  
      ByteBuffer buf = ByteBuffer.wrap(input.getBytes());  
      String fp = "D:file.txt";  
      Path pt = Paths.get(fp);  
      FileChannel channel = FileChannel.open(pt, StandardOpenOption.WRITE,StandardOpenOption.APPEND);  
      channel.position(channel.size() - 1); // position of a cursor at the end of file       
      FileLock lock = channel.lock();   
      System.out.println("The Lock is shared: " + lock.isShared());  
      channel.write(buf);  
      channel.close(); // Releases the Lock  
      System.out.println("Content Writing is complete. Therefore close the channel and release the lock.");  
      PrintFileCreated.print(fp);  
   }  
}
package com.java.nio;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class PrintFileCreated {
   public static void print(String path) throws IOException {  
      FileReader filereader = new FileReader(path);  
      BufferedReader bufferedreader = new BufferedReader(filereader);  
      String tr = bufferedreader.readLine();    
      System.out.println("The Content of testout.txt file is: ");  
      while (tr != null) {      
         System.out.println("    " + tr);  
         tr = bufferedreader.readLine();  
      }  
   filereader.close();  
   bufferedreader.close();  
   }  
}

Produzione

Input string to the test file is: Demo text to be written in locked mode.
The Lock is shared: false
Content Writing is complete. Therefore close the channel and release the lock.
The Content of testout.txt file is: 
To be or not to be?Demo text to be written in locked mode.