Oi! À medida que progrediu no CodeGym, você encontrou tipos primitivos muitas vezes. Aqui está uma pequena lista do que sabemos sobre eles:
- Eles não são objetos e representam um valor armazenado na memória
- Existem vários tipos
- Números inteiros: byte , short , int , long
- Números de ponto flutuante (fracionários): float e double
- Valores lógicos: booleano
- Valores simbólicos (para representar letras e números): char
-
Cada tipo tem seu próprio intervalo de valores:
tipo primitivo |
Tamanho na memória |
Faixa de valor |
byte |
8 bits |
-128 a 127 |
curto |
16 bits |
-32768 a 32767 |
Caracteres |
16 bits |
0 a 65536 |
int |
32 bits |
-2147483648 a 2147483647 |
longo |
64 bits |
-9223372036854775808 a 9223372036854775807 |
flutuador |
32 bits |
(2 elevado a -149) para ((2 - (2 elevado a -23)) * 2 elevado a 127) |
dobro |
64 bits |
(-2 elevado a 63) para ((2 elevado a 63) - 1) |
boleano |
8 (quando usado em arrays), 32 (se não for usado em arrays) |
verdadeiro ou falso |
Mas, além de terem valores diferentes, diferem também no espaço que ocupam na memória. Um
int leva mais de um byte. E um
longo é maior que um curto. A quantidade de memória ocupada por primitivos pode ser comparada a bonecas russas:
![Alargamento e estreitamento de tipos primitivos - 2]()
cada boneca tem espaço disponível dentro. Quanto maior a boneca de nidificação, mais espaço haverá. Uma boneca de nidificação grande (
comprida ) acomodará facilmente um
int menor . Cabe facilmente e você não precisa fazer mais nada. Em Java, ao trabalhar com primitivos, isso é chamado de conversão implícita. Ou, em outras palavras, é chamado de alargamento.
Alargamento em Java
Aqui está um exemplo simples de uma conversão de ampliação:
public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
byte littleNumber = 16;
bigNumber = littleNumber;
System.out.println(bigNumber);
}
}
Aqui, atribuímos um valor de byte a uma variável
int . A atribuição é bem-sucedida sem problemas: o valor armazenado em um byte ocupa menos memória do que um
int pode acomodar. O pequeno boneco de aninhamento (valor do byte) cabe facilmente dentro do grande boneco de aninhamento ( variável
int ). É uma questão diferente se você tentar fazer o oposto, ou seja, colocar um valor grande em uma variável cujo intervalo não pode acomodar esse tipo de big data. Com bonecos de nidificação reais, o número simplesmente não caberia. Com Java, pode, mas com nuances. Vamos tentar colocar um
int em uma variável
curta :
public static void main(String[] args) {
int bigNumber = 10000000;
short littleNumber = 1000;
littleNumber = bigNumber;// Error!
System.out.println(bigNumber);
}
Erro! O compilador entende que você está tentando fazer algo anormal ao colocar um grande boneco de aninhamento (
int ) dentro de um pequeno (
short ). Nesse caso, o erro de compilação é um aviso do compilador: "Ei, você tem
certeza absoluta de que deseja fazer isso?" Se você tiver certeza, diga ao compilador:
"Está tudo bem. Eu sei o que estou fazendo!" Esse processo é chamado de conversão de tipo explícito ou restrição.
Estreitando em Java
Para realizar uma conversão de restrição, você precisa indicar explicitamente o tipo para o qual deseja converter seu valor. Em outras palavras, você precisa responder à pergunta do compilador:
"Bem, em qual dessas pequenas bonecas você deseja colocar esta grande boneca?" No nosso caso, fica assim:
public static void main(String[] args) {
int bigNumber = 10000000;
short littleNumber = 1000;
littleNumber = (short) bigNumber;
System.out.println(littleNumber);
}
Indicamos explicitamente que queremos colocar um
int em uma variável
curta e que assumiremos a responsabilidade. Vendo que um tipo mais restrito foi explicitamente indicado, o compilador executa a conversão. Qual é o resultado? Saída do console:
-27008 Isso foi um pouco inesperado. Por que exatamente conseguimos isso? Na verdade, é tudo muito simples. Originalmente, o valor era 10000000. Foi armazenado em uma variável
int , que ocupa 32 bits. Esta é sua representação binária:
Escrevemos esse valor em uma variável
curta , que pode armazenar apenas 16 bits! Assim, apenas os primeiros 16 bits do nosso número serão movidos para lá. O restante será descartado. Como resultado, a variável curta recebe o seguinte valor
que na forma decimal é igual a -27008 É por isso que o compilador pede para você "confirmar" indicando uma conversão de restrição explícita para um tipo específico. Primeiro, isso mostra que você está assumindo a responsabilidade pelo resultado. Em segundo lugar, informa ao compilador quanto espaço alocar quando a conversão ocorrer. Afinal, no último exemplo, se atribuíssemos um valor int a uma variável byte em vez de
short , teríamos apenas 8 bits à nossa disposição, não 16, e o resultado seria diferente. Os tipos fracionários (
float e
double ) têm seu próprio processo para restringir as conversões. Se você tentar converter um número fracional para um tipo inteiro, a parte fracionária será descartada.
public static void main(String[] args) {
double d = 2.7;
long x = (int) d;
System.out.println(x);
}
Saída do console:
2
Caracteres
Você já sabe que
char é usado para exibir caracteres individuais.
public static void main(String[] args) {
char c = '!';
char z = 'z';
char i = '8';
}
Mas esse tipo de dados tem vários recursos que é importante entender. Vejamos novamente a tabela de faixas de valores:
tipo primitivo |
Tamanho na memória |
Faixa de valor |
byte |
8 bits |
-128 a 127 |
curto |
16 bits |
-32768 a 32767 |
Caracteres |
16 bits |
0 a 65536 |
int |
32 bits |
-2147483648 a 2147483647 |
longo |
64 bits |
-9223372036854775808 a 9223372036854775807 |
flutuador |
32 bits |
(2 elevado a -149) para ((2 - (2 elevado a -23)) * 2 elevado a 127) |
dobro |
64 bits |
(-2 elevado a 63) para ((2 elevado a 63) - 1) |
boleano |
8 (quando usado em arrays), 32 (se não for usado em arrays) |
verdadeiro ou falso |
O intervalo de 0 a 65536 é indicado para o tipo
de char . Mas o que isso significa? Afinal, um
caractere não representa apenas números, mas também letras, sinais de pontuação… O problema é que em Java os valores
de caracteres são armazenados no formato Unicode. Já encontramos o Unicode em uma das lições anteriores. Você provavelmente se lembra que o Unicode é um padrão de codificação de caracteres que inclui os símbolos de quase todas as línguas escritas do mundo. Em outras palavras, é uma lista de códigos especiais que representam quase todos os caracteres de qualquer idioma. Toda a tabela Unicode é muito grande e, claro, não há necessidade de decorá-la. Aqui está uma pequena parte dele:
![Alargamento e estreitamento de tipos primitivos - 5]()
O principal é entender como os caracteres são armazenados e lembrar que, se você souber o código de um determinado caractere, sempre poderá produzir esse caractere em seu programa. Vamos tentar com algum número aleatório:
public static void main(String[] args) {
int x = 32816;
char c = (char) x ;
System.out.println(c);
}
Saída do console: 耰 Este é o formato usado para armazenar
char s em Java. Cada símbolo corresponde a um número: um código numérico de 16 bits (dois bytes). Em Unicode, 32816 corresponde ao caractere chinês 耰. Anote o seguinte ponto. Neste exemplo, usamos uma variável
int . Ele ocupa 32 bits de memória, enquanto um
char ocupa 16. Aqui escolhemos um
int , pois nosso número (32816) não caberia em um
short . Embora o tamanho de um
char (assim como um
short ) seja de 16 bits, não há números negativos no intervalo
de char , então a parte "positiva" do
charintervalo é duas vezes maior (65536 em vez de 32767 para o tipo
curto ). Podemos usar um
int desde que nosso código fique abaixo de 65536. Mas se você criar um valor
int maior que 65536, ele ocupará mais de 16 bits. E isso resultará em uma conversão estreita
char c = (char) x;
os bits extras serão descartados (como discutido acima) e o resultado será bastante inesperado.
Recursos especiais de adição de caracteres e números inteiros
Vejamos um exemplo inusitado:
public class Main {
public static void main(String[] args) {
char c = '1';
int i = 1;
System.out.println(i + c);
}
}
Saída do console:
50 O_О Como isso faz sentido? 1+1. De onde vieram os 50?! Você já sabe que
char
os valores são armazenados na memória como números no intervalo de 0 a 65536 e que esses números são uma representação Unicode de um caractere.
![Alargamento e estreitamento de tipos primitivos - 6]()
Quando adicionamos um
caractere e algum tipo de número inteiro, o
caractere é convertido no número Unicode correspondente. Em nosso código, quando adicionamos 1 e '1', o símbolo '1' foi convertido em seu próprio código, que é 49 (você pode verificar isso na tabela acima). Portanto, o resultado é 50. Vamos mais uma vez pegar nosso velho amigo 耰 como exemplo e tentar adicioná-lo a algum número.
public static void main(String[] args) {
char c = '耰';
int x = 200;
System.out.println(c + x);
}
Saída do console:
33016 Já descobrimos que 耰 corresponde a 32816. E quando somamos esse número e 200, obtemos nosso resultado: 33016. :) Como você pode ver, o algoritmo aqui é bastante simples, mas você não deve esquecê-lo .
GO TO FULL VERSION