6.
ESTRUCTURAS DE DATOS CON OBJETOS
6.1 La clase String, y sus métodos
(ProgramacionJava, 2020)
Manejo de vectores con Objetos
Los arrays en Java son suficientes para guardar tipos básicos de datos, y objetos de una
determinada clase cuyo número conocemos de antemano. Algunas veces deseamos guardar
objetos en un array, pero no sabemos cuántos objetos vamos a guardar. Una solución es la
de crear un array cuya dimensión sea más grande que el número de elementos que
necesitamos guardar. La clase Vector nos proporciona una solución alternativa a este
problema. Un vector es similar a un array, la diferencia estriba en que un vector crece
automáticamente cuando alcanza la dimensión inicial máxima. Además, proporciona
métodos adicionales para añadir, eliminar elementos, e insertar elementos entre otros dos
existentes.
Crear un vector
Para usar la clase Vector hemos de poner al principo del archivo del código fuente la
siguiente sentencia import
import java.util.*;
Cuando creamos un vector u objeto de la clase Vector, podemos especificar su
dimensión inicial, y cuanto crecerá si rebasamos dicha dimensión.
Vector vector=new Vector(20, 5);
Tenemos un vector con una dimensión inicial de 20 elementos. Si rebasamos dicha
dimensión y guardamos 21 elementos la dimensión del vector crece a 25.
Al segundo constructor, solamente se le pasa la dimensión inicial.
Vector vector=new Vector(20); Si se rebasa la dimensión inicial guardando 21
elementos, la dimensión del vector se duplica. El programador ha de tener cuidado con este
constructor, ya que si se pretende guardar un número grande de elementos se tiene que
especificar el incremento de la capacidad del vector, si no se quiere desperdiciar
inútilmente la memoria el ordenador.
Con el tercer constructor, se crea un vector cuya dimensión inicial es 10.
Vector vector=new Vector();
La dimensión del vector se duplica si se rebasa la dimensión inicial, por ejemplo,
cuando se pretende guardar once elementos.
Añadir elementos al vector:
Hay dos formas de añadir elementos a un vector. Podemos añadir un elemento a
continuación del último elemento del vector, mediante la función miembro addElement.
v.addElement("uno");
Podemos también insertar un elemento en una determinada posición, mediante
insertElementAt. El segundo parámetro o índice, indica el lugar que ocupará el nuevo
objeto. Si tratamos de insertar un elemento en una posición que no existe todavía
obtenemos una excepción del tipo ArrayIndexOutOfBounds. Por ejemplo, si tratamos de
insertar un elemento en la posición 9 cuando el vector solamente tiene cinco elementos.
Para insertar el string "tres" en la tercera posición del vector v, escribimos
v.insertElementAt("tres", 2);
Para saber cuántos elementos guarda un vector, se llama a la función miembro size. Para
saber la dimensión actual de un vector se llama a la función miembro capacity. Por
ejemplo, en la porción de código hemos guardado 12 elementos en el vector v. La
dimensión de v es 20, ya que se ha superado la dimensión inicial de 10 establecida en la
llamada al tercer constructor cuando se ha creado el vector v.
System.out.println("nº de elementos "+v.size());
System.out.println("dimensión "+v.capacity());
Podemos eliminar todos los elementos de un vector, llamando a la función miembro
removeAllElements. O bien, podemos eliminar un elemento concreto, por ejemplo, el que
guarda el string "tres".
v.removeElement("tres");
Podemos eliminar dicho elemento, si especificamos su índice.
v.removeElementAt(2) . (sc.ehu, 2011)
Manejo de matrices con Objetos
Un array en Java puede tener más de una dimensión. El caso más general son los arrays
bidimensionales también llamados matrices o tablas.
La dimensión de un array la determina el número de índices necesarios para acceder a sus
elementos. Los vectores que hemos visto en otra entrada anterior son arrays
unidimensionales porque solo utilizan un índice para acceder a cada elemento.
Una matriz necesita dos índices para acceder a sus elementos. Gráficamente podemos
representar una matriz como una tabla de n filas y m columnas cuyos elementos son todos
del mismo tipo.
La siguiente figura representa un array M de 3 filas y 5 columnas:
A los elementos del array se accede mediante la fila y columna donde están
situados. A efectos prácticos, cuando trabajamos con arrays bidimensionales podemos
pensar en una tabla como la que se muestra en la imagen anterior donde los elementos están
distribuidos en filas y columnas. Pero en realidad una matriz en Java es un array de arrays.
La disposición real en memoria del array anterior la podemos representar gráficamente de
esta forma:
M es el nombre del array.
M contiene la dirección de memoria (referencia) de un array unidimensional de 3
elementos.
Cada elemento de este array unidimensional contiene la dirección de memoria de otro array
unidimensional.
Cada uno de estos últimos arrays unidimensionales contiene los valores de cada fila de la
matriz.
M.length indica el número de filas de la matriz. En este ejemplo el número de filas
(M.length) es 3.
M[i].length indica el nímero de columnas de la fila i. En este ejemplo la longitud de cada
fila del array (M[i].length) es 5.
Para acceder a cada elemento de la matriz se utilizan dos índices. El primero indica la fila y
el segundo la columna.
M[0][2] = 9; //asigna el valor 9 al elemento situado en la primera fila (fila 0) y tercera
columna (fila 2).
No dedemos olvidar que la primera fila de una matriz es la fila 0 y la primera columna de
una matriz es la columna 0.
CREAR MATRICES EN JAVA
Se crean de forma similar a los arrays unidimensionales, añadiendo un
índice.
Por ejemplo:
matriz de datos de tipo int llamado ventas de 4 filas y 6 columnas:
int [][] ventas = new int[4][6];
matriz de datos double llamado temperaturas de 3 filas y 4 columnas:
double [][] temperaturas = new double[3][4];
En Java se pueden crear arrays irregulares en los que el número de elementos de
cada fila es variable. Solo es obligatorio indicar el número de filas.
Por ejemplo:
int [][] m = new int[3][];
crea una matriz m de 3 filas.
A cada fila se le puede asignar un número distinto de columnas:
m[0] = new int[3];
m[1] = new int[5];
m[2] = new int[2];
Gráficamente podemos representar la disposición real en memoria del array anterior así:
INICIALIZAR MATRICES
Un array es un objeto, por lo tanto, cuando se crea, a sus elementos se les asigna
automáticamente un valor inicial:
0 para arrays numéricos
'\u0000' (carácter nulo) para arrays de caracteres
false para arrays booleanos
null para arrays de String y de referencias a objetos
También podemos dar otros valores iniciales al array cuando se crea.
Los valores iniciales se escriben entre llaves separados por comas.
Los valores que se le asignen a cada fila aparecerán a su vez entre llaves separados por
comas.
El número de valores determina el tamaño de la matriz.
Por ejemplo:
int [][] numeros = {{6,7,5},{3, 8, 4}, {1,0,2}, {9,5,2}};
se crea la matriz numeros de tipo int, de 4 filas y 3 columnas, y se le asignan esos valores
iniciales.
Asignando valores iniciales se pueden crear también matrices irregulares.
Por ejemplo la instrucción:
int [][] a = {{6,7,5,0,4}, {3, 8, 4}, {1,0,2,7}, {9,5}};
crea una matriz irregular de 4 filas. La primera de 5 columnas, la segunda de 3, la tercera de
4 y la cuarta de 2.
Tomado de. (puntocomnoesunlenguaje, 2021)
Manejo de listas con objetos
Java ArrayList for y size()
La forma más sencilla de recorrer una lista es a través de un bucle for y accediendo a la
propiedad size.
java arraylist for clasico
Veámoslo en código:
package com.arquitecturajava;
import java.util.ArrayList;
public class JavaFor {
public static void main(String[] args) {
ArrayList<String> lista = new ArrayList<String>();
lista.add("hola");
lista.add("que");
lista.add("tal");
lista.add("estas");
lista.add("hoy");
for (int i=0;i<lista.size();i++) {
System.out.println(lista.get(i));
}
}
}
Cuando uno empieza esta forma parece la más clara y no parece tener mucha
problemática . Sin embargo algunas veces sucede que los desarrolladores al recorrer la
lista no asignan correctamente el lista.size() o ponen otro valor sobre todo cuando se es
muy novato. Por lo tanto puede generarse un NullPointerException. No solo eso sino
que es una forma de recorrer elementos que solo nos vale para los ArrayList.
Java for e Iteradores
Una alternativa a esta situación es usar un Iterador .Un Iterador es un interface que
dispone de los métodos hasNext() y next() y nos permite recorrer una colección de
elementos.
java arraylist for
Da lo mismo que sea un ArrayList que otra estructura. Por lo tanto aporta
homogeneidad en las APIs.
package com.arquitecturajava;
import java.util.ArrayList;
import java.util.Iterator;
public class JavaFor2 {
public static void main(String[] args) {
ArrayList<String> lista = new ArrayList<String>();
lista.add("hola");
lista.add("que");
lista.add("tal");
lista.add("estas");
lista.add("hoy");
Iterator<String> it= lista.iterator();
while(it.hasNext()) {
System.out.println(it.next());
Java 5 ArrayList for
A partir de Java 5 aparece el concepto de bucle forEach y permite simplificar la forma
en la que trabajamos con los Iteradores.
package com.arquitecturajava;
import java.util.ArrayList;
public class JavaFor3 {
public static void main(String[] args) {
ArrayList<String> lista = new ArrayList<String>();
lista.add("hola");
lista.add("que");
lista.add("tal");
lista.add("estas");
lista.add("hoy");
for (String cadena: lista) {
System.out.println(cadena);
Como se puede observar la forma de recorrer una lista se simplifica sobremanera
comparado con el Iterador que aunque genera homogeneidad siempre era complicado
de usar. Esta es una de las opciones más recomendadas hasta la llegada de Java 8.
Java 8 y Streams.
En Java 8 tenemos un salto importante a niveles de programación ya que entran todas
las capacidades funcionales y con ello se pueden simplificar el manejo de listas a través
de streams.
package com.arquitecturajava;
import java.util.ArrayList;
public class JavaFor3 {
public static void main(String[] args) {
ArrayList<String> lista = new ArrayList<String>();
lista.add("hola");
lista.add("que");
lista.add("tal");
lista.add("estas");
lista.add("hoy");
lista.forEach(System.out::println);
}
}
La simplificación es clara aun así nos quedan situaciones clásicas en las que un bucle de
forEach de Java 5 nos aporta más ya que por ejemplo deseamos cambiar el contenido de
los elementos del Array y luego imprimirlo.
package com.arquitecturajava;
import java.util.ArrayList;
public class JavaFor6 {
public static void main(String[] args) {
ArrayList<String> lista = new ArrayList<String>();
lista.add("hola");
lista.add("que");
lista.add("tal");
lista.add("estas");
lista.add("hoy");
for (int i = 0; i < lista.size(); i++) {
lista.set(i, lista.get(i).toUpperCase());
}
for (String cadena : lista) {
System.out.println(cadena);
}
}
}
Este bloque nos recorrerá el array y volverá a recorrerlo de una forma clásica para
mostrar la información en pantalla actualizada. (java, 2019)
7. INTERFAZ GRÁFICA
8. SEGURIDAD ALGORITMOS DE CIFRADO
8.1 SHA-256
Las siglas SHA-256 hacen mención a la función hash que ha sido elegida para el
funcionamiento de muchas criptomonedas pues ofrece un alto nivel de seguridad, lo que la
hace perfecta para la tarea de proteger y codificar de forma segura la información de las
mismas.
Existen muchos sistemas para codificar la información y uno de ellos es el algoritmo
SHA-256. Este es un algoritmo de hash que es usado por Bitcoin para garantizar la
integridad de la información almacenada en un bloque, entre otras cosas.
Como casi todos los avances en materia de criptografía, los gobiernos del mundo han
tenido un papel fundamental debido a las guerras. El algoritmo SHA o Secure Hash
Algorithm (Algoritmo de Hash Seguro), es uno de estos avances. Este algoritmo
criptográfico fue desarrollado por la Agencia de Seguridad Nacional de los Estados Unidos
(NSA) y el National Institute of Standards and Technology (NIST). Su objetivo es generar
hashes o códigos únicos en base a un estándar con el que se pudieran asegurar documentos
o datos informáticos frente a cualquier agente externo que desee modificarlos. Este
algoritmo fue y es un gran avance en el camino a garantizar la privacidad del contenido en
el procesamiento de información.
En 1993 salió a la luz el primer protocolo SHA, también llamado coloquialmente SHA-
0. Dos años más tarde, se publicó una variante mejorada más resistente, el SHA-1. Algunos
años más tarde se lanzó SHA-2, que tiene cuatro variantes según el número de bits, como
son SHA-224, SHA-256, SHA-384 y SHA-512.
Características del algoritmo SHA-256
Un algoritmo hash funciona en una sola dirección: esto quiere decir que de cualquier
contenido podemos generar su hash (su “huella dáctilar digital”) pero de un hash no hay
forma de generar el contenido asociado a él, salvo probando al azar hasta dar con el
contenido.
Entre las diferentes formas de crear hashes, el algoritmo usado por SHA-256 es uno de
los más usados por su equilibrio entre seguridad y coste computacional de generación, pues
es un algoritmo muy eficiente para la alta resistencia de colisión que tiene.
Otra de las particularidades del algoritmo de hash SHA-256 es que la longitud del hash
resultante es siempre igual, no importa lo extenso que sea el contenido que uses para
generar el hash: ya sea de una letra o todas las palabras del libro de Harry Potter entero, el
resultado siempre es una cadena de 64 de letras y números (con una codificación de 256
bits, 32 bytes).
El objetivo del hash SHA-256 (y de toda función hash) es la de generar un resumen.
Para entender de forma simple y más detallada todo esto, no te pierdas el capítulo dedicado
a explicar las funciones hash.
En Bitcoin, el SHA-256 se utiliza para el proceso de minería (creación de bitcoins), pero
también en el proceso de generar direcciones bitcoin. Esto es así por el gran nivel de
seguridad que ofrece.
Dentro de la red blockchain todos los nodos tendrían una copia del hash de 64 caracteres
que representa la información que representa, por ejemplo, a todo un bloque. Una vez esa
información está validada por la red (o lo que es lo mismo, ya ha quedado registrada en la
cadena) cualquier manipulación de esa información intentando modificar algún carácter del
hash validado, sería detectada de forma inmediata y se descartaría.
Para aumentar la información técnica sobre esta función hash criptográfica, te
recomendamos descargar y leer el paper en español “Criptografía: Función SHA-256” de
Javier Domínguez Gómez, ingeniero de software español y hacktivista en Free Software
Foundation y Electronic Frontier Foundation.
Tomado de (academy.bit2me, 2021)
8.2 AES: Cifrado AES-256
Advanced Encryption Standard (AES) es uno de los algoritmos de cifrado más utilizados
y seguros actualmente disponibles. Es de acceso público, y es el cifrado que la NSA utiliza
para asegurar documentos con la clasificación "top secret". Su historia de éxito se inició en
1997, cuando el NIST (Instituto Nacional de Estándares y Tecnología) comenzó
oficialmente a buscar un sucesor al envejecimiento cifrado estándar DES. Un algoritmo
llamado "Rijndael", desarrollado por los criptografistas belgas Daemen y Rijmen,
sobresalía tanto en seguridad como en rendimiento y flexibilidad.
Apareció en la cima de varios competidores y se anunció oficialmente el nuevo estándar
de cifrado AES en 2001. El algoritmo se basa en varias sustituciones, permutaciones y
transformaciones lineales, cada una ejecutada en bloques de datos de 16 bytes - por lo tanto
el término blockcipher. Esas operaciones se repiten varias veces, llamadas "rondas".
Durante cada ronda, una clave circular única se calcula a partir de la clave de cifrado y se
incorpora en los cálculos. Basado en la estructura de bloques de AES, el cambio de un solo
bit, ya sea en la clave, o en el bloque de texto sin cifrado, da como resultado un bloque de
texto cifrado completamente diferente - una ventaja clara sobre los cifrados de flujo
tradicionales. La diferencia entre AES-128, AES-192 y AES-256 finalmente es la longitud
de la clave: 128, 192 o 256 bits - todas las mejoras drásticas en comparación con la clave de
56 bits de DES. A modo de ilustración: El agrietamiento de una clave AES de 128 bits con
un superordenador de última generación tomaría más tiempo que la presunta edad del
universo. Y Boxcryptor incluso utiliza claves de 256 bits. Hasta el día de hoy, no existe un
ataque factible contra AES. Por lo tanto, AES sigue siendo el estándar de cifrado preferido
para los gobiernos, bancos y sistemas de alta seguridad en todo el mundo.
9. PERSISTENCIA CON ARCHIVOS
10. MODELO VISTA CONTROLADOR
Modelo Vista Controlador (MVC) es un estilo de arquitectura de software que separa los
datos de una aplicación, la interfaz de usuario, y la lógica de control en tres componentes
distintos.
Se trata de un modelo muy maduro y que ha demostrado su validez a lo largo de los años
en todo tipo de aplicaciones, y sobre multitud de lenguajes y plataformas de desarrollo.
El Modelo que contiene una representación de los datos que maneja el sistema, su lógica
de negocio, y sus mecanismos de persistencia.
La Vista, o interfaz de usuario, que compone la información que se envía al cliente y los
mecanismos interacción con éste.
El Controlador, que actúa como intermediario entre el Modelo y la Vista, gestionando el
flujo de información entre ellos y las transformaciones para adaptar los datos a las
necesidades de cada uno.
El modelo es el responsable de:
Acceder a la capa de almacenamiento de datos. Lo ideal es que el modelo sea
independiente del sistema de almacenamiento.
Define las reglas de negocio (la funcionalidad del sistema). Un ejemplo de regla puede
ser: "Si la mercancía pedida no está en el almacén, consultar el tiempo de entrega estándar
del proveedor".
Lleva un registro de las vistas y controladores del sistema.
Si estamos ante un modelo activo, notificará a las vistas los cambios que en los datos
pueda producir un agente externo (por ejemplo, un fichero por lotes que actualiza los
datos, un temporizador que desencadena una inserción, etc.).
El controlador es responsable de:
Recibe los eventos de entrada (un clic, un cambio en un campo de texto, etc.).
Contiene reglas de gestión de eventos, del tipo "SI Evento Z, entonces Acción W". Estas
acciones pueden suponer peticiones al modelo o a las vistas. Una de estas peticiones a las
vistas puede ser una llamada al método "Actualizar()". Una petición al modelo puede ser
"Obtener_tiempo_de_entrega ( nueva_orden_de_venta )".
Las vistas son responsables de:
Recibir datos del modelo y los muestra al usuario.
Tienen un registro de su controlador asociado (normalmente porque además lo
instancia).
Pueden dar el servicio de "Actualización ()", para que sea invocado por el controlador o
por el modelo (cuando es un modelo activo que informa de los cambios en los datos
producidos por otros agentes).
Flujo MVC
El usuario interactúa con la interfaz de usuario de alguna forma (por ejemplo, el usuario
pulsa un botón, enlace, etc.)
El controlador recibe (por parte de los objetos de la interfaz-vista) la notificación de la
acción solicitada por el usuario. El controlador gestiona el evento que llega, frecuentemente
a través de un gestor de eventos (handler) o callback.
El controlador accede al modelo, actualizándolo, posiblemente modificándolo de forma
adecuada a la acción solicitada por el usuario (por ejemplo, el controlador actualiza el carro
de la compra del usuario). Los controladores complejos están a menudo estructurados
usando un patrón de comando que encapsula las acciones y simplifica su extensión.
El controlador delega a los objetos de la vista la tarea de desplegar la interfaz de usuario.
La vista obtiene sus datos del modelo para generar la interfaz apropiada para el usuario
donde se refleja los cambios en el modelo (por ejemplo, produce un listado del contenido
del carro de la compra). El modelo no debe tener conocimiento directo sobre la vista. Sin
embargo, se podría utilizar el patrón Observador para proveer cierta indirección entre el
modelo y la vista, permitiendo al modelo notificar a los interesados de cualquier cambio.
Un objeto vista puede registrarse con el modelo y esperar a los cambios, pero aun así el
modelo en sí mismo sigue sin saber nada de la vista. El controlador no pasa objetos de
dominio (el modelo) a la vista aunque puede dar la orden a la vista para que se actualice.
Nota: En algunas implementaciones la vista no tiene acceso directo al modelo, dejando que
el controlador envíe los datos del modelo a la vista.
La interfaz de usuario espera nuevas interacciones del usuario, comenzando el ciclo
nuevamente.