<h2>Introduzione</h2>Un linguaggio di programmazione, come la lingua parlata dalle persone, vive e cambia, in esso compaiono nuovi fenomeni per rendere il linguaggio più comodo da usare. E come sappiamo, il linguaggio dovrebbe esprimere adeguatamente i nostri pensieri.
Pertanto, in Java SE 5 è stato introdotto il meccanismo di boxing/unboxing. E un tutorial separato di Oracle è dedicato alle caratteristiche di questo mezzo per esprimere pensieri: Autoboxing e Unboxing . <h2>Boxing con imballaggio automatico</h2>Diamo un'occhiata a un esempio di Boxing con imballaggio automatico. Per prima cosa, vediamo come funziona. Usiamo il sito compilejava.net e creiamo una classe:
Questo è lo stesso famigerato “bytecode”. Ma ciò che è importante per noi ora è ciò che vediamo. Innanzitutto, la primitiva 8080 viene posizionata nello stack di esecuzione del metodo, quindi viene eseguito Integer.valueOf . Questa è la “magia” della boxe. E all'interno della magia appare così:
Cioè, in sostanza, ne verrà preso
Come puoi vedere, nessuna magia. Tutto è all'interno di Java. Funziona semplicemente “da solo”. Per nostra comodità. <h2>Rastrello</h2>
Qualsiasi strumento, se utilizzato in modo errato, diventa un'arma formidabile contro se stesso. E il meccanismo automatico di boxing/unboxing in Java non fa eccezione. Il primo, ovvio confronto è attraverso
public class App {
public static void main(String[] args) {
Integer portNumber = 8080;
if (args.length != 0) {
portNumber = Integer.valueOf(args[0]);
}
System.out.println("Port number is: " + portNumber);
}
}
Codice semplice. Possiamo specificare il parametro di input e modificare il valore della porta. Come vediamo, perché leggiamo il valore della porta dai String
parametri, lo otteniamo Integer
facendolo passare Integer.valueOf
. Siamo quindi costretti a specificarlo non come tipo primitivo, ma come tipo oggetto Integer
. E qui arriviamo da un lato, abbiamo una variabile oggetto e il valore predefinito è una primitiva. E funziona. Ma noi non crediamo nella magia, vero? Diamo un'occhiata "sotto il cofano", come si suol dire. Scarica il codice sorgente da compilejava.net facendo clic su "Scarica ZIP". Successivamente, estrai l'archivio scaricato in una directory e vai ad esso. Ora facciamo: javap -c -p App.class
dove App.class è il file di classe compilato per la tua classe. Vedremo contenuti come questo:
Integer
o ne verrà ottenuto uno nuovo Integer
dalla cache (la cache non è altro che un semplice array di numeri interi) a seconda del valore del numero. Naturalmente, Integer
non solo uno è stato così fortunato. Esiste un intero elenco di tipi primitivi correlati e dei relativi wrapper (classi che rappresentano le primitive nel mondo OOP). Questo elenco è riportato in fondo al Tutorial di Oracle: " Autoboxing e Unboxing ". Vale subito la pena notare che gli array costituiti da primitive non hanno un "wrapper" senza collegare librerie di terze parti. Quelli. Arrays.asList
non farà int[]
per noi List
da Integer
's. <h2>Unboxing</h2>Il processo inverso al boxing è chiamato unboxing unboxing. Diamo un'occhiata a un esempio di disimballaggio:
public class App {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Please, enter params");
return;
}
int value = Math.abs(Integer.valueOf(args[0]));
System.out.println("Absolute value is: " + value);
}
}
Math.abs
accetta solo primitive. Cosa fare? La classe wrapper ha un metodo speciale per questo caso che restituisce una primitiva. Ad esempio, questo è il Integer
metodo intValue . Se guardiamo il bytecode, è così:
==
. Penso che questo sia chiaro, ma vediamolo di nuovo:
public static void main(String[] args) {
Integer inCacheValue = 127;
Integer inCacheValue2 = 127;
Integer notInCache = 128; // new Integer(129)
Integer notInCache2 = 128; // new Integer(129)
System.out.println(inCacheValue == inCacheValue2); //true
System.out.println(notInCache == notInCache2); //false
}
Nel primo caso, il valore viene preso dalla Integer
cache dei valori (vedere la spiegazione del Boxing sopra), mentre nel secondo caso verrà creato ogni volta un nuovo oggetto. Ma qui vale la pena prenotare. Questo comportamento dipende dal limite superiore della cache ( java.lang.Integer.IntegerCache.high ). Inoltre, questo limite potrebbe cambiare a causa di altre impostazioni. Puoi leggere la discussione su questo argomento su StackOverflow: quanto è grande la cache Integer? Naturalmente gli oggetti devono essere confrontati utilizzando uguali: System.out.println(notInCache.equals(notInCache2));
il secondo problema associato allo stesso meccanismo è la prestazione. Qualsiasi boxing in Java equivale alla creazione di un nuovo oggetto. Se il numero non è incluso nei valori della cache (ad esempio da -128 a 127), verrà creato ogni volta un nuovo oggetto. Se all'improvviso l'imballaggio (cioè l'inscatolamento) viene eseguito in un ciclo, ciò causerà un enorme aumento degli oggetti non necessari e il consumo di risorse per il lavoro del garbage collector. Pertanto, non essere troppo avventato al riguardo. Un terzo rastrello, non meno doloroso, deriva dallo stesso meccanismo:
public static void check(Integer value) {
if (value <= 0) {
throw new IllegalStateException("Value is too small");
}
}
In questo codice, la persona stava chiaramente cercando di non superare l'errore. Ma non esiste alcun assegno per null
. Se si tratta dell'input null
, invece di un errore comprensibile ne otterremo uno incomprensibile NullPointerException
. Perché per fare un confronto, Java tenterà di eseguirsi value.intValue
e bloccarsi, perché... value
Volere null
. <h2>Conclusione</h2>Il meccanismo di boxing/unboxing consente al programmatore di scrivere meno codice e talvolta di non pensare nemmeno alla conversione dalle primitive agli oggetti e viceversa. Ma ciò non significa che dovresti dimenticare come funziona. Altrimenti potresti commettere un errore che potrebbe non apparire immediatamente. Non dovremmo fare affidamento su parti del sistema che non sono completamente sotto il nostro controllo (come il confine del numero intero). Ma non dimenticare tutti i vantaggi delle classi wrapper (come Integer
). Spesso queste classi wrapper hanno una serie di metodi statici aggiuntivi che renderanno la tua vita migliore e il tuo codice più espressivo. Ecco un esempio di recupero:
public static void main(String[] args) {
int first = 1;
int second = 5;
System.out.println(Integer.max(first, second));
System.out.println(Character.toLowerCase('S'));
}
La conclusione corretta di tutto è che non esiste magia, esiste una sorta di realizzazione. E non tutto sarà sempre quello che ci aspettiamo. Ad esempio, non è previsto alcun packaging: System.out.println("The number is " + 8);
l'esempio sopra verrà ottimizzato dal compilatore in una riga. Cioè è come se scrivessi “Il numero è 8”. E nell'esempio seguente non ci sarà nemmeno il packaging:
public static void main(String[] args) {
System.out.println("The number is " + Math.abs(-2));
}
Come può essere quando prendiamo println
un oggetto come input e dobbiamo in qualche modo collegare le linee. Linee... sì, ecco perché non esiste un packaging vero e proprio. Esistono Integer
metodi statici, ma alcuni lo sono package
. Cioè, non possiamo usarli, ma nello stesso Java possono essere utilizzati attivamente. Questo è esattamente il caso qui. Verrà chiamato il metodo getChars, che crea un array di caratteri dal numero. Ancora una volta, nessuna magia, solo Java). Quindi, in qualsiasi situazione poco chiara, basta guardare l'implementazione e almeno qualcosa andrà a posto. #Viacheslav
GO TO FULL VERSION