Constructores en Java

📅 Actualizado en febrero 2026 ✍️ Ángel López ⏱️ 18 min de lectura ✓ Nivel intermedio ★ ★ ★ ★ ★ (5/5)

🏗️ ¿Qué es un constructor en Java?

Un constructor es un bloque de código especial dentro de una clase Java que se ejecuta automáticamente cada vez que se crea un objeto mediante la palabra clave new. Su propósito fundamental es inicializar el estado del objeto, asignando valores a sus atributos y ejecutando cualquier lógica necesaria para que el objeto esté listo para ser utilizado desde el primer momento.

A diferencia de los métodos regulares, un constructor no tiene tipo de retorno (ni siquiera void) y su nombre debe coincidir exactamente con el nombre de la clase, respetando mayúsculas y minúsculas. Estas dos características son las que permiten al compilador de Java distinguir un constructor de un método ordinario.

Java
public class Coche {
    private String marca;
    private int velocidadMaxima;

    // Esto ES un constructor: mismo nombre que la clase, sin tipo de retorno
    public Coche(String marca, int velocidadMaxima) {
        this.marca = marca;
        this.velocidadMaxima = velocidadMaxima;
    }
}

// Uso: el constructor se invoca con new
Coche miCoche = new Coche("Toyota", 220);

Cuando la JVM (Java Virtual Machine) ejecuta new Coche("Toyota", 220), suceden tres cosas en secuencia: primero se reserva memoria en el heap para el nuevo objeto, después se ejecuta el constructor que inicializa los atributos marca y velocidadMaxima, y finalmente se devuelve una referencia al objeto recién creado que se almacena en la variable miCoche.

💡
Concepto clave: El constructor no «crea» el objeto — la JVM lo hace al asignar memoria. El constructor inicializa ese espacio de memoria con valores significativos. Sin constructor, los atributos quedarían con sus valores por defecto (null, 0, false).

📐 Sintaxis y reglas fundamentales

La sintaxis de un constructor en Java sigue un patrón preciso que todo programador debe dominar. A continuación se presenta la estructura general y las reglas que el compilador aplica de forma estricta.

Java
[modificador_acceso] NombreDeLaClase([parámetros]) {
    // Cuerpo del constructor: inicialización de atributos
}

📋 Reglas que el compilador aplica

ReglaDescripciónEjemplo
Nombre idéntico a la claseEl constructor debe llamarse exactamente igual que la clase, incluyendo mayúsculasclass Persona → public Persona()
Sin tipo de retornoNo se declara void, int, ni ningún otro tipoPersona() · ❌ void Persona()
Modificadores de accesoAdmite public, protected, private o acceso de paquete (sin modificador)private Persona() para Singleton
No puede ser staticLos constructores pertenecen a instancias, no a la clasestatic Persona()
No puede ser finalLos constructores no se heredan, por lo que final no tiene sentidofinal Persona()
No puede ser abstractUn constructor siempre debe tener implementaciónabstract Persona()
Puede lanzar excepcionesSe puede declarar throws para validacionesPersona(int edad) throws IllegalArgumentException
⚠️
Error frecuente: Si escribes public void Coche(), Java lo interpreta como un método regular llamado Coche que devuelve void, no como un constructor. El objeto se creará con los valores por defecto y tu «constructor» nunca se ejecutará automáticamente.

🔧 Constructor por defecto

Cuando escribes una clase sin declarar ningún constructor, el compilador de Java genera automáticamente un constructor por defecto (default constructor). Este constructor no recibe parámetros y no ejecuta ninguna lógica adicional — simplemente permite crear instancias de la clase con new.

Java
// Clase sin constructor explícito
public class Punto {
    private int x;
    private int y;
    // Java genera implícitamente: public Punto() { }
}

// Funciona porque existe el constructor por defecto implícito
Punto origen = new Punto(); // x = 0, y = 0

El constructor por defecto generado automáticamente tiene la misma visibilidad que la clase. Si la clase es public, el constructor será public; si la clase tiene acceso de paquete, el constructor también.

⚠️
Regla crítica: En cuanto declaras cualquier constructor (con o sin parámetros), Java deja de generar el constructor por defecto. Si sigues necesitando crear objetos sin argumentos, deberás declarar explícitamente un constructor sin parámetros.
Java
public class Punto {
    private int x;
    private int y;

    // Constructor parametrizado → el por defecto YA NO EXISTE
    public Punto(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

// ❌ Error de compilación: no existe Punto()
Punto origen = new Punto();

// ✅ Correcto: usa el constructor que sí existe
Punto p = new Punto(5, 10);

🔹 Valores por defecto de los atributos

Cuando un constructor no asigna valores explícitos a los atributos, Java les asigna sus valores por defecto según su tipo:

TipoValor por defecto
int, short, byte, long0
float, double0.0
booleanfalse
char'\u0000' (carácter nulo)
Referencias a objetos (String, arrays, etc.)null

⚙️ Constructor parametrizado

Un constructor parametrizado recibe uno o más argumentos que permiten personalizar el estado inicial del objeto en el momento de su creación. Es el tipo de constructor más utilizado en programación profesional, ya que garantiza que los objetos se creen con datos significativos desde el primer instante.

Java
public class CuentaBancaria {
    private String titular;
    private double saldo;
    private String numeroCuenta;

    public CuentaBancaria(String titular, double saldoInicial, String numeroCuenta) {
        if (titular == null || titular.isBlank()) {
            throw new IllegalArgumentException("El titular no puede estar vacío");
        }
        if (saldoInicial < 0) {
            throw new IllegalArgumentException("El saldo inicial no puede ser negativo");
        }
        this.titular = titular;
        this.saldo = saldoInicial;
        this.numeroCuenta = numeroCuenta;
    }

    public String getTitular() { return titular; }
    public double getSaldo() { return saldo; }
    public String getNumeroCuenta() { return numeroCuenta; }
}

// Creación del objeto con datos específicos
CuentaBancaria cuenta = new CuentaBancaria("María García", 1500.00, "ES76-0182-1234");
Buena práctica: Un constructor parametrizado debe validar sus argumentos antes de asignarlos. Si los datos no son válidos, lanza una excepción explicativa. Esto implementa el principio de fail-fast: el error se detecta en el momento de la creación, no durante una operación posterior.

🔄 Sobrecarga de constructores

Al igual que los métodos, los constructores en Java admiten sobrecarga (overloading): una misma clase puede tener múltiples constructores siempre que difieran en el número, tipo u orden de sus parámetros. Esto ofrece flexibilidad al usuario de la clase, permitiéndole crear objetos de distintas formas según la información disponible.

Java
public class Producto {
    private String nombre;
    private double precio;
    private int stock;
    private String categoria;

    // Constructor completo
    public Producto(String nombre, double precio, int stock, String categoria) {
        this.nombre = nombre;
        this.precio = precio;
        this.stock = stock;
        this.categoria = categoria;
    }

    // Constructor sin categoría (valor por defecto)
    public Producto(String nombre, double precio, int stock) {
        this(nombre, precio, stock, "Sin categoría");
    }

    // Constructor mínimo: solo nombre y precio
    public Producto(String nombre, double precio) {
        this(nombre, precio, 0, "Sin categoría");
    }

    @Override
    public String toString() {
        return nombre + " - " + precio + "€ (stock: " + stock + ")";
    }
}

// Las tres formas de crear un Producto son válidas:
Producto p1 = new Producto("Portátil", 899.99, 50, "Electrónica");
Producto p2 = new Producto("Ratón", 29.99, 200);
Producto p3 = new Producto("Cable HDMI", 12.50);

Observa cómo los constructores más simples delegan en el constructor completo usando this(...). Esto evita duplicar la lógica de inicialización y asegura que toda validación se centralice en un único punto.

🔑 La palabra clave this en constructores

La palabra reservada this tiene dos usos fundamentales dentro de los constructores de Java, y dominar ambos es esencial para escribir código limpio y profesional.

🔹 Uso 1: Diferenciar atributos de parámetros

Cuando un parámetro del constructor tiene el mismo nombre que un atributo de la clase, this permite referirse explícitamente al atributo del objeto actual, eliminando la ambigüedad:

Java
public class Rectangulo {
    private double ancho;
    private double alto;

    public Rectangulo(double ancho, double alto) {
        this.ancho = ancho;   // this.ancho = atributo; ancho = parámetro
        this.alto = alto;     // this.alto  = atributo; alto  = parámetro
    }
}

🔹 Uso 2: Llamar a otro constructor de la misma clase

La invocación this(...) permite que un constructor delegue en otro constructor de la misma clase. Esta llamada debe ser la primera instrucción del cuerpo del constructor:

Java
public class Rectangulo {
    private double ancho;
    private double alto;

    // Constructor principal
    public Rectangulo(double ancho, double alto) {
        if (ancho <= 0 || alto <= 0) {
            throw new IllegalArgumentException("Las dimensiones deben ser positivas");
        }
        this.ancho = ancho;
        this.alto = alto;
    }

    // Constructor para cuadrados: delega en el principal
    public Rectangulo(double lado) {
        this(lado, lado); // ← Debe ser la PRIMERA instrucción
    }

    // Constructor por defecto: cuadrado unitario
    public Rectangulo() {
        this(1.0, 1.0);
    }

    public double area() { return ancho * alto; }
}

Rectangulo r1 = new Rectangulo(10, 5);  // 10×5
Rectangulo r2 = new Rectangulo(7);       // 7×7 (cuadrado)
Rectangulo r3 = new Rectangulo();        // 1×1 (unitario)
⚠️
Error frecuente: No puedes usar this() y super() en el mismo constructor, ya que ambas deben ser la primera instrucción. Tampoco puedes crear llamadas circulares (A llama a B y B llama a A): el compilador lo detecta y genera un error.

🔗 Encadenamiento de constructores

El encadenamiento de constructores (constructor chaining) es el patrón profesional que consiste en que los constructores más simples delegan en constructores más completos mediante this(...), formando una cadena que termina en un constructor principal que contiene toda la lógica de inicialización y validación.

Java
public class Pedido {
    private String cliente;
    private String producto;
    private int cantidad;
    private double descuento;
    private boolean urgente;

    // Constructor PRINCIPAL (toda la lógica aquí)
    public Pedido(String cliente, String producto, int cantidad,
                  double descuento, boolean urgente) {
        if (cliente == null || cliente.isBlank()) {
            throw new IllegalArgumentException("Cliente requerido");
        }
        if (cantidad <= 0) {
            throw new IllegalArgumentException("Cantidad debe ser positiva");
        }
        if (descuento < 0 || descuento > 100) {
            throw new IllegalArgumentException("Descuento debe estar entre 0 y 100");
        }
        this.cliente = cliente;
        this.producto = producto;
        this.cantidad = cantidad;
        this.descuento = descuento;
        this.urgente = urgente;
    }

    // Sin urgencia
    public Pedido(String cliente, String producto, int cantidad, double descuento) {
        this(cliente, producto, cantidad, descuento, false);
    }

    // Sin descuento ni urgencia
    public Pedido(String cliente, String producto, int cantidad) {
        this(cliente, producto, cantidad, 0.0, false);
    }

    // Pedido unitario
    public Pedido(String cliente, String producto) {
        this(cliente, producto, 1, 0.0, false);
    }
}

Este patrón garantiza que la validación de datos se realice en un único punto. Si mañana necesitas añadir una nueva regla de negocio (por ejemplo, que el descuento máximo sea 50% para ciertos productos), solo modificas el constructor principal y todos los demás se benefician automáticamente.

📋 Constructor copia

Java no proporciona un constructor copia de forma nativa como C++, pero es una práctica habitual implementarlo manualmente. Un constructor copia recibe como parámetro un objeto de la misma clase y crea una nueva instancia independiente con los mismos valores en sus atributos.

Java
public class Direccion {
    private String calle;
    private String ciudad;
    private String codigoPostal;

    // Constructor parametrizado
    public Direccion(String calle, String ciudad, String codigoPostal) {
        this.calle = calle;
        this.ciudad = ciudad;
        this.codigoPostal = codigoPostal;
    }

    // Constructor copia
    public Direccion(Direccion otra) {
        this(otra.calle, otra.ciudad, otra.codigoPostal);
    }

    // Getters...
    public String getCalle() { return calle; }
    public String getCiudad() { return ciudad; }
}

// Uso del constructor copia
Direccion original = new Direccion("Gran Vía 1", "Madrid", "28013");
Direccion copia = new Direccion(original);

// Son objetos independientes: modificar uno no afecta al otro
System.out.println(original == copia);       // false (referencias distintas)
System.out.println(original.getCalle().equals(copia.getCalle())); // true (mismo valor)
💡
Copia superficial vs. copia profunda: Si la clase contiene atributos que son objetos mutables (listas, arrays, otros objetos), un constructor copia superficial (shallow copy) copiará solo las referencias. Para una copia profunda (deep copy), debes crear nuevas instancias de cada objeto mutable contenido. Esto es especialmente importante cuando trabajas con colecciones como ArrayList o HashMap.
Java
import java.util.ArrayList;
import java.util.List;

public class Carrito {
    private String cliente;
    private List<String> productos;

    public Carrito(String cliente) {
        this.cliente = cliente;
        this.productos = new ArrayList<>();
    }

    // Constructor copia PROFUNDA: crea nueva lista
    public Carrito(Carrito otro) {
        this.cliente = otro.cliente;
        this.productos = new ArrayList<>(otro.productos); // Nueva lista con mismos elementos
    }

    public void agregarProducto(String producto) {
        productos.add(producto);
    }

    public List<String> getProductos() { return productos; }
}

Carrito c1 = new Carrito("Ana");
c1.agregarProducto("Teclado");
c1.agregarProducto("Monitor");

Carrito c2 = new Carrito(c1);       // Copia profunda
c2.agregarProducto("Ratón");        // Solo afecta a c2

System.out.println(c1.getProductos()); // [Teclado, Monitor]
System.out.println(c2.getProductos()); // [Teclado, Monitor, Ratón]

🧬 Constructores y herencia: super()

Los constructores no se heredan en Java. Una clase hija no dispone automáticamente de los constructores de su clase padre. Sin embargo, toda clase hija debe invocar un constructor de la clase padre para inicializar la parte heredada del objeto. Esto se hace mediante super(...).

▶️ Llamada implícita a super()

Si el constructor de la clase hija no incluye una llamada explícita a super(...), el compilador inserta automáticamente super() sin argumentos como primera instrucción. Esto solo funciona si la clase padre tiene un constructor sin parámetros:

Java
public class Animal {
    private String nombre;

    public Animal() {
        this.nombre = "Sin nombre";
    }

    public Animal(String nombre) {
        this.nombre = nombre;
    }

    public String getNombre() { return nombre; }
}

public class Perro extends Animal {

    private String raza;

    // Java inserta automáticamente super() → llama a Animal()
    public Perro(String raza) {
        // super(); ← insertado implícitamente
        this.raza = raza;
    }

    // Llamada explícita al constructor con parámetro
    public Perro(String nombre, String raza) {
        super(nombre);    // ← Llama a Animal(String nombre)
        this.raza = raza;
    }
}

Perro p1 = new Perro("Labrador");             // nombre = "Sin nombre", raza = "Labrador"
Perro p2 = new Perro("Rex", "Pastor Alemán"); // nombre = "Rex", raza = "Pastor Alemán"

▶️ Orden de ejecución con herencia

En una jerarquía de herencia, los constructores se ejecutan de padre a hijo. Primero se completa la inicialización de la clase más alta en la jerarquía (en última instancia, Object), y después se ejecutan los constructores de las clases derivadas en orden descendente:

Java
public class A {
    public A() { System.out.println("Constructor de A"); }
}

public class B extends A {
    public B() { System.out.println("Constructor de B"); }
}

public class C extends B {
    public C() { System.out.println("Constructor de C"); }
}

new C();
// Salida:
// Constructor de A
// Constructor de B
// Constructor de C
💡
Concepto clave: El orden de ejecución es siempre ascendente en la jerarquía: primero se construye la parte más genérica del objeto (la clase padre) y luego se añaden las capas específicas (las clases hijas). Esto garantiza que los atributos heredados estén inicializados antes de que la clase hija intente usarlos.

🚫 Errores frecuentes con constructores

A continuación se documentan los errores más comunes que cometen tanto principiantes como programadores con experiencia al trabajar con constructores en Java.

❌ Error 1: Añadir tipo de retorno al constructor

Java
public class Persona {
    private String nombre;

    // ❌ INCORRECTO: esto NO es un constructor, es un método
    public void Persona(String nombre) {
        this.nombre = nombre;
    }
}

Persona p = new Persona("Ana"); // Error: no existe constructor con String

❌ Error 2: Olvidar declarar constructor sin parámetros tras crear uno parametrizado

Java
public class Sensor {
    private String tipo;

    public Sensor(String tipo) {
        this.tipo = tipo;
    }
}

// ❌ Error de compilación: no existe Sensor()
Sensor s = new Sensor();

// ✅ Solución: añadir constructor sin parámetros explícitamente
// public Sensor() { this("genérico"); }

❌ Error 3: No llamar a super() cuando la clase padre no tiene constructor vacío

Java
public class Vehiculo {
    private String matricula;

    // Único constructor: requiere matrícula
    public Vehiculo(String matricula) {
        this.matricula = matricula;
    }
}

public class Moto extends Vehiculo {
    private int cilindrada;

    // ❌ Error: el compilador inserta super() pero Vehiculo() no existe
    public Moto(int cilindrada) {
        this.cilindrada = cilindrada;
    }

    // ✅ Correcto: llamada explícita a super(String)
    // public Moto(String matricula, int cilindrada) {
    //     super(matricula);
    //     this.cilindrada = cilindrada;
    // }
}

❌ Error 4: Encadenamiento circular de constructores

Java
public class Dato {
    // ❌ Error de compilación: recursive constructor invocation
    public Dato() {
        this(0);
    }
    public Dato(int valor) {
        this(); // Llama al anterior, que llama a este → bucle infinito
    }
}

✅ Buenas prácticas profesionales

Las siguientes recomendaciones provienen de la experiencia acumulada en proyectos Java empresariales y de las guías de estilo de referencia como Effective Java de Joshua Bloch.

PrácticaDescripción
Validar siempre los parámetrosComprueba null, rangos, formatos. Lanza IllegalArgumentException o NullPointerException con mensaje descriptivo.
Usar encadenamiento con this()Centraliza la lógica de inicialización en un constructor principal. Los demás delegan en él.
Preferir constructor sobre setters para campos obligatoriosLos campos esenciales deben establecerse en el constructor, no mediante setters que podrían no invocarse.
Declarar atributos como final cuando sea posibleUn atributo final debe inicializarse en el constructor y no puede cambiar después. Esto favorece la inmutabilidad.
Considerar el patrón Builder para muchos parámetrosSi un constructor tiene más de 4-5 parámetros, el patrón Builder ofrece mejor legibilidad y flexibilidad.
No realizar trabajo pesado en el constructorEvita conexiones a bases de datos, llamadas a red o lecturas de ficheros. Usa métodos de inicialización separados.
Documentar con JavadocLos constructores públicos deben tener Javadoc que describa los parámetros y las excepciones posibles.
Consejo profesional: Si tu clase es inmutable (todos los atributos son final y la clase no tiene setters), los constructores son la única forma de establecer el estado del objeto. Esto es un patrón de diseño muy valorado en programación concurrente porque los objetos inmutables son inherentemente thread-safe.

🏢 Ejemplo integrador: sistema de gestión de empleados

Este ejemplo reúne todos los conceptos vistos en el artículo: constructor por defecto, parametrizado, sobrecarga, encadenamiento con this(), herencia con super(), constructor copia y validación de datos.

Java
// ─── Clase base: Empleado ───────────────────────────────────
public class Empleado {
    private final String nombre;
    private final String dni;
    private double salarioBase;
    private String departamento;

    // Constructor principal con validación
    public Empleado(String nombre, String dni, double salarioBase, String departamento) {
        if (nombre == null || nombre.isBlank()) {
            throw new IllegalArgumentException("El nombre no puede estar vacío");
        }
        if (dni == null || !dni.matches("\\d{8}[A-Z]")) {
            throw new IllegalArgumentException("DNI inválido: debe ser 8 dígitos + letra");
        }
        if (salarioBase < 0) {
            throw new IllegalArgumentException("El salario no puede ser negativo");
        }
        this.nombre = nombre;
        this.dni = dni;
        this.salarioBase = salarioBase;
        this.departamento = (departamento != null) ? departamento : "Sin asignar";
    }

    // Constructor sin departamento
    public Empleado(String nombre, String dni, double salarioBase) {
        this(nombre, dni, salarioBase, "Sin asignar");
    }

    // Constructor copia
    public Empleado(Empleado otro) {
        this(otro.nombre, otro.dni, otro.salarioBase, otro.departamento);
    }

    // Getters
    public String getNombre() { return nombre; }
    public String getDni() { return dni; }
    public double getSalarioBase() { return salarioBase; }
    public String getDepartamento() { return departamento; }

    public double calcularSalarioMensual() {
        return salarioBase;
    }

    @Override
    public String toString() {
        return String.format("%s (DNI: %s) - Dpto: %s - Salario: %.2f€",
                nombre, dni, departamento, calcularSalarioMensual());
    }
}

// ─── Clase hija: Gerente ────────────────────────────────────
public class Gerente extends Empleado {
    private double bonificacion;
    private int empleadosACargo;

    public Gerente(String nombre, String dni, double salarioBase,
                   String departamento, double bonificacion, int empleadosACargo) {
        super(nombre, dni, salarioBase, departamento);
        if (bonificacion < 0) {
            throw new IllegalArgumentException("La bonificación no puede ser negativa");
        }
        this.bonificacion = bonificacion;
        this.empleadosACargo = empleadosACargo;
    }

    public Gerente(String nombre, String dni, double salarioBase, String departamento) {
        this(nombre, dni, salarioBase, departamento, 0.0, 0);
    }

    @Override
    public double calcularSalarioMensual() {
        return getSalarioBase() + bonificacion;
    }

    @Override
    public String toString() {
        return super.toString() + String.format(" [Gerente - %d empleados a cargo]",
                empleadosACargo);
    }
}

// ─── Clase hija: Becario ────────────────────────────────────
public class Becario extends Empleado {
    private int horasSemanales;
    private String universidad;

    public Becario(String nombre, String dni, double salarioBase,
                   String departamento, int horasSemanales, String universidad) {
        super(nombre, dni, salarioBase, departamento);
        if (horasSemanales <= 0 || horasSemanales > 40) {
            throw new IllegalArgumentException("Horas semanales deben estar entre 1 y 40");
        }
        this.horasSemanales = horasSemanales;
        this.universidad = (universidad != null) ? universidad : "No especificada";
    }

    public Becario(String nombre, String dni, int horasSemanales, String universidad) {
        this(nombre, dni, 600.0, "Formación", horasSemanales, universidad);
    }

    @Override
    public double calcularSalarioMensual() {
        return getSalarioBase() * horasSemanales / 40.0;
    }

    @Override
    public String toString() {
        return super.toString() + String.format(" [Becario - %dh/semana - %s]",
                horasSemanales, universidad);
    }
}

// ─── Programa principal ─────────────────────────────────────
public class Main {
    public static void main(String[] args) {
        Empleado emp = new Empleado("Laura Martín", "12345678A", 2400.0, "Desarrollo");
        Gerente ger = new Gerente("Carlos Ruiz", "87654321B", 4500.0, "Dirección", 1200.0, 15);
        Becario bec = new Becario("Sofía López", "11223344C", 20, "Universidad Complutense");

        // Constructor copia
        Empleado copiaEmp = new Empleado(emp);

        System.out.println(emp);
        System.out.println(ger);
        System.out.println(bec);
        System.out.println("Copia: " + copiaEmp);
        System.out.println("¿Son el mismo objeto? " + (emp == copiaEmp)); // false
    }
}

Este ejemplo muestra cómo una jerarquía de clases bien diseñada utiliza constructores encadenados, validación en cada nivel, delegación con super() y el polimorfismo en calcularSalarioMensual() para producir código profesional, mantenible y extensible.

📝 Ejercicios resueltos

Ejercicio 1: Clase Libro con múltiples constructores

Crea una clase Libro con los atributos titulo (String), autor (String), paginas (int) y isbn (String). Implementa tres constructores: uno completo, uno sin ISBN (valor por defecto "000-0-00-000000-0") y uno que solo reciba título y autor (con páginas = 0). Usa encadenamiento de constructores. Incluye un constructor copia.

Ejercicio 2: Jerarquía Figura → Circulo → Cilindro

Crea la clase base Figura con un atributo color (String). Luego crea Circulo que extienda Figura añadiendo radio (double). Finalmente, crea Cilindro que extienda Circulo añadiendo altura (double). Cada clase debe llamar a super() correctamente. Incluye un método volumen() en Cilindro.

Ejercicio 3: Clase inmutable Coordenada con constructor copia

Diseña una clase Coordenada totalmente inmutable (todos los atributos final, sin setters) con atributos x, y y z (double). Implementa un constructor completo, uno para coordenadas 2D (z = 0), uno para el origen (0, 0, 0) y un constructor copia. Añade un método distanciaA(Coordenada otra) que calcule la distancia euclidiana en 3D.

❓ Preguntas frecuentes sobre Constructores en Java

Las dudas más comunes respondidas de forma clara y directa.

Un constructor es un bloque de código especial que se ejecuta automáticamente al crear un objeto con new. Su función principal es inicializar los atributos del objeto y garantizar que este se encuentre en un estado válido desde el primer momento. A diferencia de un método convencional, el constructor no tiene tipo de retorno y su nombre debe coincidir exactamente con el de la clase.
El constructor por defecto no recibe parámetros y asigna valores iniciales predeterminados a los atributos del objeto. Java lo proporciona automáticamente si no defines ningún constructor. El constructor parametrizado, en cambio, recibe argumentos que permiten personalizar el estado inicial del objeto al momento de crearlo. En cuanto defines al menos un constructor con parámetros, Java deja de generar el constructor por defecto.
Sí, mediante this() seguido de los argumentos correspondientes. Esto se denomina encadenamiento de constructores y debe ser la primera instrucción del constructor que realiza la llamada. Es una técnica profesional que evita duplicar código de inicialización y centraliza la lógica en un único constructor principal.
Los constructores no tienen tipo de retorno (ni siquiera void) porque su propósito no es devolver un valor, sino inicializar un objeto que acaba de ser creado por la JVM. Si añadieras un tipo de retorno, Java lo interpretaría como un método regular y no como un constructor, lo que provocaría que no se ejecutase automáticamente al usar new.
Si la clase hija no incluye una llamada explícita a super(), el compilador de Java inserta automáticamente super() sin argumentos como primera instrucción del constructor hijo. Esto funciona correctamente solo si la clase padre dispone de un constructor sin parámetros. Si la clase padre únicamente tiene constructores con parámetros, se producirá un error de compilación y será obligatorio añadir super(...) con los argumentos adecuados.
Un constructor copia es un constructor que recibe como parámetro un objeto de la misma clase y crea una nueva instancia con los mismos valores en sus atributos. Se utiliza cuando se necesita duplicar un objeto sin que ambas referencias apunten al mismo espacio en memoria. Es especialmente útil con objetos mutables para evitar efectos secundarios no deseados al compartir referencias.
Sí, un constructor puede declararse como private. Esto impide que otras clases puedan instanciar objetos de esa clase directamente con new. Es una técnica habitual en el patrón de diseño Singleton, donde se controla que exista una única instancia de la clase, y en clases de utilidad (utility classes) que solo contienen métodos estáticos y no deben instanciarse.
Valora este artículo

💬 Foro de discusión

¿Tienes dudas sobre Constructores en Java? Comparte tu pregunta con la comunidad.

¿Tienes cuenta? o comenta como invitado ↓

Todavía no hay mensajes. ¡Sé el primero en participar!