Crear objetos de una clase

Ya creamos nuestra clase CajaTexto. Ahora viene el siguiente paso natural: crear objetos a partir de esa clase.

En Java, un objeto es una instancia de una clase. Es decir: tomamos la plantilla (la clase) y construimos una versión real en memoria (el objeto). Para crear esa instancia usamos el operador new.


Crear la primera instancia con new

Volvemos a nuestra clase principal (Main) y dentro del método main declaramos una variable de tipo CajaTexto. La llamamos cajita1 y la inicializamos con new:

public class Main {
    public static void main(String[] args) {
        CajaTexto cajita1 = new CajaTexto();
    }
}

Aquí está pasando algo muy importante:

  • CajaTexto (a la izquierda) es el tipo de la variable.
  • cajita1 es el nombre de la variable.
  • new CajaTexto() crea una nueva instancia de la clase CajaTexto en memoria.

Podemos simplificar usando var

Antes de continuar, hay algo que podemos mejorar. Observa que la palabra CajaTexto aparece dos veces:

CajaTexto cajita1 = new CajaTexto();

Esto funciona perfecto, pero se puede ver repetitivo. En Java podemos simplificarlo usando var:

public class Main {
    public static void main(String[] args) {
        var cajita1 = new CajaTexto();
    }
}

Con var, el compilador deduce automáticamente el tipo basado en lo que hay a la derecha del =. En este caso, como a la derecha tenemos new CajaTexto(), Java entiende que cajita1 es de tipo CajaTexto.


var en Java no es como var en JavaScript

Ojo con esto: var en Java no funciona como en JavaScript.

En JavaScript podrías hacer algo como:

  • declarar una variable
  • y luego cambiarle el tipo de valor

Pero en Java no se puede porque Java es un lenguaje de tipado estático: el tipo se fija y no cambia durante la ejecución del programa.

Por ejemplo, esto no es válido en Java:

public class Main {
    public static void main(String[] args) {
        var cajita1 = new CajaTexto();
        cajita1 = 1; // ❌ error: no puedes cambiarle el tipo
    }
}

Una vez que Java determina que cajita1 es un CajaTexto, esa variable solo podrá referirse a objetos de ese tipo.


Acceder a los miembros con el operador punto

Ahora que tenemos un objeto, usamos el operador punto (.) para ver lo que ese objeto puede hacer y lo que ese objeto tiene.

Si escribes:

cajita1.

verás cosas como:

  • el método setText(...)
  • el método clear()
  • y el field texto

Probemos llamar un método y luego imprimir el texto:

public class Main {
    public static void main(String[] args) {
        var cajita1 = new CajaTexto();
        cajita1.setText("Cajita 1");
        System.out.println(cajita1.texto);
    }
}

Ejecutamos el programa y obtenemos:

  • Cajita 1

Perfecto.


¿Qué pasa si no llamamos setText?

Ahora comentemos la línea de setText e imprimamos directamente el field:

public class Main {
    public static void main(String[] args) {
        var cajita1 = new CajaTexto();
        // cajita1.setText("Cajita 1");
        System.out.println(cajita1.texto);
    }
}

El resultado será null.

¿Por qué? Porque texto es un String, y los String son tipos de referencia.

En Java tenemos:

  • tipos primitivos: int, boolean, char, etc.
  • tipos de referencia: todo lo demás (como String, arreglos, objetos personalizados…)

Y cuando un tipo de referencia no se inicializa, por defecto queda en null.


Por qué null puede ser peligroso

El problema con null es que puede hacer que el programa se bloquee.

Mira qué pasa si intentamos convertir el texto a mayúsculas:

public class Main {
    public static void main(String[] args) {
        var cajita1 = new CajaTexto();
        // cajita1.setText("Cajita 1");
        System.out.println(cajita1.texto.toUpperCase());
    }
}

Aquí texto es null, así que no existe un objeto real en memoria al que podamos llamarle métodos. Y como estamos intentando ejecutar toUpperCase() sobre algo inexistente, Java lanza una excepción muy famosa:

  • NullPointerException

En pocas palabras: intentamos usar un objeto… que en realidad no estaba creado.


Evitar null: inicializar el field

Una forma simple de evitar este problema es inicializar el field texto desde el inicio con una cadena vacía. Así, texto nunca será null.

public class CajaTexto {
    public String texto = ""; // ✅ inicializado

    public void setText(String texto) {
        this.texto = texto;
    }

    public void clear() {
        texto = "";
    }
}

Ahora, si ejecutamos este código:

public class Main {
    public static void main(String[] args) {
        var cajita1 = new CajaTexto();
        System.out.println(cajita1.texto.toUpperCase());
    }
}

Ya no se rompe. Solo imprime una línea en blanco (porque el texto está vacío), pero el programa está estable.


Dos objetos: mismos métodos, estados diferentes

Ahora sí: volvamos a establecer texto, y creemos una segunda instancia.

public class Main {
    public static void main(String[] args) {
        var cajita1 = new CajaTexto();
        cajita1.setText("Cajita 1");
        System.out.println(cajita1.texto.toUpperCase());

        var cajita2 = new CajaTexto();
        cajita2.setText("Cajita 2");
        System.out.println(cajita2.texto);
    }
}

Al ejecutar, verás que:

  • cajita1 imprime CAJITA 1
  • cajita2 imprime Cajita 2

Esto demuestra una idea central de POO:

Aunque los objetos provienen de la misma clase (tienen los mismos fields y métodos), son completamente independientes. Cada uno está en un espacio distinto de memoria y puede tener datos distintos, o sea, puede estar en un estado distinto.

En este ejemplo, el estado de cajita1 (su texto) es diferente al estado de cajita2, y eso es exactamente lo que hace a los objetos tan poderosos.