¡Hola! A medida que avanzaba en CodeGym, se encontró con tipos primitivos muchas veces. Aquí hay una breve lista de lo que sabemos sobre ellos:
- No son objetos y representan un valor almacenado en la memoria.
- Hay varios tipos
- Números enteros: byte , corto , int , largo
- Números de coma flotante (fraccionales): float y double
- Valores lógicos: booleano
- Valores simbólicos (para representar letras y números): char
-
Cada tipo tiene su propio rango de valores:
tipo primitivo |
Tamaño en memoria |
Rango de valores |
byte |
8 bits |
-128 a 127 |
corto |
16 bits |
-32768 a 32767 |
carbonizarse |
16 bits |
0 a 65536 |
En t |
32 bits |
-2147483648 al 2147483647 |
largo |
64 bits |
-9223372036854775808 al 9223372036854775807 |
flotar |
32 bits |
(2 elevado a -149) a ((2 - (2 elevado a -23)) * 2 elevado a 127) |
doble |
64 bits |
(-2 elevado a 63) a ((2 elevado a 63) - 1) |
booleano |
8 (cuando se usa en matrices), 32 (si no se usa en matrices) |
verdadero o falso |
Pero además de tener diferentes valores, también difieren en cuánto espacio ocupan en la memoria. Un
int toma más de un byte. Y un
largo es más grande que un corto. La cantidad de memoria que ocupan los primitivos se puede comparar con las muñecas rusas que anidan:
![Ensanchamiento y estrechamiento de tipos primitivos - 2]()
cada muñeca que anida tiene espacio disponible en su interior. Cuanto más grande es la muñeca de anidación, más espacio hay. Una muñeca de anidación grande (
larga ) se acomodará fácilmente a una
int más pequeña . Se ajusta fácilmente y no necesitas hacer nada más. En Java, cuando se trabaja con primitivas, esto se denomina conversión implícita. O dicho de otra manera, se llama ensanchamiento.
Ampliación en Java
Aquí hay un ejemplo simple de una conversión de ampliación:
public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
byte littleNumber = 16;
bigNumber = littleNumber;
System.out.println(bigNumber);
}
}
Aquí asignamos un valor de byte a una variable
int . La asignación tiene éxito sin ningún problema: el valor almacenado en un byte ocupa menos memoria de lo que puede acomodar un
int . La pequeña muñeca de anidación (valor de byte) cabe fácilmente dentro de la muñeca de anidación grande ( variable
int ). Es un asunto diferente si intenta hacer lo contrario, es decir, poner un valor grande en una variable cuyo rango no puede acomodar un tipo de datos tan grande. Con muñecas de anidación reales, el número simplemente no encajaría. Con Java, puede, pero con matices. Intentemos poner un
int en una variable
corta :
public static void main(String[] args) {
int bigNumber = 10000000;
short littleNumber = 1000;
littleNumber = bigNumber;
System.out.println(bigNumber);
}
¡Error! El compilador entiende que está tratando de hacer algo anormal al empujar una muñeca de anidamiento grande (
int ) dentro de una pequeña (
short ). En este caso, el error de compilación es una advertencia del compilador: "Oye, ¿estás
absolutamente seguro de que quieres hacer esto?" Si está seguro, le dice al compilador:
"Todo está bien. ¡Sé lo que estoy haciendo!" Este proceso se denomina conversión de tipo explícita o estrechamiento.
Estrechamiento en Java
Para realizar una conversión de restricción, debe indicar explícitamente el tipo al que desea convertir su valor. En otras palabras, debe responder la pregunta del compilador:
"Bueno, ¿en cuál de estos pequeños muñecos de anidación quiere poner este gran muñeco de anidación?" En nuestro caso, se ve así:
public static void main(String[] args) {
int bigNumber = 10000000;
short littleNumber = 1000;
littleNumber = (short) bigNumber;
System.out.println(littleNumber);
}
Indicamos explícitamente que queremos poner un
int en una variable
corta y que asumiremos la responsabilidad. Al ver que se ha indicado explícitamente un tipo más estrecho, el compilador realiza la conversión. ¿Cuál es el resultado? Salida de la consola:
-27008 Eso fue un poco inesperado. ¿Por qué exactamente obtuvimos eso? De hecho, todo es muy simple. Originalmente, el valor era 10000000. Se almacenaba en una variable
int , que ocupa 32 bits. Esta es su representación binaria:
¡Escribimos este valor en una variable
corta , que solo puede almacenar 16 bits! En consecuencia, solo los primeros 16 bits de nuestro número se moverán allí. El resto será descartado. Como resultado, la variable corta recibe el siguiente valor
que en forma decimal es igual a -27008 Es por eso que el compilador le pide que "confirme" indicando una conversión de restricción explícita a un tipo específico. Primero, esto demuestra que estás asumiendo la responsabilidad por el resultado. Y segundo, le dice al compilador cuánto espacio debe asignar cuando ocurre la conversión. Después de todo, en el último ejemplo, si asignáramos un valor int a una variable byte en lugar de un valor
corto , solo tendríamos 8 bits a nuestra disposición, no 16, y el resultado sería diferente. Los tipos fraccionarios (
float y
double ) tienen su propio proceso para reducir las conversiones. Si intenta convertir un número fraccional en un tipo entero, la parte fraccionaria se descartará.
public static void main(String[] args) {
double d = 2.7;
long x = (int) d;
System.out.println(x);
}
Salida de consola:
2
carbonizarse
Ya sabe que
char se usa para mostrar caracteres individuales.
public static void main(String[] args) {
char c = '!';
char z = 'z';
char i = '8';
}
Pero este tipo de datos tiene varias características que es importante comprender. Veamos de nuevo la tabla de rangos de valores:
tipo primitivo |
Tamaño en memoria |
Rango de valores |
byte |
8 bits |
-128 a 127 |
corto |
16 bits |
-32768 a 32767 |
carbonizarse |
16 bits |
0 a 65536 |
En t |
32 bits |
-2147483648 al 2147483647 |
largo |
64 bits |
-9223372036854775808 al 9223372036854775807 |
flotar |
32 bits |
(2 elevado a -149) a ((2 - (2 elevado a -23)) * 2 elevado a 127) |
doble |
64 bits |
(-2 elevado a 63) a ((2 elevado a 63) - 1) |
booleano |
8 (cuando se usa en matrices), 32 (si no se usa en matrices) |
verdadero o falso |
El rango de 0 a 65536 se indica para el tipo
de carácter . Pero ¿qué significa eso? Al fin y al cabo, un
char no solo representa números, sino también letras, signos de puntuación… Lo que pasa es que en Java los valores
char se almacenan en formato Unicode. Ya nos encontramos con Unicode en una de las lecciones anteriores. Probablemente recuerde que Unicode es un estándar de codificación de caracteres que incluye los símbolos de casi todos los idiomas escritos del mundo. En otras palabras, es una lista de códigos especiales que representan casi todos los caracteres de cualquier idioma. Toda la tabla Unicode es muy grande y, por supuesto, no es necesario aprenderla de memoria. He aquí una pequeña parte de ella:
![Ensanchamiento y estrechamiento de tipos primitivos - 5]()
Lo principal es comprender cómo se almacenan los caracteres y recordar que si conoce el código de un carácter en particular, siempre puede producir ese carácter en su programa. Probemos con algún número aleatorio:
public static void main(String[] args) {
int x = 32816;
char c = (char) x ;
System.out.println(c);
}
Salida de la consola: 耰 Este es el formato utilizado para almacenar
caracteres en Java. Cada símbolo corresponde a un número: un código numérico de 16 bits (dos bytes). En Unicode, 32816 corresponde al carácter chino 耰.
![Ensanchamiento y Estrechamiento de tipos primitivos - 5]()
Tome nota del siguiente punto. En este ejemplo, usamos una variable
int . Ocupa 32 bits en la memoria, mientras que un
char ocupa 16. Aquí elegimos un
int , porque nuestro número (32816) no cabe en un
short . Aunque el tamaño de un
char (al igual que un
short ) es de 16 bits, no hay números negativos en el rango
de char , por lo que la parte "positiva" del
charel rango es el doble de grande (65536 en lugar de 32767 para el tipo
corto ). Podemos usar un
int siempre que nuestro código se mantenga por debajo de 65536. Pero si crea un valor
int mayor que 65536, ocupará más de 16 bits. Y esto resultará en una conversión estrecha
char c = (char) x;
los bits adicionales se descartarán (como se mencionó anteriormente) y el resultado será bastante inesperado.
Características especiales de agregar caracteres y enteros
Veamos un ejemplo inusual:
public class Main {
public static void main(String[] args) {
char c = '1';
int i = 1;
System.out.println(i + c);
}
}
Salida de la consola:
50 O_О ¿Qué sentido tiene eso? 1+1. ¿De dónde vienen los 50? Ya sabe que
char
los valores se almacenan en la memoria como números en el rango de 0 a 65536, y que estos números son una representación Unicode de un carácter.
![Ensanchamiento y estrechamiento de tipos primitivos - 6]()
Cuando agregamos un
carácter y algún tipo de número entero, el
carácter se convierte al número Unicode correspondiente. En nuestro código, cuando agregamos 1 y '1', el símbolo '1' se convirtió en su propio código, que es 49 (puede verificar esto en la tabla anterior). Por lo tanto, el resultado es 50. Una vez más, tomemos a nuestro viejo amigo 耰 como ejemplo e intentemos sumarlo a algún número.
public static void main(String[] args) {
char c = '耰';
int x = 200;
System.out.println(c + x);
}
Salida de la consola:
33016 Ya descubrimos que 耰 corresponde a 32816. Y cuando sumamos este número y 200, obtenemos nuestro resultado: 33016. :) Como puede ver, el algoritmo aquí es bastante simple, pero no debe olvidarlo .
GO TO FULL VERSION