domingo, 13 de diciembre de 2020

Escritura en archivos

Manejo de archivos

La lectura y escritura de archivos es una de las funcionalidades que ha proveído Java desde sus inicios, siendo mejorada tras sus diversas versiones


Escribir en un archivo de texto

En el siguiente ejemplo se ingresará un texto por consola y luego se almacenará en un archivo txt llamado datos.txt que se creará en la ruta especificada. Cada vez que se ejecute el programa se reescribirá el valor del texto como si se creara una nuevo.


package test;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;

public class WriteTxt {

    public static void main(String[] args) {
    
        // Captura de datos
        Scanner s = new Scanner(System.in);
        System.out.println("Ingrese un texto y luego ENTER...");
        String texto = s.nextLine();
                
        try {
            //Creación del archivo
            FileWriter fw = new FileWriter("/media/jhon/Disco/Documentos/datos.txt");
            
            //Creación del buffer para escritura
            try (BufferedWriter bw = new BufferedWriter(fw)) {
                bw.write(texto);
            } catch (Exception e) {
                System.err.println("Error: " + e.getMessage());
            }
            
        } catch (IOException e) {
            System.err.println("Error: " + e.getMessage());
        }        

    }
}


Añadir texto

Si se desea agregar texto sin reemplazar el original, se debe indicar en el objeto de tipo FileWriter como segundo parámetro el valor lógico TRUE. Además, en el cuerpo del buffer de escritura se añade previo al texto un salto de línea.

package test;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;

public class WriteTxt {

    public static void main(String[] args) {
        // Captura de datos
        Scanner s = new Scanner(System.in);
        System.out.println("Ingrese un texto y luego ENTER...");
        String texto = s.nextLine();
                
        try {
            //Se indica TRUE para añadir texto en lugar de reemplazar
            FileWriter fw = new FileWriter("/media/jhon/Disco/Documentos/datos.txt",true);
            
            //Creación del buffer para escritura
            try (BufferedWriter bw = new BufferedWriter(fw)) {
                bw.write("\n"); //Se agrega un salto de línea
                bw.write(texto);
            } catch (Exception e) {
                System.err.println("Error: " + e.getMessage());
            }
            
        } catch (IOException e) {
            System.err.println("Error: " + e.getMessage());
        }        

    }
}

Cargar imagen en Java Swing

Para este ejemplo se emplea NetBeans, una imagen jpg y la biblioteca Java Swing. 

 

Estructura del proyecto

Se creo un JFrame que llamará a la imagen, se aprovechar esto para mostrar como llamar a la imagen desde una ubicación externa a la aplicación y desde el mismo JAR de la aplicación.


La estructura es la siguiente: La imagen cat.jgp es:

Llamada desde fuera del JAR

Las líneas de código relevante de todo el JFrame (líneas 10 a 15) son las que declaran el objeto de tipo ImageIcon desde la ruta absoluta o path seguido de un condicional que evalúa si el estado de carga de la imagen es COMPLETO y en cuyo caso procede a borrar el texto del JLabel y asignar la imagen dentro de este.


package images;

import javax.swing.ImageIcon;

public class ImagenView extends javax.swing.JFrame {

    public ImagenView() {
        initComponents();
        //Se carga la imagen en un ImageIcon
        ImageIcon icono = new ImageIcon("/NetBeansProjects/Blog/src/images/cat.jpg");
        if (icono.getImageLoadStatus() == java.awt.MediaTracker.COMPLETE) {
            lblImagen.setText("");
            lblImagen.setIcon(icono); // Se coloca en el JLabel
        }
        
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {
        lblImagen = new javax.swing.JLabel();
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Cargando imagen");
        lblImagen.setText("Sin imagen");
        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(lblImagen, javax.swing.GroupLayout.DEFAULT_SIZE, 
                226, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(lblImagen, javax.swing.GroupLayout.DEFAULT_SIZE, 
                226, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>                        

    public static void main(String args[]) {
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : 
            javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | InstantiationException | 
        IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(ImagenView.class.getName())
            	.log(java.util.logging.Level.SEVERE, null, ex);
        }

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new ImagenView().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JLabel lblImagen;
    // End of variables declaration                   
}

Llamada desde dentro del JAR

Se requiere de la clase java.net.URL para indicar la ubicación de la imagen y cargarla posteriormente


package images;

import java.net.URL;
import javax.swing.ImageIcon;

public class ImagenView extends javax.swing.JFrame {

    public ImagenView() {
        initComponents();
        
        //Se carga la imagen en un ImageIcon desde su URL
        URL url = getClass().getResource("cat.jpg");
        ImageIcon icono = new ImageIcon(url);
        
        if (icono.getImageLoadStatus() == java.awt.MediaTracker.COMPLETE) {
            lblImagen.setText("");
            lblImagen.setIcon(icono); // Se coloca en el JLabel
        }
        
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {
        lblImagen = new javax.swing.JLabel();
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Cargando imagen");
        lblImagen.setText("Sin imagen");
        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(lblImagen, javax.swing.GroupLayout.DEFAULT_SIZE, 
                226, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(lblImagen, javax.swing.GroupLayout.DEFAULT_SIZE, 
                226, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>                        

    public static void main(String args[]) {
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : 
            javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | InstantiationException | 
        IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(ImagenView.class.getName())
            	.log(java.util.logging.Level.SEVERE, null, ex);
        }

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new ImagenView().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JLabel lblImagen;
    // End of variables declaration                   
}

En ambos casos la salida en pantalla sería:

Lectura de archivos

Manejo de archivos

La lectura y escritura de archivos es una de las funcionalidades que ha proveído Java desde sus inicios, siendo mejorada tras sus diversas versiones.


Lectura de archivos de texto

Para llevar a cabo la lectura de un archivo de texto se pueden emplear diversos métodos de la clase java.io.*, pero se recomienda complementar esta con la clase java.nio.* considerada la clase de reemplazo de la anterior, que contiene entre sus componentes a Files y Paths permitiendo simplificar acciones como copiar un archivo en uno nuevo y brindando el soporte para llevar a cabo las acciones clásicas de la clase original.

Por ejemplo, supongamos un archivo txt con el siguiente contenido:

-A: Hola
-G: Hola, ¿cómo estas?
-A: Bien, y tú
-G: Igualmente
-A: Debo irme, hasta luego
-G: Hasta luego, saludos a doña Dana

Ahora una clase "main" de Java con el siguiente código que mostrará el contenido del archivo en consola empleando diversas formas:

package test;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;

public class Test {

    public static void main(String[] args) throws IOException {
        //Archivo indicando la ruta
        File archivo = new File("/media/ruta/data.txt");
        //Definición de caracteres UTF-8
        Charset cs = StandardCharsets.UTF_8;
        
        /*Lectura de datos*/
        //USANDO Files.readAllLines
        try {
            List<String> lines = Files.readAllLines(archivo.toPath(), cs);
            lines.forEach(l -> {
                System.out.println(l);
            });
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }
        
        System.out.println("====================================");
        
        //USANDO BufferedReader
        try (BufferedReader br = Files.newBufferedReader(archivo.toPath(), cs)) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }
        
        System.out.println("====================================");
        
        //USANDO InputStream
        try (InputStream is = Files.newInputStream(archivo.toPath())) {
            byte[] bytes = new byte[(int) archivo.length()];
            is.read(bytes);
            String content = new String(bytes, cs);
            System.out.println(content);
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }

    }
}

Mostrará el siguiente resultado:

-A: Hola
-G: Hola, ¿cómo estas?
-A: Bien, y tú
-G: Igualmente
-A: Debo irme, hasta luego
-G: Hasta luego, saludos a doña Dana
====================================
-A: Hola
-G: Hola, ¿cómo estas?
-A: Bien, y tú
-G: Igualmente
-A: Debo irme, hasta luego
-G: Hasta luego, saludos a doña Dana
====================================
-A: Hola
-G: Hola, ¿cómo estas?
-A: Bien, y tú
-G: Igualmente
-A: Debo irme, hasta luego
-G: Hasta luego, saludos a doña Dana


Lectura de archivos multimedia

Además, se pueden cargar archivos como:

Cada uno de estos tiene sus particularidades y diferentes formas de llamado dependiendo del entorno donde se desee mostrarlos, es diferente cargar una imagen en una aplicación WEB que en una aplicación de escritorio.

viernes, 27 de noviembre de 2020

Conectar SQLite con Eclipse

Programas o Software requerido

Para realizar la conexión necesitamos tener los siguientes programas:
  • Eclipse (Pueden elegir la versión que deseen así como la arquitectura de su Pc o Mac): http://www.eclipse.org/downloads/packages
  • El driver JDBC de SQLite (sqlite-jdbc-3.30.1).
  • Un programa que administre bases de datos de SQLite para crear las bases de datos y opcionalmente comprobar si los cambios realizados en el programa JAVA realizan cambios en la base de datos.

Paso 1 - La base de datos

Si ya posee la base de datos, solo basta conocer la dirección exacta de esta. Si no posee la base de datos, puede seguir el siguiente tutorial para generar una: https://inlearningcode.blogspot.com/2020/11/sqlite-tabla.html

Paso 2 - El proyecto en Eclipse

Ahora procedemos a Eclipse donde creamos un nuevo proyecto, colocamos el nombre del proyecto como queramos llamarlo. Luego continuamos y vamos a la pestaña Libraries (Bibliotecas) y dentro de ella agregamos una nueva (Add Library).

Despliega una nueva ventana y elegimos la opción Add External JARs y luego seleccionar el archivo JAR correspondiente, en mi caso el sqlite-jdbc-3.30.1.jar, y aceptar.

Dentro del proyecto se observa las bibliotecas del Jdk y el driver de conexión.

package util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class Conectabd {
	private final String URL = "jdbc:sqlite:D:\\bd\\blog.db";
    private final String DRIVER = "org.sqlite.JDBC";

    
    public Connection conexionDB() throws SQLException{
    Connection c = null;
        try {
            Class.forName(DRIVER).newInstance();
            c = DriverManager.getConnection(URL);
        } catch (ClassNotFoundException | 
                 IllegalAccessException | 
                 InstantiationException | 
                 SQLException e) {
            throw new SQLException(e.getMessage());
        }
    
    return c;
    }
    		
	}
	
}

La constante URL es una combinación del JDBC y la ubicación real de la base de datos SQLite que es un archivo en sí. Mientras el DRIVER es para este caso "org.sqlite.JDBC"

Al ser una base de datos de SQLite un archivo, no requiere de usuario ni de contraseña, pero mantiene la misma lógica para llevar a cabo la conexión.

Paso 3 - Probando la clase de conexión

Creamos la siguiente clase Test.java

package test;

import java.sql.Connection;

import util.Conectabd;

public class Test {

	public static void main(String[] args) {

		Conectabd conectabd = new Conectabd();
		try (Connection c = conectabd.getConexion()) {
			System.out.print("Conexión exitosa");
		} catch (Exception e) {
			System.err.print("Error: "+e.getMessage());
		}
	}

}

Al ser ejecutada la clase devolverá: "Conexión exitosa" o indicará si ocurrió una excepción. Generalmente suelen ser un error en la redacción de la URL o el driver.

miércoles, 25 de noviembre de 2020

Métodos de búsqueda

Búsqueda secuencial o lineal

Consiste en recorrer una estructura de datos comparando cada elemento con el valor buscado, si el elemento buscado se encuentra al inicio, el tiempo de búsqueda será muy corto, pero será cada vez más largo mientras el elemento se encuentre más cerca del final o no se encuentre.

Algoritmo de implementación

package busqueda;

public class Busca {

    public static Integer Secuencial(Integer[] data, int valor) {
        Integer pos = null;
        for (int i = 0; i < data.length; i++) {
            if (valor == data[i]) {
                pos = i;
                break;
            }
        }
        return pos;
    }
}

Aplicando el algoritmo a un Array

package test;

import busqueda.Busca;

public class TestBusqueda {

    public static void main(String[] args) {
        Integer[] datos = {2, 255, 10, 0, 11, 1172, 1};
        System.out.println("Array: " + Arrays.toString(datos));
        
        //Búsqueda de la posición de forma secuencial		
        Integer index = Busca.Secuencial(datos, 11); // Busca 11
        System.out.println("Buscando 11 ...");
        //Mostrar resultado
        if (index != null) {
            System.out.println("Elemento encontrado en la posición " + index);
        } else {
            System.out.println("Elemento no encontrado");
        }
		
        index = Busca.Secuencial(datos, 12); // Busca 12
        System.out.println("Buscando 12 ...");
        //Mostrar resultado
        if (index != null) {
            System.out.println("Elemento encontrado en la posición " + index);
        } else {
            System.out.println("Elemento no encontrado");
        }
    }
}

El resultado sería el siguiente:

Array: [2, 255, 10, 0, 11, 1172, 1]

Buscando 11 ...
Elemento encontrado en la posición 4

Buscando 12 ...
Elemento no encontrado


Búsqueda binaria

Consiste en recorrer una estructura de datos dividiéndola constantemente en dos partes hasta encontrar la coincidencia de búsqueda. Este método requiere que la estructura de datos este previamente ordenada de forma ascendente o descedente.

Algoritmo de implementación

package busqueda;

public class Busca {

    public static Integer Binario(Integer[] data, Integer valor) {
        int li = 0;
        int ls = data.length;
        while (li <= ls) {
            int lm = (ls - li) / 2 + li;
            if (data[lm] < valor) {
                li = lm + 1;
            } else if (data[lm] > valor) {
                ls = lm - 1;
            } else {
                return lm;
            }
        }
        return null;
    }
}

Aplicando el algoritmo a un Array

package test;

import busqueda.Busca;
import java.util.Arrays;

public class TestBusqueda {

    public static void main(String[] args) {
        Integer[] datos = {2, 255, 10, 0, 11, 1172, 1};
        System.out.println("Array: " + Arrays.toString(datos));
        
        //Búsqueda de la posición por búsqueda binaria
        Arrays.sort(datos); // Ordenamiento de datos
        System.out.println("Array ordenado: " + Arrays.toString(datos));
        
        Integer index = Busca.Binario(datos, 172); 
        System.out.println("Buscando 172 ..."); 
        //Mostrar resultado
        if (index != null) {
            System.out.println("Elemento encontrado en la posición " + index);
        } else {
            System.out.println("Elemento no encontrado");
        }
        
        index = Busca.Binario(datos, 255);
        System.out.println("Buscando 255 ...");
        //Mostrar resultado
        if (index != null) {
            System.out.println("Elemento encontrado en la posición " + index);
        } else {
            System.out.println("Elemento no encontrado");
        }
    }
}

El resultado sería el siguiente:

Array: [2, 255, 10, 0, 11, 1172, 1]
Array ordenado: [0, 1, 2, 10, 11, 255, 1172]

Buscando 172 ...
Elemento no encontrado

Buscando 255 ...
Elemento encontrado en la posición 5

miércoles, 18 de noviembre de 2020

Métodos de ordenamiento

Los problemas más comunes en la informática son la búsqueda y el ordenamiento. El proceso de ordenar consiste en recolocar los elementos de un array o colección ya sea de mayor a menor o viceversa con el fin de acelerar la búsqueda de la información.

En este aspecto existe una multitud de algoritmos de ordenamiento clasificados de diversas formas como la estabilidad, su complejidad, ubicación del proceso al ordenar, etc.

Entre los múltiples algoritmos tenemos:

  • Ordenamiento por burbuja
  • Ordenamiento por montículos
  • Ordenamiento por selección
  • Ordenamiento por inserción
  • Ordenamiento rápido (QuickSort)
  • Ordenamiento shell

Ordenamiento por burbuja

La clase con el siguiente código permite ordenar los elementos de un array unidimensional mediante este método.

package ordenamiento;

public class Ordena {
    
    //Ordenamiento de burbuja
    public static void burbuja(Integer[] data) {
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data.length - 1; j++) {
                if (data[j] > data[j + 1]) {
                    Integer temp = data[j];
                    data[j] = data[j + 1];
                    data[j + 1] = temp;
                }
            }
        }
    }
}

Para comprobar el funcionamiento se utiliza lo siguiente:

package test;

import ordenamiento.Ordena;

public class TestOrdenar {

    public static void main(String[] args) {
        Integer[] vals = {15, 60, 8, 16, 44, 27, 12, 35};
        Ordena.burbuja(vals);
        for (Integer val : vals) {
            System.out.println(val);
        }

    }
}

La salida del resultado sería:

8
12
15
16
27
35
44
60

Se comprueba el funcionamiento de este algoritmo.


Ordenamiento por selección

La clase con el siguiente código permite ordenar los elementos de un array unidimensional mediante este método.

package ordenamiento;

public class Ordena {
    
    //Ordenamiento por selección
    public static void seleccion(Integer[] data) {
        for (int i = 0; i < data.length; i++) {
            for (int j = i; j < data.length; j++) {
                if (data[i] > data[j]) {
                    Integer aux = data[j];
                    data[j] = data[i];
                    data[i] = aux;
                }
            }
        }
    }
}

Para comprobar el funcionamiento se utiliza lo siguiente:

package test;

import ordenamiento.Ordena;

public class TestOrdenar {

    public static void main(String[] args) {
        Integer[] vals = {15, 60, 8, 16, 44, 27, 12, 35};
        Ordena.seleccion(vals);
        for (Integer val : vals) {
            System.out.println(val);
        }

    }
}

La salida del resultado sería:

8
12
15
16
27
35
44
60

Se comprueba el funcionamiento de este algoritmo.


Ordenamiento por inserción

La clase con el siguiente código permite ordenar los elementos de un array unidimensional mediante este método.

package ordenamiento;

public class Ordena {
    
    //Ordenamiento por inserción
    public static void insercion(Integer[] data) {
        for (int i = 1; i < data.length; i++) {
            Integer aux = data[i];
            int j = i - 1;
            while (j >= 0 && data[j] > aux) {
                data[j + 1] = data[j];
                j--;
            }
            data[j + 1] = aux;
        }
    }
}

Para comprobar el funcionamiento se utiliza lo siguiente:

package test;

import ordenamiento.Ordena;

public class TestOrdenar {

    public static void main(String[] args) {
        Integer[] vals = {15, 60, 8, 16, 44, 27, 12, 35};
        Ordena.insercion(vals);        
        for (Integer val : vals) {
            System.out.println(val);
        }

    }
}

La salida del resultado sería:

8
12
15
16
27
35
44
60

Se comprueba el funcionamiento de este algoritmo.


Ordenamiento por montículos (HeapSort)

La clase con el siguiente código permite ordenar los elementos de un array unidimensional mediante este método.

package ordenamiento;

public class Ordena {
    
    //Ordenamiento por montículos
    public static void heapsort(Integer[] data) {
        data = monticulo(data, data.length);
        for (int i = 0; i < data.length; i++) {
            data = eliminarRaiz(data, data.length - i);
            data = monticulo(data, data.length - i - 1);
        }
    }
    
    //Función para construir montículo
    private static Integer[] monticulo(Integer[] data, int n) {
        for (int i = 0; i < n; i++) {
            if (padre(i) != -1) {
                int p = data[padre(i)];
                if (data[i] > p) {
                    data[padre(i)] = data[i];
                    data[i] = p;
                    monticulo(data, n);
                }
            }
        }
        return data;
    }        
    
    //Función para determinar padres
    private static int padre(int index) {
        return (index == 0) ? -1 : (index - 1) / 2;
    }

    //Función para eliminar las raices
    private static Integer[] eliminarRaiz(Integer[] data, int n) {
        Integer aux;
        aux = data[n - 1];
        data[n - 1] = data[0];
        data[0] = aux;
        return data;
    }
}

Para comprobar el funcionamiento se utiliza lo siguiente:

package test;

import ordenamiento.Ordena;

public class TestOrdenar {

    public static void main(String[] args) {
        Integer[] vals = {15, 60, 8, 16, 44, 27, 12, 35};
        Ordena.heapsort(vals);
        for (Integer val : vals) {
            System.out.println(val);
        }

    }
}

La salida del resultado sería:

8
12
15
16
27
35
44
60

Se comprueba el funcionamiento de este algoritmo.

lunes, 9 de noviembre de 2020

Pilas (Stacks)

 Desde el punto de vista teórico una pila es un tipo de dato abstracto (TDA) que sigue el principio LIFO (last in, first out) que implica que el último elemento en ser agregado en la pila es también el primero en ser removido de la misma.

En la imagen se observa una pila con tres elementos, cuando se inserta uno nuevo, este se coloca al final de la pila (elemento D).
Cuando se elimina un elemento, se remueve el último en haber ingresado, en caso de la pila es el elemento D.

En el mundo real podemos encontrar este ejemplo al apilar platos en un punto, cuando queremos mover los platos uno por uno se comienza retirando el último plato colocado.

Stacks en Java

Se puede crear una pila de forma sencilla con la clase Stack que hereda de la clase Vector.

El siguiente ejemplo ilustra como construir una pila, agregar elementos y luego irlos retirando. Para ello, se crea una colección llamada pila de tipo Stack que almacena valores enteros.

package test;

import java.util.Stack;

public class Pilas {

    public static void main(String[] args) {
        Stack<Integer> pila = new Stack<>();

        System.out.println("Agregando valores");
        for (int i = 0; i < 5; i++) {
            pila.push(i);
            System.out.println("Valor: " + i);
        }
        
        System.out.println("\nRetirando valores");
        while (!pila.isEmpty()) {
            System.out.println("Valor: " + pila.pop());            
        }
    }
}

La salida del resultado sería:

Agregando valores
Valor: 0
Valor: 1
Valor: 2
Valor: 3
Valor: 4

Retirando valores
Valor: 4
Valor: 3
Valor: 2
Valor: 1
Valor: 0

El último elemento en ser agregado es "4" y al retirar a los elementos, el primero en ser removido también es el "4" comprobando el principio LIFO.

Creando una clase Colas


La siguiente clase emula de forma simple el funcionamiento de una cola, para ello utiliza un array de tipo Object

package tda;

public class Pilas {

    private Object[] data;
    private final int length;
    private int pointer;

    public Pilas(int length) {
        this.length = length;
        this.data = new Object[length];
        this.pointer = -1;
    }

    public void add(Object element) {
        this.data[pointer+1] = element;
        pointer = (pointer < length - 1) ? pointer + 1 : pointer;
    }

    public Object[] getData() {
        return this.data;
    }

    public int getLength() {
        return length;
    }

    public Object pop() {
        Object value = this.data[pointer];
        this.data[pointer] = null;
        pointer--;
        return value;
    }
}

Para comprobar el funcionamiento se utiliza lo siguiente:

package test;

import java.util.Arrays;
import tda.Pilas;

public class TestPilas {

    public static void main(String[] args) {
        Pilas pilaInt = new Pilas(4);
        pilaInt.add(0);
        pilaInt.add(1);
        pilaInt.add(2);
        pilaInt.add(3);
        for (int i = 0; i < 4; i++) {
            System.out.println("Dato ingresado: "+pilaInt.getData()[i]);
        }
        System.out.println("=================================");
        System.out.println("Sale: "+pilaInt.pop());
        System.out.println("Sale: "+pilaInt.pop());
        System.out.println("Sale: "+pilaInt.pop());
        pilaInt.add(5);
        System.out.println("=================================");
        System.out.println(Arrays.toString(pilaInt.getData()));
    }
}

La salida del resultado sería:

Dato ingresado: 0
Dato ingresado: 1
Dato ingresado: 2
Dato ingresado: 3
=================================
Sale: 3
Sale: 2
Sale: 1
=================================
[0, 5, null, null]

Se comprueba el principio LIFO al insertar al final el 3, el primero en ser retirado es el mismo 3

Colas (Queues)

 Desde el punto de vista teórico una cola es un tipo de dato abstracto (TDA) que sigue el principio FIFO (first in, first out) que implica que el primer elemento en ser agregado en la cola es también el primero en ser removido de la misma.

En la imagen se observa una cola con tres elementos, cuando se inserta uno nuevo, este se coloca al final de la cola (elemento D).
Cuando se elimina un elemento, se remueve el primero en haber ingresado, en caso de la cola es el elemento A.

En el mundo real podemos encontrar este ejemplo en las colas de un banco, la cadena de impresión de documentos, etc.

En el caso de la cola en el banco, la primera persona en llegar es también la primera en irse (suponiendo una única ventanilla) y en los documentos a imprimir, la impresora imprime según el orden de llegada.

Queues en Java

Existen diversas colecciones que permite implementar de forma rápida y sencilla a las colas como la interfaz "Queue" o la clase "LinkedList"

El siguiente ejemplo ilustra como construir una cola, agregar elementos y luego irlos retirando. Para ello, se crea una colección llamada cola de tipo Queue con una implementación de lista enlazada (LinkedList) que almacena valores enteros.

package test;

import java.util.LinkedList;
import java.util.Queue;

public class Colas {

    public static void main(String[] args) {
        Queue<Integer> cola = new LinkedList<>();

        System.out.println("Agregando valores");
        for (int i = 0; i < 5; i++) {
            cola.add(i);
            System.out.println("Valor: " + i);
        }
        
        System.out.println("\nRetirando valores");
        while (cola.peek()!=null) {
            System.out.println("Valor: " + cola.poll());            
        }
    }
}

La salida del resultado sería:

Agregando valores
Valor: 0
Valor: 1
Valor: 2
Valor: 3
Valor: 4

Retirando valores
Valor: 0
Valor: 1
Valor: 2
Valor: 3
Valor: 4

El primer elemento en ser agregado es "0" y al retirar a los elementos, el primero en ser removido también es el "0" comprobando el principio FIFO.

Creando una clase Colas


La siguiente clase emula de forma simple el funcionamiento de una cola, para ello utiliza un array de tipo Object

package tda;

public class Colas {

    private Object[] data;
    private final int length;
    private int pointer;

    public Colas(int length) {
        this.length = length;
        this.data = new Object[length];
        this.pointer = -1;
    }

    public void add(Object element) {
        this.data[pointer+1] = element;
        pointer = (pointer < length - 1) ? pointer + 1 : pointer;
    }

    public Object[] getData() {
        return this.data;
    }

    public int getLength() {
        return length;
    }

    public Object pop() {
        Object value = this.data[0];
        for (int i = 0; i < data.length-1; i++) {
            data[i] = data[i+1];            
        }
        this.data[pointer] = null;
        pointer--;
        return value;
    }

}

Para comprobar el funcionamiento se utiliza lo siguiente:

package test;

import java.util.Arrays;
import tda.Colas;

public class TestColas {

    public static void main(String[] args) {
        Colas colaInt = new Colas(4);
        colaInt.add(0);
        colaInt.add(1);
        colaInt.add(2);
        colaInt.add(3);
        for (int i = 0; i < 4; i++) {
            System.out.println("Dato ingresado: "+colaInt.getData()[i]);
        }
        System.out.println("=================================");
        System.out.println("Sale: "+colaInt.pop());
        System.out.println("Sale: "+colaInt.pop());        
        System.out.println("Sale: "+colaInt.pop());
        System.out.println("=================================");
        colaInt.add(5); // Agregamos el 5
        System.out.println(Arrays.toString(colaInt.getData()));      
    }

}

La salida del resultado sería:

Dato ingresado: 0
Dato ingresado: 1
Dato ingresado: 2
Dato ingresado: 3
=================================
Sale: 0
Sale: 1
Sale: 2
=================================
[3, 5, null, null]

Se comprueba el principio FIFO al insertar primero al 0, el primero en ser retirado es el mismo 0

lunes, 26 de octubre de 2020

Componentes básicos de Java Swing

Los componentes básicos que provee la biblioteca Java Swing son

  • JButton
  • JCheckBox
  • JLabel
  • JTextArea
  • JTextField
  • JPasswordField
  • JRadioButton
  • JOptionPane

Además, el contenedor primario es el JFrame que es una subclase de java.awt.Frame. Sin embargo, a diferencia de su superclase esta puede ser cerrada u ocultada. Dentro de este contenedor particular se ubican los componentes como botones, etiquetas, cajas de texto, etc.

JFrame

El siguiente código crea un JFrame sin emplear ningún editor gráfico

package view;

import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class JFrameExample {

    public static void main(String[] args) {
        JFrame ventana = new JFrame("JFrame Demo");
        JPanel panel = new JPanel();
        JLabel label = new JLabel("JLabel");
        JButton button = new JButton();
        button.setText("JButton");

        GroupLayout groupLayout = new GroupLayout(panel);
        groupLayout.setHorizontalGroup(
                groupLayout.createParallelGroup(Alignment.LEADING)
                        .addGroup(groupLayout.createSequentialGroup()
                                .addGap(100)
                                .addGroup(groupLayout.createParallelGroup()
                                        .addComponent(label)
                                        .addComponent(button))
                                .addContainerGap(300, Short.MAX_VALUE))
        );
        groupLayout.setVerticalGroup(
                groupLayout.createParallelGroup(Alignment.LEADING)
                        .addGroup(groupLayout.createSequentialGroup()
                                .addGap(40)
                                .addComponent(label)
                                .addGap(20)
                                .addComponent(button)
                                .addContainerGap(200, Short.MAX_VALUE))
        );
        panel.setLayout(groupLayout);

        ventana.add(panel);
        ventana.setSize(600, 400);
        ventana.setLocationRelativeTo(null);
        ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ventana.setVisible(true);
    }
}

Lo que se requiere es un método "main", luego crear un JFrame en este caso llamado "ventana", es el contenedor principal, luego un JPanel dentro del cual se colocarán los JLabel y JButton. En ese momento aparece un código un poco engorroso de métodos que llaman otros métodos dentro y así sucesivamente. Ese código crea un objeto de tipo GroupLayout y lo inicializa con el parámetro JPanel, luego indica la disposición de los componentes dentro del contenedor JPanel de forma horizontal y vertical creando grupos secuenciales (un componente sigue a otro de derecha a izquierda o de arriba hacia abajo). Dentro de cada uno de ellos se indica como parámetro la alineación y se llaman otros métodos que pueden crear otros grupos como los paralelos o directamente agregar un componente con el método "addComponent". También, se encuentran los métodos "addGap" que indican distancia entre los elementos. Finalmente, se indica el "layout" o disposición al JPanel y se procede a agregar el JPanel al JFrame seguido del tamaño de la ventana, ubicación por defecto en la pantalla del monitos, acción al presionar el botón cerrar y haciéndolo visible.

Etiquetas (JLabel)

Este componente puede ser usado para mostrar texto plano, una imagen o una imagen con un texto. También, puede ser usado para mostrar los resultados de un proceso. Los métodos más empleados son:

  • setText() - Permite colocar un valor textual, puede ser empleado para mostrar un resultado en este componente.
  • setFont() - Permite definir tanto el tipo de fuente como estilo (negrita y/o cursiva) y tamaño de la letra.
  • setIcon() - Permite emplear un ícono o imagen y ser mostrado en pantalla.

Cajas de texto (JTextArea, JTextField y JPasswordField)

Estos componentes son empleados para que el usuario ingrese un valor textual y capturarlo. Los JTextField permiten ingresar una línea de texto, los JPasswordField ingresar un valor pero mostrarlo camuflado y los JTextArea permiten ingresar múltiples líneas de texto. Los métodos más empleados son:

  • setText() - Permite colocar un valor textual, puede ser empleado para mostrar un resultado en este componente.
  • getText() - Permite recuperar el valor que tiene la caja de texto.
  • setFont() - Permite definir tanto el tipo de fuente como estilo (negrita y/o cursiva) y tamaño de la letra.
  • setEnabled() - Permite deshabilitar la escritura, pudiendo usarse para mostrar un resultado al igual que en un JLabel

Botones (JButton, JCheckBox y JRadioButton)

Estos componentes permiten diversas formas gráficas con las que el usuario puede interactuar, los JRadioButton permiten elegir una opción de un grupo de estas, los JCheckBox permiten seleccionar más de una opción de un grupo de estas. Finalmente los JButton se emplean principalmente para ejecutar un evento al presionarlo.

Listas desplegables (JComboBox)

Similar al JRadioButton, permite seleccionar una opción de un grupo de estas, pero sin ocupar espacio en la ventana.

Cuadros de diálogo (JOptionPane)

Permite mostrar un cuadro con un mensaje, un ícono y botones. Un código de ejemplo sería:

JOptionPane.showMessageDialog(null,
     "Mensaje", "Título",
     JOptionPane.WARNING_MESSAGE);

El segundo y tercer parámetro indican el mensaje y el título, estos cuadros se pueden emplear para mostrar mensajes de confirmación, error, advertencia u otros.

Java Swing

Dentro de la forma de interactuar con el computador, se inició este proceso mediante las interfaces de línea de comandos, que tenían sus limitaciones como la de ejecutar una tarea a la vez, mostrando cierta discrepancia con la forma de trabajo del ser humano. Como respuesta aparecen las interfaces gráficas de usuario (GUI) con las llamadas WIMP(clic para conocer más) que implementan el uso de ventanas, menús, punteros e íconos.

En el caso de Java se debe emplear una biblioteca gráfica para el uso de las GUI como:

  • AWT, emplea los componentes gráficos del sistema operativo.
  • SWING, se abstrae del sistema operativo y contiene su propia colección de elementos gráficos.

La biblioteca Swing es parte de Java Foundation Classes (JFC), que es una colección de componentes gráficos para la construcción de aplicaciones de escritorio.

La estructura de esta biblioteca gira en torno a componentes y contenedores. Los contenedores son componentes y al mismo tiempo contienen a otros componentes, esto permite que los eventos pueden ser aplicados en contenedores y/o componentes.

Dependiendo del IDE empleado las paletas para el desarrollo gráfico pueden variar ligeramente y a su vez el código autogenerado.

Paleta de componentes en NetBeans


Paleta de componentes en Eclipse


En ambos se puede apreciar que componentes como los JPanel o Panel, JLabel o Label, JButton o Button y muchos más se repiten con diferente nombre, pero al ser visualizados como código se convierten en objetos cuya clase es derivada del JComponent. Es importante mencionar que los diversos IDE contienen herramientas que permiten construir las interfaces gráficamente y generan todo el código conforme se agregan elementos o realizan cambios. Cada IDE tiene una forma particular de crear el código según el "plug-in" que emplee, por ejemplo en NetBeans no encontrará ningún "import" mientras en Eclipse con su complemento WindowsBuilder se utilizarán todo el tiempo, en NetBeans existen partes del código que no se pueden modificar directamente sino solo a través de la pestaña de diseño, en Eclipse todo el código se puede modificar. Además, las paletas de diseño también tendrán diferente diseño, pero la ejecución del código será la misma.

Arrays

Un array en java es un objeto que contiene de cero a más elementos de un mismo tipo, los elementos que contiene son almacenados en el array y se puede acceder a ellos mediante su numeración que comienza con el número 0.



Arrays unidimensionales y bidimensionales

Los arrays representan matrices, en el caso de los de una dimensión a aquellas matrices tipo columna o fila y los bidimensionales a cualquier matriz de "n" filas por "m" columnas.

En el lenguaje Java estas se pueden definir de la siguiente forma:

        
        //Arrays unidimensionales
        int[] vector; // vector[] es un array de valores tipo int
        
        //Arrays bidimensionales
        int[][] matriz; // matriz[][] es un array de valores tipo int[]
         

Como se aprecia, las matrices son arrays de arrays y para declarar cualquier array se debe colocar primero el tipo de dato que almacenarán sea primitivo o tipo objeto, luego dependiendo de la cantidad de dimensiones los corchetes [ ].



Operaciones con arrays

Para crear un array, se debe inicializar el mismo indicando la cantidad de elementos que contendrá o indicando los elementos que va contener de cualquiera de las siguientes maneras

        
        // Sin valores asignados
        byte[] notas = new byte[5]; 
        //Con valores
        String[] cursos = {"Física", "Cálculo", "Filosofía"};
         

Para ir asignando valores dentro del array se puede emplear estructuras repetitivas o manualmente indicando el nombre del array y entre corchetes la posición (conocida como index). Esta posición siempre comienza con cero y su máximo valor es el tamaño del array menos uno.

        
        //Asignación de valores
        notas[0]=20;
        notas[1]=12;
        notas[2]=10;
        notas[3]=15;
        notas[4]=6;
         

Para modificar un valor se realiza el mismo proceso que al asignar valores con el nuevo contenido.

        
        //Modificar un valor
        cursos[1] = "Matemática"; // Cambia 'Cálculo' por 'Matemática'
         

Para poder recorrer un array se pueden emplear estructuras repetitivas "for" con variables de tipo contador o sin estas conocidos en otros lenguajes como "foreach"

        
        //Imprimiendo en consola
        System.out.println("Contenido de notas[]");
        for (int i = 0; i < notas.length; i++) {
            System.out.println(notas[i]);
        }
        
        System.out.println("\nContenido de Cursos[]");
        for (String curso : cursos) {
            System.out.println(curso);
        }
         

Todos los arrays poseen un campo llamado "length" que devuelve el tamaño de este y puede ser empleado para diversas tareas como el límite del contador en una estructura "for", en el segundo caso al recorrer el array se emplea una variable del mismo tipo que el array con un identificador diferente al array, luego dos puntos y el nombre del array, ya no es necesario indicar dentro la posición como sí en el caso del array recorrido mediante una variable de tipo contador.

El programa completo sería:

package test;

public class Arrays {

    public static void main(String[] args) {
        //Creación e inicialización        
        // Sin valores asignados
        byte[] notas = new byte[5]; 
        //Con valores
        String[] cursos = {"Física", "Cálculo", "Filosofía"};
        
        //Asignación de valores
        notas[0]=20;
        notas[1]=12;
        notas[2]=10;
        notas[3]=15;
        notas[4]=6;
        
        //Modificar un valor
        cursos[1] = "Matemática"; // Cambia 'Cálculo' por 'Matemática'
        
        //Imprimiendo en consola
        System.out.println("Contenido de notas[]");
        for (int i = 0; i < notas.length; i++) {
            System.out.println(notas[i]);
        }
        
        System.out.println("\nContenido de cursos[]");
        for (String curso : cursos) {
            System.out.println(curso);
        }
    }
}

Se obtiene un resultado como el siguiente:

Contenido de notas[]
20
12
10
15
6

Contenido de cursos[]
Física
Matemática
Filosofía

martes, 20 de octubre de 2020

Estructuras repetitivas

Las estructuras repetitivas permiten ejecutar bloques de sentencias múltiples veces, aun si no conocemos el número de veces que debe repetirse el código. Se puede usar para ello una determinada condición, de manera que el bloque se detendrá cuando no se cumpla la condición.

Ejemplo práctico: al ingresar productos en una factura, el proceso se repite una y otra vez.

Estructura FOR

Se emplea cuando se conoce la cantidad de repeticiones o al recorrer un arreglo o vector.

for ( [tipo numérico] i = [valor inicial]; i <= [valor máximo]; [aumento o decremento] ) {
   //Sentencias que se van a repetir: "(valor máximo - valor inicial)" veces
}


for ( [tipo igual al arreglo] i in [variable tipo arreglo] ) {
   //Sentencias que se van a repetir tantas veces como elementos tenga
   //el arreglo
} 


Estructura WHILE y DO WHILE

Se emplea cuando el número de repeticiones no es conocido, pues depende de una condición. El punto a analizar es si se debe o no ejecutar la iteración por lo menos una vez.

while ( condición ) {
   //Sentencias que se van a repetir mientras condición sea verdadera
}

do {
   //Sentencias que se van a repetir por lo menos una vez y mientras condición sea verdadera
}while ( condición ); 


Ejemplo en código

El siguiente programa muestra todos los números pares entre dos valores ingresados por el usuario, validando que sean positivos y el segundo valor sea mayor que el primero

package test;

import java.util.Scanner;

public class Repetitivos {

    public static void main(String[] args) {
        Scanner scanner;
        int n1, n2;
        scanner = new Scanner(System.in);
        
        do {
            System.out.print("Ingrese un número positivo: ");
            n1 = scanner.nextInt();
        } while (n1 <= 0);

        do {
            System.out.print("Ingrese un número mayor que " + n1 + ": ");
            n2 = scanner.nextInt();
        } while (n2 <= n1);

        for (int i = n1; i <= n2; i++) {
            if (i % 2 == 0) {
                System.out.print(i + " ");
            }
        }
    }
}

Estructuras condicionales

Las estructuras condicionales permiten ejecutar bloques de sentencias de código para una determinada condición, si esta no se cumple se puede optar por no realizar nada o llevar a cabo otro bloque de sentencias, de manera que no pueden ejecutarse ambos bloques, solo uno de ellos.

Ejemplo práctico: al ingresar nuestra contraseña de una red social, si es incorrecta no podemos ingresar, pero si es correcta ingresamos a nuestra cuenta.


Condicional IF ELSE Condicional SWITCH CASE
if (condicion1) {
   //Sentencias si 
   //Condicion1 es verdad
}else if (condicion2) {
   //Sentencias si 
   //Condicion2 es verdad
}else {
   //Sentencias si condicion1 y
   //condicion2 no son verdad
}
switch (expresión) {
case x :
   //Sentencias si expresión es "x"
   break;
case y :
   //Sentencias si expresión es "y"
   break;
default:
   //Sentencias para otros casos
}


Operador ternario

Cuando tenemos una estructura condicional simple no anidada y sin "else if", lo más óptimo es utilizar un operador ternario, hay que tener en cuenta que no debe ser usado a diestra y siniestra, solo en casos donde exista un retorno de valor.

Condicional if-else Condicional con operador ternario
/*Codicional clásico*/
if (condicion) {
   //Sentencias si 
   //Condicion es verdad
}else {
   //Sentencias si condicion
   //no son verdad
}
/*Codicional clásico*/
[variable u otro] (condicion) ?
   //Sentencias si 
   //Condicion es verdad
:
   //Sentencias si condicion
   //no son verdad 
;  // cerrar la sentencia al final


Código de ejemplo

El siguiente código solicita la nota de un estudiante y de acuerdo a esta muestra un mensaje. Primero valida que la nota este en el rango de 0 a 20, luego si es 10 o menor muestra el mensaje "Desaprobado", si es 15 o menor muestra "Requiere recuperación" y en los demás muestra "Aprobado"

package test;

import java.util.Scanner;

public class Condicional {

    public static void main(String[] args) {
        Scanner scanner;
        byte nota;

        System.out.print("Ingrese la nota del estudiante: ");
        scanner = new Scanner(System.in);
        nota = scanner.nextByte();
        if (nota < 0 || nota > 20) {
            System.out.println("Nota inválida");
        } else if (nota <= 10) {
            System.out.println("Desaprobado");
        } else if (nota <= 15) {
            System.out.println("Requiere recuperación");
        } else {
            System.out.println("Aprobado");
        }
    }
}

El siguiente código solicita un número entre 1 y 7 devolviendo el día correspondiente iniciando la semana en el lunes

package test;

import java.util.Scanner;

public class Tipos {

    public static void main(String[] args) {
        Scanner scanner;
        byte dia;

        System.out.print("Ingrese un número del 1 al 7: ");
        scanner = new Scanner(System.in);
        dia = scanner.nextByte();
        switch (dia){
            case 1: System.out.println("LUNES");break;
            case 2: System.out.println("MARTES");break;
            case 3: System.out.println("MIERCOLES");break;
            case 4: System.out.println("JUEVES");break;
            case 5: System.out.println("VIERNES");break;
            case 6: System.out.println("SABADO");break;
            case 7: System.out.println("DOMINGO");break;
            default: System.out.println("Valor inválido");break;
        }    
    }
}

Tipos de datos

Datos primitivos

Java admite ocho tipos de datos básicos conocidos como tipos primitivos. Además, admite clases y matrices como tipos de datos compuestos o tipos de referencia.

Los tipos primitivos son:

  • boolean
  • byte
  • short
  • int
  • float
  • double
  • long
  • char

En la siguiente tabla se aprecia las características de estos

Tipo Contenido Rango Tamaño
byte Valor numérico entero -128 a 127 8 bits
short Valor numérico entero –32768 a 32767 16 bits
int Valor numérico entero –2147483648 a 2147483647 32 bits
long Valor numérico entero –9223372036854775808 a 9223372036854775807 64 bits
float Valor numérico con decimales ±1.4E–45 a ±3.4028235E+38 32 bits
double Valor numérico con decimales ±4.9E–324 a ±1.7976931348623157E+308 64 bits
boolean Valor lógico "false" y "true" 1 bit
char Caracter UNICODE \u0000 - \uFFFF 16 bits

Podemos ver en el siguiente código como crear variables de estos tipos y asignarles un contenido.

package test;

public class Tipos {

    public static void main(String[] args) {
        /*DECLARANDO VARIABLES*/

        //Variable lógica
        boolean log;
        
        //Variables enteras
        byte ent1;
        short ent2;
        int ent3;
        long ent4;
        
        //Variables con decimal
        float dec1;
        double dec2;
        
        //Variable caracter
        char caracter;
        
        /*ASIGNANDO VALORES*/
        log = true;
        ent1 = -100;
        ent2 = 4000;
        ent3 = -120000;
        ent4 = 490000000;
        dec1 = 1e7f;
        dec2 = 3.14d;
        caracter = '\u0026';
        
        /*MOSTRANDO EL CONTENIDO*/
        System.out.print("log = ");
        System.out.println(log);
        System.out.print("ent1 = ");
        System.out.println(ent1);
        System.out.print("ent2 = ");
        System.out.println(ent2);
        System.out.print("ent3 = ");
        System.out.println(ent3);
        System.out.print("ent4 = ");
        System.out.println(ent4);
        System.out.print("dec1 = ");
        System.out.println(dec1);
        System.out.print("dec2 = ");
        System.out.println(dec2);
        System.out.print("caracter = ");
        System.out.println(caracter);
    }    
}

Podemos ver en el siguiente código como crear variables de estos tipos y asignarles un contenido.

log = true
ent1 = -100
ent2 = 4000
ent3 = -120000
ent4 = 490000000
dec1 = 1.0E7
dec2 = 3.14
caracter = &

miércoles, 8 de julio de 2020

CRUD con RESTful API y JSON

Las aplicaciones de diversos sistemas en diversos casos no pueden acceder directamente a servidores de bases de datos por diversos motivos como las aplicaciones de Android, estas pueden tener sus bases de datos locales como SQLite, pero si desean interactuar con una base de datos externa como un MySQL requieren un puente que les permita ducha conexión. En este entorno aparecen los Servicios Web, que tienen muchas más aplicaciones y funcionalidades, que permitirán a las aplicaciones acceder a esas bases de datos y realizar operaciones CRUD.

Para crear un WebService se siguen los pasos comunes en su construcción (Clic aquí para ver como crear un WebService desde cero). El siguiente paso es crear la conexión a la base de datos.

Para poder ejecutar las operaciones de CRUD mediante un Servicio Web REST con java debemos definir una conexión mediante una clase Java (clic aquí para ver la conexión de Java a MySQL) o mediante JPA


Código

NOTA: En este ejemplo no se usará la unidad de persistencia (persistence.xml).

Teniendo como base a la tabla "Inquilinos"(clic aquí para ver el DTO) a la que le realizaremos un mantenimiento (CRUD) teniendo como DAO a "DaoInquilinos" y su correspondiente implementación (clic aquí para ver el DAO).Además, las pruebas a las funciones declaradas en el DAO (clic aquí para ver el DAO).

Se creará un punto de partida para el WebService llamado RestApi que tendrá el siguiente código

package apiRest;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/service")
public class RestApi extends Application {

}

Luego debemos de crear las funciones REST que se emplearán para las diversas funcionalidades del CRUD, como vamos a emplear JSON se puede optar por usar alguna biblioteca para manipular este formato de intercambio, pero se puede realizar este mismo procedimiento sin agregar ninguna bilbioteca adicional a Jersey y JAX-RS 2.0. Tendríamos una clase llamada JSONRest

package apiRest.services;

import javax.ws.rs.Consumes;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/inquilino")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class JSONRest {
    /* Dentro se colocan los métodos 
    	-GET
        -POST
        -PUT
        -DELETE
    */    
}

SELECT

Tendremos dos casos, cuando recibimos todos los registros(inquilinosSEL) y cuando se recibe solo uno de los registros(inquilinosGET)

La clase JSONRest contiene el siguiente código

@GET
@Path("/{id}")
public Response inquilinosGET(@PathParam("id") int id) {
    DaoInquilinos dao = new DaoInquilinosImpl();
    Inquilinos inquilino=  dao.inquilinosGet(id);
    if (inquilino == null) {
        String msg = "Inquilino no encontrado con id: " + id;
        return Response.ok(msg, MediaType.TEXT_PLAIN).build();
    } else {
        return Response.ok(inquilino).build();
    }
}

@GET
public Response inquilinosSEL() {
    DaoInquilinos dao = new DaoInquilinosImpl();
    List<Inquilinos> lista = dao.inquilinosSel();
    if (lista == null) {
        String msg = "Sin usuarios";
        return Response.ok(msg, MediaType.TEXT_PLAIN).build();
    } else {
        GenericEntity<List<Inquilinos>> entity
                = new GenericEntity<List<Inquilinos>>(lista) {
        };
        return Response.ok(entity).build();
    }
}

Probando desde el navegador

  • http://localhost:8082/WebService/service/inquilinos/1
  • http://localhost:8082/WebService/service/inquilinos/

Nota: la dirección (localhost) y el puerto (8082) variarán dependiendo de la ubicación y puerto empleado por el servidor de Java EE.

En el caso de la función "inquilinoGET" que requiere un valor para hacer la búsqueda. Otra forma es usar la anotación que es @QueryParam en lugar de @PathParam y esa anotación a su vez tiene como parámetro el nombre que tendrá desde la petición, similar a:
http://localhost:8082/WebService/service/inquilino/?id=1

INSERT

Se emplea el método POST, indicado mediante una anotación, para enviar la información a insertar, en nuestro caso es la BD la que genera automáticamente el código

La clase JSONRest contiene el siguiente código

@POST
@Produces(MediaType.TEXT_PLAIN)
public Response inquilinosINS(Inquilinos inquilino) {
    DaoInquilinos dao = new DaoInquilinosImpl();
    String msg = dao.inquilinosIns(inquilino);
    return Response.ok(msg).build();
}

UPDATE

Se emplea el método PUT, indicado mediante una anotación, para enviar la información que se desea modificar.

La clase JSONRest contiene el siguiente código

@PUT
@Produces(MediaType.TEXT_PLAIN)
public Response inquilinosUPD(Inquilinos inquilino) {
    DaoInquilinos dao = new DaoInquilinosImpl();
    String msg = dao.inquilinosUpd(inquilino);
    return Response.ok(msg).build();
}

DELETE

Urilizando la anotación DELETE se envía los identificadores de los registros a eliminar separados por comas. También se puede relizar eliminaciones individuales

La clase JSONRest contiene el siguiente código

@DELETE
@Path("/{ids}")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public Response inquilinosDEL(@PathParam("ids") String ids) {
    List<Integer> list = Arrays.stream(ids.split(","))
            .map(Integer::parseInt)
            .collect(Collectors.toList());
    DaoInquilinos dao = new DaoInquilinosImpl();
    String msg = dao.inquilinosDel(list);
    return Response.ok(msg).build();
}

Código completo

package apiRest.services;

import dao.DaoInquilinos;
import dao.impl.DaoInquilinosImpl;
import dto.Inquilinos;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/inquilinos")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class JSONRest {

    @GET
    @Path("/{id}")
    public Response inquilinosGET(@PathParam("id") int id) {
        DaoInquilinos dao = new DaoInquilinosImpl();
        Inquilinos inquilino = dao.inquilinosGet(id);
        if (inquilino == null) {
            String msg = "Inquilino no encontrado con id: " + id;
            return Response.ok(msg, MediaType.TEXT_PLAIN).build();
        } else {
            return Response.ok(inquilino).build();
        }
    }

    @GET
    public Response inquilinosSEL() {
        DaoInquilinos dao = new DaoInquilinosImpl();
        List<Inquilinos> lista = dao.inquilinosSel();
        if (lista == null) {
            String msg = "Sin usuarios";
            return Response.ok(msg, MediaType.TEXT_PLAIN).build();
        } else {
            GenericEntity<List<Inquilinos>> entity
                    = new GenericEntity<List<Inquilinos>>(lista) {
            };
            return Response.ok(entity).build();
        }
    }

    @POST
    @Produces(MediaType.TEXT_PLAIN)
    public Response inquilinosINS(Inquilinos inquilino) {
        DaoInquilinos dao = new DaoInquilinosImpl();
        String msg = dao.inquilinosIns(inquilino);
        return Response.ok(msg).build();
    }

    @PUT
    @Produces(MediaType.TEXT_PLAIN)
    public Response inquilinosUPD(Inquilinos inquilino) {
        DaoInquilinos dao = new DaoInquilinosImpl();
        String msg = dao.inquilinosUpd(inquilino);
        return Response.ok(msg).build();
    }

    @DELETE
    @Path("/{ids}")
    @Consumes(MediaType.TEXT_PLAIN)
    @Produces(MediaType.TEXT_PLAIN)
    public Response inquilinosDEL(@PathParam("ids") String ids) {
        List<Integer> list = Arrays.stream(ids.split(","))
                .map(Integer::parseInt)
                .collect(Collectors.toList());
        DaoInquilinos dao = new DaoInquilinosImpl();
        String msg = dao.inquilinosDel(list);
        return Response.ok(msg).build();
    }
}

Se puede apreciar en este caso que en la anotación @Produces y @Consumes se indica explícitamente que se trabajará con JSON, pero en las respuestas de las funciones el tipo de retorno no es String, sino "Response", esto permite formatear correctamente el contenido capturado por las funciones DAO y transformarlo a JSON sin emplear otra biblioteca.

RESTful API

Crear un Web Service

Bibliotecas requeridas

  • JAX-RS 2.0
  • Jersey 2.5.1


Estructura del proyecto



Persona.java
package entity;

import java.time.LocalDate;

public class Persona {

    private String dni;
    private String name;
    private LocalDate birthday;
    private Float weight;

    public Persona() {
    }

    public String getDni() {
        return dni;
    }

    public void setDni(String dni) {
        this.dni = dni;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LocalDate getBirthday() {
        return birthday;
    }

    public void setBirthday(LocalDate birthday) {
        this.birthday = birthday;
    }

    public Float getWeight() {
        return weight;
    }

    public void setWeight(Float weight) {
        this.weight = weight;
    }    
}

RestApi.java
package apiRest;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/service")
public class RestApi extends Application {

}

TextRest.java
package apiRest.services;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/texto")
@Produces(MediaType.TEXT_PLAIN)
public class TextRest {

    @GET
    @Path("/prueba")
    public String dataGet() {
        return "Probando un Web Service";
    }
}

JsonRest.java
package apiRest.services;

import entity.Persona;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/json")
@Produces(MediaType.APPLICATION_JSON)
public class JsonRest {

    @GET
    @Path("/objeto")
    public Response getObject() {
        Persona p = new Persona();
        p.setDni("96584422");
        p.setName("Paul");
        p.setBirthday(LocalDate.of(1993, 5, 17));
        p.setWeight(62.7f);
        return Response.ok(p).build();
    }
    
    @GET
    @Path("/lista")
    public Response getList() {
        Persona p1 = new Persona();
        p1.setDni("10084422");
        p1.setName("Milagros");
        p1.setBirthday(LocalDate.of(1993, 2, 7));
        p1.setWeight(52.7f);
        Persona p2 = new Persona();
        p2.setDni("00084422");
        p2.setName("Yadira");
        p2.setBirthday(LocalDate.of(1992, 10, 1));
        p2.setWeight(57f);
        Persona p3 = new Persona();
        p3.setDni("41557422");
        p3.setName("Alejandra");
        p3.setBirthday(LocalDate.of(1990, 1, 21));
        p3.setWeight(49.9f);
        List<Persona> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        GenericEntity<List<Persona>> ge = new GenericEntity<List<Persona>>(list){};
        return Response.ok(ge).build();
    }
}

Si colocamos http://localhost:8082/RestApi/service/texto/prueba en el navegador obtendremos la respuesta:

  • Probando un Web Service


Para consumir los Web Services que devuelven Json se debe invocar a sus URI en los clientes REST que serían:
  • http://localhost:8082/RestApi/service/json/lista
  • http://localhost:8082/RestApi/service/json/objeto


Nota: la dirección (localhost) y el puerto (8082) variarán dependiendo de la ubicación y puerto empleado por el servidor de Java EE.

jueves, 2 de julio de 2020

Java Server Faces - Facelets Tags

Detrás del manejo de las páginas XHTML que componen la vista existe todo un mecanismo de optimización y administración. En JSF a las páginas que componen la vista se les suele llamar archivos *xhtml, pero en realidad en el desarrollo web las páginas XHTML tienen otro significado en su lugar se les debe de llamar "Facelets" estos mediante las instrucciones del desarrollados en XML y tras pasar por el Framework Web y otras instancias generará el archivo HTML deseado y que visualiza el cliente.

Los Facelets proporcionan etiquetas para crear fácilmente composiciones de plantillas basadas en un único archivo de plantilla, similar a la tecnología de .NET de páginas maestras. La finalidad de este mecanismo es la de reducir la duplicación de código para diversas secciones del sitio web que se repiten en todas las páginas web, como:

  • El encabezado
  • El menú de navegación
  • El pie de página.

El archivo de plantilla maestra debe representar un diseño de página web completo con todas las secciones de todo el sitio y usar etiquetas <ui: insert=""> para representar los lugares donde se pueden insertar las secciones específicas de la página.


Creando la estructura

Para ello , se creará un archivo plantilla, y archivos satélite que contengan el código de la cabecera y pie de página. Además, de diversas páginas para comprobar la navegación. La estructura debe ser la siguiente:


El código de layout.xhtml es:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
    <h:head>
        <title>#{title}</title>
    </h:head>
    <h:body>
        <header>
            <ui:include src="/WEB-INF/includes/layout/header.xhtml" />
        </header>
        <main>
            <ui:insert name="content" />
        </main>
        <footer>
            <ui:include src="/WEB-INF/includes/layout/footer.xhtml" />
        </footer>
    </h:body>
</html>
</code>


El código de header.xhtml es:
<ui:composition
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
    <a href="#{request.contextPath}/">        
        INICIO
    </a>
    <nav>
        <ul>
            <li><h:link outcome="/about" value="About" /></li>
            <li><h:link outcome="/help" value="Help" /></li>
            <li><h:link outcome="/contact" value="Contact" /></li>
        </ul>
    </nav>
    <hr/>
</ui:composition>


El código de footer.xhtml es:
<ui:composition
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
    <hr/>
    <nav>
        <ul>
            <li>Términos de servicio</li>
            <li>Política de privacidad</li>
            <li>Política de cookies</li>
        </ul>
    </nav>
    <small>
        © JavaJhon
    </small>
</ui:composition>


El código de index.xhtml es:
<ui:composition template="/WEB-INF/template/layout.xhtml"
                xmlns="http://www.w3.org/1999/xhtml"
                xmlns:h="http://xmlns.jcp.org/jsf/html"
                xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
    <ui:param name="title" value="Inicio" />
    <ui:define name="content">
        <h1>Pantalla de inicio</h1>
        <p>Lorem ipsum dolor sit amet.</p>
    </ui:define>
</ui:composition>


Tendremos una interacción como se aprecia en la imagen



Fuentes:

  • Schotlz, B. y Tijms, A. (2018). The Definitive Guide to JSF in Java EE 8:Building Web Applications with JavaServer Faces. United States of America: Apress.


miércoles, 1 de julio de 2020

Java Server Faces Resource Bundles

Un "resource bundle" en JAVA es un archivo o colección de ellos del tipo *.properties que contiene datos específicos de la localidad. Esto permite "internacionalizar" un aplicativo de forma que la configuración regional se mantiene de forma independiente a la codificación. Estos emplean una organización similar a los HashMap con clave y valor. Así podemos tener una clave "saludo" con valor "hola" en un archivo, en otro la misma clave con valor "hello" y así sucesivamente, pero estos archivos para relacionarse deben tener algo en común y es el uso del mismo nombre con la terminación "_" seguida de dos letras del código de lenguaje ISO 639-1-Alpha-2 antes de la extensión del archivo. así tendríamos los diversos archivos:
  • dictionary.properties, este sería el archivo por defecto
  • dictionary_es.properties, este sería específicamente para el español

Especificar un país se haría agregando otro "_" seguido de dos letras del código de países ISO 3166-1-Alpha-2 antes de la extensión del archivo
  • dictionary_pt_BR.properties, este sería para el portugués de Brasil.
  • dictionary_en_UK.properties, este sería para el inglés de Reino Unido.

NOTA: Lo ideal es que los nombres de los archivos y las claves se encuentren en un solo idioma y por recomendación el inglés. Aún cuando la aplicación no sea usada por personas que hablen ese idioma.


Creando un diccionario para español e inglés

Dentro de los paquetes del código fuente se creará un paquete con el nombre "bundles", dentro de este se crearán documentos con la extensión *.properties que serán:
  • text.properties
  • text_es.properties

text.properties text_es.properties
title=JSF with resource bundles
greeting=Hello my friend
button=Go to the app
title=JSF con paquetes de recursos
greeting=Hola amigo mío
button=Ve a la aplicación

Dependiendo del IDE empleado se visualizará y editará de diversas formas. Por ejemplo en Netbeans se puede apreciar ambos a la vez, mientras en otros se ven y editan por separado.




Llamar a un recurso desde una página de JSF

Estos recursos se comportan de la misma forma como una colección del tipo MAP, por lo que se les llamará de la misma forma con la siguiente sintaxis #{variable['identificador']}, pero antes de ello se debe definir la forma como se hará referencia a estos recursos. Existen dos formas, una es la llamada local mediante una etiqueta de prefijo "f" y la otra desde el archivo de configuración "faces-config.xml"


Llamado local a un recurso

Supongamos el siguiente archivo *.xhtml

<!DOCTYPE html>
<html lang="#{view.locale.toLanguageTag()}"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <f:loadBundle basename="bundles.text" var="texto" />
        <title>#{texto['title']}</title>
    </h:head>
    <h:body>
        <h2>#{texto['greeting']}</h2>
        <h:button value="#{texto['button']}"/>
    </h:body>
</html>

Observemos la línea 2 #{view.locale.toLanguageTag()}, esta permite que se establezca el lenguaje como el mismo del navegador, además se puede emplear para conocer cuál es el que se tiene.

La línea 7 es la etiqueta que hace el llamado al recurso, para ello dispone de dos atributos que son:

  • basename, este tiene como valor la ubicación del recurso dentro del proyecto.
  • var, este indica el "alias" que empleará dentro de la página xhtml


Así en las líneas 8, 11 y 12 se aprecia como se llama a los recursos como si se tratara de una colección del tipo MAP. En un navegador que tiene al español como predefinido la salida sería en el cuerpo de la página el encabezado "Hola amigo mío" seguido del botón con el texto "Ve a la aplicación"


Llamada al recurso desde faces-config

Para poder realizar el llamado desde cualquier página sin usar "f:loadBundle" se debe agregar la referencia en el archivo faces-config.xml de la siguiente forma
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
              http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd"
              version="2.2">
    <application>
        <locale-config>
            <default-locale>en</default-locale>
            <supported-locale>es</supported-locale>
        </locale-config> 
        <resource-bundle>
            <base-name>bundles.text</base-name>
            <var>texto</var>
        </resource-bundle>
    </application>
</faces-config>


Ahí indicamos el lenguaje por defecto (en) y los lenguajes soportados, en este caso el español, luego se indica la ubicación y variable que se empleará.

Se debe tener algunas consideraciones como no usar como variable el que emplee un Bean, ya que se puede causar conflicto y resultados no deseados.

viernes, 26 de junio de 2020

Solución al problema de conexión MySQL con NetBeans Apache

Al realizar conexiones de MySQL o MariaBD mediante algunos IDE, específicamente los NetBeans Apache podemos toparnos con el siguiente error, o cuando se emplea las versiones más actuales del Driver de conexión de MySQL:


Cannot establish a connection to jdbc:mysql://localhost:3306/test using com.mysql.cj.jdbc.Driver (The server time zone value 'Hora est. Pacífico, Sudamér' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a more specifc time zone value if you want to utilize time zone support.)


Una de las soluciones es modificar la URL de la siguiente manera:
   
   jdbc:mysql://localhost:3307/test?useTimezone=true&serverTimezone=UTC
   
De esta manera al intentar la conexión se obtendrá el resultado esperado.


Pero no podemos conectarnos al Servidor MySQL, ya que solo podemos hacer esto para las bases de datos que contiene y obtenemos el siguiente error al intentar conectarnos.



La solución para no tener que modificar nuestras URL y poder conectarnos al Servidor MySQL desde Servicios de NetBeans es ejecutar la siguiente sentencia en el servidor MySQL:

   -- Usando nomenclatura UTC, por ejemplo '-5:00'
   SET GLOBAL time_zone ='-5:00'
   -- El valor varía conforme la zona horaria donde te encuentres
 



Así podremos conectarnos sin necesidad de tener que modificar cada URL.