<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.
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ę:
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:
Oznacza to, że nowy zostanie pobrany
Podobno nie ma magii. Wszystko jest w Javie. To po prostu działa „same”. Dla naszej wygody. <h2>Grabie</h2>
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
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 String
parametrów, uzyskujemy Integer
ją 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.class
gdzie App.class jest skompilowanym plikiem klasy dla Twojej klasy. Zobaczymy taką treść:
Integer
lub uzyskany Integer
z 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 Integer
nie 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.asList
nie zrobimy z int[]
dla nas List
z 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.abs
akceptuje tylko elementy pierwotne. Co robić? Klasa opakowania ma w tym przypadku specjalną metodę, która zwraca operację pierwotną. Jest to na przykład Integer
metoda intValue . Jeśli spojrzymy na kod bajtowy, wygląda to tak:
==
. 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 Integer
pamię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 null
to zamiast zrozumiałego błędu otrzymamy niezrozumiały NullPointerException
. Ponieważ dla porównania Java spróbuje wykonać value.intValue
i zawiesić się, ponieważ ... value
bę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 println
jako dane wejściowe bierzemy obiekt i musimy jakoś połączyć linie. Linie... tak, dlatego nie ma opakowania jako takiego. Istnieją Integer
metody 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
GO TO FULL VERSION