Conceptos Fundamentales de Programación en Java: Estructuras, Control y POO

Estructuras de Datos Simples en Java

Variables

Una variable es una referencia a un objeto; por lo tanto, se puede declarar de la siguiente manera:

int i;
int j = 25;
long num = 30;
boolean f, t; // Corrección: 'long boolean' no es un tipo válido en Java
Button buttonOK;

Constantes

final int meses = 12;
final String primerdia = "lunes";

Clases String y StringBuffer

Las clases String y StringBuffer están orientadas a manejar cadenas de caracteres. La clase String se utiliza para cadenas de caracteres constantes, es decir, que no pueden cambiar. La clase StringBuffer permite que el programador cambie la cadena insertando, borrando, entre otras operaciones.

Ambas clases pertenecen al paquete java.lang, por lo tanto, no es necesario importarlas.

"Hola".length();

Métodos de la clase String

A continuación, se describen dos formas de crear objetos de la clase String:

String str1 = "Hola";
String str2 = new String("Hola");

Conversión de Tipos (Casting)

Casting

Por ejemplo, si se define un int y luego se maneja como long:

int entero;
long enterolargo;
enterolargo = (long) entero;

ACLARACIÓN: No lo convierte directamente, sino que una clase puede representar a otra.

Los boolean no aceptan ningún casting, mientras que el casting de char a byte o short puede resultar en un valor negativo, ya que char no contempla el signo.

Estilo de Código y Formato

Tabulación

La forma exacta de la tabulación (espacios o tabuladores) no se especifica; no obstante, la unidad de tabulación debería ser de cuatro espacios.

Longitud de Línea

Evitar líneas con una longitud no mayor de 80 caracteres (idealmente, no más de 70 caracteres), ya que pueden cortarse y algunas IDEs no las manejan adecuadamente.

Ruptura de Líneas (Wrapping Lines)

Cuando una expresión no cabe en una única línea, debe dividirse siguiendo estos principios generales:

  • Romper después de una coma.
  • Romper antes de un operador.
  • Preferir las rupturas de alto nivel a las de bajo nivel.
  • Alinear la nueva línea con el inicio de la expresión al mismo nivel que la línea anterior.

Si las reglas anteriores resultan en un código confuso o demasiado pegado al margen derecho, entonces tabular solo con 8 espacios.

Tantas normas solo por la longitud de la línea (mayor a 80 caracteres) pueden parecer innecesarias e inmanejables. Se recomienda adoptar las que mejor se adapten a su forma de trabajar y documentar el código.

Sentencias de Control de Flujo

Sentencia switch

La sentencia switch permite ejecutar una de varias acciones, de acuerdo al valor de una expresión, la cual debe ser de tipo char, byte, short o int. Es una sentencia para decisiones múltiples que elimina el if anidado.

Cada vez que un caso continúa con el siguiente (es decir, no incluye una sentencia break), se debe añadir un comentario donde iría la sentencia break.

Sentencia for

for (inicialización; condición; actualización);

Sentencia while

while (condición)
{
  sentencias;
}

Una sentencia while vacía debe tener el siguiente formato:

while (condición);

Sentencia do-while

do {
  sentencias;
}
while (condición);

Arreglos (Arrays)

Un array (o arreglo) es una estructura de datos simple que permite almacenar un conjunto de objetos del mismo tipo, ya sean primitivos u objetos.

Los arrays son objetos en Java; su uso correcto implica los siguientes pasos:

  • Declarar el array: int[] numeros;
  • Crear el array: numeros = new int[4];
  • Inicializar los elementos del array: numeros[0] = 278;

Los arrays se pueden declarar, crear e inicializar en una misma línea, del siguiente modo:

int[] numeros = {278, -45, 15, -125};

Clase Vector

java.util.Vector

Jerarquía de clases:

java.lang.Object
  java.util.AbstractCollection
    java.util.AbstractList
      java.util.Vector

Interfaces implementadas:

  • Serializable
  • Cloneable
  • Iterable
  • Collection
  • List
  • RandomAccess

Para usar la clase Vector, debe importar el paquete java.util al principio del archivo del código fuente.

Para instanciar la clase Vector, se pueden utilizar distintos constructores:

Vector vector = new Vector();

Cuando se utiliza el constructor sin argumentos, la capacidad inicial es diez; al llenarse, aumenta de tamaño duplicándose.

Vector vector = new Vector(capacidadInt, incrementoInt);
Vector vector = new Vector(5, 3); // Inicialmente tiene capacidad para 5 elementos, cuando se llena aumenta de a 3 lugares.

La menor dimensión es Vector(1,1), ya que de esta forma almacena un elemento, se llena y agrega solo de a un lugar.

Métodos Importantes de la Clase Vector

  • v.insertElementAt(8, 2);: insertar
  • size(): cantidad de elementos que guarda un vector
  • capacity(): dimensión actual del vector

Se pueden eliminar todos los elementos de un vector llamando al método removeAllElements().

Se puede eliminar un elemento concreto llamando al método v.removeElement(elemento);

Se puede eliminar dicho elemento si especificamos su índice: v.removeElementAt(2);

Acceso a los Elementos de un Vector

En lugar de usar un índice como en un Array, se utiliza el método elementAt(índice). Por ejemplo, v.elementAt(4) sería equivalente a v[4] si v fuese un array.

Enumeration permite acceder a los elementos de una estructura de datos de forma secuencial; su código es:

public interface Enumeration {
  boolean hasMoreElements();
  Object nextElement();
}

La función miembro elements() de la clase Vector devuelve un objeto de la clase VectorEnumerator que implementa la interfaz Enumeration y debe definir las dos funciones hasMoreElements() y nextElement().

Vectores vs. Arrays: ¿Cuál Utilizar?

Los arrays ofrecen tres ventajas sobre Vector:

  1. Pueden almacenar directamente tipos de datos primitivos.
  2. Sus elementos pueden ser accedidos sin necesidad de mensajes, lo que hace el código más rápido que si se usara un Vector.
  3. Como los arrays son tipados, ofrecen una verificación en tiempo de compilación que Vector no tiene. Si se almacena accidentalmente un objeto String en un Vector donde se tienen almacenados objetos Integer, Java lo permite. Sin embargo, Java generaría un error de compilación si se intenta almacenar un String en un array de referencias a Integer.

Sin embargo, los arrays también presentan algunas desventajas sobre Vector:

  1. Son de tamaño fijo y no pueden crecer.
  2. No ofrecen un conjunto de operaciones tan ricas como la clase Vector (a través de métodos como insertElementAt()).

Programación Orientada a Objetos (POO) en Java

Normalmente, las definiciones de clases incluyen los siguientes elementos:

  • Modificador de acceso: especifica la visibilidad de la clase para otras clases.
  • Palabra clave de clase: class indica a Java que el bloque de código siguiente define una clase.
  • Campos de instancia: se refiere a las variables y constantes utilizadas por los objetos de la clase.
  • Constructores: son los métodos que se invocan para instanciar la clase y tienen el mismo nombre que esta.
  • Métodos de instancia: definen las funciones que pueden actuar sobre los datos de una clase.
  • Campos de clase: contienen variables y constantes que pertenecen a la clase y que todos los objetos de la clase comparten (static).
  • Métodos de clase: son los métodos que se utilizan para controlar los valores de los campos de la clase.

Creación de Objetos

Por ejemplo, si se tiene la clase Persona, el objeto se crea de la siguiente manera:

Persona unoNuevo; // Declara la referencia al objeto
unoNuevo = new Persona(); // Crea el espacio en memoria y devuelve una instancia de la clase Persona.

La primera línea crea una referencia a un objeto llamado unoNuevo de la clase Persona. La segunda línea crea el objeto.

También se puede definir en una sola línea, de la siguiente manera:

Persona unoNuevo = new Persona();

El operador new invoca al método constructor de la clase; es decir, la llamada al operador new devuelve una referencia al objeto nuevo.

Atributos de Objeto y Encapsulamiento

unoNuevo.nom = "José";

Si el atributo es declarado con el modificador private (aplicando el concepto de encapsulamiento), la asignación directa solo podrá realizarse dentro de la misma clase. Fuera de la clase, se deberá utilizar el método de instancia público setXxx(). En este caso, sería:

unoNuevo.setNom("José");

Los tipos de valores que se definen para un atributo pueden ser:

  • Primitivos: los tipos de datos que vienen con el lenguaje Java, como int.
  • Objetos: los definidos por el lenguaje, como String o Integer, o bien definidos por el usuario. Por ejemplo: dentro de la clase Auto, el atributo volante puede ser de tipo Volante.

Concepto de Paquetes (Packages)

Java, como lenguaje, está organizado en paquetes (packages). Un package es una agrupación de clases. Existe una serie de packages incluidos en el lenguaje.

Jerarquía de Clases de Java (API)

Es importante distinguir entre lo que significa herencia y package. Un package es una agrupación arbitraria de clases, una forma de organizar las clases. La herencia, sin embargo, consiste en crear nuevas clases basándose en otras ya existentes.

En la actualidad, se cuenta con la API Java™ 2 Platform Standard Edition, más conocida como Java 2.

Herencia

Si una clase deriva de otra (extends), hereda todas sus variables y métodos. La clase derivada puede añadir nuevas variables y métodos y/o redefinir los variables y métodos heredados.

Métodos Constructores

Se utiliza new para la creación de un objeto, lo cual implica la invocación de un método constructor de la clase a la que el objeto pertenece.

Por ejemplo:

Persona unoNuevo = new Persona(); // Invoca al constructor por defecto
Persona otroNuevo = new Persona("José"); // Invoca al constructor que recibe un parámetro.

El Constructor this

Es posible pensar que this es un objeto; en realidad lo es, pero uno muy especial: es una referencia al objeto que se acaba de crear con new y cuyo constructor se está ejecutando.

this.nombre = nombre;

El Constructor super()

Sirve para acceder desde un constructor al constructor de la superclase. Al igual que this(), super() debe ser la primera invocación que aparezca en el constructor.

Concepto de Interfaces

Una interfaz debe ser considerada como un contrato entre un proveedor de servicios y sus clientes.

Es una clase abstracta que no puede ser instanciada y que debe ser implementada en otra clase.

Una clase puede implementar más de una interfaz, representando una forma alternativa de la herencia múltiple.

A su vez, una interfaz puede derivar de otra o incluso de varias interfaces, en cuyo caso incorpora todos los métodos de las interfaces de las que deriva.

modificador interface nombreI {
  modificador tipoDevuelve nombreDelMetodo(parametros);
}

La interfaz define los métodos abstractos, es decir, solo su firma, pero no su código. La clase que la implementa define el método con la misma firma, estableciendo cómo lo aplica.

Manejo de Excepciones en Java

Las excepciones son el mecanismo que utiliza la Java Virtual Machine (JVM) para el manejo de errores leves que pueden producirse durante la ejecución de una aplicación y que podrían detener su ejecución.

Las excepciones se crean en el momento en que se produce una situación anormal o excepcional en tiempo de ejecución, como por ejemplo, la utilización de una referencia con valor null.

El lenguaje Java provee la sintaxis necesaria para el control de excepciones dentro de un programa. Los operadores son:

  • try: observa el comportamiento de un determinado bloque de código que es susceptible de arrojar excepciones.
  • catch: en caso de que el bloque observado por try arroje una excepción, catch captura el error, permitiendo algún tipo de procesamiento del mismo y la posibilidad de tomar un curso correctivo.
  • finally: bloque de sentencias que se ejecutan siempre que exista el bloque try, aunque este no arroje excepción. Generalmente, se utiliza para liberar recursos, como conexiones de bases de datos o cierre de archivos abiertos.
  • throw: fuerza el lanzamiento de una excepción.
  • throws: informa que un determinado método puede arrojar algún tipo de excepción.

Entrada/Salida (I/O) Básica en Java

Básicamente, el paquete java.io define dos 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 Reader y Writer lo hacen en forma de caracteres.

Entre los objetivos que cumplen todas las clases del paquete, se pueden 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

Lectura y Escritura en Consola

Para esto, la clase System tiene un InputStream en el atributo estático in (generalmente conectado al teclado), y define además dos 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 para mensajes de error (también por defecto dirigidos a la pantalla).

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 logging.

Organización de Archivos y Estructura del Código

Un archivo consiste en secciones que deberían estar separadas por líneas en blanco y un comentario opcional que identifique cada sección. Los archivos de más de 2000 líneas son demasiado largos y deberían evitarse.

Estructura de Ficheros de Código Fuente Java

Cada archivo de código fuente Java contiene una única clase o interfaz pública. Cuando una clase pública tiene clases privadas e interfaces asociadas, se pueden colocar 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
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
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 facilitar la lectura y comprensión del código.

Formato de Declaraciones de Clase e Interfaz

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 presenta como {}.

Uso de Líneas en Blanco

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.

Uso de Espacios en Blanco

Los espacios en blanco deben usarse en los siguientes casos:

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

Convenciones de Nomenclatura