¡Esta es una revisión vieja del documento!
03 - Variables y constantes
Una variable es una posición de memoria donde se almacena un valor. Para crear una variable necesitamos darle un nombre simbólico para acceder a ella. Antes de ver como se declaran las variables en Java, vamos a ver un par de distinciones entre la forma de declarar y usar las variables en los lenguajes de programación.
Tipado de las variables
- Tipado estático o dinámico: En los lenguajes con tipado estático tenemos que indicar de qué tipo es la variable que vamos a crear (entero, cadena de caracteres…) y este tipo no puede cambiar a lo largo de la ejecución del programa (a no ser que hagamos casteo de la variable, como veremos más adelante). Por el contrario, en los lenguajes con tipado dinámico no hace falta indicar el tipo de la variable cuando la creamos. Además, podemos cambiar el tipo a mitad de programa.
- Tipado fuerte o débil: Aunque no hay un consenso claro entre lo que es fuertemente tipado o débilmente tipado, parece que la idea más general es si el lenguaje permite cambiar unos tipos por otros de forma más o menos automática o no lo permite, salvo quizás de forma explícita.
Muchas veces se confunden los términos anteriores y no está clara la distinción entre ambos (incluso en muchos sitios las definiciones de tipado estático/dinámico y fuerte/débil están intercambiadas). En cualquier caso, lo importante es saber cómo se declaran las variables en el lenguaje en el que estamos programando y qué puedo y qué no puedo hacer con ellas.
Por ejemplo, supongamos que defino una variable llamada a de tipo entero y le asigno un valor de 5 (el código no está escrito en ningún lenguaje de programación específico):
var int a a = 5
Con un lenguaje de tipado estático, la siguiente instrucción no estaría permitida:
a = "Hola"
Mientras que con un lenguaje de tipado dinámico no habría ningún problema.
Por otra parte, defino otra variable b de tipo string (cadena de caracteres):
var int a var string b a = 5 b = "Hola"
En un lenguaje con tipado fuerte, la siguiente operación no estaría permitida:
resultado = a + b
En un lenguaje de tipado débil, se convertiría una de las variables automáticamente al tipo de la otra y se realizaría la operación (el resultado, en este caso, sería “5Hola”).
Java es un lenguaje de tipado estático y “débil”. Lo de “débil” lo ponemos entre comillas, ya que, aunque permite ciertas operaciones de conversión automática de variables (por ejemplo, el código anterior no daría error), no lo permite en la mayoría de ocasiones.
Por ejemplo, el siguiente código daría error en Java, ya que hemos definido la variable a de tipo entero y después le asignamos un string (tipado estático):
public class Tipado {
void main() {
int a = 5;
IO.println(a);
a = "Hola";
IO.println(a);
}
}
javac Tipado.java
Tipado.java:8: error: incompatible types: String cannot be converted to int
a = "Hola";
^
1 error
error: compilation failed
Vamos a probar ahora el tipado débil:
public class Tipado2 {
void main() {
int a = 5;
String b = "Hola";
IO.println(a + b);
}
}
Si lo compilamos, vemos que no da ningún error.
javac Tipado2.java
Y, al ejecutarlo, nos muestra por pantalla la concatenación de 5 (lo convierte automáticamente en string) con “Hola”:
java Tipado2
5Hola
Sin embargo, si cambiamos el tipo de la variable b a boolean (sólo puede tomar como valores true o false), sí que da error de compilación:
public class Tipado3 {
void main() {
int a = 5;
boolean b = true;
IO.println(a + b);
}
}
javac Tipado3.java
Tipado3.java:8: error: bad operand types for binary operator '+'
IO.println(a + b);
^
first type: int
second type: boolean
1 error
error: compilation failed
Definición de variables
Para definir variables en Java solo hay que indicar el tipo de variable y un identificador (nombre de la variable):
int a; String b; boolean c;
Podemos aprovechar y asignarle un valor inicial a la variable al mismo tiempo que la definimos, teniendo en cuenta el tipo de variable que es:
int a = 5;
int a, b;
var numero = 10;
Tipos de datos primitivos
En Java existen dos grandes grupos de tipos de datos, los tipos de datos primitivos y los tipos de datos referencia. Los tipos de datos primitivos son elementos de información básicos como letras y números, mientras que los tipos de datos de referencia representan instancias de clases.
Los tipos de datos primitivos en Java son:
| Tipo | Tamaño(bits) | Definición | Valor por defecto |
|---|---|---|---|
| int | 32 | Número entero con signo. Rango [-2³¹, 2³¹ - 1] | 0 |
| byte | 8 | Número entero con signo. Rango [-2⁷, 2⁷ - 1] | 0 |
| short | 16 | Número entero con signo. Rango [-2¹⁶, 2¹⁶ - 1] | 0 |
| long | 64 | Número entero con signo. Rango [-2⁶⁴, 2⁶⁴ - 1] | 0L |
| float | 32 | Número real en coma flotante con precisión simple de 32 bits | 0.0f |
| double | 64 | Número real en coma flotante con precisión doble de 64 bits | 0.0d |
| char | 16 | Carácter Unicode | ‘u0000’ |
| boolean | 1 | true o false | false |
Hay un tipo de dato String para el manejo de cadenas que no es en sí un tipo de dato primitivo. Con el tipo de dato String podemos manejar cadenas de caracteres separadas por dobles comillas.
El String no es un tipo de dato primitivo del lenguaje Java, pero su uso es igual de importante que el de los tipos de datos primitivos. En realidad, una variable de tipo String es un objeto de la clase String. Al contrario de otros lenguajes, las variables de tipo String en Java son inmutables, es decir, no pueden cambiar su valor. En los siguientes temas veremos más sobre los String en Java.
float miVariable; miVariable = 0.0;
La razón es que, por defecto, Java asigna al valor 0.0 el tipo double. Igual que cuando escribimos un valor entero (1, 145…) le asigna, por defecto, el tipo int.
Si queremos asegurarnos que el valor es de tipo float deberemos añadir una f al final del valor:
float miVariable; miVariable = 0.0f;
Conversión de tipos (cast)
En ocasiones nos interesa convertir un valor en un tipo de dato diferente. Para hacerlo, podemos utilizar el operador de conversión (cast). Un operador de conversión consta de un tipo de dato dentro de un paréntesis. Se debe colocar un operador de conversión a la izquierda del valor que se desea convertir.
public class Cast {
void main() {
var a = 5; //tipo int
double b;
b = (double) a;
IO.println(b);
}
}
5.0
Si es necesario convertir más de un simple valor o una variable, entonces es importante asegurarse de colocar un paréntesis a la expresión entera que se desea convertir.
public class Cast2 {
void main() {
var a = 5.0; //tipo double
var b = 2.0; //tipo double
int resultado;
//resultado = (int) a + b; //error al convertir sólo la variable "a" a entero
resultado = (int) (a + b);
IO.println("El resultado de la suma es: " + resultado);
}
}
En el fragmento de código anterior, si no hubiésemos puesto el paréntesis a la expresión, la conversión se habría aplicado sólo al primer valor a la derecha del operador, es decir, a la variables a, en lugar de hacerlo en la expresión completa, con lo que nos daría error:
Cast2.java:10: error: incompatible types: possible lossy conversion from double to int
resultado = (int) a + b; //error al convertir sólo la variable "a" a entero
^
1 error
error: compilation failed
Ejecuta ahora el siguiente código:
public class Cast3 {
void main() {
var a = 9;
var b = 2;
float c;
c = (float) (a / b);
//c = (float) a / b;
IO.println(c);
}
}
4.0
¿Por qué no está haciendo bien, aparentemente, la conversión a float? El resultado debería ser 4.5, sin embargo la aplicación muestra 4.0. La razón es que la división entre dos enteros devuelve un entero truncado, con lo que en realidad lo que está haciendo es primero dividir 9 entre 2, que es igual a 4 (trunca el número 4.5) y después convertir ese número a float (4.0). Para solucionarlo, lo que debemos hacer es convertir uno de los operandos en float, no toda la expresión. De esta forma, Java convertirá automáticamente el otro operando en float para poder hacer la división entre números decimales:
public class Cast3 {
void main() {
var a = 9;
var b = 2;
float c;
//c = (float) (a / b);
c = (float) a / b;
IO.println(c);
}
}
4.5
String cadena = "0";
int numero;
numero = (int) cadena;
Cast.java:39: error: incompatible types: String cannot be converted to int
numero = (int) cadena;
^
1 error
Para intentar convertir datos de tipo String en int podemos utilizar el método Integer.parseInt():
String cadena = "0";
int numero;
numero = Integer.parseInt(cadena);
Igual que la clase String, tenemos clases para tipos primitivos con funcionalidad añadida, como Integer, por ejemplo. Si queremos convertir un String a Integer (en lugar de a int) podemos usar Integer.valueOf(String).
Nombre de las variables
En Java podemos utilizar cualquier palabra como nombre de variable cumpliendo una serie de normas:
- Un identificador se puede definir con uno o más caracteres.
- El primer carácter tiene que ser una letra (a, b, c…), el carácter guion bajo (_) o el dólar ($).
- Los siguientes caracteres pueden ser letras, el guión bajo, el dólar o dígitos (0, 1, 2…).
Además de las normas anteriores, existen ciertas palabras reservadas en Java que no podemos utilizar como nombre de variables:
| abstract | continue | for | new | switch |
| assert | default | goto | package | synchronized |
| boolean | do | if | private | this |
| break | double | implements | protected | throw |
| byte | else | import | public | throws |
| case | enum | instanceof | return | transient |
| catch | extends | int | short | try |
| char | final | interface | static | void |
| class | finally | long | strctfp | volatile |
| const | float | native | super | while |
Aparte de las palabras anteriores, true, false y null tampoco se pueden utilizar para uso propio.
Buenas prácticas
Aunque podemos escoger cualquier nombre para nuestras variables (excepto los nombres reservados), deberíamos seguir una serie de normas de estilo para aumentar la legibilidad de nuestro código. Mira el siguiente ejemplo:
class Nombres
{
void main()
{
int d = 135;
}
}
¿A qué hace referencia esa d? Compáralo con el siguiente código:
class Nombres
{
void main()
{
int distanciaKilometros = 135;
}
}
En el segundo ejemplo, sabemos a primera vista lo que significa esa variable (la distancia en kilométros). Además, si ponemos nombres descriptivos a nuestras variables nos podemos ahorrar comentarios para describir, precisamente, esas variables. Por ejemplo, en el primer trozo de código no es raro ver algo similar a ésto:
class Nombres
{
void main()
{
//distancia en Km.
int d = 135;
}
}
En el segundo ejemplo, no sería necesario ese comentario, ya que sabemos que representa esa variable con ver su nombre.
Tampoco deberíamos, como norma general, usar abreviaturas, sobre todo cuando puede dar lugar a equívocos (en el ejemplo anterior podríamos haber usado la abreviatura Km, ya que es difícil confundirla con otra palabra). Por ejemplo, en el siguiente código, ¿qué significa hp?
class Nombres
{
void main()
{
int hp;
}
}
Podríamos estar refiriéndonos a la hipotenusa de un triángulo, o a un tipo de ordenador, o a puntos horizontales… Está claro que podríamos sacar el significado por el contexto de la clase a la que pertenece, pero siempre es mejor evitar equívocos a la hora de referirnos a las variables.
Otra recomendación es evitar nombres de variables similares con variaciones mínimas. Por ejemplo:
class Nombres
{
void main()
{
String ValueFromJsonDocumentByKey;
String ValueFromXMLDocumentByKey;
}
}
¿Cuánto tiempo se tarda en apreciar la diferencia entre ambas variables? Mejor nombrar las variables de forma que podamos distinguirlas de un vistazo.
Los espacios no están permitidos para nombrar variables en Java. Si necesitas nombrar variables con espacios en su nombre, existen varias opciones, entre las cuales una de la más extendidas es la notación camelCase. Esta notación consiste en nombrar todas las variables en minúscula y, si el nombre consta de varias palabras, empezar siempre la primera en minúscula y el resto de palabras empezarlas en mayúsculas, sin dejar espacio ni guiones entre ellas. Por ejemplo:
int altura; int alturaEdificio; int alturaEdificioGubernamental;
Si quieres ampliar información sobre cómo nombrar variables (y funciones, y clases…) echa un vistazo al libro de Robert C. Martin Código limpio.
Uso de las variables
Una vez hemos definido una variable, ya podemos usarla para operar con ella. Por ejemplo, podemos asignar un valor a la variable con símbolo igual( = ).
public class UsoVariables {
void main() {
var a = 5;
IO.println("La variable a vale " + a);
a = 19;
IO.println("Ahora, la variable a vale " + a);
}
}
La variable a vale 5 Ahora, la variable a vale 19
También podemos usar operadores para hacer cálculos con ella (en temas posteriores ya veremos los distintos operadores para cada tipo de variable). Por ejemplo, podemos usar los operadores aritméticos típicos para sumar, restar, multiplicar y dividir variables. También usaremos la concatenación (unión) de cadenas con el operador +.
public class UsoVariables2 {
void main() {
var a = 8;
var b = 4;
int resultado;
resultado = a + b;
IO.println("El resultado de la suma es: " + resultado);
resultado = a - b;
IO.println("El resultado de la resta es: " + resultado);
resultado = a * b;
IO.println("El resultado de la multiplicación es: " + resultado);
resultado = a / b;
IO.println("El resultado de la división es: " + resultado);
}
}
El resultado de la suma es: 12 El resultado de la resta es: 4 El resultado de la multiplicación es: 32 El resultado de la división es: 2
Constantes
Las constantes hacen referencia a posiciones de memoria donde se almacena un valor que no cambia a lo largo de la ejecución del programa. La diferencia con las variables es obvia; mientras éstas (las variables) pueden cambiar su valor a lo largo del programa, las constantes, como su nombre indica, permanecen con su valor constante durante todo el proceso.
Para definir constantes en Java, simplemente añadimos la palabra reservada final al comienzo de la declaración de una variable:
final float PI = 3.1416f;
Por convención, se suelen nombrar las constantes en mayúsculas y separando las palabras con un guión bajo (_).
Si intentamos cambiar el valor de una variable en mitad del código, nos dará un error de compilación:
public class Constantes {
void main() {
final float PI = 3.1416f;
PI = 3.14f;
}
}
Constantes.java:8: error: cannot assign a value to final variable PI
PI = 3.14f;
^
1 error
error: compilation failed
Aunque son menos habituales en una aplicación que las variables, hay situaciones donde nos facilitan mucho las cosas. Por ejemplo, mira el siguiente código:
public class Constantes2 {
void main() {
float distancia;
float retardoPropagacion;
distancia = 250.0f;
retardoPropagacion = distancia / 299792.458f;
IO.println("El retardo de propagación es: " + retardoPropagacion + " segundos");
}
}
¿Qué significado tiene ese 299792.458f? Igual puede ser muy claro para los expertos en ciencia, pero a la mayoría de gente nos costaría encontrar el sentido. Compara el código anterior con el siguiente:
public class Constantes3 {
void main() {
final float VELOCIDAD_LUZ = 299792.458f;
float distancia;
float retardoPropagacion;
distancia = 250.0f;
retardoPropagacion = distancia / VELOCIDAD_LUZ;
IO.println("El retardo de propagación es: " + retardoPropagacion + " segundos");
}
}
Ahora ya tenemos claro que ese valor corresponde a la velocidad de la luz en el vacío. Además, si queremos redondear el valor a 300000 km/s, sólo tenemos que cambiarlo en un sitio: la declaración de la constante, mientras que en el primer caso deberíamos buscar en todas las líneas de nuestro programa donde hemos utilizado ese valor y modificarlo.
Entrada/salida de datos. La clase IO
Aunque en una aplicación web la entrada y la salida de datos no se hace a través de consola, por ahora vamos a utilizar la clase IO, que nos proporciona métodos muy sencillos para leer y escribir datos por pantalla sin necesidad de importar librerías ni instanciar objetos.
Salida de datos
Para mostrar algo por la salida predeterminada (pantalla) utilizamos los métodos IO.println o IO.print. La diferencia es que el primero imprime el texto que le pasemos más un intro (salto de línea) al final y el segundo no.
Si usamos IO.print, el siguiente texto se pegará a continuación:
public class Salida {
void main() {
IO.println("Este método escribe un texto con salto de línea");
IO.println("Si escribo un nuevo texto a continuación, se escribirá en una nueva línea");
IO.print("Este es un texto impreso sin salto de línea");
IO.print("Si escribo un nuevo texto a continuación, se escribirá en la misma línea");
}
}
Este método escribe un texto con salto de línea Si escribo un nuevo texto a continuación, se escribirá en una nueva línea Este es un texto impreso sin salto de líneaSi escribo un nuevo texto a continuación, se escribirá en la misma línea(base)
Entrada de datos
Para leer lo que el usuario escribe por teclado utilizamos el método IO.readln(). Este método detiene la ejecución del programa, espera a que el usuario escriba algo y pulse Intro, y devuelve todo el texto introducido en forma de cadena de caracteres (String).
A diferencia de otros métodos antiguos de Java, no necesitas importar nada en la primera línea del archivo ni crear variables complejas.
Lectura de cadenas de texto (String)
Podemos usar IO.readln() directamente o incluso pasarle un texto entre paréntesis para que muestre el mensaje de petición antes de leer:
public class Entrada {
void main() {
String texto;
IO.println("Introduce un texto: ");
texto = IO.readln();
IO.println("El texto introducido es: " + texto);
}
}
Introduce un texto: Hola El texto introducido es: Hola
También podemos simplificar el código anterior pasándole el mensaje directamente a IO.readln():
String texto = IO.readln("Introduce un texto: ");
Ejercicios
Ejercicio 1.a
Crea un programa que calcule el área de un triángulo con base 5 y altura 10 (recuerda que la fórmula del área es (base * altura) / 2). Tanto la base como la altura deberán ser constantes de tipo entero y el área una variable del mismo tipo (entero).
Ejercicio 1.b
Modifica el tipo de la variable area a float. Cambia el valor de la altura por 9 y ejecuta el programa de nuevo. Calcula el área a mano y comprueba si coincide con la salida del programa. Haz los cambios necesarios sin modificar el tipo de las constantes para que ambas respuestas sean iguales.
Ejercicio 1.c
Modifica el tipo de las constantes en el ejercicio 1a para que salga la respuesta correcta.
Ejercicio 1.d
Haz que la altura y la base las introduzca el usuario por teclado.
Ejerccio 2.a
Crea la constante NUMBER de tipo entero con un valor de 8. Muestra por pantalla la tabla de multiplicar de ese número.
Ejercicio 2.b
Modifica el ejercicio anterior para que el resultado sea un float sin modificar el tipo de la constante.
Ejercicio 2.c
Haz que la tabla de multiplicar se muestre del número introducido por el usuario por el teclado.