You are on page 1of 18

El paquete

java.io

Taller de
Algoritmos y
Estructuras
de Datos I

1
Entrada /Salida básica
El paquete java.io contiene todas las clases necesarias para la lectura y
escritura de datos en archivos y conexiones a sistemas que necesitan
transmitir flujos de datos. En el paquete se encuentran distintas
implementaciones, que se caracterizan por el origen y forma de los
datos.

Los términos "entrada" y "salida" a veces pueden ser un poco


confusos. La entrada de datos para una aplicación es a menudo la
salida de otra parte de la misma aplicación, o proviene de otra
aplicación.

Básicamente, el paquete IO define 2 tipos principales: los streams de


entrada o salida (InputStream y OutputStream) y los Reader y Writer.
Los streams se encargan de manejar datos en forma de bytes, mientras
que los Readers y Writers lo hacen en forma de caracteres.

Estos 2 grandes grupos, engloban una serie muy compleja de clases


que permiten utilizar y transformar los datos entre ellos, dentro de un
programa.

Entre los objetivos que cumplen todas las clases del paquete, podemos
nombrar:

 Acceso a redes de datos

 Comunicación asíncrona entre hilos (pipes)

 Acceso a archivos en disco

 Acceso a datos en memoria

 Parseo de datos

 Serialización / deserialización de objetos

 Acceso a buffers de memoria

 Lectura y escritura de datos en la consola

A modo de organización, se presenta la siguiente tabla, en donde se


separan los 2 grupos y se los asocia con las distintas clases que los
implementan.
Streams Caracteres
Entrada Salida Entrada Salida
Reader Writer
Base InputStream OutputStream
InputStreamReader OutputStreamWriter
Arrays ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
Pipes PipedInputStream PipedOutputStream PipedReader PipedWriter
Buffers BufferedinputStream BufferedOutputStream BufferedReader BufferedWriter
Cadenas -- -- StringReader StringWriter
FileInputStream FileOutputStream
Archivos FileReader FileWriter
RandomAccesFile RandomAccesFile
Datos DataInputStream DataOutputStream -- --
Datos con
-- PrintStream -- PrintWriter
formato
Objetos ObjectInputStream ObjectOutputStream -- --

A continuación, desarrollaremos paso a paso las clases utilizadas con


mayor frecuencia en los desarrollos actuales.

Lectura y escritura de datos en consola


La lectura de datos de consola permite que los usuarios ingresen datos
mediante la interfaces de comandos del sistema operativo subyacente.
Así mismo, la escritura de datos en consola permite la visualización de
datos o mensajes que arroja la ejecución de nuestra aplicación.

Para esto, la clase System tiene un InputStream en el atributo estático


in generalmente conectado al teclado, y define además 2 atributos
estáticos del tipo PrintStream denominados out y err. El primero es la
salida estándar del sistema, generalmente conectado a la pantalla, y el
segundo es específico de los mensajes de errores, también por default
dirigidos a la pantalla.

Ejemplo:

import java.io.*;
public class Test{
public static void main(String[] arg) {
InputStream entrada1 = System.in;
InputStreamReader entrada2 = new InputStreamReader(entrada1);
BufferedReader entrada = new BufferedReader(entrada2);
PrintStream salida = System.out;
PrintStream salidaError = System.err;

salida.println("Programa de prueba de entrada salida por consola ");


salida.println("Ingrese una frase y presione Enter");
try {
String cadena = entrada.readLine(); //#7
salida.println(“La cadena ingresada es ” + cadena); //#8
} catch (IOException e) {
salidaError.println(e.toString()); //#9
}
}
}
En el ejemplo podemos observar que se crean instancias de
InputStream, InputStreamReader y de BufferedReader. Cada una de
ellas pasa como parámetro a la anterior en la llamada a su constructor.
Esto se debe a que System.in es un InputStream que lee datos según
estos vayan ingresando. El inputStreamReader transforma los
caracteres ingresados en una cadena, mientras el BufferedReader los
acumula hasta que el usuario presiona la tecla Enter luego de haber
ingresado la cadena.
Luego observamos que los mensajes son mostrados en pantalla
mediante el método println() del PrintStream salida. En caso de que se
produzca un error durante la ejecución del programa, los mensajes se
imprimen en el PrintStream salidaError.

A partir de la versión 6 del JDK, se encuentra la clase Console en el


paquete java.io. La misma permite leer y escribir en la línea de
comandos de forma muy similar al ejemplo anterior.

Ejemplo:

import java.io.*;
public class Test{
public static void main(String[] arg) {
Console consola=System.console();
consola.writer().println("Programa de prueba de entrada salida por consola");
consola.writer().println("Ingrese una frase y presione Enter");
try {
String cadena = consola.readLine();
consola.writer().println(“La cadena ingresada es ” + cadena);
} catch (IOException e) {
consola.writer().println(e.toString()); //#9
}
}
}

Observamos que el objeto Console se obtiene llamando al método


estático console() de la clase System. Luego leemos los caracteres
ingresados mediante el método readLine().

Para la impresión de mensajes por pantalla, llamamos al método


writer() que nos retorna una instancia del PrintWriter asociado a la
pantalla.

Luego imprimimos la salida mediante el método println(), tal cual lo


hacíamos en el ejemplo anterior.
Lectura y escritura de archivos en disco
La lectura y escritura de archivos es muy común en aplicaciones de
escritorio. También se utiliza en APIs como Log4j, que sirve para
escribir en distintos dispositivos de salida los estados por los que pasa
la ejecución de un programa, también conocido como loging. Una de
las formas de escribir estos estados es agregarlos a un archivo de log.
Dicho archivo se escribe mediante las clases de java.io.

Ahora veremos un ejemplo de lectura sobre un archivo de texto:

import java.io.*;

public class TestReadFile {


public static void main (String args[]) {
//Crea un FileInputStream para leer el archivo
FileInputStream lector;
try{
// Abre el archivo
lector = new FileInputStream ("archivo.txt");

// Lee una linea del archivo


System.out.println( new BufferedReader(new InputStreamReader(
lector )).readLine() );
// Cierra el lector
lector.close();
}
// Agarra cualquier excepcion que se puede producer al leer el
archivo
catch (IOException e) {
System.err.println ("No se ha podido leer el archivo");
System.exit(-1);
}
}
}
Para el ejemplo anterior, necesitamos que el archivo de texto esté
situado en la misma carpeta que la clase TestReadFile para que
funcione, de lo contrario lanzará una excepción de
FileNotFoundException.
Luego de utilizar las funciones de los objetos del paquete IO, es
recomendable que los recursos sean liberados. Caso contrario, se
pueden producir errores inesperados. Para lograr esto, se llama al
método close().

Estándares y codificación para una buena práctica de


codificación en Java

Las convenciones de código son importantes no solo para los


programadores, sino para todos los integrantes del equipo de trabajo, por
numerosas razones:

Las convenciones de código aumentan la legibilidad de los programas,


permitiendo al equipo de desarrolladores comprender nuevo código rápida
y perfectamente.

Si el código fuente se distribuye como un producto, es necesario asegurar


que está tan bien empaquetado y limpio como cualquier otro producto que
se crea.

Nombre del archivo

1- Sufijos de archivo

Java utiliza los siguientes sufijos de archivo:

Tipo de archivo Sufijo

Código fuente Java .java

Código compilado Java .class

2- Nombres de archivos comunes

Nombre de
Uso
archivo

Nombre preferido para el archivo que resume


LEEME
el contenido de un directorio en particular.
Organización de archivos
Un archivo consiste en secciones que deberían estar separadas por líneas en
blanco y un comentario opcional identificando cada sección.

Los archivos de más de 2000 líneas son demasiado largos y deberían


evitarse.

Ficheros de código fuente Java

Cada archivo de código fuente Java contiene una única clase o interfaz
público. Cuando una clase pública tiene clases privadas e interfaces
asociados, se pueden poner en el mismo archivo de código fuente que la
clase pública. La clase pública debería ser la primera clase o interfaz en el
archivo.

Los archivos de código fuente Java tienen la siguiente ordenación:

 Comentarios iniciales

 Sentencias package e import

 Declaraciones de clase e interfaz

Comentarios iniciales:

Todos los archivos de código fuente deberían comenzar con un comentario


que muestre el nombre de la clase, información sobre la versión, la fecha y
el copyright.

/*

* Nombre de la clase

* Información sobre la versión

* Fecha

* Copyright

*/

Sentencias package e import

La primera línea que no sea un comentario es una sentencia package.


Después, puede haber sentencias import. Por ejemplo:
package edu.modelo;

import java.util.List;

El primer componente de un nombre de paquete único se escribe en letras


ASCII minúsculas y es uno de los nombres de dominio de nivel superior
(actualmente com, edu, gov, mil, net, org o uno de los códigos de país de
dos letras, como se especifica en el estándar ISO 3166).

Declaraciones de clase e interfaz

En el cuerpo de la clase es necesario colocar comentarios, en las


declaraciones de las variables o antes de un método. Este comentario debe
contener aclaraciones sobre la implementación del código de la clase o de la
interfaz.

Variables de clase (estáticas). El orden correcto de aparición de las


variables es: primero las variables públicas (public), luego las protegidas
(protected), después las de paquete (sin modificador de acceso) y por
último las privadas (private).

Variables de instancia. Primero las variables públicas (public), luego las


protegidas (protected), después las de paquete (sin modificador de acceso)
y por último las privadas (private).

Métodos. Los métodos deberían estar agrupados por funcionalidad en lugar


de por ámbito o accesibilidad. Por ejemplo, un método estático privado
puede estar entre dos métodos de instancia públicos.
El objetivo es hacer la lectura y comprensión del código más fácil.

Declaraciones

Número por línea

Se recomienda una declaración por línea

int level; // nivel de tabulación

int size; // tamaño de la tabla es preferible antes que

int level, size;

No poner tipos diferentes en la misma línea. Por ejemplo:

int foo, fooarray[]; // ¡MAL!

Ejemplo:

int level; // nivel de tabulación


int size; // tamaño de la tabla

Object currentEntry; // elemento de la tabla seleccionado

Importante: Los ejemplos anteriores usan un espacio entre el tipo y el


identificador. Otra alternativa aceptable es usar tabuladores

Inicialización

Intente inicializar las variables locales donde se declaren. Si el valor inicial


depende de algún cálculo que debe ocurrir primero, es la única razón para
no inicializar una variable donde se declara.

Colocación

Coloque las declaraciones sólo al principio de los bloques. (Se entiende por
bloque cualquier código rodeado por llaves "{" y "}".)

void metodo() {

int int1 = 0; // principio del bloque de método

if (condición) {

int int2 = 0; // principio del bloque de "if"

...

Excepción a esta regla: se da con el índice de los bucles for, que en Java se
puede declarar dentro de la sentencia for:

for (int i = 0; i < tope; i++) { ... }

Evite declaraciones locales que oculten declaraciones de más alto nivel. Por
ejemplo, no declare el mismo nombre de variable en un bloque interno:

int count;
...
metodo() {
if (condición) {
int count = 0; // EVITAR
...
}
...
}
Declaraciones de clase e interfaz

Mientras se codifican clases e interfaces Java, se deben seguir las siguientes


reglas de formato:

Ningún espacio entre el nombre del método y el paréntesis "(" que abre su
lista de parámetros.

La llave de apertura "{" aparece al final de la misma línea que la sentencia


de declaración.

La llave de cierre "}" comienza una línea nueva tabulada para coincidir con
su sentencia de apertura correspondiente, excepto cuando es un bloque
vacío que se presentan juntas {}

Los métodos están separados por una línea en blanco.

Veamos un ejemplo correcto:

class Ejemplo extends Object {

int ivar1;

int ivar2;

Ejemplo(int i, int j) {

ivar1 = i;

ivar2 = j;

int emptyMethod() {}

...

Sentencias

Sentencias simples

Cada línea debe contener una sentencia como mucho. Por ejemplo:

argv++; // Correcto

argc--; // Correcto
argv++; argc--; // ¡EVITAR!

Sentencias compuestas

Las sentencias compuestas son sentencias que contienen una lista de


sentencias encerradas entre llaves "{" y "}".

Las sentencias internas deben estar tabuladas un nivel más que la sentencia
compuesta.

La llave de apertura debe estar al final de la línea que comienza la sentencia


compuesta; la llave de cierre debe estar en una nueva línea y estar tabulada
al nivel del principio de la sentencia compuesta.

Las llaves se usan en todas las sentencias compuestas, incluidas las


sentencias únicas, cuando forman parte de una estructura de control. .

Sentencia return

Una sentencia return con una estructura de datos no debería usar


paréntesis a menos que destaque el valor devuelto por alguna razón
aceptable. Por ejemplo:

return;

return metodo();

return (size > 0 ? size : defaultSize);

Sentencia try-catch

Una sentencia try-catch debe tener el siguiente formato:

try {

sentencias;

} catch (ExceptionClass e) {

sentencias;

Una sentencia try-catch puede venir seguida de una sentencia finally, la


cual se ejecuta siempre independientemente de que el bloque try se haya
completado correctamente o no.

try {

sentencias;

} catch (ExceptionClass e) {

sentencias;
} finally {

sentencias;

Espacios en blanco

Líneas en blanco

Las líneas en blanco para separar son necesarias porque mejoran la


legibilidad resaltando secciones de código que están relacionadas
lógicamente.

En los siguientes casos, siempre se deben usar dos líneas en blanco:

Entre secciones de un archivo fuente.

Entre definiciones de clases e interfaces.

En los siguientes casos, siempre se debe usar una línea en blanco:

Entre métodos.

Entre las variables locales de un método y su primera sentencia.

Antes de un comentario de bloque o de una sola línea.

Entre las secciones lógicas de un método, para mejorar la legibilidad.

Espacios en blanco

Los espacios en blanco deben usarse en los siguientes casos:

Una palabra clave seguida por un paréntesis debe estar separado por un
espacio. Por ejemplo:

while (true) {

...

En las listas de argumentos, debe haber un espacio después de cada coma.

Todos los operadores binarios, excepto el operador punto (.) deben estar
separados de sus operandos por espacios. Los operadores unarios
(incremento ++, decremento --, negativo -) nunca deben estar separados de
sus operandos. Por ejemplo:

a += c + d;

a = (a + b) / (c * d);

while (d++ = s++) {

n++;

}
print("el resultado es " + n + "\n");
Las expresiones de una sentencia for deben estar separadas por espacios en
blanco. Por ejemplo:

for (expr1; expr2; expr3)

Las conversiones de tipo (cast) deben estar seguidas de un espacio en


blanco. Por ejemplo:

metodo((byte) aNum, (Object) x);

metodo((int) (cp + 5), ((int) (i + 3))

+ 1);

Convenciones de nombrado

Las convenciones de nombrado hacen los programas más fáciles de leer.


También pueden dar información acerca de la función del identificador (por
ejemplo, si se trata de una constante, un paquete o una clase), puede ayudar
a entender el código.

Tipo de
Reglas de nombrado Ejemplos
identificador

El prefijo de un nombre de com.sun.modelo


paquete único se escribe
siempre en letras ASCII
minúsculas y debería ser uno de
los nombres de dominio de nivel com.apple.quicktime.v2
Paquetes superior (actualmente com, edu,
gov, mil, net, org o uno de los
códigos de país de dos letras, edu.ue.cs.estilos
como se especifica en el
estándar ISO 3166 [6]).

es.nom.aperez

Los nombres de clases deben ser


sustantivos, escritos en
CamelCase
(Mayúscula/minúscula/Mayúsc
ula o viceversa). Trate que los class Raster
nombres de clases sean simples
Clases
y descriptivos. Use palabras
completas, evite acrónimos y class ImageSprite
abreviaturas (a menos que la
abreviatura sea mucho más
usada que la forma larga, como
URL o HTML).
Tipo de
Reglas de nombrado Ejemplos
identificador

Los nombres de interfaz deben interface


Interfaces ser escritos como los nombres RasterDelegate
de clases. interface Storing

Los nombres de métodos deben run();


ser verbos, escritos en
Métodos runFast();
CamelCase con la primera letra
en minúscula. getBackground();

Todos los nombres de variable


deben estar escritos en
CamelCase con la primera letra
en minúscula. No deben
comenzar con un caracter de
subrayado (_) o un signo de
dólar ($), aunque ambos están
permitidos.
int i;
Los nombres de variables deben
ser cortos y significativos. El char c;
Variables nombre de variable debe ser
mnemotécnica, esto es, pensada float myWidth;
para indicar la intención de su
uso a un posible observador String streetName;
ocasional. Se deber evitar los
nombres de variables de un solo
caracter excepto para variables
temporales "desechables".
Algunos nombres comunes para
variables temporales son i, j, k,
m y n para números enteros; c,
d y e para caracteres.

static final int


Los nombres de variables MIN_WIDTH = 4;
declaradas como constantes de
static final int
clase deben estar escritos todo
Constantes MAX_WIDTH = 999;
en mayúsculas separando las
palabras con un caracter de static final String
subrayado (_). DEFAULT_PROTOCO
L = "http";
Prácticas de programación

Proporcionar acceso a variables de clase e instancia

No debe ser pública ninguna variable de clase o instancia, a no ser que sea
necesario y justificable. Por lo general, las variables de instancia no
necesitan ser accedidas o modificadas explícitamente.

Referenciar variables y métodos de clase

Evite usar una instancia de un objeto para acceder a un método o variable


de clase (estática). Use el nombre de la clase en su lugar. Por ejemplo:

classMethod(); // OK

AClass.classMethod(); // OK

anObject.classMethod(); // ¡EVITAR!

Constantes

Las constantes numéricas no deben codificarse directamente, excepto -1, 0


y 1, que pueden aparecer en un bucle for como contadores. Por ejemplo:

static final int MAX_SIZE = 25;

for (i = 0; i < MAX_SIZE; i++)

Asignaciones de variables

Evite asignar a varias variables el mismo valor en una sola sentencia, es


difícil de leer. Por ejemplo:

str = str2 = 'c'; // ¡EVITAR!

No use el operador de asignación (=) en un lugar donde se pueda confundir


fácilmente con el operador de igualdad (==). Ejemplo:

if (c++ = d++) { // ¡EVITAR! (Java lo rechaza)

...

debe ser escrito así:

if ((c++ = d++) != 0) {

...

No use asignaciones incrustadas en un intento de mejorar el rendimiento


en tiempo de ejecución, esto es trabajo del compilador. Ejemplo:
d = (a = b + c) + r; // ¡EVITAR!

debe ser escrito de la siguiente manera:

a = b + c;

d = a + r;

Prácticas varias

Paréntesis

Generalmente, es una buena idea usar paréntesis en expresiones que tienen


operadores para evitar problemas de precedencia de operadores. Incluso si
la precedencia de operador parece clara, puede no serlo para otros.

if (a == b && c == d) // ¡EVITAR!

if ((a == b) && (c == d)) // BIEN

Devolver valores

Trate de hacer que la estructura del programa coincida con su propósito.


Ejemplo:

if (expresiónBoolean) {

return true;

} else {

return false;

debe ser escrito así:

return expresiónBoolean;

Igualmente,

if (condición) {

return x;

return y;

debe ser escrito así:

return (condición ? x : y);


Expresiones antes de '?' en el operador condicional
Si una expresión con un operador binario aparece antes del signo “?” en el
operador ternario ?:, debe ser puesta entre paréntesis. Ejemplo:

(x >= 0) ? x : -x;

Comentarios especiales

Use XXX en un comentario para indicar algo que funciona pero que no está
del todo bien.
Use FIXME (fix me, corrígeme) para indicar algo que no funciona del todo y
debe corregirse.
Use TODO (to do, hacer) para indicar algo que no está totalmente
terminado.

Integración

Las buenas prácticas de programación exigen un orden, una normalización,


estandarización de la escritura y estructura capaz de ser entendida por
cualquier persona que lea su código, es por ello que las convenciones del
lenguaje y del Paradigma Orientado a Objeto las tenemos que conocer,
respetar y utilizar.

Reuso de Software

Una de las características más importantes de la Programación Orientada a


Objetos es el reuso de software, o sea, si una funcionalidad dentro de la
clase la usa más de una vez, no debe escribirlo N veces, sino una vez y
llamarlo N veces. Para lograr este cometido es importante analizar el
comportamiento de los objetos para decidir dónde colocar el método a
reusar. Si no perteneciera a un comportamiento de un objeto específico, se
coloca en una clase que tiene funcionalidades. Esta clase se debe colocar en
un paquete bien identificado que le permita utilizarlo en distintos
proyectos, así se convierte en parte de su librería. Por ejemplo, las
validaciones de datos ingresados que deben ser numéricos, es una
validación que hará más de una vez y en todos los proyectos. Es conveniente
hacer una clase para estos métodos considerados “librerías” y colocarlos en
un package, para poder reusar en más de un proyecto, o sea en más de un
módulo o sistema.

Conclusión

Con lo visto en esta unidad usted está en condiciones de plantear el diseño


de clases y la codificación organizada en paquetes de dichas clases.

En el planteo de solución de un caso trate de integrar todos los conceptos


definidos, Herencia, encapsulamiento, Interfaz, Excepciones, validaciones
utilizando la nomenclatura correcta.

Un programa de calidad no solo respeta la Programación Orientada a


Objetos sino que también funciona correctamente, sin errores ni
excepciones.

Espero que le haya sido de utilidad lo aprendido en este módulo.


Referencia Bibliográfica
Mark Allen Weiss, “Estructuras de Datos en Java”, ed. Addison Wesley.

Deitel y Deitel, “Java cómo programar ”, séptima edición, ed. Pearson, 2008.

Pressman Roger, (2006), “Ingeniería de Software. Un enfoque práctico” 6ta.


edición, Ed. McGraw Hill

ORACLE, Oracle 10g: Programación Java, Guía del Alumno, 2003

You might also like