Ir al contenido

UD02 · Elementos básicos de la programación

En la unidad anterior programaste sin escribir código. Aquí empieza Java de verdad: los datos que maneja un programa, las operaciones que se hacen con ellos y las estructuras que deciden qué se ejecuta y cuántas veces.

Es la unidad más densa del trimestre, y también la más rentable: todo lo que hay aquí lo vas a usar en cada programa que escribas durante el resto del ciclo, escribas en Java o en cualquier otro lenguaje.

  • Declarar variables y elegir bien el tipo de dato.
  • Operar con esos datos y controlar las trampas de las conversiones de tipo.
  • Leer datos del usuario y presentarlos con el formato que quieras.
  • Tomar decisiones con if y switch.
  • Repetir código con while, do-while y for.
  • Trabajar con IntelliJ IDEA: crear un proyecto, ejecutarlo y depurarlo.

Hasta ahora te ha bastado un editor de texto y un terminal. A partir de esta unidad los programas crecen, y con ellos la cantidad de tiempo que se pierde en cosas que una herramienta puede hacer por ti: avisarte de un error mientras escribes, completar nombres, buscar dónde se usa una variable o dejarte parar el programa a mitad para ver qué está pasando.

Vamos a usar IntelliJ IDEA Community Edition, que es gratuita y es el entorno más extendido en el mundo Java.

  1. Descarga IntelliJ IDEA Community Edition desde la web de JetBrains. Cuidado con bajarte la Ultimate, que es de pago.

  2. Al crear un proyecto nuevo, elige JDK 25. Si no aparece, desde el propio diálogo puedes descargarlo.

  3. Escribe el código en el fichero .java y ejecútalo con el botón ▶ del margen izquierdo, o con Ctrl+Mayús+F10.

  4. La salida aparece abajo, en la ventana Run. Ahí es donde escribirás también cuando el programa pida datos.

Seguimos escribiendo programas con la sintaxis compacta de Java 25, la misma de la UD01:

Bienvenida.java
void main() {
IO.println("Empezamos la UD02");
}

Un programa no es más que datos moviéndose. Y para manejar un dato hay que guardarlo en algún sitio con un nombre: eso es una variable.

Variable: un espacio en la memoria del ordenador, con un nombre y un tipo, cuyo contenido puede cambiar durante la ejecución del programa.

Piensa en una taquilla de un gimnasio. La taquilla tiene un número (el nombre), un tamaño que limita lo que cabe dentro (el tipo) y un contenido que puedes cambiar cuando quieras (el valor).

En Java, antes de usar una variable hay que declararla, diciendo de qué tipo es. Después se le puede asignar un valor con =:

int edad; // declaración
edad = 17; // asignación
int cursos = 2; // declaración e inicialización, todo junto

Las reglas del lenguaje son pocas y de obligado cumplimiento:

  • Pueden llevar letras, dígitos, _ y $, pero no pueden empezar por un dígito.
  • No pueden llevar espacios ni signos de puntuación.
  • No pueden ser una palabra reservada del lenguaje (int, class, for, if, new…).
  • Java distingue mayúsculas de minúsculas: nota, Nota y NOTA son tres variables diferentes.

Y luego están los convenios, que no son obligatorios para el compilador pero sí para cualquiera que trabaje contigo:

Elemento Convenio Ejemplo
Variables y métodos camelCase, empezando en minúscula notaMedia, precioSinIva
Constantes Todo en mayúsculas, con _ IVA_GENERAL, AFORO_MAXIMO
Clases PascalCase, empezando en mayúscula CuentaBancaria

Un consejo que vale más que la tabla: el nombre tiene que decir qué guarda la variable. n no dice nada; numeroDeAlumnos sí. Escribir un nombre largo cuesta dos segundos gracias al autocompletado; entender dentro de un mes qué era x2 cuesta mucho más.

Java es un lenguaje fuertemente tipado: cada variable tiene un tipo, ese tipo se decide al declararla y no cambia. El compilador lo comprueba, y por eso caza errores antes de ejecutar nada.

Los tipos primitivos son ocho y son los ladrillos del lenguaje:

Tipo Guarda Tamaño Rango aproximado
byte Enteros 8 bits −128 a 127
short Enteros 16 bits −32.768 a 32.767
int Enteros 32 bits ±2.147 millones
long Enteros 64 bits ±9,2 trillones
float Decimales 32 bits ~7 dígitos significativos
double Decimales 64 bits ~15 dígitos significativos
char Un carácter 16 bits 'a', 'ñ', '7', '€'
boolean Cierto o falso true o false

En la práctica, el 90 % del tiempo vas a usar cuatro: int para enteros, double para decimales, boolean para condiciones y char para caracteres sueltos. Los demás aparecen cuando hay una razón concreta: long para milisegundos o poblaciones, byte y short para ahorrar memoria en estructuras muy grandes, float cuando la precisión no importa y el espacio sí.

Un literal es un valor escrito tal cual en el código. Cada tipo tiene su forma:

int edad = 17;
long milisegundos = 1298332800000L; // la L es obligatoria: no cabe en un int
double precio = 10.5;
float altura = 1.75f; // la f es obligatoria: si no, es double
char inicial = 'C'; // comillas simples
boolean aprobado = true;
String nombre = "Lucía"; // comillas dobles
int millon = 1_000_000; // los _ solo ayudan a leer, no cambian nada
int binario = 0b1010; // 10 en binario
int hexadecimal = 0xFF; // 255 en hexadecimal

Cuando un valor no debe cambiar, se declara con final. Si alguien intenta modificarlo, el programa ni siquiera compila:

final double IVA = 0.21;
final int AFORO = 540;
IVA = 0.10; // ✗ error de compilación

Úsalas siempre que un número tenga significado propio. Un 540 suelto en mitad del código no le dice nada a nadie; AFORO sí. Y si el aforo cambia, se toca en un sitio y no en catorce.

Desde Java 10 puedes declarar variables locales con var y dejar que el compilador deduzca el tipo a partir del valor:

var nombre = "Lucía"; // String
var edad = 17; // int
var nota = 8.75; // double

Ojo con la confusión clásica: var no es que la variable no tenga tipo. Lo tiene, se decide al declararla y no cambia. Esto sigue siendo un error:

var edad = 17;
edad = "diecisiete"; // ✗ error: edad es int para siempre

Usa var cuando el tipo se lea en la misma línea (var nombre = "Lucía"), y escríbelo cuando no (double total = calcular();). La regla es que quien lea el código sepa qué hay dentro sin tener que investigar.

Los ocho tipos de la tabla son primitivos: la variable contiene el valor. El resto de tipos de Java son referenciados: la variable no contiene el objeto, sino la dirección donde está.

El referenciado que vas a usar desde el primer día es String:

String saludo = "Hola";

Dos consecuencias prácticas que hay que saber ya:

Un referenciado puede estar vacío del todo. Eso se llama null y significa «esta variable no apunta a nada». Un primitivo nunca puede ser null: un int sin inicializar vale 0.

Los String no se comparan con ==. Esto es la fuente de errores número uno de todo primero de DAM:

String a = IO.readln("Escribe hola: ");
if (a == "hola") { ... } // ✗ compara direcciones de memoria
if (a.equals("hola")) { ... } // ✓ compara el contenido

Con == estás preguntando «¿son el mismo objeto?»; con .equals() preguntas «¿dicen lo mismo?». Casi siempre quieres lo segundo. Y si además da igual que se escriba en mayúsculas, tienes equalsIgnoreCase.

Otras operaciones con cadenas que te harán falta enseguida:

String texto = "Programación";
texto.length() // 12 — cuántos caracteres tiene
texto.toUpperCase() // "PROGRAMACIÓN"
texto.toLowerCase() // "programación"
texto.charAt(0) // 'P' — el carácter de la posición 0
texto.substring(0, 4) // "Prog" — desde la 0 hasta la 4 sin incluirla
texto.trim() // quita los espacios de los extremos
texto.isEmpty() // ¿está vacío?

Un programa que no habla con nadie sirve de poco. En Java 25 la entrada y la salida básicas están en la clase IO:

IO.println("Hola"); // escribe y salta de línea
IO.print("Sin salto de línea"); // escribe y se queda ahí
String nombre = IO.readln("¿Cómo te llamas? "); // pregunta y lee la respuesta

IO.readln() siempre devuelve texto, aunque el usuario escriba 42. Para poder operar hay que convertirlo:

int edad = Integer.parseInt(IO.readln("¿Cuántos años tienes? "));
double nota = Double.parseDouble(IO.readln("Dime tu nota: "));
IO.println("El año que viene tendrás " + (edad + 1));

Si el usuario escribe hola donde esperabas un número, parseInt revienta con un error en tiempo de ejecución. Controlar eso con elegancia se hace con excepciones, y llega en la UD04; de momento nos fiamos.

Concatenar con + funciona, pero se vuelve ilegible enseguida. Para salidas con formato tienes formatted:

double total = 1234.5678;
IO.println("Total: " + total); // Total: 1234.5678
IO.println("Total: %.2f €".formatted(total)); // Total: 1234,57 €
IO.println("%-15s%5d".formatted("Camisetas", 3)); // texto a la izquierda, número a la derecha

Los marcadores más útiles:

Marcador Sirve para Ejemplo
%d Enteros %d42
%f Decimales %.2f3,14
%s Texto %sLucía
%n Salto de línea
%-10s Texto ajustado a 10 a la izquierda Para tablas
%8.2f Decimal en 8 huecos, con 2 decimales Para columnas de precios

También tienes secuencias de escape dentro de las cadenas: \n salta de línea, \t mete un tabulador, \" escribe una comilla y \\ una barra.

Y para textos de varias líneas, los bloques de texto evitan el festival de \n:

IO.println("""
==========================
AUDITORIO DE MARTOS
==========================
""");
Operador Operación Ejemplo
+ Suma 7 + 29
- Resta 7 - 25
* Multiplicación 7 * 214
/ División 7 / 23
% Resto (módulo) 7 % 21

Fíjate en la división, porque es la trampa más repetida del curso:

int a = 7, b = 2;
IO.println(a / b); // 3 ¡división entera!
IO.println(7.0 / 2); // 3.5 basta con que uno sea decimal
IO.println((double) a / b); // 3.5 forzando la conversión

Si los dos operandos son enteros, el resultado es entero y la parte decimal se tira a la basura. No se redondea: se trunca.

El operador % parece menor y lo vas a usar constantemente: n % 2 == 0 detecta los pares, n % 5 == 0 los múltiplos de cinco, y n % 10 te da la última cifra de un número.

contador += 5; // equivale a contador = contador + 5
precio *= 1.21; // y así con -=, /=, %=
contador++; // suma 1
contador--; // resta 1

Devuelven true o false: == (igual), != (distinto), >, <, >=, <=. Recuerda que para comparar cadenas no se usa ==, sino .equals().

Combinan condiciones: && (y), || (o), ! (no).

if (edad >= 18 && tieneCarnet) { ... }
if (dia == 6 || dia == 7) { ... }
if (!aprobado) { ... }

Estos dos operadores son de cortocircuito: si la primera parte de un && es falsa, Java ni evalúa la segunda, porque el resultado ya no puede cambiar. Eso no es un detalle académico, es lo que permite escribir con seguridad:

if (divisor != 0 && total / divisor > 10) { ... }

Es un if en una línea, cuando lo único que quieres es elegir entre dos valores:

int mayor = (a > b) ? a : b;
String mensaje = (nota >= 5) ? "Aprobado" : "Suspenso";

Léelo como: ¿condición? entonces esto : si no, esto otro. Está bien para casos cortos; si empiezas a anidar ternarios, usa un if y hazle un favor a quien lea el código.

Java sigue las reglas de siempre: primero *, / y %; después + y -; luego las comparaciones; al final los lógicos. Y los paréntesis mandan sobre todo lo demás.

En la duda, pon paréntesis. No cuestan nada y evitan discusiones:

double media = nota1 + nota2 + nota3 / 3; // ✗ solo divide la tercera
double media = (nota1 + nota2 + nota3) / 3; // ✓

A veces hay que pasar un dato de un tipo a otro. Java distingue dos casos.

Conversión implícita (ensanchamiento). Va de un tipo pequeño a uno más grande, no se pierde nada, y Java la hace sola:

int entero = 7;
double decimal = entero; // 7.0, sin avisar

Conversión explícita (casting). Va de grande a pequeño, puede perder información, y por eso hay que pedirla a mano:

double precio = 10.99;
int truncado = (int) precio; // 10, la parte decimal se pierde

El orden de menor a mayor es: byteshortintlongfloatdouble. Hacia la derecha es automático; hacia la izquierda hay que forzarlo y asumir las consecuencias.

Y un caso aparte: convertir texto a número no es un casting, porque un String no es un número disfrazado. Hay que parsearlo:

int n = Integer.parseInt("42"); // ✓
double d = Double.parseDouble("3.14"); // ✓
int mal = (int) "42"; // ✗ ni compila

Al revés es más fácil, porque cualquier cosa concatenada con una cadena se convierte a texto:

String texto = "" + 42; // "42"
String otro = String.valueOf(42); // "42", más explícito
// Comentario de una línea
/*
Comentario de varias líneas
*/
/**
* Comentario de documentación (Javadoc).
* Con este se genera la documentación del programa. Lo veremos en la UD04.
*/

Ahora bien, comentar no es narrar lo que ya se ve:

contador++; // suma uno al contador ← inútil

Un comentario bueno explica por qué, no qué:

// El aforo se reduce en 20 butacas: la última fila la ocupa el equipo técnico.
final int AFORO = 520;

Si sientes que necesitas un comentario para explicar qué hace un trozo de código, casi siempre la solución mejor es ponerle mejores nombres a las variables.

Hasta ahora tus programas se ejecutaban en línea recta. Las estructuras de control son las que permiten decidir y repetir, y con ellas ya puedes escribir cualquier programa.

int nota = Integer.parseInt(IO.readln("Nota: "));
if (nota >= 5) {
IO.println("Aprobado");
} else {
IO.println("Suspenso");
}

Cuando hay más de dos caminos, se encadenan. El orden importa: se comprueban de arriba abajo y se ejecuta el primero que se cumple.

if (nota < 5) {
IO.println("Insuficiente");
} else if (nota < 6) {
IO.println("Suficiente");
} else if (nota < 7) {
IO.println("Bien");
} else if (nota < 9) {
IO.println("Notable");
} else {
IO.println("Sobresaliente");
}

Cuando lo que haces es comparar una misma variable con varios valores concretos, el switch se lee mucho mejor que quince else if. En Java moderno se escribe con flechas y no necesita break:

int dia = Integer.parseInt(IO.readln("Día de la semana (1-7): "));
switch (dia) {
case 1 -> IO.println("Lunes");
case 2 -> IO.println("Martes");
case 3 -> IO.println("Miércoles");
case 4 -> IO.println("Jueves");
case 5 -> IO.println("Viernes");
case 6, 7 -> IO.println("Fin de semana");
default -> IO.println("Ese día no existe");
}

Mejor todavía: en Java el switch puede ser una expresión, es decir, puede devolver un valor directamente:

String nombreDia = switch (dia) {
case 1 -> "Lunes";
case 2 -> "Martes";
case 3 -> "Miércoles";
case 4 -> "Jueves";
case 5 -> "Viernes";
case 6, 7 -> "Fin de semana";
default -> "Día no válido";
};
IO.println(nombreDia);

Repite mientras se cumpla una condición. Se comprueba antes de entrar, así que puede no ejecutarse ninguna vez:

int intentos = 0;
while (intentos < 4) {
String clave = IO.readln("Combinación: ");
if (clave.equals("1234")) {
IO.println("La caja fuerte se ha abierto");
break;
}
intentos++;
}

Es la estructura adecuada cuando no sabes cuántas vueltas vas a dar: hasta que el usuario acierte, hasta que meta un número negativo, hasta que se acabe el fichero.

Igual, pero la condición se comprueba al final, así que el cuerpo se ejecuta al menos una vez. Es lo que quieres en los menús:

int opcion;
do {
IO.println("""
1. Vender entradas
2. Consultar evento
3. Salir
""");
opcion = Integer.parseInt(IO.readln("Opción: "));
switch (opcion) {
case 1 -> IO.println("Vendiendo…");
case 2 -> IO.println("Consultando…");
case 3 -> IO.println("Hasta luego");
default -> IO.println("Opción no válida");
}
} while (opcion != 3);

Cuando sí sabes cuántas vueltas vas a dar, el for reúne en una sola línea las tres cosas que gobiernan el bucle:

for (int i = 1; i <= 10; i++) {
IO.println(i + " x 3 = " + (i * 3));
}
  • Inicialización (int i = 1): se ejecuta una vez, al principio.
  • Condición (i <= 10): se comprueba antes de cada vuelta.
  • Actualización (i++): se ejecuta al final de cada vuelta.

Cualquier for se puede escribir como un while y al revés. Se usa el for cuando hay un contador claro, porque tenerlo todo junto en una línea evita olvidos.

Los bucles se pueden anidar, y ahí es donde empieza lo interesante. Una pirámide de asteriscos es exactamente eso: un bucle de filas, y dentro, uno de columnas.

int altura = 5;
for (int fila = 1; fila <= altura; fila++) {
for (int columna = 1; columna <= fila; columna++) {
IO.print("*");
}
IO.println();
}

break sale del bucle inmediatamente. continue salta el resto de la vuelta actual y pasa a la siguiente.

for (int i = 1; i <= 100; i++) {
if (i % 3 == 0) continue; // los múltiplos de 3 se saltan
if (i > 50) break; // pasado el 50, se acabó
IO.println(i);
}

Son cómodos, pero conviene no abusar: un bucle con cuatro break repartidos es difícil de seguir. Si la condición de salida es importante, casi siempre queda más claro en el while.

Para la práctica de esta unidad vas a necesitar trabajar con fechas: calcular cuántos días faltan para un evento y comprobar si estás dentro del periodo de descuento.

Lo esencial, para que te suene:

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
void main() {
LocalDate hoy = LocalDate.now();
LocalDate evento = LocalDate.of(2024, 11, 20);
long dias = ChronoUnit.DAYS.between(hoy, evento);
IO.println("Faltan " + dias + " días");
}

Pídele a una IA que te resuelva esto:

Programa en Java que pida dos números por teclado y diga cuál es mayor.

Analiza lo que te devuelva y responde:

  1. ¿Ha usado Scanner o IO.readln? ¿Por qué crees que ha elegido eso?
  2. ¿Ha cerrado el Scanner? ¿Sabrías decir qué pasa si no se cierra?
  3. ¿Compara los números con ==, o con >? ¿Y si en vez de números fueran textos?
  4. Pídele que lo reescriba con la sintaxis compacta de Java 25. ¿Lo consigue a la primera?
Currículo oficial del móduloRA1 · RA2 · RA3 · RA5 — 13 h · 1º trimestre

Resultados de aprendizaje y criterios de evaluación que se trabajan en esta unidad, según la ORDEN de 16 de junio de 2011 (BOJA núm. 142). Ver el mapa completo del módulo.

Las estructuras de control (selección, repetición y salto) se trabajan en esta unidad, no en la UD04: así lo hace el material de clase, aunque la programación didáctica las sitúe más adelante. La UD04 retoma el RA3 con el control de excepciones, las pruebas y la documentación.

RA1Reconoce la estructura de un programa informático, identificando y relacionando los elementos propios del lenguaje de programación utilizado.

  • a) Se han identificado los bloques que componen la estructura de un programa informático.
  • b) Se han creado proyectos de desarrollo de aplicaciones.
  • c) Se han utilizado entornos integrados de desarrollo.
  • d) Se han identificado los distintos tipos de variables y la utilidad específica de cada uno.
  • e) Se ha modificado el código de un programa para crear y utilizar variables.
  • f) Se han creado y utilizado constantes y literales.
  • g) Se han clasificado, reconocido y utilizado en expresiones los operadores del lenguaje.
  • h) Se ha comprobado el funcionamiento de las conversiones de tipos explícitas e implícitas.
  • i) Se han introducido comentarios en el código.

RA2Escribe y prueba programas sencillos, reconociendo y aplicando los fundamentos de la programación orientada a objetos.

  • b) Se han escrito programas simples.

RA3Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje.

  • a) Se ha escrito y probado código que haga uso de estructuras de selección.
  • b) Se han utilizado estructuras de repetición.
  • c) Se han utilizado estructuras de repetición.
  • d) Se han reconocido las posibilidades de las sentencias de salto.
  • f) Se han creado programas ejecutables utilizando diferentes estructuras de control.
  • g) Se han probado y depurado los programas.
  • h) Se ha comentado y documentado el código.

RA5Realiza operaciones de entrada y salida de información, utilizando procedimientos específicos del lenguaje y librerías de clases.

  • a) Se ha utilizado la consola para realizar operaciones de entrada y salida de información.
  • b) Se han aplicado formatos en la visualización de la información.

© 2026 Carlos Barroso · Contenido bajo CC BY-NC-SA 4.0