Java
static y final
Qué significa que algo pertenezca a la clase y no al objeto, por qué main es static, y qué garantiza exactamente final en una variable, un método y una clase.
Dos palabras que vienes arrastrando
Desde la primera línea de Java que escribiste está ahí:
public static void main(String[] args) { }Y en Métodos apareció otra vez, con la explicación en suspenso: “el método pertenece a la clase, no requiere crear un objeto”. Ahora que existen los objetos, esa frase se puede terminar.
static: pertenecer a la clase, no al objeto
static se traduce como “estático”, en el sentido de “fijo, que no se mueve”. La idea: un miembro static existe una sola vez, en la clase, en vez de una vez por cada objeto.
public class Alumno {
private String nombre; // uno por cada Alumno
private static int totalAlumnos; // UNO para todos los Alumno
public Alumno(String nombre) {
this.nombre = nombre;
totalAlumnos++;
}
public static int getTotalAlumnos() {
return totalAlumnos;
}
}new Alumno("Ana");
new Alumno("Beto");
new Alumno("Caro");
System.out.println(Alumno.getTotalAlumnos());3Cada new Alumno(...) crea su propio nombre, pero totalAlumnos es uno solo que todos comparten e incrementan. Y fíjate en cómo se llama al método: Alumno.getTotalAlumnos(), con el nombre de la clase, sin ningún objeto de por medio. Eso es lo que significa “pertenece a la clase”.
Miembro de instancia (sin static) | Miembro de clase (static) | |
|---|---|---|
| Cuántas copias hay | Una por cada objeto | Una sola, compartida |
| Cómo se accede | objeto.metodo() | Clase.metodo() |
| Cuándo existe | Desde que se hace new | Desde que la clase se carga, antes de cualquier new |
Puede usar this | Sí | No — no hay ningún objeto al que referirse |
Por qué main es static
Con eso, la respuesta es directa: cuando lanzas java Main, la JVM tiene que ejecutar main antes de que exista ningún objeto — de hecho, main es el lugar donde se crea el primero. Si main no fuera static, harían falta un objeto para llamarlo y un main para crear ese objeto: un círculo sin salida. Declararlo static lo hace invocable con solo el nombre de la clase, que es todo lo que la JVM tiene al arrancar.
El error clásico: non-static … cannot be referenced from a static context
Este mensaje aparece siempre, y ahora tiene explicación:
public class Main {
private int contador = 0;
public static void main(String[] args) {
contador++; // ❌ non-static variable contador
// cannot be referenced from a static context
}
}contador existe una vez por objeto, pero main corre sin ningún objeto: no hay ningún contador concreto al que sumarle uno. Las dos salidas:
public static void main(String[] args) {
Main app = new Main();
app.contador++;
}private static int contador = 0;
public static void main(String[] args) {
contador++;
}La tentación al ver ese error es agregar static a todo hasta que compile. Funciona, pero convierte el programa en una única bolsa de variables globales, que es justo lo que la encapsulación evita. Pregúntate primero si el dato es de verdad único para todo el programa; si es de cada objeto, la respuesta es la opción A.
Métodos static útiles de verdad
El uso más limpio de static son los métodos que no dependen de ningún estado: reciben datos, calculan y devuelven. Ya vienes usando varios:
Math.max(3, 7); // Math nunca se instancia
Integer.parseInt("42");
Double.compare(a, b);
Collections.sort(lista);Ninguna de esas llamadas necesitó un new. Son funciones de utilidad, y static es exactamente su lugar.
final: esto ya no cambia
final se traduce como “final” en el sentido de “definitivo”. Significa cosas ligeramente distintas según dónde se escriba:
| Dónde | Qué garantiza |
|---|---|
| En una variable | Se le asigna un valor una sola vez; reasignarla no compila |
| En un método | Ninguna subclase puede sobrescribirlo |
| En una clase | Ninguna clase puede heredar de ella (extends no compila) |
final en variables: constantes
public class Circulo {
private static final double PI = 3.14159;
private final String id;
public Circulo(String id) {
this.id = id; // ✅ primera y única asignación
}
}static final juntos es la forma canónica de una constante: una sola en toda la aplicación (static) e inmodificable (final). La convención de nombres para estas es MAYÚSCULAS_CON_GUION_BAJO, y verla escrita así en código ajeno es la señal de que estás mirando una constante:
public static final int MAX_ALUMNOS = 40;
public static final String VERSION = "2.1";id en cambio es final sin static: cada círculo tiene el suyo, asignado en el constructor, y después nadie puede cambiarlo. Es la forma de expresar en el propio tipo lo que en Encapsulación se resolvía “poniendo solo getter”: ahora ni siquiera la propia clase puede reasignarlo por accidente.
final en una variable de tipo objeto congela la referencia, no el contenido. Una final List<String> nombres no se puede hacer apuntar a otra lista, pero nombres.add("Ana") funciona perfectamente. Es el mismo “la variable guarda una referencia” de Object, toString y equals, visto desde el otro lado.
final en métodos y clases
public class Cuenta {
public final double getSaldo() { ... } // ninguna hija puede cambiarlo
}
public final class Config { ... } // de esta nadie heredaSe usa cuando el comportamiento es parte de una garantía que no debe poder romperse desde afuera. El ejemplo que ya conoces es String: es una clase final, y por eso nadie puede escribir un MiString extends String que se comporte raro donde el resto del programa espera un String normal.
static y final juntos, en una tabla
| Declaración | Significa |
|---|---|
int x; | Un valor por objeto, modificable |
final int x; | Un valor por objeto, fijado en el constructor |
static int x; | Un valor compartido por toda la clase, modificable |
static final int X; | Una constante: única y fija — el caso más común |
Pon a prueba
public class Contador {
int personales = 0;
static int compartido = 0;
public Contador() {
personales++;
compartido++;
}
}
// en main:
new Contador();
new Contador();
Contador c = new Contador();
System.out.println(c.personales);
System.out.println(Contador.compartido);Este código tiene tres errores de compilación. ¿Cuáles son y cómo se arregla cada uno?
public class Config {
private static final int MAX = 100;
private String entorno = "dev";
public static void mostrar() {
System.out.println(entorno);
}
public static void subirTope() {
MAX = 200;
}
public void usarThis() {
System.out.println(this.MAX);
}
}1. System.out.println(entorno) dentro de mostrar(). mostrar() es static y entorno es de instancia: no hay ningún objeto del que leer el entorno. Se arregla haciendo entorno también static, o quitándole el static a mostrar().
2. MAX = 200;. MAX es final: ya tiene su valor y reasignarlo da cannot assign a value to final variable MAX. Si el tope tiene que poder cambiar, no debería ser final.
3. this.MAX. Este es más sutil y en muchos compiladores es solo una advertencia, no un error: acceder a un miembro static a través de this (o de un objeto) funciona pero es engañoso, porque sugiere que hay un MAX por objeto cuando hay uno solo. La forma correcta es Config.MAX o simplemente MAX.
El patrón detrás de los tres: desde un contexto static no existe ningún objeto, y final significa que la asignación ya ocurrió.
Banco tiene una tasa fija compartida por todas las cuentas, un número de cuenta propio que no cambia nunca una vez creado, y un método de utilidad que no necesita objeto.
public class Banco {
public static double TASA = 0.03;
private String numeroCuenta;
public Banco(String numeroCuenta) {
.numeroCuenta = numeroCuenta;
}
public double interesDe(double monto) {
return monto * TASA;
}
}Ordena las líneas para escribir una clase con una constante, un contador compartido y un getter estático.
- activas++;
- public class Sesion {
- }
- public static int getActivas() {
- }
- private static int activas = 0;
- public static final int MAX_INTENTOS = 3;
- return activas;
- public Sesion() {
- }
Siguiente paso
Con esto cierra la orientación a objetos. Falta lo que pasa cuando el programa se encuentra con algo que no puede resolver — una nota inválida, un archivo que no existe, una división por cero — y en vez de seguir como si nada, corta y avisa: ver Excepciones: try, catch y throw.