|
| Volver a índice |
Un tipo define una expresión o variable de manera que se puede predecir su comportamiento. Este capítulo trata todos los tipos principales de Java, mostrando cómo declarar variables, asignar valores y combinar tipos en expresiones.
Los identificadores se utilizan como nombres de clase, método y variable. Un identificador puede ser cualquier sentencia descriptiva de letras en mayúscula o minúscula , números y los caracteres subrayado (_) y signo de dólar ($). No se deban comenzar por número. Java diferencia entre mayúsculas/minúsculas, lo que significa que VALOR es un identificador diferente de Valor.
Las palabras clave reservadas son identificadores especiales que el lenguaje Java se ha reservado para controlar cómo está definido su programa. Se utilizan para identificar los tipos, modificadores y mecanismos para control de secuencia incorporados. Estas palabras clave sólo se pueden utilizar para su propósito original y no se pueden utilizar como identificadores de nombres de variable, clase o método. Hay 59 palabras clave reservadas definidas en la versión Java 1.0, que se muestran en la siguiente tabla.
La variable es la unidad básica de almacenamiento en un programa en Java. Una variable se define mediante la combinación de un identificador, un tipo y un ámbito.
La forma básica de una declaración de variable es:
tipo identificador [ = valor ] [, identificador [ = valor ] ... ] ;
El tipo puede ser: byte, short, int, long, char, float, double, boolean o el nombre de una clase o interfaz. Conceptos todos que describiremos más adelante.
Los bloques de sentencias compuestas en Java se delimitan con dos llaves { }. Las variables de Java sólo son válidas desde el punto donde están declaradas hasta el final de la sentencia compuesta que la engloba. Se pueden anidar estas sentencias compuestas y cada una puede contener su propio conjunto de declaraciones de variables locales. Sin embargo, no se puede declarar una variable con el mismo nombre que una de un ámbito superior.
En Java, que es un lenguaje de programación orientado a objetos, se ha renunciado a la abstracción uniforme del concepto "todo es un objeto" por cuestiones de eficiencia. El rendimiento del sistema fue una meta fundamental en el desarrollo de Java. Esta decisión condujo a la creación de los tipos simples de Java. No están en absoluto orientados a objeto, y son análogos a los tipos simples de la mayoría de los otros lenguajes que no utilizan objetos. Los tipos simples representan expresiones atómicas, de un solo valor, como números enteros, de coma flotante, caracteres y valores booleanos. Java también tiene un tipo para matrices, que son conjuntos de tamaño fijo de un único tipo. También tiene los tipos compuestos, clases e interfaces que veremos en un próximo capítulo. Parte de la seguridad y robustez de Java viene del hecho de que Java es un lenguaje fuertemente tipado. Cada expresión tiene un tipo. Cada variable tiene un tipo y cada tipo está definido estrictamente. En todas las asignaciones de expresiones a variables, tanto explícitas como a través de paso de parámetros en llamadas a método, se comprueba la compatibilidad de los tipos. El compilador de Java comprueba estáticamente todo el código que compila para asegurar que los tipos sean correctos. Si no coinciden los tipos no se generan avisos de compilador, sino que se generan errores que se deben corregir antes de que el compilador termine de compilar la clase. Para cada tipo se define explícitamente el único conjunto de valores que puede expresar, junto con un conjunto definido de operaciones que están permitidas. Por ejemplo, los tipos numéricos int y float se pueden sumar entre ellos, pero los tipos boolean no.
Los tipos numéricos son los que contienen números y pueden
ser de dos clases, los que guardan números de valor
completo sin parte fraccionaria, llamados enteros, y los
que pueden almacenar una parte fraccionaria, llamados
números en coma flotante. La magnitud del rango o grado de
precisión de una componente fraccionaria que se va a
necesitar dependerá de su aplicación.
Todos estos tipos en Java tienen definido un rango
explícito y un comportamiento matemático. La mayor parte
del código no portable de otros lenguajes está repleto de
problemas debidos al comportamiento no especificado del
tipo int. La base de este problema reside en un concepto
llamado tamaño de palabra de la máquina. El tamaño de
palabra de una CPU dada está determinado por el número de
bits que utiliza internamente para representar sus
registros más básicos, que se utilizan para almacenar y
manipular los números. Debido a la evolución de los PC y al
tamaño de palabra variable de estos, algunos compiladores
implementan int como un entero de tamaño de palabra de 32
bits, otros puede que sólo tengan 16 bits, y los
compiladores de los sistemas más modernos puede que incluso
utilicen 64 bits. En Java, no hay una conexión entre el
tamaño de palabra de la máquina y el rango de un tipo
numérico. Un valor int siempre tiene 32 bits en todos los
intérpretes Java, independientemente de la plataforma de la
que se trate. Esto permite que los programas que se
escriban tengan garantizada su ejecución en cualquier
arquitectura sin tener que transportarlos.
Todos los tipos numéricos de Java son valores con signo. Esta ausencia de signo reduce el número de tipos de entero a cuatro, cada uno de los cuales representa 1, 2, 4 y 8 bytes de almacenamiento. Veamos en detalle cada uno de los cuatro tipos, byte, short, int y long.
byte
byte es un tipo de 8 bits con signo. Su rango comprende
desde -128 a 127. Es especialmente útil cuando se tiene un
flujo de bytes externos recibidos desde una red o archivo.
Si se necesita analizar gramaticalmente un protocolo de red
o un formato de archivo, o resolver problemas de
ordenamiento de bytes, el tipo byte es el apropiado.
Las variables byte se declaran utilizando la palabra clave
byte. Por ejemplo, el código siguiente declara dos
variables byte llamadas b y c, inicializando c con el valor
hexadecimal 0x55.
byte b;
byte c = 0x55;
En general, se debería evitar la utilización del tipo byte excepto cuando se trabaje con manipulación de bits. Para los enteros normales, que se utilizan para contar y operar, int, que se describe más adelante, es una elección mucho más adecuada.
short
short es un tipo de 16 bits con signo. Su rango comprende
desde -32768 a 32767. Probablemente es el tipo de Java
menos utilizado. Ahora que las computadoras de 16 bits
empiezan a estar en desuso ya no hay muchos valores short
con los que trabajar.
Ejemplos de declaraciones de variables short:
short s;
short t = 0x55aa;
int
int es un tipo de 32 bits con signo. su rango comprende
desde -2.147.483.648 a 2.147.483.647. Es el tipo más
utilizado habitualmente para almacenar valores enteros
simples. Con un rango de miles de millones, es ideal para
la mayoría de las iteraciones con matrices y para contar.
Siempre que se tenga una expresión con enteros que incluya
byte, short, int y números literales, la expresión completa
se promociona a int antes de realizar el cálculo.
Ejemplos de declaraciones de variables int:
int i;
int j = 0x55aa0000;
long
long es un tipo de 64 bits con signo. Hay algunas ocasiones
en las que un tipo int no es lo suficientemente grande como
para guardar un valor deseado. Cuando se calcular
expresiones enteras con números grandes, una multiplicación
puede generar algunos valores intermedios de miles de
billones. También, cuando se calcula el tiempo, el número
de milisegundos en un año es de cerca de 30.000 millones y
se desbordará un int de 32 bits. En estos casos se necesita
utilizar un long.
Ejemplos de declaraciones de variables long:
long m;
long n = 0x55aa000055aa0000;
Tabla de anchuras y rangos para cada uno de los tipos enteros.
| Nombre | Anchura | Rango |
| long | 64 | -9.223.372.036.854.775.808..9.223.372.036.854.775.807 |
| int | 32 | -2.147.483.648..2.147.483.647 |
| short | 16 | -32.768..32.767 |
| byte | 8 | -128..127 |
Los números en coma flotante, también conocidos como números reales en otros lenguajes, se utilizan cuando se calculan funciones que requieren precisión fraccionaria. Los cálculos complejos, como la raíz cuadrada, o trigonométricas, como el seno y el coseno, tienen como resultado un valor cuya precisión requiere un tipo en coma flotante. Hay dos clases de tipos en coma flotante, float y double, como se describen:
| Nombre | Anchura | Rango |
| double | 64 | 1.7e-308..1.7e+308 |
| float | 32 | 3.4e-038..3.4e+038 |
float
La precisión simple, especificada por la palabra clave
float, utiliza 32 bits para almacenar un valor. La
precisión simple es más rápida en algunos procesadores y
ocupa la mitad de espacio, pero comenzará a ser imprecisa
cuando los valores sean muy grandes o muy pequeños.
Ejemplos de declaraciones de variables float:
float f;
float f2 = 3.14f;
double
La precisión doble, especificada por la palabra clave
double, utiliza 64 bits para almacenar un valor. Realmente
la precisión doble es más rápida que la simple en algunos
procesadores modernos que han sido optimizados para
cálculos matemáticos a alta velocidad. Cuando se necesita
mantener la precisión tras muchos cálculos iterativos, o
está manipulando números de gran valor, double es la mejor
opción.
Ejemplos de declaraciones de variables double:
double d;
double pi = 3.14159365358979323846;
Hay situaciones en las cuales se tiene un valor de un tipo dado y se desea almacenar ese valor en una variable de un tipo diferente. En algunos tipos es posible almacenar simplemente el valor sin una conversión de tipos; lo que se denomina conversión automática. Esto sólo es posible en Java si el compilador reconoce que la variable destino tiene la suficiente precisión para contener el valor origen, como almacenar un valor byte en una variable int. A esto se le llama ensanchamiento o promoción, dado que el tipo más pequeño se ensancha o promociona al tipo compatible más grande. Si por el contrario, se desea asignar un valor de variable int a una variable byte se necesita realizar una conversión de tipos explícita. A esto se le llama estrechamiento, dado que se estrecha explícitamente el valor para que quepa en el destino. La conversión de un tipo se realiza poniendo delante un nombre de tipo entre paréntesis, por ejemplo, (tipo) valor. El código siguiente demuestra la conversión de tipos de int a byte. Si el valor del entero fuese mayor que el rango de un byte, se reduciría al módulo (resto de la división) del rango de byte.
int a = 100;
byte b = (byte) a;
Dado que Java utiliza Unicode para representar los
caracteres de una cadena, el tipo char es de 16 bits sin
signo. El rango de un carácter es de 0 a 65536. No hay
caracteres negativos. Unicode es una unificación de docenas
de conjuntos de caracteres, incluyendo el latín, griego,
arábigo, cirílico, hebreo, katakana, hangul y muchos más.
Ejemplos de declaraciones char:
char c;
char c2 = 0xf132;
char c3 = 'a';
char c4 = '\n';
Aunque no se utilicen los caracteres como enteros, puede operar con ellos como si lo fueran. Esto permite sumar dos caracteres o incrementar el valor de una variable carácter.
int tres = 3;
char uno = '1';
char cuatro = (char) (tres + uno);
La variable cuatro termina con un '4' almacenado en ella. Observe que uno fue promocionado a int en la expresión, por lo que se requiere la conversión de tipos para volver a char antes de la asignación a cuatro.
Java tiene un tipo simple para los valores lógicos, llamado
boolean. Sólo puede tomar uno de estos dos posibles
valores, true (verdadero) o false (falso) que son palabras
reservadas. Este es el tipo que devuelven todos los
operadores de comparación o que se requiere en todos los
operadores de control de flujo que se explicarán en
capítulos posteriores.
Ejemplo de una declaración de tipo boolean:
boolean terminado = false;
Las matrices son un tipo especial que agrupa un conjunto de variables del mismo tipo. Si se desea crear una matriz de doce enteros, se crea un tipo especial, que es una "matriz de int". Este ejemplo muestra la declaración de una variable month_day con el tipo "matriz de int":
int month_days[];
Para las matrices, hay un valor especial llamado null, que representa una matriz sin ningún valor. Se debe utilizar un operador especial, new (nuevo), para asignar el espacio de una matriz. Para utilizar el operador new se debe promocionar un tipo y un número entero no negativo de elementos a asignar. En este ejemplo, se asignan 12 enteros a una matriz a la que se referencia como month_days.
month_days = new int[12];
No hay realmente matrices multidimensionales en Java. Hay matrices de matrices, que se parecen mucho a matrices multidimensionales, con un par de diferencias. En Java, se puede declarar que una variable sea tridimensional, pero no definir las dimensiones segunda y tercera, y después asignar las direcciones Y y Z de manera separada. El código siguiente crea una matriz tradicional de 16 doubles. Internamente esta matriz se implementa como una matriz de matrices double.
double matrix[][] = new double[4][4];
El código que sigue inicializa la misma cantidad de memoria pero haciendo las últimas inicializaciones a mano para mostrar cómo las distintas dimensiones son en realidad matrices anidadas.
double matrix[][] = new double[4][];
matrix[0] = new double[4];
matrix[1] = new double[4];
matrix[2] = new double[4];
matrix[3] = { 0, 1, 2, 3 };