"¡Hola, amigo! Hoy nos familiarizaremos con los flujos de entrada/salida . Elegimos este tema hace un par de días, pero hoy lo exploraremos a fondo. Los flujos de entrada/salida se dividen en 4 categorías:"

1) Los flujos se dividen según su dirección: flujos de entrada y flujos de salida

2) Los flujos se dividen según su tipo de datos: los que funcionan con bytes y los que funcionan con caracteres .

Aquí estas divisiones están representadas en una tabla:

Flujo de entrada Flujo de salida
Funciona con bytes Flujo de entrada Flujo de salida
Funciona con personajes Lector Escritor

Si un objeto implementa la interfaz InputStream , admite la capacidad de leer secuencialmente bytes de él.

Si un objeto implementa la interfaz OutputStream , admite la capacidad de escribir bytes secuencialmente en él.

Si un objeto implementa la interfaz del lector , admite la capacidad de leer secuencialmente caracteres (chars) de él.

Si un objeto implementa la interfaz Writer , admite la capacidad de escribir secuencialmente caracteres (chars) en él.

Flujos de entrada/salida - 1

Un flujo de salida es como una impresora. Podemos enviar documentos a la impresora. Podemos enviar datos a un flujo de salida.

Por su parte, un flujo de entrada se puede comparar con un escáner, o quizás con una toma de corriente. Con un escáner, podemos traer documentos a nuestra computadora. O podemos enchufarnos a una toma de corriente y recibir electricidad de ella. Podemos recibir datos de un flujo de entrada.

"¿Dónde se usan?"

"Estas clases se usan en todas partes en Java. Nuestro amigo familiar System.in es una variable InputStream estática nombrada en la clase System ".

"¿En serio? Así que todo este tiempo he estado usando un InputStream y ni siquiera me di cuenta. ¿System.out también es un flujo?"

"Sí, System.out es una variable PrintStream estática (un descendiente de OutputStream ) en la clase System".

"¿Quieres decirme que siempre he estado usando flujos y ni siquiera lo sabía?"

"Sí, y eso solo nos dice qué tan convenientes son estas transmisiones. Simplemente tome una y úsela".

"Pero no se puede decir eso de System.in. Constantemente teníamos que agregarle BufferedReader o InputStreamReader".

"Eso es cierto. Pero también había razones para eso".

Hay muchos tipos de datos y muchas maneras de trabajar con ellos. De modo que el número de clases de E/S estándar creció muy rápidamente, aunque lo hacían todo casi de la misma manera. Para evitar esta complejidad, los desarrolladores de Java utilizaron el principio de abstracción y dividieron las clases en muchas partes pequeñas.

Pero puede conectar estas partes de manera coherente y obtener una funcionalidad muy compleja, si la necesita. Mira este ejemplo:

Salida de una cadena a la consola
System.out.println("Hello");
Almacene el flujo de salida de la consola en una variable separada.
Salida de una cadena a la secuencia.
PrintStream console = System.out;
console.println("Hello");
Cree una matriz de bytes dinámica (expandible) en la memoria.
Conéctelo a un nuevo flujo de salida (objeto PrintStream).
Salida de una cadena a la secuencia.
ByteArrayOutputStream stream = new ByteArrayOutputStream();
PrintStream console = new PrintStream(stream);
console.println("Hello");

"Honestamente, esto es como un juego de Lego. Solo que no me queda claro qué está haciendo este código".

"No te preocupes por eso por ahora. Todo a su debido tiempo".

Esto es lo que quiero que recuerde: si una clase implementa la interfaz OutputStream, puede escribir bytes en ella. Casi exactamente como envía datos a la consola. Lo que haga con él es asunto suyo. Con nuestro "kit Lego", no nos importa el propósito de cada pieza individual. Nos preocupamos por el hecho de que la gran selección de piezas nos permite construir cosas tan geniales.

"Está bien. Entonces, ¿por dónde empezamos?"