Java NIO - Guia rápido
O pacote Java.nio foi introduzido em java 1.4. Em contraste com o java I / O no java NIO, o fluxo de dados orientado para buffer e canal para operações de I / O é introduzido, o que resulta em uma execução mais rápida e melhor desempenho.
Além disso, a API NIO oferece seletores que introduzem a funcionalidade de escuta de vários canais para eventos de IO de forma assíncrona ou não bloqueadora.
As abstrações centrais das APIs NIO são as seguintes -
Buffers, que são contêineres para dados, conjuntos de caracteres e seus decodificadores e codificadores associados, que traduzem entre bytes e caracteres Unicode.
Canais de vários tipos, que representam conexões a entidades capazes de realizar operações de I / O
Seletores e chaves de seleção, que junto com canais selecionáveis definem um recurso de E / S multiplexado e não bloqueado.
Esta seção o orienta sobre como baixar e configurar o Java em sua máquina. Siga as etapas a seguir para configurar o ambiente.
Java SE está disponível gratuitamente no link Download Java . Portanto, você baixa uma versão com base em seu sistema operacional.
Siga as instruções para baixar java e executar o .exepara instalar o Java em sua máquina. Depois de instalar o Java em sua máquina, você precisará definir variáveis de ambiente para apontar para os diretórios de instalação corretos -
Configurando o caminho para Windows 2000 / XP
Supondo que você tenha instalado o Java no diretório c: \ Arquivos de programas \ java \ jdk -
Clique com o botão direito em 'Meu Computador' e selecione 'Propriedades'.
Clique no botão 'Variáveis de ambiente' na guia 'Avançado'.
Agora altere a variável 'Path' para que também contenha o caminho para o executável Java. Exemplo, se o caminho está definido atualmente para 'C: \ WINDOWS \ SYSTEM32', altere seu caminho para 'C: \ WINDOWS \ SYSTEM32; c: \ Arquivos de programas \ java \ jdk \ bin'.
Configurando o caminho para o Windows 95/98 / ME
Supondo que você tenha instalado o Java no diretório c: \ Arquivos de programas \ java \ jdk -
Edite o arquivo 'C: \ autoexec.bat' e adicione a seguinte linha no final:
'SET PATH =% PATH%; C: \ Arquivos de programas \ java \ jdk \ bin'
Configurando o caminho para Linux, UNIX, Solaris, FreeBSD
A variável de ambiente PATH deve ser definida para apontar para onde os binários java foram instalados. Consulte a documentação do shell se tiver problemas para fazer isso.
Por exemplo, se você usar bash como seu shell, você adicionaria a seguinte linha ao final de seu '.bashrc: export PATH = / path / to / java: $ PATH'
Editores Java populares
Para escrever seus programas java, você precisará de um editor de texto. Existem IDE ainda mais sofisticados disponíveis no mercado. Mas, por agora, você pode considerar um dos seguintes -
Notepad - Na máquina Windows você pode usar qualquer editor de texto simples como o Notepad (recomendado para este tutorial), TextPad.
Netbeans - é um IDE Java de código aberto e gratuito que pode ser baixado de http://www.netbeans.org/index.html.
Eclipse - também é um IDE Java desenvolvido pela comunidade de código aberto eclipse e pode ser baixado de https://www.eclipse.org/.
Como sabemos, o java NIO é introduzido para o avanço da API Java IO convencional. As principais melhorias que tornam o NIO mais eficiente do que o IO são o modelo de fluxo de dados do canal usado no NIO e o uso do sistema operacional para tarefas convencionais de IO.
A diferença entre Java NIO e Java IO pode ser explicada da seguinte forma -
Conforme mencionado na postagem anterior no buffer NIO e fluxo de dados orientado a canal para operações de I / O que fornecem execução mais rápida e melhor desempenho em comparação com IO. Também NIO usa o sistema operacional para tarefas de I / O convencionais, o que novamente o torna mais eficiente.
Outro aspecto da diferença entre NIO e IO é que IO usa fluxo de dados de linha de fluxo, ou seja, um byte a mais por vez e depende da conversão de objetos de dados em bytes e vice-e-versa, enquanto NIO lida com os blocos de dados que são blocos de bytes.
Em java IO stream, os objetos são unidirecionais, enquanto em NIO os canais são bidirecionais, o que significa que um canal pode ser usado para leitura e gravação de dados.
O fluxo de dados simplificado em IO não permite mover para frente e para trás nos dados. Se for necessário mover para frente e para trás nos dados lidos de um fluxo, é necessário armazená-los em um buffer primeiro. Enquanto no caso de NIO, usamos orientado a buffer que permite acessar os dados de um lado para outro sem a necessidade de armazenamento em cache.
A API NIO também oferece suporte a multithreading para que os dados possam ser lidos e gravados de forma assíncrona, de forma que, durante a execução de operações de IO, o thread atual não seja bloqueado. Isso novamente o torna mais eficiente do que a API IO Java convencional.
O conceito de multi-threading é introduzido com a introdução de Selectors em java NIO que permite escutar múltiplos canais para eventos IO de forma assíncrona ou não bloqueadora.
Multi threading em NIO torna-o sem bloqueio, o que significa que thread é solicitado para ler ou gravar apenas quando os dados estão disponíveis, caso contrário, o thread pode ser usado em outra tarefa por um tempo médio. Mas isso não é possível no caso de java IO convencional, pois não há multi threading é suportado nele, o que o torna como Bloqueio.
NIO permite gerenciar vários canais usando apenas um único thread, mas o custo é que analisar os dados pode ser um pouco mais complicado do que ao ler dados de um fluxo de bloqueio no caso de java IO. Portanto, no caso de menos conexões com largura de banda muito alta são necessárias com o envio de muitos dados de uma vez, que neste caso a API Java IO pode ser a melhor opção.
Descrição
Como o nome sugere, o canal é usado como meio de fluxo de dados de uma extremidade a outra. Aqui no canal java NIO agem da mesma forma entre o buffer e uma entidade na outra extremidade, em outras palavras, o canal é usado para ler dados no buffer e também gravar dados do buffer.
Ao contrário de streams que são usados em canais Java IO convencionais são bidirecionais, ou seja, podem ler e gravar. O canal Java NIO suporta fluxo assíncrono de dados em modo de bloqueio e não de bloqueio.
Implementações de Canal
O canal Java NIO é implementado principalmente nas seguintes classes -
FileChannel- Para ler os dados do arquivo, usamos canal de arquivo. O objeto do canal de arquivo pode ser criado apenas chamando o método getChannel () no objeto de arquivo, pois não podemos criar o objeto de arquivo diretamente.
DatagramChannel - O canal de datagrama pode ler e gravar os dados na rede via UDP (User Datagram Protocol). O objeto de DataGramchannel pode ser criado usando métodos de fábrica.
SocketChannel- O canal SocketChannel pode ler e escrever os dados na rede via TCP (Transmission Control Protocol). Ele também usa os métodos de fábrica para criar o novo objeto.
ServerSocketChannel- O ServerSocketChannel lê e grava os dados em conexões TCP, da mesma forma que um servidor web. Para cada conexão de entrada, um SocketChannel é criado.
Exemplo
O exemplo a seguir lê de um arquivo de texto de C:/Test/temp.txt e imprime o conteúdo no 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();
}
}
Resultado
Hello World!
Descrição
Como já mencionado, a implementação de FileChannel do canal Java NIO é introduzida para acessar propriedades de metadados do arquivo, incluindo criação, modificação, tamanho, etc. Junto com isso, os canais de arquivo são multiencadeados, o que novamente torna o Java NIO mais eficiente do que o Java IO.
Em geral, podemos dizer que FileChannel é um canal conectado a um arquivo pelo qual você pode ler dados de um arquivo e gravar dados em um arquivo. Outra característica importante de FileChannel é que ele não pode ser configurado no modo sem bloqueio e sempre é executado no modo de bloqueio.
Não podemos obter o objeto do canal de arquivo diretamente, o objeto do canal de arquivo é obtido por -
getChannel() - método em qualquer FileInputStream, FileOutputStream ou RandomAccessFile.
open() - método do canal de arquivo que, por padrão, abre o canal.
O tipo de objeto do canal File depende do tipo de classe chamada a partir da criação do objeto, ou seja, se o objeto é criado chamando o método getchannel de FileInputStream, o canal File é aberto para leitura e lançará NonWritableChannelException no caso de tentativa de gravação.
Exemplo
O exemplo a seguir mostra como ler e gravar dados do Java NIO FileChannel.
O exemplo a seguir lê de um arquivo de texto de C:/Test/temp.txt e imprime o conteúdo no 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();
}
}
Resultado
Hello World! Welcome to TutorialsPoint
O Java NIO Datagram é usado como canal que pode enviar e receber pacotes UDP através de uma conexão sem protocolo. Por padrão, o canal de datagrama está bloqueando enquanto pode ser usado no modo sem bloqueio. Para torná-lo não bloqueador, podemos usar o configureBlocking ( false) method.DataGram canal pode ser aberto chamando seu um dos métodos estáticos nomeados como open() que também pode ter o endereço IP como parâmetro para que possa ser usado para multicast.
O canal de datagrama semelhante ao FileChannel não é conectado por padrão para torná-lo conectado, nós temos que chamar explicitamente seu método connect (). No entanto, o canal de datagrama não precisa ser conectado para que os métodos de envio e recebimento sejam usados enquanto ele deve ser conectado para usar os métodos de leitura e escrita, uma vez que esses métodos não aceitam ou retornam endereços de soquete.
Podemos verificar o status da conexão do canal de datagrama chamando seu isConnected() Método Uma vez conectado, um canal de datagrama permanece conectado até que seja desconectado ou fechado. Canais de datagrama são thread-safe e suportam multi-threading e simultaneidade simultaneamente.
Métodos importantes de canal de datagrama
bind(SocketAddress local) - Este método é usado para vincular o soquete do canal de datagrama ao endereço local que é fornecido como parâmetro para este método.
connect(SocketAddress remote) - Este método é usado para conectar o soquete ao endereço remoto.
disconnect() - Este método é usado para desconectar o soquete do endereço remoto.
getRemoteAddress() - Este método retorna o endereço do local remoto ao qual o soquete do canal está conectado.
isConnected() - Como já mencionado, este método retorna o status da conexão do canal de datagrama, ou seja, se está conectado ou não.
open() and open(ProtocolFamily family) - O método aberto é usado para abrir um canal de datagrama para endereço único, enquanto o método aberto parametrizado abre o canal para vários endereços representados como família de protocolo.
read(ByteBuffer dst) - Este método é usado para ler dados de um determinado buffer por meio do canal de datagrama.
receive(ByteBuffer dst) - Este método é usado para receber datagramas através deste canal.
send(ByteBuffer src, SocketAddress target) - Este método é usado para enviar datagramas através deste canal.
Exemplo
O exemplo a seguir mostra como enviar dados do Java NIO DataGramChannel.
Servidor: 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();
}
}
Resultado
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();
}
}
Resultado
Executar o cliente imprimirá a seguinte saída no servidor.
Server Started: localhost/127.0.0.1:8989
Client at /127.0.0.1:64857 sent: Hello World!
O canal de soquete Java NIO é um canal de tipo selecionável, o que significa que pode ser multiplexado usando o seletor, usado para o fluxo de dados orientado por fluxo conectando soquetes. O canal de soquete pode ser criado chamando seu canal estático open() , desde que nenhum soquete pré-existente já esteja presente. O canal de soquete é criado invocando o método aberto, mas ainda não conectado. Para conectar o canal de soquete connect() método deve ser chamado. Um ponto a ser mencionado aqui é se o canal não estiver conectado e qualquer operação de E / S for tentada, então NotYetConnectedException é acionado por este canal. Portanto, deve-se garantir que o canal está conectado antes de realizar qualquer IO operação. Uma vez que o canal é conectado, ele permanece conectado até que seja fechado. O estado do canal de soquete pode ser determinado invocando seu isConnected método.
A conexão do canal de soquete pode ser finalizada invocando seu finishConnect() Método. Se uma operação de conexão está em andamento ou não, pode ser determinado invocando o método isConnectionPending. Meu canal de soquete padrão suporta conexão sem bloqueio. Também suporta desligamento assíncrono, que é semelhante à operação de fechamento assíncrono especificada na classe Canal.
Canais de soquete são seguros para uso por vários threads simultâneos. Eles suportam leitura e gravação simultâneas, embora no máximo um thread possa estar lendo e no máximo um thread possa estar gravando a qualquer momento. Os métodos connect e finishConnect são mutuamente sincronizados entre si, e uma tentativa de iniciar uma operação de leitura ou gravação enquanto uma chamada de um desses métodos está em andamento será bloqueada até que a chamada seja concluída.
Métodos importantes de canal Socket
bind(SocketAddress local) - Este método é usado para vincular o canal de soquete ao endereço local que é fornecido como parâmetro para este método.
connect(SocketAddress remote) - Este método é usado para conectar o soquete ao endereço remoto.
finishConnect() - Este método é usado para terminar o processo de conexão de um canal de socket.
getRemoteAddress() - Este método retorna o endereço do local remoto ao qual o soquete do canal está conectado.
isConnected() - Como já foi mencionado, este método retorna o status da conexão do canal socket, ou seja, se ele está conectado ou não.
open() and open((SocketAddress remote) - O método aberto é usado para abrir um canal de soquete para nenhum endereço especificado enquanto o método aberto parametrizado abre o canal para o endereço remoto especificado e também se conecta a ele. Este método de conveniência funciona como se invocasse o método open (), invocando o método de conexão após o resultado canal de soquete, passando-o remoto e, em seguida, retornando esse canal.
read(ByteBuffer dst) - Este método é usado para ler dados do buffer fornecido através do canal de socket.
isConnectionPending() - Este método informa se uma operação de conexão está ou não em andamento neste canal.
Exemplo
O exemplo a seguir mostra como enviar dados do 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();
}
}
Resultado
Executar o cliente não imprimirá nada até que o servidor seja iniciado.
Servidor: 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();
}
}
Resultado
Executar o servidor imprimirá o seguinte.
Connection Set: /127.0.0.1:49558
File Received
O canal de soquete do servidor Java NIO é novamente um canal de tipo selecionável usado para fluxo de dados orientado conectando soquetes. O canal de soquete do servidor pode ser criado chamando seu canal estático open() , desde que nenhum socket pré-existente já esteja presente. O canal de socket de servidor é criado invocando o método aberto, mas ainda não vinculado. bind() método deve ser chamado.
Um ponto a ser mencionado aqui é se o canal não está vinculado e qualquer operação de E / S é tentada, então NotYetBoundException é lançada por este canal. Portanto, é necessário garantir que o canal seja limitado antes de executar qualquer operação de E / S.
As conexões de entrada para o canal de soquete do servidor são ouvidas chamando o método ServerSocketChannel.accept (). Quando o método accept () retorna, ele retorna um SocketChannel com uma conexão de entrada. Portanto, o método accept () bloqueia até que uma conexão de entrada chegue. Se o canal estiver no modo sem bloqueio, o método accept retornará imediatamente nulo se não houver conexões pendentes. Caso contrário, ele bloqueará indefinidamente até que uma nova conexão esteja disponível ou ocorra um erro de E / S.
O soquete do novo canal é inicialmente desvinculado; ele deve ser vinculado a um endereço específico por meio de um dos métodos de vinculação de seu soquete antes que as conexões possam ser aceitas. Além disso, o novo canal é criado invocando o método openServerSocketChannel do objeto SelectorProvider padrão de todo o sistema.
Como canal de soquete, servidor de canal de soquete pode ler dados usando read()método. Primeiro, o buffer é alocado. Os dados lidos de um ServerSocketChannel são armazenados no buffer. Em segundo lugar, chamamos o método ServerSocketChannel.read () e ele lê os dados de um ServerSocketChannel em um buffer. O valor inteiro do método read () retorna quantos bytes foram gravados no buffer
Da mesma forma, os dados podem ser gravados no canal de soquete do servidor usando write() método usando buffer como parâmetro. Normalmente usa o método write em um loop while, pois é necessário repetir o método write () até que o buffer não tenha mais bytes disponíveis para gravar.
Métodos importantes de canal Socket
bind(SocketAddress local) - Este método é usado para vincular o canal de soquete ao endereço local que é fornecido como parâmetro para este método.
accept() - Este método é usado para aceitar uma conexão feita ao soquete deste canal.
connect(SocketAddress remote) - Este método é usado para conectar o soquete ao endereço remoto.
finishConnect() - Este método é usado para terminar o processo de conexão de um canal de socket.
getRemoteAddress() - Este método retorna o endereço do local remoto ao qual o soquete do canal está conectado.
isConnected() - Como já foi mencionado, este método retorna o status da conexão do canal socket, ou seja, se ele está conectado ou não.
open() - O método aberto é usado para abrir um canal de soquete para nenhum endereço especificado. Este método conveniente funciona como se invocasse o método open (), invocando o método de conexão no canal de soquete do servidor resultante, passando-o remoto e retornando esse canal.
read(ByteBuffer dst) - Este método é usado para ler dados do buffer fornecido através do canal de socket.
setOption(SocketOption<T> name, T value) - Este método define o valor de uma opção de soquete.
socket() - Este método recupera um soquete de servidor associado a este canal.
validOps() - Este método retorna um conjunto de operações que identifica as operações suportadas deste canal. Os canais de soquete do servidor suportam apenas a aceitação de novas conexões, portanto, este método retorna SelectionKey.OP_ACCEPT.
Exemplo
O exemplo a seguir mostra como enviar dados do 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();
}
}
Resultado
Executar o cliente não imprimirá nada até que o servidor seja iniciado.
Servidor: 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();
}
}
Resultado
Executar o servidor imprimirá o seguinte.
Connection Set: /127.0.0.1:49558
File Received
Como sabemos que Java NIO é uma API mais otimizada para operações de IO de dados em comparação com a API IO convencional de Java. Um suporte adicional que o Java NIO fornece é ler / gravar dados de / para vários buffers para o canal. e o suporte de gravação é denominado Scatter and Gather, no qual os dados são espalhados para vários buffers de um único canal no caso de dados lidos, enquanto os dados são reunidos de vários buffers para um único canal no caso de dados de gravação.
A fim de alcançar essa leitura e gravação múltipla do canal, existe a API ScatteringByteChannel e GatheringByteChannel que o Java NIO fornece para leitura e gravação de dados conforme ilustrado no exemplo abaixo.
ScatteringByteChannel
Read from multiple channels - Nesse caso, fizemos a leitura de dados de um único canal em vários buffers. Para isso, vários buffers são alocados e adicionados a uma matriz de tipo de buffer. Em seguida, essa matriz é passada como parâmetro para o método ScatteringByteChannel read (), que então grava dados de o canal na sequência em que os buffers ocorrem na matriz. Uma vez que um buffer está cheio, o canal se move para preencher o próximo buffer.
O exemplo a seguir mostra como a dispersão de dados é realizada em 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();
}
}
}
Resultado
Scattering : Len1 = 1214606444
Scattering : Len2 = 0
Por fim, pode-se concluir que a abordagem de dispersão / coleta em Java NIO é introduzida como um otimizado e multitarefa quando usado corretamente. Permite delegar ao sistema operacional o trabalho árduo de separar os dados lidos em vários depósitos ou de montagem blocos díspares de dados em um todo. Sem dúvida, isso economiza tempo e usa o sistema operacional com mais eficiência, evitando cópias de buffer, e reduz a quantidade de código necessária para escrever e depurar.
Como sabemos que Java NIO é uma API mais otimizada para operações de IO de dados em comparação com a API IO convencional de Java. Um suporte adicional que o Java NIO fornece é ler / gravar dados de / para vários buffers para o canal. e o suporte de gravação é denominado Scatter and Gather, no qual os dados são espalhados para vários buffers de um único canal no caso de dados lidos, enquanto os dados são reunidos de vários buffers para um único canal no caso de dados de gravação.
A fim de alcançar essa leitura e gravação múltipla do canal, existe a API ScatteringByteChannel e GatheringByteChannel que o Java NIO fornece para leitura e gravação de dados conforme ilustrado no exemplo abaixo.
GatheringByteChannel
write to multiple channels - Nesse caso, escrevemos dados de vários buffers em um único canal. Novamente, vários buffers são alocados e adicionados a uma matriz de tipo de buffer. Em seguida, essa matriz é passada como parâmetro para o método GatheringByteChannel write () que então grava os dados a partir dos vários buffers na sequência, os buffers ocorrem na matriz. Um ponto a ser lembrado aqui é que apenas os dados entre a posição e o limite dos buffers são gravados.
O exemplo a seguir mostra como a coleta de dados é realizada em 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();
}
}
}
Resultado
Gathering : Len1 = 24
Gathering : Len2 = 25
Por fim, pode-se concluir que a abordagem de dispersão / coleta em Java NIO é introduzida como um otimizado e multitarefa quando usado corretamente. Permite delegar ao sistema operacional o trabalho árduo de separar os dados lidos em vários depósitos ou de montagem blocos díspares de dados em um todo. Sem dúvida, isso economiza tempo e usa o sistema operacional com mais eficiência, evitando cópias de buffer, e reduz a quantidade de código necessária para escrever e depurar.
Buffers em Java NIO podem ser tratados como um objeto simples que age como um contêiner de tamanho fixo de blocos de dados que podem ser usados para gravar dados no canal ou ler dados do canal para que os buffers atuem como terminais para os canais.
Ele fornece um conjunto de métodos que tornam mais conveniente lidar com o bloco de memória para ler e gravar dados de e para os canais.
Os buffers tornam o pacote NIO mais eficiente e rápido em comparação ao IO clássico, pois no caso de IO os dados são negociados na forma de fluxos que não suportam fluxo de dados assíncrono e simultâneo. Também IO não permite a execução de dados em blocos ou grupos de bytes .
Os parâmetros primários que definem o buffer Java NIO podem ser definidos como -
Capacity - Quantidade máxima de dados / byte que pode ser armazenado no Buffer. A capacidade de um buffer não pode ser alterada. Uma vez que o buffer esteja cheio, ele deve ser limpo antes de gravá-lo.
Limit - Limite tem significado de acordo com o modo de Buffer, ou seja, no modo de gravação do Buffer Limit é igual à capacidade, o que significa que o máximo de dados que podem ser gravados no buffer. Enquanto estiver no modo de leitura do buffer, Limite significa o limite de quantos dados podem ser ler do Buffer.
Position - Aponta para a localização atual do cursor no buffer. Inicialmente definido como 0 no momento da criação do buffer ou em outras palavras, é o índice do próximo elemento a ser lido ou escrito que é atualizado automaticamente por get () e put ( ) métodos.
Mark - Marque um marcador da posição em um buffer. Quando o método mark () é chamado, a posição atual é gravada e quando reset () é chamado, a posição marcada é restaurada.
Tipo de Buffer
Os buffers Java NIO podem ser classificados nas seguintes variantes com base nos tipos de dados com os quais o buffer lida -
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
Métodos importantes de buffer
Como já mencionado, o Buffer atua como um objeto de memória que fornece um conjunto de métodos que torna mais conveniente lidar com o bloco de memória. A seguir estão os métodos importantes de Buffer -
allocate(int capacity) - Este método é usado para alocar um novo buffer com capacidade como parâmetro. O método Allocate lança IllegalArgumentException no caso de a capacidade passada ser um número inteiro negativo.
read() and put() - o método read do canal é usado para gravar dados do canal para o buffer, enquanto put é um método do buffer que é usado para gravar dados no buffer.
flip() - O método flip muda o modo do Buffer do modo de gravação para o modo de leitura. Ele também define a posição de volta para 0 e define o limite de onde a posição estava no momento da gravação.
write() and get() - o método write do canal é usado para escrever dados do buffer para o canal, enquanto get é um método do buffer que é usado para ler dados do buffer.
rewind() - o método de retrocesso é usado quando a releitura é necessária, pois define a posição de volta para zero e não altera o valor do limite.
clear() and compact() - clear e compact ambos os métodos são usados para fazer buffer do modo de leitura para gravação.clear() O método torna a posição zero e o limite igual à capacidade, neste método os dados no buffer não são apagados, apenas os marcadores são reinicializados.
Por outro lado compact() método é usado quando restaram alguns dados não lidos e ainda usamos o modo de gravação do buffer, neste caso, o método compacto copia todos os dados não lidos para o início do buffer e define a posição logo após o último elemento não lido. A propriedade limit ainda é definido para capacidade.
mark() and reset() - Como o nome sugere, o método de marca é usado para marcar qualquer posição particular em um buffer enquanto reinicia a posição de volta para a posição marcada.
Exemplo
O exemplo a seguir mostra a implementação dos métodos definidos acima.
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());
}
}
Resultado
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
Como sabemos que o Java NIO suporta várias transações de e para canais e buffer. Portanto, a fim de examinar um ou mais canais NIO e determinar quais canais estão prontos para a transação de dados, ou seja, ler ou gravar Java NIO fornecer Selector.
Com o Selector podemos fazer um thread para saber qual canal está pronto para a escrita e leitura de dados e poderemos lidar com aquele canal específico.
Podemos obter a instância do seletor chamando seu método estático open().Após o seletor aberto, temos que registrar um canal de modo sem bloqueio com ele, que retorna uma instância de SelectionKey.
SelectionKey é basicamente uma coleção de operações que podem ser realizadas com o canal ou podemos dizer que podemos saber o estado do canal com a ajuda da tecla de seleção.
As principais operações ou estado do canal representado pela tecla de seleção são -
SelectionKey.OP_CONNECT - Canal que está pronto para se conectar ao servidor.
SelectionKey.OP_ACCEPT - Canal que está pronto para aceitar conexões de entrada.
SelectionKey.OP_READ - Canal que está pronto para leitura de dados.
SelectionKey.OP_WRITE - Canal que está pronto para escrever dados.
A chave de seleção obtida após o registro tem alguns métodos importantes, conforme mencionado abaixo -
attach() - Este método é usado para anexar um objeto com a chave. O objetivo principal de anexar um objeto a um canal é reconhecer o mesmo canal.
attachment() - Este método é usado para reter o objeto anexado do canal.
channel() - Este método é usado para obter o canal para o qual a chave específica foi criada.
selector() - Este método é usado para obter o seletor para o qual a chave específica foi criada.
isValid() - Este método retorna se a chave é válida ou não.
isReadable() - Este método afirma que o canal da chave do tempo está pronto para leitura ou não.
isWritable() - Este método afirma que o canal da chave do tempo está pronto para gravação ou não.
isAcceptable() - Este método afirma que o canal da chave do tempo está pronto para aceitar a conexão de entrada ou não.
isConnectable() - Este método testa se o canal desta chave concluiu ou falhou ao concluir sua operação de conexão de soquete.
isAcceptable() - Este método testa se o canal desta chave está pronto para aceitar uma nova conexão de soquete.
interestOps() - Este método recupera o conjunto de interesse desta chave.
readyOps() - Este método recupera o conjunto pronto, que é o conjunto de operações para as quais o canal está pronto.
Podemos selecionar um canal do seletor chamando seu método estático select().Select método do seletor está sobrecarregado como -
select() - Este método bloqueia o segmento atual até que pelo menos um canal esteja pronto para os eventos para os quais está registrado.
select(long timeout) - Este método faz o mesmo que select (), exceto que bloqueia o thread por um tempo máximo de milissegundos (o parâmetro).
selectNow() - Este método não bloqueia de forma alguma. Ele retorna imediatamente com quaisquer canais que estejam prontos.
Além disso, para deixar um tópico bloqueado que chama o método selecionado,wakeup() O método pode ser chamado a partir da instância do seletor, após a qual o thread esperando dentro de select () retornará imediatamente.
Por último, podemos fechar o seletor chamando close() que também invalida todas as instâncias de SelectionKey registradas com este Seletor, juntamente com o fechamento do seletor.
Exemplo
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();
}
}
}
}
Em Java, o canal NIO é um componente usado para gravar e ler dados entre dois threads. O canal consiste principalmente em dois canais que são responsáveis pela propagação dos dados.
Entre os dois canais constituintes, um é chamado de canal Sink, que se destina principalmente à gravação de dados, e o outro é o canal Source, cujo objetivo principal é ler dados do canal Sink.
A sincronização de dados é mantida em ordem durante a gravação e leitura dos dados, pois deve ser garantido que os dados sejam lidos na mesma ordem em que foram gravados no Pipe.
Deve-se observar que é um fluxo unidirecional de dados no Pipe, ou seja, os dados são gravados apenas no canal Sink e só podem ser lidos no canal Fonte.
Em Java, o canal NIO é definido como uma classe abstrata com principalmente três métodos, dos quais dois são abstratos.
Métodos da classe Pipe
open() - Este método é usado para obter uma instância de Pipe ou podemos dizer que o pipe é criado chamando esse método.
sink() - Este método retorna o canal coletor do Pipe que é usado para gravar dados chamando seu método de gravação.
source() - Este método retorna o canal de origem do Pipe que é usado para ler dados chamando seu método de leitura.
Exemplo
O exemplo a seguir mostra a implementação do canal Java NIO.
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();
}
}
}
Resultado
Test Data to Check java NIO Channels Pipe.
Supondo que temos um arquivo de texto c:/test.txt, que possui o seguinte conteúdo. Este arquivo será usado como uma entrada para nosso programa de exemplo.
Como o nome sugere, Path é o local específico de uma entidade, como um arquivo ou um diretório em um sistema de arquivos, para que se possa pesquisar e acessá-lo naquele local específico.
Tecnicamente em termos de Java, Path é uma interface que é introduzida no pacote de arquivos Java NIO durante a versão 7 do Java e é a representação da localização em um sistema de arquivos específico. Como a interface do caminho está no pacote Java NIO, obtém seu nome qualificado como java .nio.file.Path.
Em geral, o caminho de uma entidade pode ser de dois tipos, um é o caminho absoluto e o outro é o caminho relativo. Como o nome de ambos os caminhos sugere que o caminho absoluto é o endereço de localização da raiz para a entidade onde está localizada, enquanto o caminho relativo é o endereço de localização que é relativo a algum outro caminho. O caminho usa delimitadores em sua definição como "\" para Windows e "/" para sistemas operacionais unix.
Para obter a instância do Path, podemos usar o método estático da classe java.nio.file.Paths get().Este método converte uma string de caminho, ou uma sequência de strings que quando unidas formam uma string de caminho, em uma instância de Path. Esse método também lança InvalidPathException se os argumentos passados contiverem caracteres ilegais.
Conforme mencionado acima, o caminho absoluto é recuperado passando o elemento raiz e a lista de diretórios completa necessária para localizar o arquivo. Enquanto o caminho relativo pode ser recuperado combinando o caminho base com o caminho relativo. A recuperação de ambos os caminhos seria ilustrada no exemplo a seguir
Exemplo
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);
}
}
Até agora sabemos o que é a interface de caminho, por que precisamos disso e como poderíamos acessá-la. Agora saberíamos quais são os métodos importantes que a interface de caminho nos fornece.
Métodos importantes de Path Interface
getFileName() - Retorna o sistema de arquivos que criou este objeto.
getName() - Retorna um elemento de nome deste caminho como um objeto Path.
getNameCount() - Retorna o número de elementos de nome no caminho.
subpath() - Retorna um caminho relativo que é uma subsequência dos elementos de nome deste caminho.
getParent() - Retorna o caminho pai ou nulo se este caminho não tiver um pai.
getRoot() - Retorna o componente raiz deste caminho como um objeto Path, ou nulo se este caminho não tiver um componente raiz.
toAbsolutePath() - Retorna um objeto Path que representa o caminho absoluto desse caminho.
toRealPath() - Retorna o caminho real de um arquivo existente.
toFile() - Retorna um objeto File que representa este caminho.
normalize() - Retorna um caminho que é este caminho com elementos de nome redundantes eliminados.
compareTo(Path other) - Compara dois caminhos abstratos lexicograficamente. Este método retorna zero se o argumento for igual a este caminho, um valor menor que zero se este caminho for lexicograficamente menor que o argumento ou um valor maior que zero se este caminho for lexicograficamente maior que o argumento .
endsWith(Path other) - Testa se este caminho termina com o caminho fornecido. Se o caminho fornecido tiver N elementos, e nenhum componente raiz, e este caminho tiver N ou mais elementos, então este caminho termina com o caminho fornecido se os últimos N elementos de cada caminho, começando no elemento mais distante da raiz, são iguais.
endsWith(String other) - Testa se este caminho termina com um Path, construído pela conversão da string de caminho fornecida, exatamente da maneira especificada pelo método endsWith (Path).
Exemplo
O exemplo a seguir ilustra os diferentes métodos de interface de caminho mencionados acima -
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);
}
}
O pacote Java NIO fornece mais uma API de utilitário denominada Files que é basicamente usada para manipular arquivos e diretórios usando seus métodos estáticos que funcionam principalmente no objeto Path.
Conforme mencionado no tutorial Path, essa interface Path é introduzida no pacote Java NIO durante a versão Java 7 no pacote de arquivos. Portanto, este tutorial é para o mesmo pacote de arquivos.
Essa classe consiste exclusivamente em métodos estáticos que operam em arquivos, diretórios ou outros tipos de arquivos. Na maioria dos casos, os métodos definidos aqui delegam ao provedor de sistema de arquivos associado a execução das operações de arquivo.
Existem muitos métodos definidos na classe Files que também podem ser lidos em documentos Java. Neste tutorial, tentamos cobrir alguns dos métodos importantes entre todos os métodos da classe Java NIO Files.
Métodos importantes da classe Files.
A seguir estão os métodos importantes definidos na classe Arquivos Java NIO.
createFile(Path filePath, FileAttribute attrs) - A classe Files fornece este método para criar arquivos usando o caminho especificado.
Exemplo
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();
}
}
}
Resultado
Created a file at : D:\data\file.txt
copy(InputStream in, Path target, CopyOption… options) - Este método é usado para copiar todos os bytes do fluxo de entrada especificado para o arquivo de destino especificado e retorna o número de bytes lidos ou gravados como valor longo.LinkOption para este parâmetro com os seguintes valores -
COPY_ATTRIBUTES - copia atributos para o novo arquivo, por exemplo, atributo da hora da última modificação.
REPLACE_EXISTING - substitua um arquivo existente se ele existir.
NOFOLLOW_LINKS - Se um arquivo for um link simbólico, o próprio link, e não o destino do link, será copiado.
Exemplo
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);
}
}
}
Resultado
To be or not to be?
createDirectories(Path dir, FileAttribute<?>...attrs) - Este método é usado para criar diretórios usando o caminho fornecido, criando todos os diretórios pais inexistentes.
delete(Path path) - Este método é usado para excluir o arquivo do caminho especificado. Ele lança NoSuchFileException se o arquivo não existir no caminho especificado ou se o arquivo for um diretório e não estiver vazio e não puder ser excluído.
exists(Path path) - Este método é usado para verificar se o arquivo existe no caminho especificado e se o arquivo existir, ele retornará verdadeiro ou então retornará falso.
readAllBytes(Path path) - Este método é usado para ler todos os bytes do arquivo em determinado caminho e retorna a matriz de bytes que contém os bytes lidos do arquivo.
Exemplo
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);
}
}
}
Resultado
Welcome to file.
size(Path path) - Este método é usado para obter o tamanho do arquivo no caminho especificado em bytes.
write(Path path, byte[] bytes, OpenOption… options) - Este método é usado para gravar bytes em um arquivo no caminho especificado.
Exemplo
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);
}
}
}
Resultado
To be or not to be?
Como sabemos, o Java NIO suporta simultaneidade e multi-threading, o que nos permite lidar com diferentes canais simultaneamente ao mesmo tempo. Portanto, a API responsável por isso no pacote Java NIO é AsynchronousFileChannel, que é definido no pacote de canais NIO. para AsynchronousFileChannel é java.nio.channels.AsynchronousFileChannel.
AsynchronousFileChannel é semelhante ao FileChannel do NIO, exceto que este canal permite que as operações de arquivo sejam executadas de forma assíncrona, ao contrário da operação de E / S síncrona na qual um thread entra em uma ação e espera até que a solicitação seja concluída. Assim, os canais assíncronos são seguros para uso por vários threads simultâneos.
No assíncrono, a solicitação é passada por thread para o kernel do sistema operacional para que seja realizada enquanto a thread continua a processar outro job. Uma vez que a tarefa do kernel é concluída, ele sinaliza a thread, então a thread reconhece o sinal e interrompe a tarefa atual e processa o Trabalho de E / S conforme necessário.
Para alcançar a simultaneidade, este canal oferece duas abordagens, que incluem uma como o retorno de um java.util.concurrent.Future object e outro é passar para a operação um objeto do tipo java.nio.channels.CompletionHandler.
Vamos entender ambas as abordagens com a ajuda de exemplos, um por um.
Future Object - Neste, uma instância da Interface Futura é retornada do canal. Na interface Futura, há get() método que retorna o status da operação que é tratada de forma assíncrona com base na qual a execução posterior de outra tarefa pode ser decidida. Também podemos verificar se a tarefa foi concluída ou não chamando seu isDone método.
Exemplo
O exemplo a seguir mostra como usar o objeto Future e realizar tarefas de forma assíncrona.
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();
}
}
Resultado
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 -
Esta abordagem é muito simples, pois usamos a interface CompletionHandler e substituímos seus dois métodos, um deles é completed() método que é chamado quando a operação de E / S é concluída com sucesso e outro é failed() método que é chamado se as operações de E / S falharem. Nesse caso, um manipulador é criado para consumir o resultado de uma operação de E / S assíncrona, pois uma vez que uma tarefa é concluída, apenas o manipulador tem funções que são executadas.
Exemplo
O exemplo a seguir mostra como usar CompletionHandler para realizar tarefas de forma assíncrona.
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();
}
}
Resultado
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.
Em Java, para cada caractere, há unidades de código unicode bem definidas que são tratadas internamente por JVM. O pacote Java NIO define uma classe abstrata chamada Charset, que é usada principalmente para codificação e decodificação de charset e UNICODE.
Conjuntos de caracteres padrão
Os Charset suportados em java são fornecidos abaixo.
US-ASCII - Caracteres ASCII de sete bits.
ISO-8859-1 - Alfabeto latino ISO.
UTF-8 - Este é o formato de transformação UCS de 8 bits.
UTF-16BE - Este é o formato de transformação UCS de 16 bits com ordem de bytes big endian.
UTF-16LE - Esta é a transformação UCS de 16 bits com ordem de bytes pouco endian.
UTF-16 - Formato de transformação UCS de 16 bits.
Métodos importantes da classe Charset
forName() - Este método cria um objeto charset para o nome do charset fornecido. O nome pode ser canônico ou um alias.
displayName() - Este método retorna o nome canônico de determinado conjunto de caracteres.
canEncode() - Este método verifica se o conjunto de caracteres fornecido suporta codificação ou não.
decode() - Este método decodifica a string de um determinado conjunto de caracteres em charbuffer do conjunto de caracteres Unicode.
encode() - Este método codifica charbuffer do conjunto de caracteres Unicode no buffer de bytes de determinado conjunto de caracteres.
Exemplo
O exemplo a seguir ilustra métodos importantes da 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();
}
}
Resultado
US-ASCII
Demo text for conversion.
Como sabemos, o Java NIO oferece suporte a simultaneidade e multiencadeamento, o que permite que ele lide com vários encadeamentos operando em vários arquivos ao mesmo tempo. Mas em alguns casos, exigimos que nosso arquivo não seja compartilhado por nenhum encadeamento e fique inacessível.
Para tal requisito, a NIO fornece novamente uma API conhecida como FileLock que é usada para fornecer bloqueio sobre todo o arquivo ou parte do arquivo, de forma que o arquivo ou sua parte não seja compartilhado ou acessível.
para fornecer ou aplicar tal bloqueio, temos que usar FileChannel ou AsynchronousFileChannel, que fornece dois métodos lock() e tryLock()para este fim. A fechadura fornecida pode ser de dois tipos -
Exclusive Lock - Um bloqueio exclusivo impede que outros programas obtenham um bloqueio sobreposto de qualquer tipo.
Shared Lock - Um bloqueio compartilhado evita que outros programas em execução simultânea adquiram um bloqueio exclusivo sobreposto, mas permite que eles adquiram bloqueios compartilhados sobrepostos.
Métodos usados para obter arquivo de bloqueio -
lock() - Este método de FileChannel ou AsynchronousFileChannel adquire um bloqueio exclusivo sobre um arquivo associado ao canal fornecido. O tipo de retorno desse método é FileLock, que é usado posteriormente para monitorar o bloqueio obtido.
lock(long position, long size, boolean shared) - Este método novamente é o método sobrecarregado do método de bloqueio e é usado para bloquear uma parte específica de um arquivo.
tryLock() - Este método retorna um FileLock ou um null se o bloqueio não puder ser adquirido e ele tenta adquirir um bloqueio explicitamente exclusivo no arquivo deste canal.
tryLock(long position, long size, boolean shared) - Este método tenta adquirir um bloqueio em uma determinada região do arquivo deste canal que pode ser exclusivo ou do tipo compartilhado.
Métodos da classe FileLock
acquiredBy() - Este método retorna o canal em cujo arquivo o bloqueio foi adquirido.
position() - Este método retorna a posição dentro do arquivo do primeiro byte da região bloqueada. Uma região bloqueada não precisa estar contida, ou mesmo sobrepor, o arquivo subjacente real, então o valor retornado por este método pode exceder o tamanho atual do arquivo.
size() - Este método retorna o tamanho da região bloqueada em bytes. Uma região bloqueada não precisa estar contida, ou mesmo sobrepor, o arquivo subjacente real, portanto, o valor retornado por este método pode exceder o tamanho atual do arquivo.
isShared() - Este método é usado para determinar se o bloqueio é compartilhado ou não.
overlaps(long position,long size) - Este método informa se este bloqueio se sobrepõe ou não ao intervalo de bloqueio fornecido.
isValid() - Este método informa se o bloqueio obtido é válido ou não. Um objeto de bloqueio permanece válido até que seja liberado ou o canal de arquivo associado seja fechado, o que ocorrer primeiro.
release()- Libera o bloqueio obtido. Se o objeto de bloqueio for válido, a invocação deste método libera o bloqueio e torna o objeto inválido. Se este objeto de bloqueio for inválido, chamar esse método não terá efeito.
close()- Este método invoca o método release (). Ele foi adicionado à classe para que pudesse ser usado em conjunto com a construção de bloco de gerenciamento automático de recursos.
Exemplo para demonstrar o bloqueio de arquivo.
O exemplo a seguir cria um bloqueio sobre um arquivo e grava conteúdo nele
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();
}
}
Resultado
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.