Saltar a contenido

10. Java

Elementos básicos en Java

10.1 Lenguaje de programación JAVA

Guia de Java de Autentia

10.1.1 Colecciones

Collection Framework Hierarchy

alt text

Algunos de los interfaces más usados:

  • List: admite elementos repetidos y mantiene un orden inicial.
  • ArrayList: array redimensionable que aumenta su tamaño según crece la colección de elementos.
  • LinkedList: se basa en una lista doblemente enlazada de los elementos, teniendo cada uno de los elementos un puntero al anterior y al siguiente elemento.

La interfaz Iterable ofrece un método con el que podemos obtener un objeto Iterator para una colección. Este objeto permite iterar por la colección, accediendo sucesivamente a cada uno de sus elementos. En el caso de las listas, existe la interfaz ListIterator que nos permite iterar también hacia atrás. Por ejemplo:

List<Integer> myMarks = new ArrayList();
  myMarks.add(7);

  myMarks.add(8);
  myMarks.add(9);
  Iterator it = myMarks.iterator();
  Integer n1 = it.next(); // n1 = 7;
  while (it.hasNext()) {
    System.out.println(it.next()); // Output: 8, 9.
  }

10.1.2 Genéricos (tipos parametrizados)

Se llaman parametrizados porque el tipo de dato con el que opera la funcionalidad se pasa como parámetro. Pueden usarse en clases, interfaces y métodos, denominándose clases, interfaces o métodos genéricos respectivamente.

En Java, la declaración de un tipo genérico se hace entre símbolos <>, pudiendo definir uno o más parámetros, por ejemplo: , . Existe una convención a la hora de nombrarlos: * E – Element (usado bastante por Java Collections Framework). * K – Key (usado en mapas). * N – Number (para números). * T – Type (representa un tipo, es decir, una clase). * V – Value (representa el valor, también se usa en mapas). * S, U, V etc. – usado para representar otros tipos.

Los habrá visto en declaraciones de colecciones:

  ArrayList<String>  lis = new ArrayList<>()

También puedes crear clases que utilicen genericos.
Ejemplo de genérico:

//Definición de una clase genérica que usa dos genéricos
// GenericClass.java file
public class GenericClass<T, K> {
  private T g1;
  private K g2;
  public GenericClass(T g1, K g2) {
    this.g1 = g1;
    this.g2 = g2;
  }
  public T getGenericOne() {
    return g1;
  }
  public K getGenericTwo() {
    return g2;
  }
}
// Main.java file
public class Main{
public static void main(String[] args) throws Exception {
  GenericClass<Integer, String> clazz =
    new GenericClass<>(1, "generic");
    Integer param1 = clazz.getGenericOne();
    String param2 = clazz.getGenericTwo();
    System.out.println(String.format("Param1 %d - Param2 %s",
    param1, param2));

  GenericClass<Double, Int> xxx = new GenericClass<>()
  }
}

10.1.3 Funciones Lambda

Las lambdas fueron introducidas a partir de Java 8. No son más que funciones anónimas que nos permiten programar en Java con un estilo más funcional y, en ocasiones, declarativo.

La sintaxis de las funciones lambda:

( tipo1 param1, tipoN paramN) -> { cuerpo de la lambda }
No es necesario incluir el tipo ya que este puede ser inferido. El paréntesis de los parámetros puede omitirse cuando sólo existe un parámetro y no incluimos el tipo. Si no hay parámetros los paréntesis son necesarios.

(param1, param2) -> { cuerpo }
param1 -> { cuerpo }
() -> { cuerpo }
En el caso del cuerpo, si solo tenemos una sentencia, podremos omitir las llaves y el return, por ejemplo:
numero -> String.valueOf(numero)
Ejemplo de uso de funciones lambda:

10.1.3.1 Procesamiento de colecciones con stream

En el siguiente ejemplo como para los métodos filter y map

List<String> nombres = Arrays.asList("Ana", "Juan", "Pedro", "Maria", "Luis");

// Filtrar nombres que empiezan con 'M' y convertirlos a mayúsculas
List<String> nombresConM = nombres.stream()
    .filter(nombre -> nombre.startsWith("M"))
    .map(nombre -> nombre.toUpperCase())
    .collect(Collectors.toList());

System.out.println(nombresConM); // Output: [MARIA]

10.1.3.2 Manejo de eventos

Por ejemplo con Swing

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class EjemploLambdaSwing {

    public static void main(String[] args) {
        // Crear el marco de la aplicación
        JFrame frame = new JFrame("Ejemplo de Lambda con Swing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300, 200);

        // Crear un panel para contener los componentes
        JPanel panel = new JPanel();

        // Crear un botón
        JButton boton = new JButton("Haz clic aquí");

        // Agregar un ActionListener usando una expresión lambda
        boton.addActionListener(e -> {
            System.out.println("El botón ha sido clicado.");
        });

        // Agregar el botón al panel
        panel.add(boton);

        // Agregar el panel al marco
        frame.add(panel);

        // Hacer visible el marco
        frame.setVisible(true);
    }
}

10.1.4 Data Processing STREAM

Los stream se usan como alternativa al uso de bucles para procesar colecciones desde Java 8.

  1. Presentan las siguientes características:
  2. Operaciones fácilmente paralelizables.
  3. Estilo declarativo de operaciones.
  4. Concatenación de operaciones en un pipeline.

Para utilizar los streams sobre una colección, basta con invocar al método stream() o parallelStream(), en función de si queremos paralelizar las operaciones o no.
Un stream no almacena los valores, sino que se limita a computarlos. Obtiene los datos de una colección y genera un resultado tras el procesado de las operaciones intermedias del pipeline mediante una operación terminal.

Es importante tener en cuenta que las operaciones intermedias devuelven un stream, mientras que las operaciones terminales no. Las operaciones intermedias no se ejecutan hasta que se realiza una operación terminal.

Ejemplo:

  ArrayList<Other> l1 = new ArrayList<>();
  List<Other> l2 = l1.stream()
    .filter(elem -> elem.getAge() < 65)
    .sorted() // Ordena según la implementación de Comparable
    .map(elem -> new Other(elem.getName,() elem.getAge()))
    .collect(toList());
En función de su objetivo, podemos dividir las operaciones por grupos:

  • Filtrado.
  • Búsqueda.
  • Mapeado.
  • Matching.
  • Reducción.
  • Iteración

Puedes encontrar un listado completo de las operaciones soportadas por los streams en la interfaz java.util.stream.Stream.

Podemos crear stream en otros tipos de datos:
Para convertir un archivo en un stream de líneas, podemos utilizar Files.lines() como en el siguiente ejemplo:

  long numberOfLines = Files.lines(
    Paths.get("yourFile.txt"),
    Charset.defaultCharset()
  ).count();
El hecho de que los streams computen elementos, hace que podamos crear streams infinitos a partir de funciones mediante Stream.generate y Stream.iterate. Por ejemplo, puede ser interesante para obtener un valor constante o número aleatorio.

Para finalizar, es importante aclarar que el método collect() es una operación terminal en los Streams que acepta un parámetro de tipo Collector. Podemos crear estos Collectors utilizando los métodos estáticos proporcionados por la clase Collectors. Dependiendo del tipo de Collector que utilicemos, el tipo de colección resultante (como List, Set, Map, etc.) será diferente.

Es el paso final en una secuencia de operaciones de un Stream que permite recopilar los elementos procesados.
Uso de la Clase Collectors:

  • La clase Collectors proporciona métodos de fábrica estáticos que nos permiten crear diferentes tipos de Collectors. Ejemplos comunes incluyen Collectors.toList(), Collectors.toSet(), Collectors.groupingBy(), entre otros.
List<String> nombres = Arrays.asList("Ana", "Juan", "Pedro", "Maria", "Luis");

// Usando collect() para obtener una lista
List<String> listaNombres = nombres.stream()
    .filter(nombre -> nombre.startsWith("A"))
    .collect(Collectors.toList());

// Usando collect() para obtener un conjunto (Set)
Set<String> conjuntoNombres = nombres.stream()
    .filter(nombre -> nombre.length() > 3)
    .collect(Collectors.toSet());

10.1.5 E/S (I/O)

10.1.5.1 Serializable

En Java, el término Serializable se refiere a una interfaz (java.io.Serializable) que marca una clase para que sus objetos puedan ser serializados y deserializados.

La serialización es el proceso de convertir un objeto en una secuencia de bytes, lo que permite almacenar el objeto en un medio de almacenamiento (como un archivo o una base de datos) o transmitirlo a través de una red. La deserialización es el proceso inverso: reconstruir el objeto original a partir de la secuencia de bytes.

Interfaz serializable
La interfaz Serializable es una interfaz marcadora (marker interface), lo que significa que no declara ningún método. Su propósito es indicar al mecanismo de serialización de Java que la clase puede ser serializada.

¿Por Qué Usar Serialización? * Persistencia: Guardar el estado de un objeto para su posterior recuperación. * Comunicación: Enviar objetos a través de redes (por ejemplo, en aplicaciones distribuidas o RMI - Remote Method Invocation). * Caching: Almacenar objetos en caché para mejorar el rendimiento.

Cómo Serializar un Objeto Para serializar un objeto, se utiliza la clase ObjectOutputStream. A continuación se muestra un ejemplo básico:

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Persona implements Serializable {
    private String nombre;
    private int edad;

    public Persona(String nombre, int edad) {
        this.nombre = nombre;
        this.edad = edad;
    }

    // Getters y setters omitidos para brevedad

    public static void main(String[] args) {
        Persona persona = new Persona("Juan", 30);

        try (FileOutputStream archivo = new FileOutputStream("persona.ser");
             ObjectOutputStream salida = new ObjectOutputStream(archivo)) {

            salida.writeObject(persona);
            System.out.println("Objeto serializado correctamente.");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Cómo Deserializar un Objeto

Para deserializar un objeto, se utiliza la clase ObjectInputStream.

Ejemplo: Deserialización de un objeto:

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class LeerPersona {
    public static void main(String[] args) {
        try (FileInputStream archivo = new FileInputStream("persona.ser");
             ObjectInputStream entrada = new ObjectInputStream(archivo)) {

            Persona persona = (Persona) entrada.readObject();
            System.out.println("Nombre: " + persona.getNombre());
            System.out.println("Edad: " + persona.getEdad());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

10.2 Ejemplos de clases

Como usar librerias usando JFreeChart y lo mismo con Eclipse: https://jfree.github.io/Eclipse-JFreeChart/
y usando maven: https://stackoverflow.com/questions/30964344/adding-jfreechart-to-maven-in-eclipse

10.2.1 Juego de la vida

https://es.m.wikipedia.org/wiki/Juego_de_la_vida y más general : https://es.m.wikipedia.org/wiki/Aut%C3%B3mata_celular

10.2.2 la hormiga de Langton

10.3 Librerias Java

10.3.1 Librerias de tod

  • Apache common. Con multiples librerias, por ejemplo:
  • CSV para lectura escritura de formatos cvs
  • CLI Command line
  • NET, utilidades de red
  • Hibernate para base de datos

10.3.2 Servidores de juegos

10.4 GRAFICOS

10.4.1 Librerias

10.4.2 Chart

Para empezar este hilo de stackoverflow * Tutorial JFreechart * Charts4j * jzy3d *