JavaRush /Java Blog /Random-IT /Autoboxing e unboxing in Java
Viacheslav
Livello 3

Autoboxing e unboxing in Java

Pubblicato nel gruppo Random-IT
<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.
Autoboxing e unboxing in Java - 1
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:
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 Stringparametri, lo otteniamo Integerfacendolo 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.classdove App.class è il file di classe compilato per la tua classe. Vedremo contenuti come questo:
Autoboxing e unboxing in Java - 2
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ì:
Autoboxing e unboxing in Java - 3
Cioè, in sostanza, ne verrà preso Integero ne verrà ottenuto uno nuovo Integerdalla cache (la cache non è altro che un semplice array di numeri interi) a seconda del valore del numero. Naturalmente, Integernon 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.asListnon farà int[]per noi Listda 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.absaccetta solo primitive. Cosa fare? La classe wrapper ha un metodo speciale per questo caso che restituisce una primitiva. Ad esempio, questo è il Integermetodo intValue . Se guardiamo il bytecode, è così:
Autoboxing e unboxing in Java - 4
Come puoi vedere, nessuna magia. Tutto è all'interno di Java. Funziona semplicemente “da solo”. Per nostra comodità. <h2>Rastrello</h2>
Autoboxing e unboxing in Java - 5
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 ==. 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 Integercache 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.intValuee bloccarsi, perché... valueVolere 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 printlnun oggetto come input e dobbiamo in qualche modo collegare le linee. Linee... sì, ecco perché non esiste un packaging vero e proprio. Esistono Integermetodi 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
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION