CodeGym /Blog Java /Random-PL /Poszerzanie i zawężanie typów pierwotnych
Autor
Aditi Nawghare
Software Engineer at Siemens

Poszerzanie i zawężanie typów pierwotnych

Opublikowano w grupie Random-PL
Cześć! W miarę postępów w CodeGym wielokrotnie napotykałeś prymitywne typy. Oto krótka lista tego, co o nich wiemy:
  1. Nie są obiektami i reprezentują wartość przechowywaną w pamięci
  2. Istnieje kilka rodzajów
    • Liczby całkowite: byte , short , int , long
    • Liczby zmiennoprzecinkowe (ułamkowe): float i double
    • Wartości logiczne: logiczne
    • Wartości symboliczne (do reprezentacji liter i cyfr): char
  3. Każdy typ ma swój własny zakres wartości:

Typ pierwotny Rozmiar w pamięci Zakres wartości
bajt 8 bitów -128 do 127
krótki 16 bitów -32768 do 32767
zwęglać 16 bitów od 0 do 65536
int 32 bity -2147483648 do 2147483647
długi 64 bity -9223372036854775808 do 9223372036854775807
platforma 32 bity (2 do potęgi -149) do ((2 - (2 do potęgi -23)) * 2 do potęgi 127)
podwójnie 64 bity (-2 do potęgi 63) do ((2 do potęgi 63) - 1)
logiczna 8 (gdy jest używany w tablicach), 32 (jeśli nie jest używany w tablicach) prawda czy fałsz
Ale oprócz tego, że mają różne wartości, różnią się także tym, ile miejsca zajmują w pamięci. Int zajmuje więcej niż bajt . A długi jest większy niż krótki. Ilość pamięci zajmowanej przez prymitywy można porównać do rosyjskich lalek lęgowych: Poszerzanie i zawężanie typów pierwotnych - 2 każda lalka lęgowa ma wolne miejsce w środku. Im większa lalka gniazdująca, tym więcej miejsca. Duża lalka gniazdująca ( długa ) z łatwością pomieści mniejszą int . Łatwo się dopasowuje i nie musisz robić nic więcej. W Javie podczas pracy z prymitywami nazywa się to konwersją niejawną. Inaczej mówiąc, nazywa się to poszerzeniem.

Poszerzenie w Javie

Oto prosty przykład konwersji rozszerzającej:

public class Main {

   public static void main(String[] args) {
      
       int bigNumber = 10000000;

       byte littleNumber = 16;

       bigNumber = littleNumber;
       System.out.println(bigNumber);
   }
}
Tutaj przypisujemy wartość bajtu do zmiennej int . Przypisanie powiodło się bez żadnych problemów: wartość przechowywana w bajcie zajmuje mniej pamięci niż to, co może pomieścić int . Mała lalka zagnieżdżona (wartość bajtu) z łatwością mieści się w dużej lalce zagnieżdżonej ( zmienna int ). Inna sprawa, jeśli spróbujesz zrobić coś przeciwnego, tj. umieścić dużą wartość w zmiennej, której zakres nie może pomieścić tak dużego typu danych. W przypadku prawdziwych lalek gniazdujących liczba po prostu nie pasowałaby. W Javie jest to możliwe, ale z pewnymi niuansami. Spróbujmy umieścić int w krótkiej zmiennej:

public static void main(String[] args) {

   int bigNumber = 10000000;
  
   short littleNumber = 1000;

   littleNumber = bigNumber;// Error!
   System.out.println(bigNumber);
}
Błąd! Kompilator rozumie, że próbujesz zrobić coś nienormalnego, wpychając dużą lalkę zagnieżdżoną ( int ) do małej ( short ). W tym przypadku błąd kompilacji jest ostrzeżeniem kompilatora: „Hej, czy jesteś absolutnie pewien , że chcesz to zrobić?” Jeśli jesteś pewien, powiedz kompilatorowi: „Wszystko jest w porządku. Wiem, co robię!” Proces ten nazywany jest jawną konwersją typu lub zawężaniem.

Zawężanie w Javie

Aby wykonać konwersję zawężającą, musisz wyraźnie wskazać typ, na który chcesz przekonwertować swoją wartość. Innymi słowy, musisz odpowiedzieć na pytanie kompilatora: „Do której z tych małych lalek do gniazdowania chcesz włożyć tę dużą lalkę do gniazdowania?” W naszym przypadku wygląda to tak:

public static void main(String[] args) {

   int bigNumber = 10000000;

   short littleNumber = 1000;

   littleNumber = (short) bigNumber;
   System.out.println(littleNumber);
}
Wyraźnie wskazujemy, że chcemy umieścić int w zmiennej short i że weźmiemy na siebie odpowiedzialność. Widząc, że wyraźnie wskazano węższy typ, kompilator dokonuje konwersji. Jaki jest wynik? Wyjście konsoli: -27008 To było trochę nieoczekiwane. Dlaczego dokładnie to otrzymaliśmy? W rzeczywistości wszystko jest bardzo proste. Oryginalnie wartość wynosiła 10000000. Była przechowywana w zmiennej int , która zajmuje 32 bity. To jest jego binarna reprezentacja:
Poszerzanie i zawężanie typów pierwotnych - 3
Zapisujemy tę wartość w krótkiej zmiennej, która może przechowywać tylko 16 bitów! W związku z tym zostanie tam przeniesionych tylko pierwszych 16 bitów naszego numeru. Reszta zostanie odrzucona. W rezultacie krótka zmienna otrzymuje następującą wartość
Poszerzanie i zawężanie typów pierwotnych - 4
co w postaci dziesiętnej jest równe -27008 Dlatego kompilator prosi o „potwierdzenie” poprzez wskazanie jawnej konwersji zawężającej do określonego typu. Po pierwsze, pokazuje to, że bierzesz odpowiedzialność za wynik. Po drugie, mówi kompilatorowi, ile miejsca ma przydzielić podczas konwersji. W końcu, w ostatnim przykładzie, gdybyśmy przypisali wartość int zmiennej bajtowej zamiast short , to mielibyśmy do dyspozycji tylko 8 bitów, a nie 16, a wynik byłby inny. Typy ułamkowe ( float i double ) mają własny proces zawężania konwersji. Jeśli spróbujesz rzucić liczbę ułamkową na typ całkowity, część ułamkowa zostanie odrzucona.

public static void main(String[] args) {

   double d = 2.7;

   long x = (int) d;
   System.out.println(x);
}
Wyjście konsoli: 2

zwęglać

Wiesz już, że char służy do wyświetlania pojedynczych znaków.

public static void main(String[] args) {

   char c = '!';
   char z = 'z';
   char i = '8';
  
}
Ale ten typ danych ma kilka funkcji, które są ważne do zrozumienia. Spójrzmy jeszcze raz na tabelę zakresów wartości:
Typ pierwotny Rozmiar w pamięci Zakres wartości
bajt 8 bitów -128 do 127
krótki 16 bitów -32768 do 32767
zwęglać 16 bitów od 0 do 65536
int 32 bity -2147483648 do 2147483647
długi 64 bity -9223372036854775808 do 9223372036854775807
platforma 32 bity (2 do potęgi -149) do ((2 - (2 do potęgi -23)) * 2 do potęgi 127)
podwójnie 64 bity (-2 do potęgi 63) do ((2 do potęgi 63) - 1)
logiczna 8 (gdy jest używany w tablicach), 32 (jeśli nie jest używany w tablicach) prawda czy fałsz
Dla typu char wskazany jest zakres od 0 do 65536 . Ale co to oznacza? W końcu znak to nie tylko cyfry, ale także litery, znaki interpunkcyjne… Rzecz w tym, że w Javie wartości char są zapisywane w formacie Unicode. Unicode napotkaliśmy już na jednej z poprzednich lekcji. Prawdopodobnie pamiętasz, że Unicode to standard kodowania znaków, który obejmuje symbole prawie wszystkich języków pisanych na świecie. Innymi słowy, jest to lista specjalnych kodów, które reprezentują prawie każdy znak w dowolnym języku. Cała tabela Unicode jest bardzo duża i oczywiście nie ma potrzeby uczenia się jej na pamięć. Oto mała jego część: Poszerzanie i zawężanie typów pierwotnych - 5 Najważniejszą rzeczą jest zrozumienie sposobu przechowywania znaków i pamiętanie, że jeśli znasz kod konkretnego znaku, zawsze możesz go utworzyć w swoim programie. Spróbujmy z jakąś losową liczbą:

public static void main(String[] args) {

   int x = 32816;

   char c = (char) x ;
   System.out.println(c);
}
Dane wyjściowe konsoli: 耰 Jest to format używany do przechowywania znaków char s w Javie. Każdy symbol odpowiada liczbie: 16-bitowemu (dwubajtowemu) kodowi numerycznemu. W Unicode 32816 odpowiada chińskiemu znakowi 耰. Zwróć uwagę na następujący punkt. W tym przykładzie użyliśmy zmiennej int . Zajmuje 32 bity w pamięci, podczas gdy char zajmuje 16. Tutaj wybraliśmy int , ponieważ nasza liczba (32816) nie zmieści się w short . Chociaż rozmiar znaku ( podobnie jak short ) wynosi 16 bitów, w zakresie znaków nie ma liczb ujemnych , więc „dodatnia” część znakuzasięg jest dwa razy większy (65536 zamiast 32767 dla typu krótkiego ). Możemy użyć int , o ile nasz kod pozostaje poniżej 65536. Ale jeśli utworzysz wartość int większą niż 65536, zajmie ona więcej niż 16 bitów. A to spowoduje zawężenie konwersji

char c = (char) x;
dodatkowe bity zostaną odrzucone (jak omówiono powyżej), a wynik będzie dość nieoczekiwany.

Cechy szczególne dodawania znaków i liczb całkowitych

Spójrzmy na nietypowy przykład:

public class Main {

   public static void main(String[] args) {

      char c = '1';

      int i = 1;

       System.out.println(i + c);
   }
}
Wyjście konsoli: 50 O_О Jaki to ma sens? 1+1. Skąd się wzięło 50?! Wiesz już, że charwartości są przechowywane w pamięci jako liczby z zakresu od 0 do 65536 i że te liczby są reprezentacją znaku w Unicode. Poszerzanie i zawężanie typów pierwotnych - 6 Kiedy dodamy znak i jakiś typ liczby całkowitej, znak jest konwertowany na odpowiedni numer Unicode. W naszym kodzie, kiedy dodaliśmy 1 i „1”, symbol „1” został zamieniony na własny kod, czyli 49 (możesz to sprawdzić w powyższej tabeli). Zatem wynikiem jest 50. Ponownie weźmy jako przykład naszego starego znajomego 耰 i spróbujmy dodać go do jakiejś liczby.

public static void main(String[] args) {

   char c = '耰';
   int x = 200;

   System.out.println(c + x);
}
Wyjście konsoli: 33016 Odkryliśmy już, że 耰 odpowiada 32816. A kiedy dodamy tę liczbę do 200, otrzymamy wynik: 33016. :) Jak widać, algorytm tutaj jest dość prosty, ale nie należy o tym zapominać .
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION