Java Language
Tipi di dati primitivi
Ricerca…
introduzione
byte
, short
, int
, long
, char
, boolean
, float
e double
sono i tipi che memorizzano la maggior parte dei dati numerici grezzi nei programmi Java.
Sintassi
int aInt = 8; // La parte di definizione (numero) di questa dichiarazione int è detta letterale.
int hexInt = 0x1a; // = 26; È possibile definire valori letterali con valori esadecimali preceduti da 0x .
int binInt = 0b11010; // = 26; È inoltre possibile definire valori letterali binari; prefisso con 0b .
long good Long = 10000000000L; // Per impostazione predefinita, i valori letterali interi sono di tipo int. Aggiungendo la L alla fine del letterale stai dicendo al compilatore che il letterale è lungo. Senza questo il compilatore genererebbe un errore "Numero intero troppo grande".
double aDouble = 3,14; // I letterali a virgola mobile sono di tipo double per impostazione predefinita.
float aFloat = 3.14F; // Per impostazione predefinita questo letterale sarebbe stato un double (e causato un errore "Tipi incompatibili"), ma aggiungendo una F diciamo al compilatore che è un float.
Osservazioni
Java ha 8 tipi di dati primitivi , vale a dire boolean
, byte
, short
, char
, int
, long
, float
e double
. (Tutti gli altri tipi sono tipi di riferimento, inclusi tutti i tipi di array e tipi / classi di oggetti incorporati che hanno un significato speciale nel linguaggio Java, ad esempio String
, Class
e Throwable
e le relative sottoclassi.)
Il risultato di tutte le operazioni (addizione, sottrazione, moltiplicazione, ecc.) Su un tipo primitivo è almeno un int
, quindi aggiungendo un short
a un short
produce un int
, come aggiungere un byte
a un byte
o un char
a un char
. Se vuoi assegnare il risultato di quel ritorno ad un valore dello stesso tipo, devi lanciarlo. per esempio
byte a = 1;
byte b = 2;
byte c = (byte) (a + b);
La mancata esecuzione dell'operazione comporterà un errore di compilazione.
Ciò è dovuto alla seguente parte della Java Language Spec, §2.11.1 :
Un compilatore codifica carichi di valori letterali di tipi
byte
eshort
utilizzando le istruzioni Java Virtual Machine che firmano-estendono tali valori ai valori di tipoint
al momento della compilazione o in fase di esecuzione. Carichi di valori letterali di tipiboolean
echar
sono codificati usando istruzioni che estendono a zero il valore letterale a un valore di tipoint
al momento della compilazione o in fase di esecuzione. [..]. Quindi, la maggior parte delle operazioni sui valori di tipi realiboolean
,byte
,char
eshort
vengono eseguite correttamente mediante istruzioni che operano su valori di tipo computazionaleint
.
La ragione di ciò è specificata anche in quella sezione:
Data la dimensione dell'opcode a byte singolo di Java Virtual Machine, i tipi di codifica in opcode mettono pressione sul design del set di istruzioni. Se ogni istruzione digitata supportava tutti i tipi di dati di runtime della Java Virtual Machine, ci sarebbero più istruzioni di quante potrebbero essere rappresentate in un
byte
. [...] È possibile utilizzare istruzioni separate per convertire tra tipi di dati non supportati e supportati, se necessario.
Il primitivo int
Un tipo di dati primitivo come int
detiene i valori direttamente nella variabile che lo utilizza, nel frattempo una variabile che è stata dichiarata utilizzando Integer
contiene un riferimento al valore.
In base all'API java : "La classe Integer racchiude un valore del tipo primitivo int in un oggetto. Un oggetto di tipo Integer contiene un singolo campo il cui tipo è int."
Per impostazione predefinita, int
è un numero intero con int
a 32 bit. Può memorizzare un valore minimo di -2 31 e un valore massimo di 2 31 - 1.
int example = -42;
int myInt = 284;
int anotherInt = 73;
int addedInts = myInt + anotherInt; // 284 + 73 = 357
int subtractedInts = myInt - anotherInt; // 284 - 73 = 211
Se è necessario memorizzare un numero al di fuori di questo intervallo, dovrebbe essere usato a long
. Il superamento dell'intervallo di valori di int
causa un overflow di un intero, causando il valore che supera l'intervallo da aggiungere al sito opposto dell'intervallo (positivo diventa negativo e viceversa). Il valore è ((value - MIN_VALUE) % RANGE) + MIN_VALUE
o ((value + 2147483648) % 4294967296) - 2147483648
int demo = 2147483647; //maximum positive integer
System.out.println(demo); //prints 2147483647
demo = demo + 1; //leads to an integer overflow
System.out.println(demo); // prints -2147483648
I valori massimi e minimi di int
possono essere trovati a:
int high = Integer.MAX_VALUE; // high == 2147483647
int low = Integer.MIN_VALUE; // low == -2147483648
Il valore predefinito di un int
è 0
int defaultInt; // defaultInt == 0
Il breve primitivo
Un short
è un intero con segno a 16 bit. Ha un valore minimo di -2 15 (-32.768) e un valore massimo di 2 15 -1 (32.767)
short example = -48;
short myShort = 987;
short anotherShort = 17;
short addedShorts = (short) (myShort + anotherShort); // 1,004
short subtractedShorts = (short) (myShort - anotherShort); // 970
I valori massimi e minimi del short
possono essere trovati a:
short high = Short.MAX_VALUE; // high == 32767
short low = Short.MIN_VALUE; // low == -32768
Il valore predefinito di un short
è 0
short defaultShort; // defaultShort == 0
Il lungo primitivo
Per impostazione predefinita, long
è un intero con segno a 64 bit (in Java 8, può essere firmato o non firmato). Firmato, può memorizzare un valore minimo di -2 63 e un valore massimo di 2 63 - 1, e senza segno può memorizzare un valore minimo di 0 e un valore massimo di 2 64 - 1
long example = -42;
long myLong = 284;
long anotherLong = 73;
//an "L" must be appended to the end of the number, because by default,
//numbers are assumed to be the int type. Appending an "L" makes it a long
//as 549755813888 (2 ^ 39) is larger than the maximum value of an int (2^31 - 1),
//"L" must be appended
long bigNumber = 549755813888L;
long addedLongs = myLong + anotherLong; // 284 + 73 = 357
long subtractedLongs = myLong - anotherLong; // 284 - 73 = 211
I valori massimo e minimo di long
possono essere trovati a:
long high = Long.MAX_VALUE; // high == 9223372036854775807L
long low = Long.MIN_VALUE; // low == -9223372036854775808L
Il valore predefinito di long
è 0L
long defaultLong; // defaultLong == 0L
Nota: la lettera "L" aggiunta alla fine del letterale long
fa distinzione tra maiuscole e minuscole, tuttavia è buona norma utilizzare il capitale in quanto è più semplice distinguere dal primo:
2L == 2l; // true
Avviso: cache Java istanze di oggetti interi nell'intervallo da -128 a 127. Il ragionamento è spiegato qui: https://blogs.oracle.com/darcy/entry/boxing_and_caches_integer_valueof
I seguenti risultati possono essere trovati:
Long val1 = 127L;
Long val2 = 127L;
System.out.println(val1 == val2); // true
Long val3 = 128L;
Long val4 = 128L;
System.out.println(val3 == val4); // false
Per confrontare correttamente 2 valori Long Object, utilizzare il codice seguente (da Java 1.7 in poi):
Long val3 = 128L;
Long val4 = 128L;
System.out.println(Objects.equal(val3, val4)); // true
Confrontare un primitivo lungo ad un oggetto lungo non darà come risultato un falso negativo come confrontare 2 oggetti con ==.
Il primitivo booleano
Un boolean
può memorizzare uno dei due valori, true
o false
boolean foo = true;
System.out.println("foo = " + foo); // foo = true
boolean bar = false;
System.out.println("bar = " + bar); // bar = false
boolean notFoo = !foo;
System.out.println("notFoo = " + notFoo); // notFoo = false
boolean fooAndBar = foo && bar;
System.out.println("fooAndBar = " + fooAndBar); // fooAndBar = false
boolean fooOrBar = foo || bar;
System.out.println("fooOrBar = " + fooOrBar); // fooOrBar = true
boolean fooXorBar = foo ^ bar;
System.out.println("fooXorBar = " + fooXorBar); // fooXorBar = true
Il valore predefinito di un boolean
è falso
boolean defaultBoolean; // defaultBoolean == false
Il byte primitivo
Un byte
è un intero con byte
a 8 bit. Può memorizzare un valore minimo di -2 7 (-128) e un valore massimo di 2 7 - 1 (127)
byte example = -36;
byte myByte = 96;
byte anotherByte = 7;
byte addedBytes = (byte) (myByte + anotherByte); // 103
byte subtractedBytes = (byte) (myBytes - anotherByte); // 89
I valori massimi e minimi di byte
possono essere trovati su:
byte high = Byte.MAX_VALUE; // high == 127
byte low = Byte.MIN_VALUE; // low == -128
Il valore predefinito di un byte
è 0
byte defaultByte; // defaultByte == 0
Il galleggiante primitivo
Un float
è un numero a virgola mobile IEEE 754 a 32 bit a precisione singola. Per impostazione predefinita, i decimali vengono interpretati come doppi. Per creare un float
, è sufficiente aggiungere un f
al letterale decimale.
double doubleExample = 0.5; // without 'f' after digits = double
float floatExample = 0.5f; // with 'f' after digits = float
float myFloat = 92.7f; // this is a float...
float positiveFloat = 89.3f; // it can be positive,
float negativeFloat = -89.3f; // or negative
float integerFloat = 43.0f; // it can be a whole number (not an int)
float underZeroFloat = 0.0549f; // it can be a fractional value less than 0
I float gestiscono le cinque operazioni aritmetiche comuni: addizione, sottrazione, moltiplicazione, divisione e modulo.
Nota: quanto segue può variare leggermente a causa di errori in virgola mobile. Alcuni risultati sono stati arrotondati per chiarezza e leggibilità (il risultato stampato dell'esempio di addizione era in realtà 34.600002).
// addition
float result = 37.2f + -2.6f; // result: 34.6
// subtraction
float result = 45.1f - 10.3f; // result: 34.8
// multiplication
float result = 26.3f * 1.7f; // result: 44.71
// division
float result = 37.1f / 4.8f; // result: 7.729166
// modulus
float result = 37.1f % 4.8f; // result: 3.4999971
A causa del modo in cui i numeri in virgola mobile vengono memorizzati (ad esempio in formato binario), molti numeri non hanno una rappresentazione esatta.
float notExact = 3.1415926f;
System.out.println(notExact); // 3.1415925
Sebbene l'uso di float
bene per la maggior parte delle applicazioni, non è necessario utilizzare né float
né double
per memorizzare rappresentazioni esatte di numeri decimali (come importi monetari) o numeri in cui è richiesta una maggiore precisione. Invece, dovrebbe essere usata la classe BigDecimal
.
Il valore predefinito di un float
è 0.0f .
float defaultFloat; // defaultFloat == 0.0f
Un float
è preciso a circa un errore di 1 su 10 milioni.
Nota: Float.POSITIVE_INFINITY
, Float.NEGATIVE_INFINITY
, Float.NaN
sono float
valori. NaN
sta per risultati di operazioni che non possono essere determinate, come la divisione di 2 valori infiniti. Inoltre 0f
e -0f
sono diversi, ma ==
si -0f
vero:
float f1 = 0f;
float f2 = -0f;
System.out.println(f1 == f2); // true
System.out.println(1f / f1); // Infinity
System.out.println(1f / f2); // -Infinity
System.out.println(Float.POSITIVE_INFINITY / Float.POSITIVE_INFINITY); // NaN
Il doppio primitivo
Un double
è un numero a virgola mobile IEEE 754 a 64 bit a doppia precisione.
double example = -7162.37;
double myDouble = 974.21;
double anotherDouble = 658.7;
double addedDoubles = myDouble + anotherDouble; // 315.51
double subtractedDoubles = myDouble - anotherDouble; // 1632.91
double scientificNotationDouble = 1.2e-3; // 0.0012
A causa del modo in cui vengono memorizzati i numeri in virgola mobile, molti numeri non hanno una rappresentazione esatta.
double notExact = 1.32 - 0.42; // result should be 0.9
System.out.println(notExact); // 0.9000000000000001
Mentre l'uso del double
va bene per la maggior parte delle applicazioni, non è necessario utilizzare né float
né double
per memorizzare numeri precisi come la valuta. Invece, dovrebbe essere usata la classe BigDecimal
Il valore predefinito di un double
è 0.0d
public double defaultDouble; // defaultDouble == 0.0
Nota: Double.POSITIVE_INFINITY
, Double.NEGATIVE_INFINITY
, Double.NaN
sono valori double
. NaN
sta per risultati di operazioni che non possono essere determinate, come la divisione di 2 valori infiniti. Inoltre 0d
e -0d
sono diversi, ma ==
si -0d
vero:
double d1 = 0d;
double d2 = -0d;
System.out.println(d1 == d2); // true
System.out.println(1d / d1); // Infinity
System.out.println(1d / d2); // -Infinity
System.out.println(Double.POSITIVE_INFINITY / Double.POSITIVE_INFINITY); // NaN
Il primitivo char
Un char
può memorizzare un singolo carattere Unicode a 16 bit. Un letterale di carattere è racchiuso tra virgolette singole
char myChar = 'u';
char myChar2 = '5';
char myChar3 = 65; // myChar3 == 'A'
Ha un valore minimo di \u0000
(0 nella rappresentazione decimale, chiamata anche carattere null ) e un valore massimo di \uffff
(65.535).
Il valore predefinito di un char
è \u0000
.
char defaultChar; // defaultChar == \u0000
Per definire un char di '
valore, è necessario utilizzare una sequenza di escape (carattere preceduto da una barra rovesciata):
char singleQuote = '\'';
Ci sono anche altre sequenze di escape:
char tab = '\t';
char backspace = '\b';
char newline = '\n';
char carriageReturn = '\r';
char formfeed = '\f';
char singleQuote = '\'';
char doubleQuote = '\"'; // escaping redundant here; '"' would be the same; however still allowed
char backslash = '\\';
char unicodeChar = '\uXXXX' // XXXX represents the Unicode-value of the character you want to display
È possibile dichiarare un char
di qualsiasi carattere Unicode.
char heart = '\u2764';
System.out.println(Character.toString(heart)); // Prints a line containing "❤".
È anche possibile aggiungere a un char
. ad esempio per scorrere tutte le lettere minuscole, è possibile fare quanto segue:
for (int i = 0; i <= 26; i++) {
char letter = (char) ('a' + i);
System.out.println(letter);
}
Rappresentazione del valore negativo
Java e molti altri linguaggi memorizzano numeri interi negativi in una rappresentazione chiamata notazione complemento a 2 .
Per una rappresentazione binaria univoca di un tipo di dati utilizzando n
bit, i valori sono codificati in questo modo:
I bit meno significativi di n-1
memorizzano un numero integrale positivo x
in rappresentazione integrale. Il valore più significativo memorizza un valore di bit vith s
. Il valore ripreso da quei bit è
x - s * 2 n-1
cioè se il bit più significativo è 1, allora un valore che è solo di 1 più grande del numero che potresti rappresentare con gli altri bit ( 2 n-2 + 2 n-3 + ... + 2 1 + 2 0 = 2 n-1 - 1
) viene sottratto consentendo una rappresentazione binaria univoca per ogni valore da - 2 n-1 (s = 1; x = 0) a 2 n-1 - 1 (s = 0; x = 2 n-1 - 1).
Questo ha anche il bell'effetto collaterale, che è possibile aggiungere le rappresentazioni binarie come se fossero numeri binari positivi:
v1 = x1 - s1 * 2n-1 v2 = x2 - s2 * 2n-1
s1 | s2 | x1 + x2 overflow | risultato aggiuntivo |
---|---|---|---|
0 | 0 | No | x1 + x2 = v1 + v2 |
0 | 0 | sì | troppo grande per essere rappresentata con il tipo di dati (overflow) |
0 | 1 | No | x1 + x2 - 2n-1 = x1 + x2 - s2 * 2n-1 |
0 | 1 | sì | (x1 + x2) mod 2n-1 = x1 + x2 - 2n-1 |
1 | 0 | * | vedi sopra (scambia i contatti) |
1 | 1 | No | troppo piccolo per essere rappresentato con il tipo di dati (x1 + x2 - 2 n <-2 n-1 ; underflow) |
1 | 1 | sì | (x1 + x2) mod 2n-1 - 2n-1 = (x1 + x2 - 2n-1) - 2n-1 |
Si noti che questo fatto rende facile trovare la rappresentazione binaria dell'inverso additivo (cioè il valore negativo):
Osservare che aggiungendo il complemento bit per bit al numero risulta che tutti i bit siano 1. Ora aggiungi 1 per rendere il valore overflow e ottieni l'elemento neutro 0 (tutti i bit 0).
Quindi il valore negativo di un numero i
può essere calcolata utilizzando (ignorando possibile promozione a int
qui)
(~i) + 1
Esempio: prendendo il valore negativo di 0 ( byte
):
Il risultato della negazione di 0
è 11111111
. L'aggiunta di 1 fornisce un valore di 100000000
(9 bit). Poiché un byte
può solo memorizzare 8 bit, il valore più a sinistra viene troncato e il risultato è 00000000
Originale | Processi | Risultato |
---|---|---|
0 (00000000) | Negare | -0 (11111111) |
11111111 | Aggiungi 1 al binario | 100000000 |
100000000 | Tronca a 8 bit | 00000000 (-0 è uguale a 0) |
Consumo di memoria dei primitivi rispetto ai primitivi inscatolati
Primitivo | Tipo in scatola | Dimensione della memoria di primitivo / in scatola |
---|---|---|
booleano | booleano | 1 byte / 16 byte |
byte | Byte | 1 byte / 16 byte |
corto | Corto | 2 byte / 16 byte |
carbonizzare | carbonizzare | 2 byte / 16 byte |
int | Numero intero | 4 byte / 16 byte |
lungo | Lungo | 8 byte / 16 byte |
galleggiante | Galleggiante | 4 byte / 16 byte |
Doppio | Doppio | 8 byte / 16 byte |
Gli oggetti in scatola richiedono sempre 8 byte per tipo e gestione della memoria, e poiché la dimensione degli oggetti è sempre un multiplo di 8, tutti i tipi di box richiedono 16 byte totali . Inoltre , ogni utilizzo di un oggetto scatolato comporta la memorizzazione di un riferimento che tiene conto di altri 4 o 8 byte, a seconda delle opzioni JVM e JVM.
Nelle operazioni a uso intensivo di dati, il consumo di memoria può avere un notevole impatto sulle prestazioni. Il consumo di memoria aumenta ulteriormente quando si utilizzano gli array: un array float[5]
richiede solo 32 byte; mentre un Float[5]
memorizza 5 valori distinti non nulli richiederà un totale di 112 byte (su 64 bit senza puntatori compressi, questo aumenta a 152 byte).
Cache di valore in scatola
Gli overheads spaziali dei tipi di box possono essere mitigati in misura maggiore dalle cache del valore in box. Alcuni tipi di box implementano una cache di istanze. Ad esempio, per impostazione predefinita, la classe Integer
memorizzerà nella cache le istanze per rappresentare numeri nell'intervallo da -128
a +127
. Ciò, tuttavia, non riduce il costo aggiuntivo derivante dall'indirizzamento aggiuntivo della memoria.
Se si crea un'istanza di un tipo in box mediante autoboxing o chiamando il valueOf(primitive)
statico valueOf(primitive)
, il sistema runtime tenterà di utilizzare un valore memorizzato nella cache. Se l'applicazione utilizza molti valori nell'intervallo che viene memorizzato nella cache, questo può ridurre in modo sostanziale la penalità della memoria dell'utilizzo di tipi di box. Certamente, se crei istanze di valore in box "a mano", è meglio usare valueOf
piuttosto che new
. (La new
operazione crea sempre una nuova istanza.) Se, tuttavia, la maggior parte dei valori non si trova nell'intervallo memorizzato nella cache, può essere più veloce chiamare la new
e salvare la ricerca della cache.
Conversione di primitivi
In Java, possiamo convertire tra valori interi e valori a virgola mobile. Inoltre, poiché ogni carattere corrisponde a un numero nella codifica Unicode, i tipi di char
possono essere convertiti in e da tipi interi e in virgola mobile. boolean
è l'unico tipo di dati primitivo che non può essere convertito in o da qualsiasi altro tipo di dati primitivo.
Esistono due tipi di conversioni: conversione allargata e conversione restringimento .
Una conversione allargata è quando un valore di un tipo di dati viene convertito in un valore di un altro tipo di dati che occupa più bit rispetto al primo. In questo caso non vi è alcun problema di perdita di dati.
Corrispondentemente, una conversione restringimento è quando un valore di un tipo di dati viene convertito in un valore di un altro tipo di dati che occupa meno bit del primo. La perdita di dati può verificarsi in questo caso.
Java esegue automaticamente l' ampliamento delle conversioni . Ma se si desidera eseguire una conversione di restringimento (se si è certi che non si verificherà alcuna perdita di dati), è possibile forzare Java ad eseguire la conversione utilizzando un costrutto linguistico noto come cast
.
Conversione allargata:
int a = 1;
double d = a; // valid conversion to double, no cast needed (widening)
Riduzione della conversione:
double d = 18.96
int b = d; // invalid conversion to int, will throw a compile-time error
int b = (int) d; // valid conversion to int, but result is truncated (gets rounded down)
// This is type-casting
// Now, b = 18
Cheatsheet Tipi primitivi
Tabella che mostra le dimensioni e l'intervallo di valori di tutti i tipi primitivi:
tipo di dati | rappresentazione numerica | intervallo di valori | valore predefinito |
---|---|---|---|
booleano | n / A | falso e vero | falso |
byte | Firmato a 8 bit | -2 7 a 2 7 - 1 | 0 |
Da -128 a +127 | |||
corto | Firmato a 16 bit | -2 15 a 2 15 - 1 | 0 |
Da -32.768 a +32.767 | |||
int | Firmato a 32 bit | -2 31 a 2 31 - 1 | 0 |
-2,147,483,648 a +2,147,483,647 | |||
lungo | Firmato a 64 bit | -2 63 a 2 63 - 1 | 0L |
-9.223.372.036.854.775.808 a 9.223.372.036.854.775.807 | |||
galleggiante | 32 virgola mobile | 1.401298464e-45 a 3.402823466e + 38 (positivo o negativo) | 0.0f |
Doppio | Virgola mobile a 64 bit | 4.94065645841246544e-324d a 1.79769313486231570e + 308d (positivo o negativo) | 0.0D |
carbonizzare | 16 bit senza segno | Da 0 a 2 16 - 1 | 0 |
Da 0 a 65.535 |
Gli appunti:
- La specifica del linguaggio Java richiede che i tipi integrali firmati (da
byte
along
) utilizzino la rappresentazione binaria del complemento a due e che i tipi a virgola mobile utilizzino rappresentazioni in virgola mobile binarie IEE 754 standard. - Java 8 e versioni successive forniscono metodi per eseguire operazioni aritmetiche non firmate su
int
elong
. Sebbene questi metodi consentano a un programma di trattare i valori dei rispettivi tipi come non firmati, i tipi restano tipi firmati. - Il più piccolo punto fluttuante mostrato sopra è subnormale ; cioè hanno meno precisione di un valore normale . I numeri normali più piccoli sono 1.175494351e-38 e 2.2250738585072014e-308
- Un
char
rappresenta convenzionalmente un'unità di codice Unicode / UTF-16. - Sebbene un
boolean
contenga solo un bit di informazione, la sua dimensione in memoria varia a seconda dell'implementazione di Java Virtual Machine (si veda il tipo booleano ).