O2 Nio Nio 2
NIO - NIO.2
A medida que las aplicaciones se han hecho más complejas y demandan más eficiencia en el manejo de grandes volúmenes de datos, estas APIs han ganado relevancia. Vamos a ver de manera detallada qué son, cómo se utilizan, y qué los diferencia de la API tradicional de I/O en Java, basada en clases como FileInputStream o FileOutputStream.
Lección 1: NIO (New I/O) y NIO.2 (Introducción y Uso Básico)
| Tema | Descripción | Ejemplo |
|---|---|---|
| NIO (New I/O) | Introducción de una nueva API para I/O en Java (Java 1.4) para mejorar la eficiencia en lectura y escritura de archivos. | Uso de FileChannel y ByteBuffer para leer un archivo. |
| Buffers y Canales | Utiliza buffers (memoria intermedia) para almacenar datos y canales para leer/escribir de manera eficiente. | Ejemplo de lectura de un archivo con FileChannel y ByteBuffer. |
| Selección (Selectors) | Permite multiplexar múltiples canales, ideal para aplicaciones como servidores concurrentes. | Ejemplo no cubierto explícitamente, pero es útil en servidores de alto rendimiento. |
| NIO.2 (Introducción) | Mejora de NIO (Java 7), que introduce la clase Path, Files, y mejoras para trabajar con archivos y directorios. |
Uso de Files.createFile(), Files.write(), Files.readAllLines() para crear, escribir y leer archivos. |
| Clases Principales de NIO.2 | Path, Files, y WatchService para operaciones de archivos más sencillas y avanzadas. |
Ejemplos de uso de Path, como crear directorios con Files.createDirectories(). |
1. ¿Qué es NIO (New I/O)?
NIO fue introducido en Java 1.4 y tiene como objetivo proporcionar una manera más eficiente y flexible de trabajar con datos en comparación con la tradicional API de I/O (Input/Output) de Java. Algunos de los puntos clave de NIO son:
- Buffering (Almacenamiento en buffer): La API NIO hace uso de buffers para almacenar datos temporalmente. Este enfoque es más eficiente en comparación con el enfoque tradicional basado en streams.
- Canales (Channels): En lugar de utilizar streams como en la I/O tradicional, NIO utiliza canales (
Channel) para leer y escribir datos. Los canales son más eficientes, especialmente para grandes volúmenes de datos. - Selección (Selectors): NIO introduce el concepto de
Selector, que permite multiplexar múltiples canales de entrada y salida, lo cual es útil en aplicaciones como servidores de alta concurrencia (ej. servidores HTTP, servidores de chat, etc.).
Ejemplo Básico con NIO
Aquí vamos a ver cómo leer un archivo usando NIO con buffers y canales.
import java.nio.file.*;
import java.nio.charset.*;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class EjemploNIO {
public static void main(String[] args) {
Path path = Paths.get("ejemplo.txt"); // Archivo de texto
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (fileChannel.read(buffer) > 0) {
buffer.flip(); // Prepara el buffer para ser leído
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
buffer.clear(); // Limpia el buffer para la siguiente lectura
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
En este ejemplo:
- Se utiliza
FileChannelpara abrir el archivo. - Se lee el archivo en bloques usando un
ByteBuffer. - Se utiliza
flip()para preparar el buffer para ser leído después de escribir en él. - Finalmente, se limpia el buffer con
clear()para volver a usarlo.
2. ¿Qué es NIO.2?
NIO.2 es una extensión de NIO, introducida en Java 7. La principal novedad de NIO.2 es la API de archivos (Path y Files), que facilita el manejo de archivos, directorios y enlaces simbólicos, y permite realizar operaciones más complejas de manera más sencilla y eficiente. Algunas de sus características incluyen:
- Path: La clase
Pathes más flexible y moderna que la antigua claseFile. Permite representar rutas de archivos y directorios de manera más consistente. - Files: La clase
Filesproporciona métodos para operaciones comunes sobre archivos (como copiar, mover, eliminar, leer, etc.) de una manera más sencilla que la API tradicional. - WatchService: Permite observar cambios en directorios, lo cual es útil en aplicaciones como editores de texto o sistemas de gestión de archivos.
- Operaciones Asíncronas: NIO.2 soporta operaciones de E/S asincrónicas, lo que puede mejorar el rendimiento de aplicaciones que manejan múltiples operaciones de archivo simultáneamente.
Ejemplo Básico con NIO.2
Vamos a ver cómo crear un archivo y escribir en él usando NIO.2:
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class EjemploNIO2 {
public static void main(String[] args) {
Path path = Paths.get("archivo_nuevo.txt");
// Crear archivo y escribir en él
try {
// Si el archivo no existe, se crea
if (!Files.exists(path)) {
Files.createFile(path);
}
// Escribir texto en el archivo
String contenido = "Hola, esta es una prueba con NIO.2!";
Files.write(path, contenido.getBytes());
// Leer el archivo
List<String> lineas = Files.readAllLines(path);
lineas.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
En este código:
- Usamos
Paths.get()para obtener la ruta del archivo. Files.createFile()crea el archivo si no existe.Files.write()permite escribir directamente en el archivo.Files.readAllLines()lee todas las líneas del archivo en una lista de strings.
3. Comparación entre I/O tradicional, NIO y NIO.2
I/O Tradicional (Streams):
- Basado en clases como
FileInputStream,FileOutputStream,BufferedReader, etc. - Cada operación de lectura/escritura es bloqueante y puede ser lenta para grandes cantidades de datos.
- No tiene soporte directo para operaciones asíncronas ni para trabajar con directorios de manera eficiente.
NIO:
- Introduce canales (Channels) y buffers para mejorar el rendimiento en la lectura y escritura de datos.
- Los canales pueden ser no bloqueantes, lo que permite manejar múltiples conexiones de manera más eficiente.
- Soporta operaciones asíncronas usando
Selector, lo que lo hace ideal para servidores de alto rendimiento.
NIO.2:
- Introduce la clase
Pathpara una manipulación más moderna y flexible de rutas de archivos. - Ofrece una API más sencilla y poderosa para trabajar con archivos y directorios.
- Permite la observación de cambios en directorios con
WatchService. - Proporciona métodos más fáciles para operaciones como copiar, mover, o eliminar archivos.
4. Ejemplo Avanzado: Uso de WatchService
Ahora vamos a ver un ejemplo más avanzado utilizando WatchService, que nos permite monitorear cambios en un directorio.
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.IOException;
public class MonitorizarDirectorio {
public static void main(String[] args) {
Path directorio = Paths.get("directorio_a_monitorizar");
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
directorio.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
WatchKey key;
try {
key = watchService.take(); // Bloquea hasta que ocurra un evento
} catch (InterruptedException e) {
return;
}
for (WatchEvent<?> event : key.pollEvents()) {
System.out.println("Evento: " + event.kind() + " en " + event.context());
}
boolean valid = key.reset();
if (!valid) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
En este ejemplo:
- WatchService se utiliza para monitorear los cambios en un directorio (creación, eliminación, modificación de archivos).
- Usamos un bucle infinito para esperar y manejar eventos, lo que sería útil en un sistema que necesita reaccionar a cambios en tiempo real.