extendsimplementsJava es un lenguaje de programación compilado e interpretado, creado por Sun Microsystems en 1995 (actualmente mantenido por Oracle). Sus características más importantes son:
Java es principalmente un lenguaje multi-paradigma, aunque su diseño central es:
Es el paradigma principal. Todo el código se organiza en clases que definen atributos (estado) y métodos (comportamiento). Los cuatro pilares de la POO están presentes: encapsulamiento, herencia, polimorfismo y abstracción.
Java describe paso a paso cómo se ejecutan las instrucciones: bucles for, asignaciones, condicionales if/else, etc.
A partir de Java 8 se incorporaron expresiones lambda, streams e interfaces funcionales, permitiendo aplicar conceptos funcionales como funciones de orden superior, inmutabilidad y composición de funciones.
// Ejemplo funcional con streams y lambdas
List<String> nombres = Arrays.asList("Ana", "Luis", "Pedro");
nombres.stream()
.filter(n -> n.startsWith("A"))
.forEach(System.out::println);
Mediante generics, Java permite escribir clases y métodos parametrizados por tipos, evitando duplicación de código y aumentando la seguridad de tipos.
public class Caja<T> {
private T contenido;
public void guardar(T item) { this.contenido = item; }
public T obtener() { return contenido; }
}
// Declaración del paquete (opcional)
package com.ejemplo;
// Importaciones
import java.util.List;
import java.util.ArrayList;
// Declaración de la clase pública
public class HolaMundo {
// Método principal — punto de entrada
public static void main(String[] args) {
System.out.println("Hola, mundo!");
}
}
| Tipo | Tamaño | Ejemplo |
|---|---|---|
byte |
8 bits | byte b = 100; |
short |
16 bits | short s = 30000; |
int |
32 bits | int i = 42; |
long |
64 bits | long l = 100L; |
float |
32 bits | float f = 3.14f; |
double |
64 bits | double d = 3.14159; |
char |
16 bits | char c = 'A'; |
boolean |
1 bit | boolean ok = true; |
| Modificador | Clase | Paquete | Subclase | Mundo |
|---|---|---|---|---|
public |
✔ | ✔ | ✔ | ✔ |
protected |
✔ | ✔ | ✔ | ✗ |
| (paquete) | ✔ | ✔ | ✗ | ✗ |
private |
✔ | ✗ | ✗ | ✗ |
| Palabra | Uso principal |
|---|---|
class |
Declara una clase |
interface |
Declara una interfaz |
extends |
Herencia de clase |
implements |
Implementación de interfaces |
abstract |
Clase o método abstracto |
final |
Clase no heredable, método no sobreescribible, constante |
static |
Miembro de clase (no de instancia) |
this |
Referencia al objeto actual |
super |
Referencia a la clase padre |
new |
Crea una nueva instancia |
null |
Referencia vacía |
instanceof |
Comprueba el tipo de un objeto |
void |
Método sin valor de retorno |
Una clase es la plantilla; un objeto es la instancia concreta.
public class Persona {
// Atributos (estado)
private String nombre;
private int edad;
// Constructor
public Persona(String nombre, int edad) {
this.nombre = nombre;
this.edad = edad;
}
// Métodos (comportamiento)
public String getNombre() { return nombre; }
public int getEdad() { return edad; }
public void saludar() {
System.out.println("Hola, soy " + nombre);
}
}
// Uso
Persona p = new Persona("Ana", 30);
p.saludar(); // "Hola, soy Ana"
extendsLa herencia es uno de los pilares de la POO. Permite que una clase (subclase o clase hija) adquiera los atributos y métodos de otra clase (superclase o clase padre), añadiendo o redefiniendo comportamiento.
En Java, la herencia simple se expresa con la palabra reservada extends:
// Clase padre (superclase)
public class Animal {
protected String nombre;
public Animal(String nombre) {
this.nombre = nombre;
}
public void hacerSonido() {
System.out.println("Sonido genérico");
}
}
// Clase hija (subclase) — hereda de Animal
public class Perro extends Animal {
private String raza;
public Perro(String nombre, String raza) {
super(nombre); // llama al constructor del padre
this.raza = raza;
}
@Override
public void hacerSonido() {
System.out.println(nombre + " dice: ¡Guau!");
}
public String getRaza() { return raza; }
}
Puntos clave sobre extends:
super permite acceder al constructor y métodos de la clase padre.@Override indica que se está sobreescribiendo un método heredado (no obligatorio, pero es buena práctica).extends, hereda implícitamente de java.lang.Object.// Uso
Perro miPerro = new Perro("Rex", "Labrador");
miPerro.hacerSonido(); // "Rex dice: ¡Guau!"
System.out.println(miPerro.getRaza()); // "Labrador"
// Un Perro ES-UN Animal (relación "is-a")
Animal a = new Perro("Toby", "Pastor Alemán");
a.hacerSonido(); // polimorfismo: "Toby dice: ¡Guau!"
public class Cachorro extends Perro {
// hereda de Perro, que hereda de Animal
}
final impide la herenciapublic final class CuentaBancaria {
// Ninguna clase puede extender esta
}
implementsUna interfaz define un contrato (qué debe hacer una clase) sin implementar el cómo (salvo los métodos default desde Java 8). Una clase puede implementar múltiples interfaces.
public interface Volador {
void volar(); // método abstracto por defecto
default String descripcion() {
return "Soy un ser que vuela";
}
}
public interface Nadador {
void nadar();
}
// Una clase puede implementar varias interfaces
public class Pato extends Animal implements Volador, Nadador {
public Pato(String nombre) { super(nombre); }
@Override
public void volar() { System.out.println(nombre + " vuela bajo"); }
@Override
public void nadar() { System.out.println(nombre + " nada"); }
@Override
public void hacerSonido() { System.out.println("¡Cuac!"); }
}
Diferencia clave entre extends e implements:
| Aspecto | extends |
implements |
|---|---|---|
| Se usa con | Clases | Interfaces |
| Cantidad | Solo una clase | Múltiples interfaces |
| Hereda implementación | Sí | Solo métodos default |
| Relación | "es-un" (herencia real) | "puede-hacer" (contrato) |
Ocultar el estado interno y exponer solo lo necesario mediante getters y setters.
public class CuentaBancaria {
private double saldo; // privado: no accesible desde fuera
public double getSaldo() { return saldo; }
public void depositar(double cantidad) {
if (cantidad > 0) saldo += cantidad;
}
public void retirar(double cantidad) {
if (cantidad > 0 && cantidad <= saldo) saldo -= cantidad;
}
}
Un mismo método puede comportarse de forma distinta según el objeto real que lo ejecuta.
Animal[] animales = { new Perro("Rex", "Labrador"), new Pato("Donald") };
for (Animal a : animales) {
a.hacerSonido(); // cada uno ejecuta su propia versión
}
// Salida:
// Rex dice: ¡Guau!
// ¡Cuac!
Las clases abstractas no se pueden instanciar directamente; sirven como base para otras clases.
public abstract class Figura {
protected String color;
public abstract double calcularArea(); // subclases deben implementarlo
public void mostrarColor() {
System.out.println("Color: " + color);
}
}
public class Circulo extends Figura {
private double radio;
public Circulo(double radio, String color) {
this.radio = radio;
this.color = color;
}
@Override
public double calcularArea() {
return Math.PI * radio * radio;
}
}
Es fundamental distinguir los distintos tipos de relaciones entre clases, ya que son conceptos frecuentes en exámenes:
extends// Un Gato ES UN Animal
public class Gato extends Animal { ... }
Una clase contiene una instancia de otra clase como atributo. No se usa extends.
public class Motor {
private int cilindros;
public void arrancar() { System.out.println("Motor arrancando"); }
}
public class Coche {
// Composición: un Coche TIENE UN Motor
private Motor motor;
public Coche() {
this.motor = new Motor();
}
public void conducir() {
motor.arrancar();
System.out.println("Coche en marcha");
}
}
La composición es preferida a la herencia cuando la relación no es genuinamente "es-un", ya que reduce el acoplamiento.
Similar a la composición, pero el objeto contenido puede existir de forma independiente.
public class Universidad {
private List<Estudiante> estudiantes; // los estudiantes existen sin la universidad
}
Una clase puede declararse dentro de otra clase. No tienen relación de herencia.
public class Externa {
private int valor = 10;
// Clase interna (nested)
class Interna {
void mostrar() {
System.out.println("Valor: " + valor); // accede a miembros de la externa
}
}
// Clase estática anidada
static class AnidadaEstatica {
void mostrar() { System.out.println("Clase estática anidada"); }
}
}
String es una clase (no un tipo primitivo). Los strings son inmutables en Java.
String saludo = "Hola";
String nombre = new String("Mundo");
String completo = saludo + ", " + nombre + "!";
// Métodos útiles
saludo.length(); // 4
saludo.toUpperCase(); // "HOLA"
saludo.substring(1, 3); // "ol"
saludo.equals("Hola"); // true (comparar contenido, no con ==)
saludo.contains("ol"); // true
Para concatenaciones intensivas, usar StringBuilder:
StringBuilder sb = new StringBuilder();
sb.append("Hola").append(", ").append("mundo");
String resultado = sb.toString();
int[] numeros = new int[5]; // array de 5 enteros
int[] inicializado = {1, 2, 3, 4, 5};
String[] colores = {"rojo", "verde", "azul"};
// Acceso
System.out.println(colores[0]); // "rojo"
System.out.println(numeros.length); // 5
int nota = 75;
if (nota >= 90) {
System.out.println("Sobresaliente");
} else if (nota >= 70) {
System.out.println("Notable");
} else if (nota >= 50) {
System.out.println("Aprobado");
} else {
System.out.println("Suspenso");
}
// switch
String dia = "LUNES";
switch (dia) {
case "LUNES":
case "MARTES":
System.out.println("Inicio de semana");
break;
case "VIERNES":
System.out.println("Fin de semana laboral");
break;
default:
System.out.println("Otro día");
}
// for clásico
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
// for-each (enhanced for)
int[] nums = {1, 2, 3};
for (int n : nums) {
System.out.println(n);
}
// while
int i = 0;
while (i < 5) {
System.out.println(i++);
}
// do-while (ejecuta al menos una vez)
int j = 0;
do {
System.out.println(j++);
} while (j < 5);
Java usa excepciones para gestionar errores en tiempo de ejecución.
// try-catch-finally
try {
int resultado = 10 / 0; // lanza ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Error: " + e.getMessage());
} finally {
System.out.println("Bloque finally: siempre se ejecuta");
}
Throwable
├── Error (errores graves del sistema: OutOfMemoryError)
└── Exception
├── RuntimeException (no comprobadas: NullPointerException, ArrayIndexOutOfBoundsException)
└── IOException (comprobadas: deben ser capturadas o declaradas)
throws o capturarse con try-catch. Ejemplo: IOException.RuntimeException, no obligatorio capturarlas. Ejemplo: NullPointerException.public class SaldoInsuficienteException extends Exception {
public SaldoInsuficienteException(String mensaje) {
super(mensaje);
}
}
// Uso
public void retirar(double cantidad) throws SaldoInsuficienteException {
if (cantidad > saldo) {
throw new SaldoInsuficienteException("Saldo insuficiente");
}
saldo -= cantidad;
}
El framework de colecciones de Java (java.util) es fundamental.
| Interfaz | Características | Implementaciones comunes |
|---|---|---|
List |
Ordenada, permite duplicados | ArrayList, LinkedList |
Set |
Sin duplicados | HashSet, TreeSet |
Map |
Pares clave-valor | HashMap, TreeMap |
Queue |
FIFO | LinkedList, PriorityQueue |
Deque |
Doble extremo | ArrayDeque |
// List
List<String> lista = new ArrayList<>();
lista.add("Java");
lista.add("Python");
lista.get(0); // "Java"
lista.size(); // 2
// Map
Map<String, Integer> edades = new HashMap<>();
edades.put("Ana", 30);
edades.put("Luis", 25);
edades.get("Ana"); // 30
edades.containsKey("Luis"); // true
// Set
Set<String> conjunto = new HashSet<>();
conjunto.add("a");
conjunto.add("a"); // ignorado (duplicado)
conjunto.size(); // 1
JUnit es el framework estándar para pruebas unitarias en Java. La versión actual es JUnit 5 (también llamado JUnit Jupiter).
| Anotación | Función |
|---|---|
@Test |
Marca un método como prueba |
@BeforeEach |
Ejecuta antes de cada prueba |
@AfterEach |
Ejecuta después de cada prueba |
@BeforeAll |
Ejecuta una vez antes de todas las pruebas (método static) |
@AfterAll |
Ejecuta una vez después de todas las pruebas |
@DisplayName |
Nombre descriptivo para la prueba |
@Disabled |
Deshabilita una prueba temporalmente |
@ParameterizedTest |
Prueba con múltiples conjuntos de datos |
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
public class CalculadoraTest {
private Calculadora calc;
@BeforeEach
void setUp() {
calc = new Calculadora(); // se ejecuta antes de cada @Test
}
@Test
@DisplayName("La suma de dos positivos es correcta")
void testSuma() {
int resultado = calc.sumar(3, 4);
assertEquals(7, resultado, "3 + 4 debe ser 7");
}
@Test
void testDivisionPorCero() {
assertThrows(ArithmeticException.class, () -> calc.dividir(10, 0));
}
@Test
void testResultadoNoNulo() {
assertNotNull(calc.sumar(1, 1));
}
@AfterEach
void tearDown() {
calc = null; // limpieza
}
}
| Método | Descripción |
|---|---|
assertEquals(esperado, actual) |
Comprueba igualdad |
assertNotEquals(a, b) |
Comprueba que no son iguales |
assertTrue(condicion) |
Comprueba que la condición es true |
assertFalse(condicion) |
Comprueba que la condición es false |
assertNull(objeto) |
Comprueba que el objeto es null |
assertNotNull(objeto) |
Comprueba que el objeto no es null |
assertThrows(Excepcion, lambda) |
Comprueba que se lanza una excepción |
assertAll(ejecutables...) |
Agrupa varias aserciones |
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
Maven gestiona el ciclo de vida del proyecto mediante un fichero pom.xml. Gradle es una alternativa más moderna con DSL Groovy/Kotlin.
<!-- pom.xml básico -->
<project>
<groupId>com.ejemplo</groupId>
<artifactId>mi-proyecto</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.1.0</version>
</dependency>
</dependencies>
</project>
El framework más popular del ecosistema Java empresarial. Proporciona:
@RestController
@RequestMapping("/api")
public class HolaController {
@GetMapping("/saludo")
public String saludo() {
return "Hola desde Spring Boot";
}
}
Framework ORM (Object-Relational Mapping) que mapea clases Java a tablas de una base de datos relacional, eliminando la necesidad de escribir SQL manualmente en la mayoría de casos.
@Entity
@Table(name = "empleados")
public class Empleado {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "nombre")
private String nombre;
@Column(name = "salario")
private double salario;
// getters y setters...
}
Librerías de logging estándar en Java.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MiServicio {
private static final Logger log = LoggerFactory.getLogger(MiServicio.class);
public void procesar() {
log.info("Iniciando procesamiento");
log.debug("Valor calculado: {}", 42);
log.error("Error inesperado", new RuntimeException("fallo"));
}
}
El proceso de ejecución de un programa Java es:
Código fuente (.java)
↓ javac (compilador)
Bytecode (.class)
↓ JVM (intérprete + JIT compiler)
Código máquina nativo
javac), JVM y bibliotecas. Para desarrollar.private, acceso mediante getters/setters.PascalCase → CuentaBancariacamelCase → calcularSaldo()UPPER_SNAKE_CASE → MAX_INTENTOSlowercase → com.empresa.proyectocatch (Exception e) sin tratarlas./**
* Calcula el área de un círculo dado su radio.
*
* @param radio el radio del círculo (debe ser positivo)
* @return el área del círculo
* @throws IllegalArgumentException si el radio es negativo
*/
public double calcularArea(double radio) {
if (radio < 0) throw new IllegalArgumentException("El radio no puede ser negativo");
return Math.PI * radio * radio;
}
Documento elaborado como guía de estudio para el lenguaje Java, cubriendo sintaxis, paradigmas, POO, relaciones entre clases y las librerías más utilizadas en el ecosistema Java profesional.