DevTools

Cheatsheet Java

Linguagem de programação orientada a objetos

Volver a los lenguajes
Java
126 tarjetas encontradas
Categorías:
Versiones:

Sintaxe Básica


14 cards
Hola Mundo
public class App {
    public static void main(String[] args) {
        System.out.println("Hola Mundo");
    }
}

Todo programa Java empieza en un método main dentro de una class. System.out.println() imprime una línea en la consola. El nombre del archivo debe igualar el nombre de la clase pública.

Constantes (final)
final int MAX = 100;
final String APP = "MiApp";

// constante de clase
public static final double PI = 3.14159;

La palabra clave final hace la variable inmutable tras la primera asignación. Por convención, las constantes usan MAYÚSCULAS. Combina con static para constantes compartidas por la clase.

Comparar Strings
String a = "Ana";
String b = new String("Ana");

a.equals(b);          // true (contenido)
a == b;               // false (referencia)
a.equalsIgnoreCase("ana"); // true

Para comparar el contenido de strings usa equals(), nunca == (que compara referencias). El equalsIgnoreCase() ignora mayúsculas. Es uno de los errores más comunes en Java.

Arrays multidimensionales
int[][] matriz = {
    { 1, 2, 3 },
    { 4, 5, 6 }
};
int val = matriz[1][2]; // 6

Un array de arrays crea una matriz. Declara con tipo[][] y accede con dos índices [fila][columna]. Las filas pueden tener tamaños diferentes, pues cada fila es un array independiente.

Variables
int edad = 30;
String nombre = "Ana";
double precio = 9.99;
boolean activo = true;
char letra = 'A';

Declara variables indicando el tipo seguido del nombre y del valor. Java es fuertemente tipado, por lo que cada variable tiene un tipo fijo. Usa String para texto y char para un único carácter entre comillas simples.

Operadores aritméticos
int a = 10, b = 3;
int suma = a + b;   // 13
int resto = a % b;  // 1
a++;                // incrementa
b--;                // decrementa
int div = a / b;    // 3 (entero)

Los operadores + - * / hacen las cuentas básicas y % devuelve el resto de la división. Como ambos son int, la división trunca el resultado. Usa ++ y -- para incrementar o decrementar.

Casting
int n = (int) 3.99;     // 3 (trunca)
double d = (double) 5;  // 5.0
long l = 100;           // widening automático
int x = (int) l;        // narrowing manual

El casting convierte entre tipos. El widening (pequeño a grande) es automático; el narrowing (grande a pequeño) exige (tipo) explícito y puede perder datos.

Estructura y comentarios
// comentario de línea
/* comentario
   de bloque */
/** Javadoc: documentación */

package com.ejemplo;
import java.util.List;

Java tiene comentarios de línea (//), de bloque (/* */) y de documentación (/** */). El package organiza las clases y el import trae clases de otros paquetes.

Tipos primitivos
byte  b = 127;      // 8 bits
short s = 32000;    // 16 bits
int   i = 2_000_000; // 32 bits
long  l = 9_000_000_000L;
float f = 3.14f;
double d = 3.14159;
boolean ok = true;
char c = '€';

Los tipos primitivos no son objetos y guardan el valor directamente. El int es el entero por defecto y el double el decimal por defecto. El sufijo L marca un long y f un float.

Comparación y lógicos
boolean iguais = (a == b);
boolean mayor  = (a > b);
boolean ambos  = (a > 0 && b > 0);
boolean algum  = (a > 0 || b > 0);
boolean negado = !ativos;

Los operadores de comparación (==, !=, >, <=) devuelven boolean. Los lógicos && (y), || (o) y ! (negación) combinan condiciones. Usa == solo para primitivos.

Conversiones de texto
int n = Integer.parseInt("42");
double d = Double.parseDouble("3.14");
String s = String.valueOf(42);
String t = 42 + "";      // atajo
String bin = Integer.toBinaryString(10);

Convierte texto en numero con Integer.parseInt() y Double.parseDouble(). Para el sentido inverso usa String.valueOf(). Estos métodos lanzan NumberFormatException si el texto es inválido.

Wrappers
Integer n = 10;        // autoboxing
int x = n;             // unboxing
int v = Integer.parseInt("123");
double d = Double.parseDouble("3.14");
int max = Integer.MAX_VALUE;

Cada tipo primitivo tiene una clase wrapper (Integer, Double, Boolean...). El autoboxing convierte automáticamente entre primitivo y objeto. Los wrappers ofrecen métodos útiles como parseInt().

Strings
String s = "Hola " + nombre;
String f = String.format("%d anios", edad);
int tam = s.length();
String maius = s.toUpperCase();
boolean tem = s.contains("Hola");

La String es inmutable en Java. Concatena con + o formatea con String.format(). Métodos útiles: length(), toUpperCase(), contains(), substring() y trim().

Arrays
int[] nums = { 1, 2, 3 };
int[] out = new int[5];
out[0] = 10;
int tam = nums.length;
int primero = nums[0];

Un array tiene un tamaño fijo definido en la creación. Declara con tipo[] y accede por índice (base 0). La propiedad length da el tamaño. Usa new int[n] para crear un array vacío.

Controlo de Fluxo


11 cards
if / else
if (edad >= 18) {
    System.out.println("adulto");
} else if (edad > 12) {
    System.out.println("joven");
} else {
    System.out.println("niño");
}

El if ejecuta un bloque cuando la condición es verdadera. Encadena alternativas con else if y usa else para el caso restante. Las llaves son opcionales para una sola instrucción, pero recomendadas.

for
for (int i = 0; i < 10; i++) {
    System.out.println(i);
}

for (int i = 10; i > 0; i--) {
    System.out.println(i);
}

El bucle for tiene tres partes: inicialización, condición e incremento. Repite mientras la condición sea verdadera. Es ideal cuando sabes cuántas iteraciones quieres.

Operador ternario
String r = edad >= 18 ? "adulto" : "menor";

int max = (a > b) ? a : b;

El operador condicion ? valorSiVerdad : valorSiFalso es una condicional en una línea. Devuelve uno de dos valores según la condición. Es práctico para asignaciones simples.

switch
switch (dia) {
    case 1:
        System.out.println("Seg");
        break;
    case 2:
        System.out.println("Ter");
        break;
    default:
        System.out.println("?");
}

El switch compara un valor con varios case. Cada caso necesita break para salir, si no continúa al siguiente (fall-through). El default trata los valores no previstos.

for-each
int[] nums = { 1, 2, 3 };
for (int x : nums) {
    System.out.println(x);
}

for (String s : lista) {
    System.out.println(s);
}

El for-each recorre cada elemento de un array o colección sin gestionar índices. La sintaxis es for (tipo item : coleccion). Es más legible, pero no da acceso directo al índice.

break y continue
for (int i = 0; i < 10; i++) {
    if (i == 3) continue; // salta
    if (i == 7) break;    // termina
    System.out.println(i);
}

El continue salta a la próxima iteración y el break termina el bucle por completo. Ambos ayudan a controlar el flujo dentro de bucles sin condiciones complejas.

switch expression (14+)
String r = switch (dia) {
    case 1 -> "Seg";
    case 2 -> "Ter";
    case 6, 7 -> "Fin de semana";
    default -> "?";
};

La sintaxis moderna con -> devuelve un valor directamente y no necesita break. Puedes agrupar varios casos con comas (case 6, 7). Es más segura y legible que el switch clásico.

while
int x = 10;
while (x > 0) {
    System.out.println(x);
    x--;
}

El while repite mientras la condición sea verdadera, probando antes de cada iteración. Si la condición empieza falsa, el cuerpo nunca se ejecuta. No olvides actualizar la variable de control.

Bucles con etiqueta
externo:
for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
        if (i * j > 6) break externo;
    }
}

Una etiqueta (label) identifica un bucle externo. El break etiqueta sale directamente del bucle etiquetado, útil en bucles anidados donde un break simple solo saldría del más interno.

switch con yield
int letras = switch (dia) {
    case 1 -> 3;
    case 2 -> {
        System.out.println("a calcular");
        yield 3;
    }
    default -> 0;
};

Cuando un caso necesita varias instrucciones, usa un bloque con yield para devolver el valor. El yield termina el caso y proporciona el resultado de la switch expression.

do-while
int x = 0;
do {
    System.out.println(x);
    x++;
} while (x < 10);

El do-while ejecuta el cuerpo al menos una vez, pues prueba la condición al final. Es útil para menús o validaciones en los que quieres ejecutar el bloque antes de verificar.

Métodos e Lambdas


11 cards
Método simple
public int sumar(int a, int b) {
    return a + b;
}

int r = sumar(2, 3); // 5

Un método se declara con modificador tipoRetorno nombre(parámetros). Usa return para devolver el resultado. El tipo de retorno debe coincidir con el valor devuelto.

Sobrecarga (overload)
int area(int l) { return l * l; }
int area(int a, int b) { return a * b; }
double area(double r) { return Math.PI * r * r; }

La sobrecarga permite varios métodos con el mismo nombre, siempre que los parámetros difieran en numero o tipo. El compilador elige la versión correcta según los argumentos pasados.

Referencia a método
lista.forEach(System.out::println);
lista.sort(String::compareTo);

Supplier<List<String>> s = ArrayList::new;

La referencia a método Clase::método es un atajo para lambdas que solo llaman a un método. Formas comunes: objeto::método, Clase::método y Clase::new para constructores.

Método void
public void imprimir(String msg) {
    System.out.println(msg);
}

imprimir("Hola"); // sem retorno

Un método void no devuelve ningún valor. Se usa para acciones con efectos secundarios, como imprimir o cambiar estado. No necesita return (o usa return; para salir antes).

Constructor
public class Persona {
    String nombre;

    public Persona(String nombre) {
        this.nombre = nombre;
    }
}
Persona p = new Persona("Ana");

El constructor tiene el mismo nombre que la clase y no tiene tipo de retorno. Se llama con new para inicializar el objeto. this distingue el campo del parámetro con el mismo nombre.

Recursión
int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}
factorial(5); // 120

Un método recursivo se llama a sí mismo, resolviendo el problema en casos más pequeños. Siempre necesita un caso base para parar, si no provoca un StackOverflowError.

Método estático
public static int doble(int x) {
    return x * 2;
}

int r = Math.max(3, 5); // chama sem instancia

Un método static pertenece a la clase y no a una instancia, por lo que puede llamarse sin crear un objeto. No accede a campos de instancia (this). Es común en utilidades como Math.

Encadenamiento de constructores
public class Ponto {
    int x, y;
    Ponto() { this(0, 0); }
    Ponto(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

Un constructor puede llamar a otro de la misma clase con this(...), evitando repetir código. La llamada debe ser la primera instrucción. Es útil para definir valores por defecto.

Interfaces funcionales
Predicate<Integer> mayor = n -> n > 10;
Function<Integer, Integer> doble = n -> n * 2;
Supplier<String> hola = () -> "hola";
Consumer<String> imprime = System.out::println;

El paquete java.util.function trae interfaces funcionales listas: Predicate (prueba), Function (transforma), Supplier (proporciona) y Consumer (consume). Son la base de las lambdas.

Varargs
int suma(int... nums) {
    int total = 0;
    for (int n : nums) total += n;
    return total;
}
suma(1, 2, 3, 4); // 10

El parámetro tipo... acepta un numero variable de argumentos, tratados internamente como un array. Debe ser el último parámetro del método. Práctico para funciones como String.format().

Lambda
Runnable r = () -> System.out.println("Hola");

lista.forEach(x -> System.out.println(x));

Comparator<Integer> c = (a, b) -> a - b;

Una lambda es una función anónima con la sintaxis (params) -> cuerpo. Implementa interfaces funcionales de forma concisa. Se usa mucho con forEach(), sort() y streams.

Classes e Objetos


12 cards
Clase con getter/setter
public class Persona {
    private String nombre;

    public String getName() { return nombre; }
    public void setName(String n) { nombre = n; }
}

El encapsulamiento oculta los campos con private y los expone a través de getters y setters. Así controlas cómo se leen y modifican los datos.

Enum con campos
enum Planet {
    TERRA(5.97e24), MARTE(6.39e23);

    final double massa;
    Planet(double massa) { this.massa = massa; }
}

Un enum puede tener campos, constructor y métodos como una clase. Cada constante llama al constructor con sus argumentos. Útil para asociar datos a cada valor.

Clase anónima
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hola");
    }
};

Una clase anónima define e instancia una implementación sin darle nombre. Se usa para implementar interfaces o extender clases puntualmente. Hoy a menudo se sustituye por lambdas.

Modificadores de acceso
public    // todos
protected // subclasses + paquete
(default) // solo o paquete
private   // solo a propia clase

Java tiene cuatro niveles de acceso. public es visible en todas partes, private solo dentro de la clase, protected para subclases y el predeterminado (sin modificador) lo restringe al paquete.

this y super
this.nombre = nombre;   // campo da instancia
this.método();      // método atual

super();            // construtor da superclasse
super.método();     // método do pai

this se refiere a la instancia actual y super a la superclase. this() llama a otro constructor de la misma clase y super() al constructor del padre.

Clase inmutable
public final class Moneda {
    private final double valor;
    public Moneda(double valor) { this.valor = valor; }
    public double getValor() { return valor; }
}

Una clase inmutable tiene campos final, un constructor que los define y solo getters (sin setters). Marcar la clase como final impide la herencia. Los objetos inmutables son seguros en concurrencia.

Record (16+)
public record Ponto(int x, int y) {}

Ponto p = new Ponto(1, 2);
p.x();        // 1 (no es getX)
p.toString(); // Ponto[x=1, y=2]

Un record es una clase de datos inmutable generada automáticamente. Crea el constructor, los getters (con el nombre del campo), equals(), hashCode() y toString(). Ideal para transportar datos.

static
public class Contador {
    static int total = 0;
    static void inc() { total++; }
}
Contador.inc(); // partilhado por todos

Los miembros static pertenecen a la clase y son compartidos por todas las instancias. Se acceden con Clase.miembro. Un bloque static { } se ejecuta una vez al cargar la clase.

toString()
@Override
public String toString() {
    return "Persona[nombre=" + nombre + "]";
}
System.out.println(persona); // chama toString()

El método toString() devuelve una representación textual del objeto. Se llama automáticamente al imprimir o concatenar. Sobrescríbelo para obtener una salida legible en lugar de Clase@hash.

Enum
enum Estado { ACTIVO, INACTIVO, SUSPENDIDO }

Estado e = Estado.ACTIVO;
Estado[] todos = Estado.values();
String nombre = e.name();

Un enum define un conjunto fijo de constantes con nombre. Es más seguro que usar enteros o cadenas. Los métodos values() y name() listan y obtienen los valores.

Clases anidadas
class Externa {
    class Interna { }          // inner
    static class Estatica { }  // nested
}
Externa.Interna i = new Externa().new Interna();

Una clase interna (inner) tiene acceso a los campos de la clase externa y necesita una instancia de ella. Una static nested es independiente. Sirven para agrupar lógica relacionada.

equals() y hashCode()
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Persona p)) return false;
    return nombre.equals(p.nombre);
}
@Override
public int hashCode() { return nombre.hashCode(); }

Sobrescribe equals() para comparar por contenido y hashCode() para usar el objeto en un HashMap/HashSet. Deben sobrescribirse siempre juntos: los objetos iguales tienen el mismo hashCode.

Herança e Interfaces


11 cards
Herencia (extends)
public class Animal {
    public void hablar() { }
}
public class Perro extends Animal {
    @Override
    public void hablar() { }
}

La herencia usa extends para que una clase hija herede campos y métodos del padre. Java solo permite heredar de una clase. @Override indica que el método sustituye al del padre.

Polimorfismo
Animal a = new Perro();
a.hablar(); // versión de Perro

List<String> l = new ArrayList<>();

El polimorfismo permite tratar un objeto por su superclase o interface, ejecutando el método de la clase real. La variable Animal guarda un Perro, pero llama a la implementación concreta en tiempo de ejecución.

Sealed classes (17+)
public sealed interface Forma
    permits Círculo, Cuadrado { }

public final class Círculo implements Forma { }

Una clase sealed controla qué clases pueden extenderla o implementarla, mediante la cláusula permits. Las hijas deben ser final, sealed o non-sealed. Da seguridad al switch por patrones.

Clase abstracta
public abstract class Forma {
    public abstract double area();
    public void describir() { }
}

Una clase abstract no puede instanciarse y puede tener métodos abstractos (sin cuerpo) que las subclases están obligadas a implementar. Sirve de plantilla base con lógica compartida.

final
public final class Inmutable { }
// class Filha extends Inmutable { } // ERROR

public final int x = 5; // constante
public final void método() { } // no sobrescreve

final aplicado a una clase impide la herencia, a un método impide la sobrescritura y a una variable la convierte en constante. La clase String es final.

Múltiples interfaces
public class Perro extends Animal
    implements Nadador, Juguetón {
    public void nadar() { }
    public void brincar() { }
}

Java no permite herencia múltiple de clases, pero una clase puede implementar varias interfaces, separadas por comas. Así se obtiene comportamiento múltiple sin los problemas del diamante.

Interface
public interface Animal {
    void hablar();
}
public class Perro implements Animal {
    public void hablar() { }
}

Una interface define un contrato de métodos que las clases implementan con implements. Una clase puede implementar varias interfaces. Es la base del polimorfismo en Java.

instanceof
if (obj instanceof Perro) {
    Perro c = (Perro) obj;
    c.hablar();
}

El operador instanceof verifica si un objeto es de un tipo determinado antes de hacer casting. Evita ClassCastException. Devuelve true o false.

Object: la raíz
Object o = "texto"; // tudo es Object

o.toString();
o.equals(outro);
o.hashCode();

Todas las clases heredan implícitamente de Object. Por eso cualquier objeto tiene toString(), equals() y hashCode(). Una variable Object acepta cualquier tipo.

Default methods
public interface Animal {
    void hablar();
    default void dormir() {
        System.out.println("zzz");
    }
}

Un default method tiene implementación en la propia interface, evitando romper clases existentes al añadir métodos. Las clases pueden usarlo directamente o sobrescribirlo.

instanceof pattern (16+)
if (obj instanceof Perro c) {
    c.hablar(); // c ya está disponible
}

El pattern matching para instanceof combina la verificación y el casting en una sola instrucción. La variable c queda disponible dentro del bloque si la prueba pasa, eliminando el cast manual.

Coleções


13 cards
List (ArrayList)
List<Integer> lista = new ArrayList<>();
lista.add(1);
lista.add(2);
lista.get(0);     // 1
lista.remove(0);
lista.size();     // 1

Una List es una colección ordenada que acepta duplicados. El ArrayList es la implementación más común, basada en un array dinámico. Accede por índice con get(i) y añade con add().

Set (HashSet)
Set<String> set = new HashSet<>();
set.add("a");
set.add("a");      // ignorado
set.contains("a"); // true
set.size();        // 1

Un Set es una colección sin duplicados. El HashSet es el más rápido, sin orden garantizado. Llamar a add() con un elemento existente no tiene efecto. Útil para eliminar repetidos.

Iterator
Iterator<String> it = lista.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.isEmpty()) it.remove();
}

Un Iterator recorre una colección con hasNext() y next(). Es la única forma segura de eliminar elementos con remove() durante la iteración sin lanzar ConcurrentModificationException.

Colecciones inmutables
List<String> l = List.of("a", "b", "c");
Set<Integer> s = Set.of(1, 2, 3);
Map<String, Integer> m = Map.of("a", 1, "b", 2);
// l.add("d"); // UnsupportedOperationException

Los métodos de fábrica List.of(), Set.of() y Map.of() crean colecciones inmutables y compactas. Cualquier intento de modificación lanza UnsupportedOperationException.

LinkedList
List<String> lista = new LinkedList<>();
lista.add("a");
lista.addFirst("b");
lista.addLast("c");
String p = lista.get(0);

La LinkedList guarda los elementos en nodos enlazados. Es eficiente al insertar/eliminar en los extremos con addFirst() y addLast(), pero más lenta al acceder por índice que el ArrayList.

TreeSet
Set<Integer> nums = new TreeSet<>(List.of(3, 1, 2));
nums.first(); // 1
nums.last();  // 3

El TreeSet mantiene los elementos ordenados. Ofrece métodos como first() y last() para acceder a los extremos. Los elementos deben ser comparables entre sí.

Comparable
public class Persona implements Comparable<Persona> {
    @Override
    public int compareTo(Persona o) {
        return nombre.compareTo(o.nombre);
    }
}

La interface Comparable define el orden natural de una clase a través del método compareTo(). Devuelve negativo, cero o positivo. Permite usar Collections.sort() directamente.

Map (HashMap)
Map<String, Integer> mapa = new HashMap<>();
mapa.put("a", 1);
mapa.get("a");          // 1
mapa.containsKey("a");  // true
mapa.getOrDefault("z", 0);

Un Map asocia claves con valores. El HashMap es la implementación por defecto, sin orden garantizado. Usa put() para insertar y get() para leer. getOrDefault() evita null.

Queue y Deque
Queue<String> fila = new LinkedList<>();
fila.offer("a");   // adiciona
fila.poll();       // remove e devuelve
fila.peek();       // ve sem remover

Deque<String> pilha = new ArrayDeque<>();
pilha.push("x");   // LIFO
pilha.pop();

La Queue es una cola FIFO con offer()/poll(). El Deque permite insertar/eliminar en ambos extremos y funciona como pila LIFO con push()/pop().

Comparator
lista.sort(Comparator.comparing(Persona::getName));
lista.sort(Comparator.comparingInt(Persona::getIdade).reversed());

Un Comparator define un orden externo, sin alterar la clase. Se crea con Comparator.comparing() y se encadena con reversed() o thenComparing(). Ideal para varios órdenes.

TreeMap y LinkedHashMap
Map<String, Integer> ord = new TreeMap<>();   // chaves ordenadas
Map<String, Integer> ins = new LinkedHashMap<>(); // ordem de inserción

El TreeMap mantiene las claves ordenadas naturalmente y el LinkedHashMap preserva el orden de inserción. Elige según necesites ordenación o mantener la secuencia en que añadiste.

Arrays (utilidad)
int[] nums = { 3, 1, 2 };
Arrays.sort(nums);
Arrays.toString(nums);     // [1, 2, 3]
List<Integer> l = Arrays.asList(1, 2, 3);
int[] copia = Arrays.copyOf(nums, 5);

La clase Arrays trae utilidades para arrays: sort() ordena, toString() imprime, asList() convierte en lista y copyOf() copia. No confundir con el tipo array.

Collections
Collections.sort(lista);
Collections.reverse(lista);
Collections.shuffle(lista);
int max = Collections.max(nums);
List<String> sync = Collections.synchronizedList(lista);

La clase Collections ofrece algoritmos estáticos: sort(), reverse(), shuffle(), max()/min(). También crea vistas sincronizadas o inmutables de colecciones.

Exceções


10 cards
try / catch / finally
try {
    int r = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println(e.getMessage());
} finally {
    System.out.println("sempre executa");
}

El bloque try contiene código de riesgo y catch maneja la excepción si ocurre. finally se ejecuta siempre, haya o no error, siendo ideal para la limpieza.

Checked vs Unchecked
// checked: obriga a tratar (IOException)
// unchecked: RuntimeException
NullPointerException
IllegalArgumentException
IndexOutOfBoundsException

Las excepciones checked (heredan de Exception) obligan a tratarlas con try o throws. Las unchecked (heredan de RuntimeException) indican errores de programación y no obligan.

Excepciones comunes
NullPointerException     // usar null
ArrayIndexOutOfBoundsException // índice inválido
ClassCastException       // cast errado
NumberFormatException    // parse inválido
IllegalArgumentException // argumento mau

Las excepciones unchecked más frecuentes: NullPointerException al usar null, IndexOutOfBounds en índices inválidos y NumberFormatException al convertir texto. La prevención vale más que la captura.

throw
if (valor < 0) {
    throw new IllegalArgumentException("Valor inválido");
}

La instrucción throw lanza una excepción manualmente, interrumpiendo el flujo. Se crea una instancia de la excepción con un mensaje. Se usa para validar argumentos y estados inválidos.

multi-catch
try {
    // código
} catch (IOException | SQLException e) {
    System.out.println("Error: " + e);
}

multi-catch maneja varias excepciones en el mismo bloque, separadas por |. Reduce código duplicado cuando el tratamiento es igual. Las excepciones no pueden ser de la misma jerarquía.

Buenas prácticas con excepciones
// específico antes de genérico
try { } catch (IOException e) { }
      catch (Exception e) { }

// no usar excepciones para controlo de fluxo

Captura excepciones específicas antes que las genéricas y nunca ignores un catch vacío. Usa excepciones para errores excepcionales, no para el control de flujo normal. Prefiere Optional a devolver null.

throws (declarar)
public void leer() throws IOException {
    Files.readString(Path.of("f.txt"));
}

La cláusula throws en la firma declara que el método puede lanzar una excepción checked, transfiriendo la responsabilidad a quien lo llama. El llamador debe manejarla o volver a declararla.

Excepción personalizada
public class SaldoException extends Exception {
    public SaldoException(String msg) {
        super(msg);
    }
}
throw new SaldoException("Saldo insuficiente");

Crea tu propia excepción extendiendo Exception (checked) o RuntimeException (unchecked). Pasa el mensaje al constructor con super(msg). Da nombres terminados en Exception.

try-with-resources
try (var br = Files.newBufferedReader(p)) {
    String linea = br.readLine();
} // fecha automaticamente

try-with-resources declara recursos entre paréntesis y los cierra automáticamente al final, incluso con error. Exige que el recurso implemente AutoCloseable. Evita fugas de recursos.

Información de la excepción
catch (Exception e) {
    String msg = e.getMessage();
    e.printStackTrace();
    Throwable causa = e.getCause();
}

Un objeto de excepción ofrece getMessage() (mensaje), printStackTrace() (pila de llamadas) y getCause() (excepción original). Útiles para registrar y depurar errores.

Recursos Modernos


11 cards
var (inferencia)
var lista = new ArrayList<String>();
var n = 10;
var s = "texto"; // String

for (var i = 0; i < 5; i++) { }

var (Java 10+) deja que el compilador infiera el tipo de la variable local a partir del valor. Solo funciona en variables locales inicializadas. No cambia el tipo: sigue fuertemente tipado.

main simplificado (21+)
void main() {
    System.out.println("Hola");
}

Con la vista previa de instance main methods, el punto de entrada puede ser un simple void main() sin public static ni String[] args. Reduce la ceremonia para programas pequeños.

Stream.iterate y generate
Stream.iterate(1, n -> n * 2)
    .limit(5)
    .forEach(System.out::println);

Stream.generate(Math::random).limit(3);

Stream.iterate() genera una secuencia infinita aplicando una función al valor anterior. Stream.generate() crea valores con un Supplier. Usa limit() para terminarlos.

Text blocks (15+)
String json = """
        {
            "nombre": "Ana",
            "edad": 30
        }
        """;

Un text block usa tres comillas """ para cadenas multilínea sin escapes. La indentación común se elimina automáticamente. Ideal para JSON, SQL y HTML legibles.

String: nuevos métodos
"  x  ".strip();      // "x" (Unicode)
"  x  ".trim();       // "x"
"".isBlank();         // true
"ab".repeat(3);       // "ababab"
"a\nb".lines();       // Stream<String>

Métodos modernos de String: strip() elimina espacios (Unicode-aware), isBlank() comprueba si está vacía/espacios, repeat(n) repite y lines() devuelve una stream de líneas.

var en lambdas
BiFunction<Integer, Integer, Integer> suma =
    (var a, var b) -> a + b;

lista.forEach((var x) -> System.out.println(x));

Puedes usar var en los parámetros de una lambda para mantener la sintaxis consistente o para añadirles anotaciones. Todos los parámetros deben usar var o ninguno.

Switch pattern matching (21+)
String r = switch (obj) {
    case Integer i -> "entero " + i;
    case String s  -> "texto " + s;
    case null      -> "nulo";
    default        -> "outro";
};

El switch moderno acepta patrones de tipo en lugar de solo constantes. Cada case Tipo var prueba y extrae el valor. Con clases sealed, el default puede omitirse.

Compact constructor (record)
public record Edad(int valor) {
    public Edad {
        if (valor < 0)
            throw new IllegalArgumentException();
    }
}

Un compact constructor en un record omite los parámetros y valida los campos antes de la asignación automática. No necesita this.valor = valor. Ideal para validar invariantes en records.

Unnamed variables (22+)
try {
    // ...
} catch (Exception _) {
    System.out.println("error");
}
int total = 0;
for (var _ : lista) total++;

La variable sin nombre _ marca valores que no vas a usar, como excepciones o elementos de iteración. Deja clara la intención y evita nombres descartables. Puede repetirse en el mismo ámbito.

Pattern con guard (when)
String r = switch (valor) {
    case Integer i when i < 0 -> "negativo";
    case Integer i            -> "positivo";
    default                   -> "outro";
};

La cláusula when añade una condición extra (guard) a un patrón. Permite refinar casos sin un if interno. Los casos más específicos deben ir antes que los genéricos.

Sequenced Collections (21+)
SequencedCollection<String> s = new LinkedHashSet<>();
s.addFirst("a");
s.addLast("z");
s.getFirst();
s.reversed();

La interface SequencedCollection uniformiza las colecciones con orden, exponiendo addFirst(), addLast(), getFirst() y reversed(). También existe SequencedMap.

Dicas e Boas Práticas


12 cards
StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String s = sb.toString();

El StringBuilder es eficiente para concatenar cadenas en bucles, pues es mutable y evita crear objetos en cada +. Usa append() y convierte al final con toString().

Convenciones de nombres
class MiClase { }       // PascalCase
int miVariable;          // camelCase
void myMethod() { }        // camelCase
final int MAX_VALOR = 100;  // UPPER_SNAKE
package com.empresa.app;    // minúsculas

Por convención: clases en PascalCase, variables y métodos en camelCase, constantes en UPPER_SNAKE_CASE y paquetes en minúsculas. Seguir el estándar hace el código idiomático.

printf y formateo
System.out.printf("Nombre: %s, Edad: %d%n", nombre, edad);
System.out.printf("Precio: %.2f%n", 9.99);
String s = String.format("%05d", 42); // "00042"

printf() formatea la salida con marcadores: %s (string), %d (entero), %f (decimal, %.2f con 2 decimales) y %n (nueva línea). String.format() devuelve la cadena formateada.

Verificar null
if (obj != null) { }
Objects.requireNonNull(obj, "no pode ser null");
boolean eq = Objects.equals(a, b);
String h = Objects.toString(obj, "patrón");

La clase Objects ayuda a lidiar con null: requireNonNull() lanza una excepción si es nulo, equals() compara con seguridad y toString() da un valor por defecto.

Math
Math.max(3, 5);     // 5
Math.min(3, 5);     // 3
Math.abs(-7);       // 7
Math.pow(2, 3);     // 8.0
Math.sqrt(16);      // 4.0
Math.round(3.6);    // 4

La clase Math ofrece funciones matemáticas estáticas: max()/min(), abs() (valor absoluto), pow() (potencia), sqrt() (raíz) y round() (redondear).

Random
Random r = new Random();
int n = r.nextInt(100);     // 0 a 99
double d = r.nextDouble();  // 0.0 a 1.0

int dado = ThreadLocalRandom.current().nextInt(1, 7);

La clase Random genera numeros aleatorios: nextInt(límite) para enteros y nextDouble() para decimales. ThreadLocalRandom es preferible en concurrencia y acepta intervalos.

Importar
import java.util.List;
import java.util.*;
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;

import trae clases de otros paquetes. import java.util.* importa todas las clases del paquete e import static permite usar miembros estáticos (como PI) sin el nombre de la clase.

Fechas (java.time)
LocalDate hoy = LocalDate.now();
LocalDate data = LocalDate.of(2024, 1, 15);
LocalDate mañana = hoy.plusDays(1);
int anio = hoy.getYear();

El paquete java.time (Java 8+) trata las fechas de forma inmutable. LocalDate es una fecha sin hora, LocalTime una hora y LocalDateTime ambos. Usa plusDays() para sumar.

Archivos (Files)
Path p = Path.of("datos.txt");
String texto = Files.readString(p);
List<String> lineas = Files.readAllLines(p);
Files.writeString(p, "Hola");

La clase Files (con Path) simplifica la lectura y escritura de archivos. readString() lee todo, readAllLines() devuelve una lista de líneas y writeString() escribe. Lanza IOException.

Compilar y ejecutar
javac App.java      // compila -> App.class
java App            // executa

java App.java       // executa directo (11+)
java -jar app.jar   // executa um JAR

javac compila el código fuente a bytecode (.class) y java lo ejecuta en la JVM. Desde Java 11 puedes ejecutar un archivo directamente con java App.java.

Leer input (Scanner)
Scanner sc = new Scanner(System.in);
String nombre = sc.nextLine();
int edad = sc.nextInt();
sc.close();

El Scanner lee datos de la consola. nextLine() lee una línea de texto y nextInt() un entero. Ciérralo con close() cuando termines. Para rendimiento usa BufferedReader.

Buenas prácticas
// prefere interfaces nos tipos
List<String> l = new ArrayList<>();

// usa try-with-resources
// evita null: devuelve Optional ou listas vacías
// métodos curtos e coesos

Declara las variables por el tipo de la interface (List en lugar de ArrayList) para flexibilidad. Devuelve colecciones vacías en lugar de null, usa try-with-resources y mantén los métodos pequeños.

Streams e Funcional


12 cards
Stream: filter y map
List<Integer> r = lista.stream()
    .filter(x -> x > 5)
    .map(x -> x * 2)
    .collect(Collectors.toList());

Una stream procesa colecciones de forma funcional y declarativa. filter() selecciona elementos y map() los transforma. La stream no altera la colección original.

Stream: groupingBy
Map<String, List<Persona>> porCidade = personas.stream()
    .collect(Collectors.groupingBy(Persona::getCidade));

Collectors.groupingBy() agrupa elementos por una clave, devolviendo un Map de listas. Es el equivalente de un "group by" de SQL. Combínalo con counting() para contar por grupo.

Optional: crear
Optional<String> a = Optional.of("Ana");
Optional<String> b = Optional.ofNullable(nombre);
Optional<String> v = Optional.empty();

El Optional es un contenedor que puede o no tener un valor, evitando null. of() exige un valor presente, ofNullable() acepta null y empty() crea un Optional vacío.

Stream: agregación
long n = lista.stream().count();
boolean algum = lista.stream().anyMatch(x -> x > 10);
boolean todos = lista.stream().allMatch(x -> x > 0);
Optional<Integer> prim = lista.stream().findFirst();

Las operaciones terminales de agregación resumen la stream: count() cuenta, anyMatch()/allMatch() prueban condiciones y findFirst() devuelve el primer elemento en un Optional.

Stream: flatMap
List<String> todas = listas.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());

flatMap() transforma cada elemento en una stream y aplana todo en una sola. Es útil cuando cada elemento genera varios resultados (p. ej. listas de listas) y quieres una stream única y plana.

Optional: obtener valor
String r = opt.orElse("Anónimo");
String l = opt.orElseGet(() -> calcular());
opt.ifPresent(System.out::println);
String v = opt.orElseThrow();

Para obtener el valor con seguridad usa orElse() (valor por defecto), orElseGet() (calculado), ifPresent() (solo si existe) o orElseThrow(). Evita llamar a get() directamente.

Stream: reduce
int suma = lista.stream()
    .reduce(0, (acc, x) -> acc + x);

int producto = lista.stream()
    .reduce(1, (a, b) -> a * b);

reduce() combina todos los elementos en un único valor, partiendo de un acumulador inicial. El primer argumento es el valor inicial y el segundo la función que acumula. Sustituye los bucles de suma/producto.

Stream: distinct/sorted/limit
List<Integer> r = lista.stream()
    .distinct()
    .sorted()
    .limit(5)
    .collect(Collectors.toList());

Operaciones intermedias comunes: distinct() elimina duplicados, sorted() ordena y limit(n) restringe a los primeros n elementos. Se encadenan antes de la operación terminal.

Optional: map y filter
String r = opt
    .map(String::toUpperCase)
    .filter(s -> s.length() > 2)
    .orElse("vacio");

El Optional admite operaciones funcionales: map() transforma el valor si está presente y filter() lo mantiene solo si pasa la condición. Si queda vacío en cualquier paso, orElse() devuelve el predeterminado.

Stream: collect
List<String> nombres = personas.stream()
    .map(Persona::getName)
    .collect(Collectors.toList());

String csv = personas.stream()
    .map(Persona::getName)
    .collect(Collectors.joining(", "));

collect() acumula los resultados en una estructura. Collectors.toList() crea una lista y Collectors.joining() une cadenas con un separador. Es la operación terminal más versátil.

Streams numéricos
int suma = lista.stream().mapToInt(x -> x).sum();
double media = lista.stream().mapToInt(x -> x).average().orElse(0);
int max = lista.stream().mapToInt(x -> x).max().orElse(0);

Las streams numéricas (IntStream, DoubleStream) evitan el boxing y ofrecen sum(), average() y max(). Convierte con mapToInt() para acceder a estas operaciones.

Pipeline completo
List<String> top = personas.stream()
    .filter(p -> p.getIdade() >= 18)
    .sorted(Comparator.comparing(Persona::getName))
    .map(Persona::getName)
    .limit(3)
    .toList();

Un pipeline típico encadena varias operaciones: filtra, ordena, transforma y limita. toList() (Java 16+) es un atajo para collect(Collectors.toList()). Las operaciones solo se ejecutan en la terminal.

Genéricos


9 cards
Clase genérica
public class Caja<T> {
    private T valor;
    public void set(T valor) { this.valor = valor; }
    public T get() { return valor; }
}
Caja<String> c = new Caja<>();

Una clase genérica usa un parámetro de tipo <T> que se sustituye al instanciar. Permite escribir código reutilizable y type-safe. T es una convención para "Type".

Wildcard (?)
void imprimir(List<?> lista) {
    for (Object o : lista) System.out.println(o);
}

El wildcard ? representa un tipo desconocido. List<?> acepta listas de cualquier tipo, pero solo puedes leer los elementos como Object. Da flexibilidad a los métodos.

Restricciones
// NO permitido:
// List<int> x;        // solo objetos
// new T();            // sem instanciar
// new T[10];          // sem arrays de T
// if (x instanceof List<String>) {}

Los genéricos solo aceptan tipos de referencia (no primitivos — usa wrappers). No puedes instanciar T, crear arrays de T ni usar instanceof con tipos genéricos, debido al type erasure.

Método genérico
public <T> T primero(List<T> lista) {
    return lista.get(0);
}
String s = primero(List.of("a", "b"));

Un método genérico declara el tipo antes del retorno: <T> T nombre(...). El tipo se infiere a partir de los argumentos. Funciona en cualquier clase, incluso no genérica.

Upper bounded (? extends)
double suma(List<? extends Number> nums) {
    double total = 0;
    for (Number n : nums) total += n.doubleValue();
    return total;
}

? extends Number acepta Number o subtipos (Integer, Double). Es covariante: puedes leer como Number, pero no añadir elementos. Ideal para productores de datos.

Diamond operator
List<String> a = new ArrayList<String>(); // antiguo
List<String> b = new ArrayList<>();       // diamond

Map<String, List<Integer>> m = new HashMap<>();

El operador diamond <> deja que el compilador infiera los tipos a partir de la declaración, evitando repetirlos. Hace el código más limpio sin perder seguridad de tipos.

Lower bounded (? super)
void agregar(List<? super Integer> lista) {
    lista.add(1);
    lista.add(2);
}

? super Integer acepta Integer o supertipos (Number, Object). Es contravariante: puedes añadir Integer con seguridad. Ideal para consumidores de datos (regla PECS).

Bounded types
public <T extends Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}

La restricción <T extends Tipo> limita el genérico a un tipo o sus subtipos. Así puedes llamar métodos de ese tipo (aquí compareTo()). Acepta clases e interfaces.

Type erasure
List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
a.getClass() == b.getClass(); // true!

El type erasure elimina los tipos genéricos en tiempo de ejecución, sustituyendo T por su límite. Por eso List<String> y List<Integer> son la misma clase en runtime. La verificación es solo en compilación.