🚀 ¿Qué es el método main?
El método main es el punto de entrada de cualquier aplicación Java. Cuando se ejecuta un programa, la Máquina Virtual de Java (JVM) busca dentro de la clase especificada un método con una firma muy concreta —public static void main(String[] args)— y comienza a ejecutar el código que contiene. Sin este método, la JVM no sabe por dónde empezar y el programa no puede arrancar.
Se puede pensar en el método main como la «puerta de entrada» de un edificio: un edificio puede tener cientos de habitaciones (clases y métodos), pero quien llega desde fuera necesita una puerta principal claramente identificada. La JVM actúa como ese visitante externo: localiza la puerta (main), la abre y a partir de ahí recorre el interior del programa.
public static void main(String[] args) tiene una razón de ser. No es una convención arbitraria: la JVM impone exactamente estos modificadores y tipos. A lo largo de este artículo se explica el porqué de cada uno.// El programa Java más sencillo posible public class HolaMundo { public static void main(String[] args) { System.out.println("¡Hola, Mundo!"); } }
Al compilar y ejecutar este archivo, la JVM localiza la clase HolaMundo, encuentra el método main y ejecuta la instrucción System.out.println, que imprime el texto en la consola. Este sencillo flujo es el mismo que sigue cualquier aplicación Java, por grande que sea: todo comienza en main.
📝 Sintaxis completa del método main
La firma del método main que la JVM reconoce como punto de entrada es exactamente la siguiente:
public static void main(String[] args) { // Código del programa }
Cada elemento de esta firma cumple una función específica que la JVM verifica antes de iniciar la ejecución:
| Elemento | Significado | Razón |
|---|---|---|
public |
Modificador de acceso | Visible desde cualquier lugar, incluida la JVM |
static |
Método de clase | Se invoca sin crear un objeto de la clase |
void |
Tipo de retorno | No devuelve ningún valor a la JVM |
main |
Nombre del método | Nombre fijo que la JVM busca literalmente |
String[] args |
Parámetro | Recibe los argumentos de línea de comandos |
static o se cambia void por int— el método sigue siendo válido como método Java normal, pero la JVM no lo reconocerá como punto de entrada y lanzará un error al intentar ejecutar la clase.🔓 ¿Por qué public?
El modificador public indica que el método es accesible desde cualquier punto del programa, incluidos paquetes externos y, por supuesto, la propia JVM. Cuando se ejecuta java MiClase, la Máquina Virtual necesita invocar main desde fuera del paquete de la aplicación. Si el método fuese private, protected o tuviese visibilidad de paquete (sin modificador), la JVM no podría acceder a él.
En la práctica, public aquí no es una decisión de diseño del programador, sino un requisito impuesto por la especificación del lenguaje. La JVM debe poder localizar y ejecutar main sin restricciones de visibilidad.
// Correcto: la JVM puede acceder a main public static void main(String[] args) { System.out.println("Programa iniciado"); } // Incorrecto: la JVM no puede acceder private static void main(String[] args) { System.out.println("Nunca se ejecutará"); }
⚡ ¿Por qué static?
La palabra clave static significa que el método pertenece a la clase y no a una instancia concreta de esa clase. Esto es fundamental: cuando la JVM inicia la ejecución, no existe ningún objeto creado todavía. Si main fuese un método de instancia, la JVM necesitaría crear primero un objeto de la clase, pero ¿cómo lo haría sin un constructor explícito o sin saber qué parámetros pasarle?
Al declarar main como static, se resuelve este problema de raíz: la JVM simplemente invoca MiClase.main(args) directamente sobre la clase, sin necesidad de instanciarla.
// La JVM ejecuta internamente algo equivalente a: HolaMundo.main(new String[]{}); // Esto funciona porque main es static. // Si no fuese static, se necesitaría: // HolaMundo obj = new HolaMundo(); ← ¿Con qué constructor? // obj.main(args); ← Problema resuelto con static
static no puede acceder directamente a variables ni métodos de instancia de su clase. Si desde main necesitas usar miembros no estáticos, primero debes crear un objeto: MiClase obj = new MiClase(); y luego invocar obj.miMetodo().🔹 Diferencia entre método de instancia y método de clase
Para entender mejor la razón del static, conviene distinguir claramente ambos tipos:
| Característica | Método de instancia | Método static (de clase) |
|---|---|---|
| Pertenece a | Un objeto concreto | La clase en sí |
| Requiere instancia | Sí: obj.metodo() |
No: Clase.metodo() |
Acceso a this |
Sí | No |
| Ejemplo típico | cuenta.getSaldo() |
Math.sqrt(25) |
🔇 ¿Por qué void?
El tipo de retorno void indica que el método no devuelve ningún valor. En el caso de main, esto tiene una razón histórica y práctica: la JVM no espera recibir un valor de retorno del programa. Cuando main termina, la JVM simplemente finaliza el proceso.
En lenguajes como C o C++, la función main devuelve un int que el sistema operativo interpreta como código de salida (0 = éxito, distinto de 0 = error). Java adoptó un enfoque diferente: si se necesita indicar un código de salida al sistema operativo, se utiliza System.exit(int código).
public class ValidadorEntrada { public static void main(String[] args) { if (args.length == 0) { System.err.println("Error: se requiere al menos un argumento"); System.exit(1); // Código de error para el SO } System.out.println("Argumento recibido: " + args[0]); // Si main termina normalmente, el código de salida es 0 } }
int main() y se devuelve un entero. En Java se escribe void main() y se usa System.exit() solo cuando se necesita un código de salida explícito. Si no se invoca System.exit(), Java devuelve 0 al sistema operativo automáticamente.📦 El parámetro String[] args
El parámetro String[] args es un array de cadenas que recoge los argumentos que el usuario pasa al programa a través de la línea de comandos. Cada palabra separada por espacios se convierte en un elemento del array.
// Archivo: Imprime.java public class Imprime { public static void main(String[] args) { System.out.println("Número de argumentos: " + args.length); for (int i = 0; i < args.length; i++) { System.out.println("args[" + i + "] = " + args[i]); } } }
Si se compila y ejecuta con argumentos:
$ javac Imprime.java $ java Imprime hola que tal estas
La salida será:
Número de argumentos: 4 args[0] = hola args[1] = que args[2] = tal args[3] = estas
🔹 ¿Qué pasa si no se pasan argumentos?
Si se ejecuta java Imprime sin nada más, el array args existe pero tiene longitud cero (args.length == 0). Nunca es null. Esto significa que siempre es seguro consultar args.length sin verificar null previamente.
🔹 Recibir números como argumentos
Los argumentos siempre llegan como String. Si se necesitan como números, hay que convertirlos explícitamente con métodos como Integer.parseInt() o Double.parseDouble():
public class Suma { public static void main(String[] args) { if (args.length != 2) { System.out.println("Uso: java Suma <num1> <num2>"); return; } int a = Integer.parseInt(args[0]); int b = Integer.parseInt(args[1]); System.out.println("La suma es: " + (a + b)); } }
$ java Suma 15 27
La suma es: 42
java Suma hola 5), Integer.parseInt() lanza una NumberFormatException. En programas profesionales, siempre se envuelve la conversión en un bloque try-catch.🔄 Variantes válidas de la firma
Aunque la forma más habitual es public static void main(String[] args), Java admite ciertas variaciones sintácticas que la JVM sigue reconociendo como punto de entrada válido:
| Firma | ¿Válida? | Observación |
|---|---|---|
public static void main(String[] args) |
✅ | Forma canónica y recomendada |
public static void main(String args[]) |
✅ | Corchetes junto a la variable (estilo C) |
public static void main(String... args) |
✅ | Varargs — equivalente a String[] |
static public void main(String[] args) |
✅ | Orden de modificadores invertido (válido pero poco convencional) |
public static void main(String[] parametros) |
✅ | Nombre del parámetro cambiado — irrelevante para la JVM |
public static int main(String[] args) |
❌ | Tipo de retorno distinto de void |
public void main(String[] args) |
❌ | Falta static |
static void main(String[] args) |
❌ | Falta public |
public static void main(String[] args). Las variantes funcionan pero confunden a otros programadores y herramientas de análisis estático.⚙️ Flujo de ejecución de la JVM
Cuando se escribe java MiClase arg1 arg2 en la terminal, la JVM realiza una secuencia precisa de pasos antes de que la primera línea de main se ejecute:
🔹 Paso 1: Carga de la clase
El Class Loader busca el archivo MiClase.class en el classpath. Si no lo encuentra, lanza ClassNotFoundException. Si lo encuentra, lee el bytecode y lo carga en memoria.
🔹 Paso 2: Enlace (linking)
La JVM verifica que el bytecode es válido (verificación), asigna memoria para las variables estáticas (preparación) y resuelve las referencias simbólicas (resolución).
🔹 Paso 3: Inicialización
Se ejecutan los bloques static { } de la clase y se asignan los valores iniciales a las variables estáticas. Este paso ocurre antes de que se invoque main.
🔹 Paso 4: Invocación de main
La JVM busca un método con la firma exacta public static void main(String[]). Si lo encuentra, crea el array de String con los argumentos de línea de comandos y lo pasa como parámetro. Si no lo encuentra, lanza NoSuchMethodError.
public class DemoFlujo { static { System.out.println("1. Bloque estático ejecutado"); } public static void main(String[] args) { System.out.println("2. Método main ejecutado"); } }
$ java DemoFlujo
1. Bloque estático ejecutado
2. Método main ejecutado
Observa que el bloque static se ejecuta antes que main. Este orden es determinista y forma parte de la especificación de Java.
🏗️ Ejemplo completo integrador
El siguiente programa combina todos los conceptos del artículo: argumentos de línea de comandos, conversión de tipos, validación de entrada, manejo de errores y uso de métodos estáticos y de instancia desde main.
/** * Calculadora de nómina simple. * Uso: java CalculadoraNomina <nombre> <horasTrabajadas> <precioPorHora> */ public class CalculadoraNomina { private String nombre; private int horas; private double precioHora; public CalculadoraNomina(String nombre, int horas, double precioHora) { this.nombre = nombre; this.horas = horas; this.precioHora = precioHora; } public double calcularSalarioBruto() { double salario = horas * precioHora; if (horas > 40) { // Horas extra al 150% int horasExtra = horas - 40; salario = 40 * precioHora + horasExtra * precioHora * 1.5; } return salario; } public void mostrarResumen() { System.out.println("=== Resumen de Nómina ==="); System.out.println("Empleado: " + nombre); System.out.println("Horas: " + horas); System.out.printf("Precio/hora: %.2f €%n", precioHora); System.out.printf("Salario bruto: %.2f €%n", calcularSalarioBruto()); } // --- Punto de entrada --- public static void main(String[] args) { // Paso 1: Validar número de argumentos if (args.length != 3) { System.err.println("Uso: java CalculadoraNomina <nombre> <horas> <precioHora>"); System.exit(1); } // Paso 2: Leer y convertir argumentos String nombre = args[0]; int horas; double precioHora; try { horas = Integer.parseInt(args[1]); precioHora = Double.parseDouble(args[2]); } catch (NumberFormatException e) { System.err.println("Error: horas y precioHora deben ser números."); System.exit(1); return; // Inalcanzable, pero satisface al compilador } // Paso 3: Crear objeto y mostrar resultado CalculadoraNomina nomina = new CalculadoraNomina(nombre, horas, precioHora); nomina.mostrarResumen(); } }
$ java CalculadoraNomina Ana 45 20.00
=== Resumen de Nómina ===
Empleado: Ana
Horas: 45
Precio/hora: 20,00 €
Salario bruto: 950,00 €
main (estático) lee los datos de entrada, crea un objeto de la clase y delega la lógica de negocio en métodos de instancia. Esta separación mantiene el código limpio y testable: los métodos de instancia se pueden probar independientemente sin depender de main.🐛 Errores frecuentes
Estos son los errores más comunes que encuentran los principiantes al trabajar con el método main:
❌ Error 1: Olvidar static
// Error: Main method is not static in class MiClase public void main(String[] args) { ... }
Solución: Añadir static a la declaración del método.
❌ Error 2: Escribir Main con mayúscula
// Error: la JVM busca exactamente "main", no "Main" public static void Main(String[] args) { ... }
Solución: Java es sensible a mayúsculas (case-sensitive). El nombre debe ser main en minúsculas.
❌ Error 3: Parámetro incorrecto
// Error: no es punto de entrada válido public static void main(int[] args) { ... } public static void main() { ... }
Solución: El parámetro debe ser exactamente String[] (o String...). Sin parámetros o con otro tipo, la JVM no reconoce el método.
❌ Error 4: Acceder a miembros de instancia desde main
public class MiClase { int valor = 10; public static void main(String[] args) { // Error: non-static variable valor cannot be referenced from a static context System.out.println(valor); } }
Solución: Crear una instancia de la clase y acceder a través del objeto:
public static void main(String[] args) { MiClase obj = new MiClase(); System.out.println(obj.valor); // Correcto }
✏️ Ejercicios prácticos
Ejercicio 1: Saludo personalizado por línea de comandos
Escribe un programa que reciba por línea de comandos un nombre y un idioma (es, en o fr) y muestre un saludo en el idioma correspondiente. Si no se pasan exactamente 2 argumentos, debe mostrar las instrucciones de uso.
Ejercicio 2: Calculadora de media aritmética
Escribe un programa que reciba por línea de comandos una cantidad variable de números enteros y calcule su media aritmética. Si no se pasan argumentos, debe indicar el error. Si algún argumento no es un número válido, debe mostrarse un mensaje informativo.
Ejercicio 3: Registro de estudiantes por línea de comandos
Diseña un programa con una clase Estudiante (nombre, nota) y un método main que reciba pares de argumentos <nombre> <nota>, cree objetos Estudiante, los almacene en un array y muestre un listado final indicando quién aprobó (nota >= 5) y quién no.
❓ Preguntas frecuentes sobre El método main en Java
Las dudas más comunes respondidas de forma clara y directa.
💬 Foro de discusión
¿Tienes dudas sobre El método main en Java? Comparte tu pregunta con la comunidad.
Todavía no hay mensajes. ¡Sé el primero en participar!