JavaRush /Blog Java /Random-PL /Autoboxing i unboxing w Javie
Viacheslav
Poziom 3

Autoboxing i unboxing w Javie

Opublikowano w grupie Random-PL
<h2>Wprowadzenie</h2>Język programowania, podobnie jak język, którym ludzie mówią, żyje i się zmienia, pojawiają się w nim nowe zjawiska, aby język był wygodniejszy w użyciu. A jak wiemy, język powinien w wygodny sposób wyrażać nasze myśli.
Autoboxing i unboxing w Javie - 1
Dlatego w Java SE 5 wprowadzono mechanizm pakowania/rozpakowywania. Osobny samouczek firmy Oracle poświęcony jest funkcjom tego sposobu wyrażania myśli: Autoboxing i Unboxing . <h2>Boks z automatycznym pakowaniem</h2>Przyjrzyjmy się przykładowi z boksem z automatycznym pakowaniem. Najpierw zobaczmy, jak to działa. Skorzystajmy ze strony Compiljava.net i utwórzmy klasę:
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);
    }
}
Prosty kod. Możemy określić parametr wejściowy i zmienić wartość portu. Jak widzimy, ponieważ odczytujemy wartość portu z Stringparametrów, uzyskujemy Integerją poprzez przejście Integer.valueOf. Dlatego jesteśmy zmuszeni określić go nie jako typ pierwotny, ale jako typ obiektowy Integer. I tu z jednej strony mamy zmienną obiektową, a wartością domyślną jest wartość pierwotna. I to działa. Ale my nie wierzymy w magię, prawda? Zajrzyjmy „pod maskę”, jak mówią. Pobierz kod źródłowy z Compiljava.net, klikając „Pobierz ZIP”. Następnie rozpakuj pobrane archiwum do katalogu i przejdź do niego. Teraz zróbmy: javap -c -p App.classgdzie App.class jest skompilowanym plikiem klasy dla Twojej klasy. Zobaczymy taką treść:
Autoboxing i unboxing w Javie - 2
Jest to ten sam znany „kod bajtowy”. Ale teraz ważne jest dla nas to, co widzimy. Najpierw operacja podstawowa 8080 jest umieszczana na stosie wykonawczym metody, a następnie wykonywana jest Integer.valueOf . Na tym polega „magia” boksu. A w środku magia wygląda tak:
Autoboxing i unboxing w Javie - 3
Oznacza to, że nowy zostanie pobrany Integerlub uzyskany Integerz pamięci podręcznej (pamięć podręczna to nic innego jak tablica liczb całkowitych) w zależności od wartości liczby. Oczywiście Integernie tylko jeden miał tyle szczęścia. Istnieje cała lista powiązanych typów pierwotnych i ich opakowań (klas reprezentujących prymitywy w świecie OOP). Ta lista znajduje się na samym dole samouczka firmy Oracle: „ Autoboxing i rozpakowywanie ”. Warto od razu zaznaczyć, że tablice wykonane z prymitywów nie posiadają „opakowania” bez podłączenia zewnętrznych bibliotek. Te. Arrays.asListnie zrobimy z int[]dla nas Listz Integer's. <h2>Rozpakowywanie</h2>Proces odwrotny do rozpakowywania nazywa się rozpakowywaniem i rozpakowywaniem. Spójrzmy na przykład rozpakowywania:
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.absakceptuje tylko elementy pierwotne. Co robić? Klasa opakowania ma w tym przypadku specjalną metodę, która zwraca operację pierwotną. Jest to na przykład Integermetoda intValue . Jeśli spojrzymy na kod bajtowy, wygląda to tak:
Autoboxing i unboxing w Javie - 4
Podobno nie ma magii. Wszystko jest w Javie. To po prostu działa „same”. Dla naszej wygody. <h2>Grabie</h2>
Autoboxing i unboxing w Javie - 5
Każde narzędzie, jeśli zostanie użyte niewłaściwie, staje się potężną bronią przeciwko sobie. Mechanizm automatycznego pakowania/rozpakowywania w Javie nie jest wyjątkiem. Pierwsze, oczywiste porównanie następuje poprzez ==. Myślę, że to jest jasne, ale spójrzmy na to jeszcze raz:
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
}
W pierwszym przypadku wartość jest pobierana z Integerpamięci podręcznej wartości (patrz wyjaśnienie Boxingu powyżej), w drugim przypadku za każdym razem tworzony będzie nowy obiekt. Ale tutaj warto dokonać rezerwacji. To zachowanie zależy od wysokiego ograniczenia pamięci podręcznej ( java.lang.Integer.IntegerCache.high ). Ponadto limit ten może ulec zmianie ze względu na inne ustawienia. Możesz przeczytać dyskusję na ten temat na stackoverflow: Jak duża jest pamięć podręczna Integer? Naturalnie obiekty należy porównywać za pomocą równości: System.out.println(notInCache.equals(notInCache2)); drugim problemem związanym z tym samym mechanizmem jest wydajność. Każde opakowanie w Javie jest równoznaczne z utworzeniem nowego obiektu. Jeśli liczba nie jest uwzględniona w wartościach pamięci podręcznej (tj. -128 do 127), to za każdym razem zostanie utworzony nowy obiekt. Jeśli nagle pakowanie (czyli pakowanie) zostanie wykonane w pętli, spowoduje to ogromny wzrost niepotrzebnych obiektów i zużycie zasobów na pracę śmieciarza. Dlatego nie podchodź do tego zbyt lekkomyślnie. Trzeci, nie mniej bolesny grabież wynika z tego samego mechanizmu:
public static void check(Integer value) {
    if (value <= 0) {
        throw new IllegalStateException("Value is too small");
    }
}
W tym kodzie osoba wyraźnie starała się nie ominąć błędu. Ale nie ma czeku dla null. Jeśli chodzi o wejście nullto zamiast zrozumiałego błędu otrzymamy niezrozumiały NullPointerException. Ponieważ dla porównania Java spróbuje wykonać value.intValuei zawiesić się, ponieważ ... valuebędzie null. <h2>Wniosek</h2>Mechanizm pakowania/rozpakowywania pozwala programiście napisać mniej kodu i czasami nawet nie myśleć o konwersji z prymitywów na obiekty i odwrotnie. Ale to nie znaczy, że powinieneś zapomnieć, jak to działa. W przeciwnym razie możesz popełnić błąd, który może nie pojawić się od razu. Nie powinniśmy polegać na częściach systemu, które nie są całkowicie pod naszą kontrolą (takich jak granica liczb całkowitych). Ale nie zapomnij o wszystkich zaletach klas opakowujących (takich jak Integer). Często te klasy opakowań mają zestaw dodatkowych metod statycznych, które sprawią, że Twoje życie będzie lepsze, a kod bardziej wyrazisty. Oto przykład nadrobienia zaległości:
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'));
}
Prawidłowy wniosek ze wszystkiego jest taki, że nie ma magii, jest jakiś rodzaj urzeczywistnienia. I nie wszystko zawsze będzie takie, jakiego oczekujemy. Na przykład nie ma opakowania: System.out.println("The number is " + 8); powyższy przykład zostanie zoptymalizowany przez kompilator do jednej linii. To tak, jakbyś napisał „Liczba to 8”. W poniższym przykładzie również nie będzie opakowania:
public static void main(String[] args) {
    System.out.println("The number is " + Math.abs(-2));
}
Jak to możliwe, że printlnjako dane wejściowe bierzemy obiekt i musimy jakoś połączyć linie. Linie... tak, dlatego nie ma opakowania jako takiego. Istnieją Integermetody statyczne, ale niektóre z nich są metodami package. Oznacza to, że nie możemy ich używać, ale w samej Javie można z nich aktywnie korzystać. Dokładnie tak jest w tym przypadku. Zostanie wywołana metoda getChars, która utworzy tablicę znaków z liczby. Ponownie, bez magii, tylko Java). Zatem w każdej niejasnej sytuacji wystarczy spojrzeć na wdrożenie i przynajmniej coś się wyjaśni. #Wiaczesław
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION