Sintaxis de Java práctica — la ficha de matrícula, el horario de FP2 y un recibo con tres errores (para perderle el miedo a javac)

La primera entrega de Java que hice tenía dos errores de compilación. Los arreglé, volví a compilar tan contento y javac me devolvió… un error nuevo que antes no salía. Pensé que había roto algo al arreglar los otros. No: javac revisa por rondas, y el tercer error estaba ahí desde el principio, esperando a que los otros dos le dejaran paso. Lo verás con salidas reales en el programa 3.

En la teoría de sintaxis de Java vimos la estructura de un programa: clase, main, llaves, punto y coma, println, print, printf y concatenación. Ahora escribimos tres programas desde cero, de nivel medio y sin nada que no sea de este tema (ni variables, ni if, ni bucles, ni Scanner: eso llega en los temas siguientes). El hilo es la ficha de una estudiante de FP2: su matrícula alineada en columnas con printf, su horario en una tabla de texto y el recibo de lo que paga. Todos los datos son inventados, y las salidas son reales, con Java 21 y el ordenador en español.

Antes de empezar

Conviene tener claro:

  • La estructura mínima: public class Nombre { public static void main(String[] args) { … } }, y que el archivo se llama Nombre.java.
  • La diferencia entre println, print y printf, y los huecos %s, %d, %.2f y %n.
  • Que + pega textos de izquierda a derecha y que los paréntesis cambian el orden.
  • Cómo se compila y se ejecuta: javac Nombre.java y java Nombre.

Si alguno te suena dudoso, repasa la teoría antes de seguir.

Crea una carpeta practica_sintaxis y un archivo por programa, con el nombre exacto de su clase: FichaMatricula.java, Horario.java y Recibo.java. Si usas VS Code, el botón Run compila y ejecuta; si prefieres la terminal, javac y java.

Programa 1: la ficha de matrícula con printf

Queremos imprimir la ficha de matrícula del segundo semestre: datos de la estudiante, una tabla de asignaturas con su código y sus créditos (ECTS) en columnas, y el importe a pagar. Lo que lo complica: los nombres de las asignaturas tienen longitudes muy distintas y los números tienen que quedar alineados a la derecha, como en una tabla de verdad.

La idea

Nada de contar espacios a mano: cada columna es un hueco con ancho de printf. %-6s es un texto en 6 columnas pegado a la izquierda; %-30s, uno de 30; %5d, un entero en 5 columnas pegado a la derecha. Si todas las filas usan el mismo formato, las columnas cuadran solas. Los importes se calculan dentro del propio printf (24 × 15,50): todavía no tenemos variables, así que los números van escritos tal cual.

Antes de programar, suma los anchos de una fila (6 + 1 + 30 + 1 + 5) y comprueba que la cabecera y las líneas de guiones miden lo mismo.

El código

/** Ficha de matrícula con printf: columnas alineadas y la coma decimal del español (datos inventados). */
public class FichaMatricula {
    public static void main(String[] args) {
        System.out.println("============================================");
        System.out.println("    FICHA DE MATRÍCULA · SEGUNDO SEMESTRE");
        System.out.println("============================================");
        System.out.printf("%-12s %s%n", "Estudiante:", "Laura Santana Ruiz");
        System.out.printf("%-12s %s%n", "Grado:", "GCID");
        System.out.printf("%-12s %d.º%n", "Curso:", 1);
        System.out.println("--------------------------------------------");
        System.out.printf("%-6s %-30s %5s%n", "Cód.", "Asignatura", "ECTS");     // cabecera con los mismos anchos
        System.out.printf("%-6s %-30s %5d%n", "FP2", "Fundamentos de Programación II", 6);
        System.out.printf("%-6s %-30s %5d%n", "IC2", "Ingeniería de Computadores II", 6);
        System.out.printf("%-6s %-30s %5d%n", "EST", "Estadística", 6);
        System.out.printf("%-6s %-30s %5d%n", "ALG", "Álgebra", 6);
        System.out.println("--------------------------------------------");
        System.out.printf("%-37s %5d%n", "Total de créditos", 6 + 6 + 6 + 6);
        System.out.printf("%-30s %10.2f €%n", "Precio (24 x 15,50 €)", 24 * 15.50);
        System.out.printf("%-30s %10.2f €%n", "Tasa de secretaría", 27.30);
        System.out.printf("%-30s %10.2f €%n", "A pagar", 24 * 15.50 + 27.30);
        System.out.println("============================================");
    }
}

Salida

============================================
    FICHA DE MATRÍCULA · SEGUNDO SEMESTRE
============================================
Estudiante:  Laura Santana Ruiz
Grado:       GCID
Curso:       1.º
--------------------------------------------
Cód.   Asignatura                      ECTS
FP2    Fundamentos de Programación II     6
IC2    Ingeniería de Computadores II      6
EST    Estadística                        6
ALG    Álgebra                            6
--------------------------------------------
Total de créditos                        24
Precio (24 x 15,50 €)              372,00 €
Tasa de secretaría                  27,30 €
A pagar                            399,30 €
============================================

Comprobémoslo. Créditos: 6 + 6 + 6 + 6 = 24. Precio: 24 × 15,50 = 372,00 €. Total: 372,00 + 27,30 = 399,30 €. Los decimales salen con coma porque el ordenador está en español (printf usa el idioma del sistema). Y la columna de los ECTS cuadra con la cabecera porque las cinco filas tienen el mismo formato.

Anchos de printfUna fila de la ficha de matrícula: %-6s ocupa 6 columnas alineado a la izquierda con FP2, %-30s ocupa 30 con el nombre de la asignatura y %5d ocupa 5 alineado a la derecha con el 6.printf("%-6s %-30s %5d%n", "FP2", "Fundamentos de Programación II", 6)FP2%-6sizquierda, 6Fundamentos de Programación II%-30sizquierda, 306%5dderecha, 5043Los espacios entre huecos del formato también salen: 6 + 1 + 30 + 1 + 5 = 43 columnas.%.2f usa la coma con el idioma español (8,75); con Locale.US, el punto (8.75).
El número de cada hueco es su ancho: con el signo menos, el texto se pega a la izquierda; sin él, a la derecha.

Preguntas que te puedes hacer

¿Por qué %-37s en la línea del total? Porque queremos que el 24 caiga justo debajo de la columna ECTS. Las filas de asignaturas ocupan 6 + 1 + 30 = 37 caracteres antes del hueco de los créditos, así que el texto «Total de créditos» tiene que ocupar esos mismos 37.

¿Qué pasa si un nombre es más largo que su hueco? printf no lo corta: lo imprime entero y empuja todo lo demás. «Fundamentos de Programación II» tiene exactamente 30 caracteres; con %-25s esa fila quedaría desplazada cinco columnas.

¿Por qué 1.º sale bien con %d.º? Porque todo lo que no es un hueco se copia tal cual: %d pone el 1 y .º va detrás.

¿Puedo forzar el punto decimal? Sí: System.out.printf(java.util.Locale.US, "%.2f%n", 399.30) imprime 399.30. Úsalo si un enunciado exige el punto.

¿Por qué el € va fuera del hueco? Porque el hueco solo formatea el número. %10.2f € reserva 10 columnas para el importe y después escribe un espacio y el símbolo.

Que cada fila use un ancho distinto (%-30s en una, %-28s en otra). Las columnas se tuercen y parece un fallo misterioso. Copia el formato entero de una fila a otra.

Pruébalo tú

  • Añade una quinta asignatura de 4,5 créditos. ¿Qué hueco tienes que cambiar para que el número quepa? (pista: %5d no sirve para decimales).
  • Cambia el formato para que la columna de créditos vaya pegada a la izquierda y mira cómo se desordena.

Programa 2: el horario de FP2 en una tabla de texto

Ahora el horario semanal de FP2 e IC2 dibujado con caracteres: +, - y |. Lo hacemos de tres maneras: con espacios contados a mano, con printf y con tabulaciones (\t). Lo que lo complica: «Miércoles» tiene 9 letras y «Lunes» 5, así que algo tiene que rellenar la diferencia.

La idea

  • A mano: cada línea es un println con los espacios ya escritos. Funciona, pero si cambias una palabra tienes que recontar.
  • Con printf: %-9s rellena hasta 9 columnas, así que «Lunes» y «Miércoles» ocupan lo mismo sin contar nada.
  • Con \t: la tabulación salta a la siguiente columna múltiplo de 8. Parece cómoda, pero ya verás qué pasa con «Miércoles».

Antes de programar, cuenta cuántas letras tiene la palabra más larga de cada columna. Ese es el ancho mínimo del hueco.

El código

/** Horario de FP2 e IC2 en una tabla de texto: a mano con espacios y con printf (horario inventado). */
public class Horario {
    public static void main(String[] args) {
        // 1. A mano: cada espacio contado
        System.out.println("+-----------+-------+-------+");
        System.out.println("| Día       | 9:00  | 11:00 |");
        System.out.println("+-----------+-------+-------+");
        System.out.println("| Lunes     | FP2   | IC2   |");
        System.out.println("| Miércoles | IC2   | FP2   |");
        System.out.println("| Viernes   | FP2   | -     |");
        System.out.println("+-----------+-------+-------+");
        System.out.println();
        // 2. Con printf: el ancho lo pone %-9s y no hay que contar
        System.out.printf("| %-9s | %-5s | %-5s |%n", "Día", "9:00", "11:00");
        System.out.printf("| %-9s | %-5s | %-5s |%n", "Lunes", "FP2", "IC2");
        System.out.printf("| %-9s | %-5s | %-5s |%n", "Miércoles", "IC2", "FP2");
        System.out.printf("| %-9s | %-5s | %-5s |%n", "Viernes", "FP2", "-");
        System.out.println();
        // 3. Con \t: la tabulación salta a la columna 8, 16, 24…
        System.out.println("Día\t9:00\t11:00");
        System.out.println("Lunes\tFP2\tIC2");
        System.out.println("Miércoles\tIC2\tFP2");
        System.out.println("Viernes\tFP2\t-");
    }
}

Salida

+-----------+-------+-------+
| Día       | 9:00  | 11:00 |
+-----------+-------+-------+
| Lunes     | FP2   | IC2   |
| Miércoles | IC2   | FP2   |
| Viernes   | FP2   | -     |
+-----------+-------+-------+

| Día       | 9:00  | 11:00 |
| Lunes     | FP2   | IC2   |
| Miércoles | IC2   | FP2   |
| Viernes   | FP2   | -     |

Día	9:00	11:00
Lunes	FP2	IC2
Miércoles	IC2	FP2
Viernes	FP2	-

Las dos primeras tablas son idénticas: %-9s ha puesto los mismos espacios que contamos a mano. La tercera se tuerce justo en el miércoles. ¿Por qué? «Lunes» tiene 5 letras y su \t salta a la columna 8; «Miércoles» tiene 9, ya ha pasado la 8, así que su \t salta a la 16. Las tabulaciones solo alinean si todas las palabras de una columna tienen menos de 8 caracteres.

Preguntas que te puedes hacer

¿Por qué "9:00" va entre comillas y no como número? Porque es un texto con dos puntos dentro; además, así sirve el mismo hueco %-5s que para «FP2».

¿Hay que escribir el | en el formato? Sí: todo lo que no es un hueco se imprime tal cual. "| %-9s | %-5s | %-5s |%n" pone barras y espacios alrededor de cada valor.

¿Por qué la segunda tabla no tiene los bordes +---+? Para que compares solo las filas; añádelos con println y verás que cuadran igual.

¿Entonces nunca uso \t? Sirve para separar columnas que luego va a leer otro programa (por ejemplo, para pegar en una hoja de cálculo), pero no para alinear a la vista.

Alinear con \t y probar solo con palabras cortas. Funciona hasta que aparece «Miércoles». Para tablas, printf con anchos.

Pruébalo tú

  • Añade la columna de las 13:00 a las dos primeras tablas.
  • Cambia %-9s por %9s en la columna del día. ¿Hacia dónde se pegan ahora los nombres?

Programa 3: un recibo con tres errores, arreglados ronda a ronda

El último programa imprime el recibo de la matrícula, pero lo escribimos con tres errores de los que más se ven en los primeros laboratorios, y los arreglamos leyendo lo que dice javac. Lo que lo complica: javac no los enseña todos a la vez.

La idea

Compilar, leer el primer error, arreglarlo, volver a compilar. Siempre en ese orden: un error de escritura puede tapar a los de debajo, y a veces arreglar el primero hace desaparecer otros que eran consecuencia suya.

Antes de compilar, lee el código de abajo y busca tú los tres errores. Después compara con lo que encuentra javac.

El código

Versión con errores:

public class ReciboMal {
    public static void main(String[] args) {
        System.out.println("Recibo de matrícula")
        System.out.println("Concepto: 24 créditos);
        System.out.printn("Total: " + 399.30 + " €");
    }
}

Ronda 1, javac ReciboMal.java:

ReciboMal.java:3: error: ';' expected
        System.out.println("Recibo de matrícula")
                                                 ^
ReciboMal.java:4: error: unclosed string literal
        System.out.println("Concepto: 24 créditos);
                           ^
2 errors

Dos errores de escritura: falta el ; de la línea 3 y las comillas de la línea 4 no se cierran (unclosed string literal: el texto empieza y no termina). Los arreglamos y compilamos otra vez (guardado como ReciboMal2.java, para que veas las dos rondas):

public class ReciboMal2 {
    public static void main(String[] args) {
        System.out.println("Recibo de matrícula");
        System.out.println("Concepto: 24 créditos");
        System.out.printn("Total: " + 399.30 + " €");
    }
}

Ronda 2:

ReciboMal2.java:5: error: cannot find symbol
        System.out.printn("Total: " + 399.30 + " €");
                  ^
  symbol:   method printn(String)
  location: variable out of type PrintStream
1 error

¡Un error que antes no salía! cannot find symbol … method printn(String): no existe ningún método printn. javac primero comprueba que el texto está bien escrito (comillas, puntos y coma, llaves) y solo cuando todo está bien escrito busca los nombres (métodos, clases). Con los errores de escritura no llegó a esta fase.

Los errores llegan por rondasTres rondas de javac con el recibo: primero dos errores de escritura (punto y coma y comillas), después uno de nombres (printn no existe) y al final compila.Ronda 1: ReciboMal.javalínea 3: ';' expectedlínea 4: unclosed stringerrores de escrituraRonda 2: arreglados 3 y 4línea 5: cannot findsymbol printnerrores de nombresRonda 3: printlncompilay se ejecutasin erroresjavac primero comprueba que el texto está bien escrito y, solo si lo está, busca los nombres
Que javac diga «2 errors» no significa que solo haya dos: los de nombres aparecen cuando arreglas los de escritura.

Versión arreglada, con el total bien formateado:

public class Recibo {
    public static void main(String[] args) {
        System.out.println("Recibo de matrícula");
        System.out.println("Concepto: 24 créditos");
        System.out.printf("Total: %.2f €%n", 24 * 15.50 + 27.30);              // printf: coma decimal (español)
        System.out.println("Total con +: " + (24 * 15.50 + 27.30) + " €");   // +: siempre con punto
    }
}

Salida

Recibo de matrícula
Concepto: 24 créditos
Total: 399,30 €
Total con +: 399.3 €

Fíjate en las dos últimas líneas: la misma cuenta, 24 × 15,50 + 27,30, sale 399,30 con printf y 399.3 con +. printf usa el idioma (coma) y los decimales que le pidas; + convierte el número tal cual, siempre con punto y sin ceros de relleno. Para dinero, printf.

Preguntas que te puedes hacer

¿Por qué en la ronda 1 no salió el error de printn? Porque javac trabaja por fases: primero lee el texto y, si hay errores de escritura, se para ahí. Los nombres se comprueban en una fase posterior.

¿Por qué la flechita de unclosed string literal apunta a la comilla de apertura? Porque es donde empieza el texto que no se cierra. El error de verdad está al final de la línea, donde falta la otra comilla.

¿Qué significan symbol y location en el mensaje? symbol es el nombre que no encuentra (printn con un String) y location dónde lo buscaba: en out, que es un PrintStream. Traducido: «System.out no tiene ningún método printn».

¿Y si arreglo un error y aparecen más? Es normal, como acabas de ver. Lo que no es normal es que el número de errores suba mucho tras un cambio pequeño: entonces has abierto una comilla o una llave que no cierras.

Intentar arreglar todos los errores a la vez empezando por el último. Empieza siempre por el primero: muchas veces los siguientes son consecuencia suya.

Pruébalo tú

  • Quita una llave de cierre del Recibo arreglado y lee el error. ¿Qué línea señala?
  • Cambia %.2f por %d en el printf del total. ¿Compila? ¿Qué pasa al ejecutarlo?

Errores típicos en esta práctica

  • Usar anchos distintos en filas de la misma tabla. Las columnas se tuercen.
  • Alinear con \t. Solo funciona si todas las palabras de la columna tienen menos de 8 letras.
  • Olvidar %n al final del printf. La siguiente salida se pega en la misma línea.
  • Arreglar los errores de javac desde el último. Empieza por el primero y vuelve a compilar.
  • Pensar que javac dice todos los errores de una vez. Los de nombres llegan después de los de escritura.
  • Imprimir dinero con +. Sale 399.3 en lugar de 399,30.

El error que más va a aparecer en estos programas

El punto y coma. En los tres programas hay muchas líneas casi iguales y es fácil que en una se escape. Recuerda: ';' expected señala la línea del error o la de debajo, y las líneas que terminan en { o en } no lo llevan.

Míralo paso a paso en Python Tutor

Entra en Python Tutor, elige Java y pega esta versión corta de la ficha:

public class Tutor {
    public static void main(String[] args) {
        System.out.printf("%-6s %-12s %5s%n", "Cód.", "Asignatura", "ECTS");
        System.out.printf("%-6s %-12s %5d%n", "FP2", "Programación", 6);
        System.out.printf("%-6s %-12s %5d%n", "IC2", "Computadores", 6);
        System.out.printf("%-19s %5d%n", "Total", 6 + 6);
        System.out.printf("A pagar: %.2f €%n", 12 * 15.50);
    }
}

Salida:

Cód.   Asignatura    ECTS
FP2    Programación     6
IC2    Computadores     6
Total                  12
A pagar: 186,00 €

Qué observar paso a paso:

  1. Cada printf añade una línea completa al panel de salida: ninguna se queda a medias porque todas terminan en %n.
  2. «Programación» y «Computadores» tienen 12 letras y caben justas en %-12s; cambia una por «IA» y mira cómo se rellena con espacios.
  3. 6 + 6 dentro del printf es una suma (12), porque es un argumento aparte, no un trozo de texto.
  4. La última línea calcula 12 × 15,50 = 186. Si Python Tutor la enseña con punto (186.00), es por el idioma con el que ejecuta Java.

Resumen y siguiente paso

  • printf con anchos (%-30s, %5d, %10.2f) alinea tablas sin contar espacios.
  • El signo menos pega a la izquierda; sin él, a la derecha.
  • \t salta a columnas múltiplo de 8: no sirve para alinear palabras largas.
  • javac revisa por rondas: primero la escritura, después los nombres.
  • Arregla siempre el primer error y vuelve a compilar.
  • Para importes, printf (coma y dos decimales); + da el número tal cual.

Checklist: ¿lo has practicado?

  • ¿Has ejecutado los tres programas y comprobado las salidas?
  • ¿Sabes calcular cuánto mide una fila de printf sumando sus anchos?
  • ¿Puedes explicar por qué el miércoles tuerce la tabla con \t?
  • ¿Has leído los mensajes de javac hasta entender qué señala cada flechita?
  • ¿Has intentado los «Pruébalo tú» sin mirar la teoría?

Si has respondido que sí a casi todo, estás listo para los ejercicios de sintaxis de Java, donde ya no te doy el código hecho. ¡Nos vemos allí!

Siguiente paso: ejerciciosSintaxis de Java ejercicios — el conversor, el informe de notas y el tique del cajero, alineados al milímetro (con soluciones y chuletario)

¿Te ha servido este artículo? Así puedes apoyar SergioLearns →