Hoi! Naarmate je verder bent gekomen in CodeGym, ben je vaak primitieve typen tegengekomen. Hier is een korte lijst van wat we over hen weten:
- Het zijn geen objecten en vertegenwoordigen een waarde die in het geheugen is opgeslagen
- Er zijn verschillende soorten
- Gehele getallen: byte , short , int , long
- Floating-point (fractionele) getallen: float en double
- Logische waarden: booleaans
- Symbolische waarden (voor weergave van letters en cijfers): char
-
Elk type heeft zijn eigen waardenbereik:
Primitieve soort |
Grootte in het geheugen |
Waardebereik |
byte |
8 bits |
-128 tot 127 |
kort |
16 bits |
-32768 tot 32767 |
char |
16 bits |
0 tot 65536 |
int |
32 bits |
-2147483648 tot 2147483647 |
lang |
64 bits |
-9223372036854775808 tot 9223372036854775807 |
vlot |
32 bits |
(2 tot de macht van -149) tot ((2 - (2 tot de macht van -23)) * 2 tot de macht van 127) |
dubbele |
64 bits |
(-2 tot de macht van 63) tot ((2 tot de macht van 63) - 1) |
booleaans |
8 (indien gebruikt in arrays), 32 (indien niet gebruikt in arrays) |
waar of niet waar |
Maar behalve dat ze verschillende waarden hebben, verschillen ze ook in de hoeveelheid ruimte die ze in het geheugen innemen. Een
int duurt meer dan een byte. En een
lange is groter dan een korte. De hoeveelheid geheugen die door primitieven wordt ingenomen, kan worden vergeleken met Russische nestpoppen:
![Verbreding en vernauwing van primitieve typen - 2]()
elke nestpop heeft binnenin ruimte beschikbaar. Hoe groter de nestpop, hoe meer ruimte er is. Een grote nestpop (
lang ) zal gemakkelijk een kleinere
int huisvesten . Het past gemakkelijk en je hoeft verder niets te doen. In Java wordt dit bij het werken met primitieven impliciete conversie genoemd. Of anders gezegd, het wordt verbreding genoemd.
Verbreding op Java
Hier is een eenvoudig voorbeeld van een verbredende conversie:
public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
byte littleNumber = 16;
bigNumber = littleNumber;
System.out.println(bigNumber);
}
}
Hier kennen we een bytewaarde toe aan een
int- variabele. De toewijzing lukt zonder problemen: de waarde die in een byte is opgeslagen, neemt minder geheugen in beslag dan wat een
int aankan. De kleine nestpop (bytewaarde) past gemakkelijk in de grote nestpop (
int- variabele). Het is een andere zaak als je het tegenovergestelde probeert te doen, dwz een grote waarde in een variabele stopt waarvan het bereik niet geschikt is voor zo'n type big data. Met echte nestpoppen zou het aantal gewoon niet passen. Met Java kan het, maar met nuances. Laten we proberen een
int in een
korte variabele te plaatsen:
public static void main(String[] args) {
int bigNumber = 10000000;
short littleNumber = 1000;
littleNumber = bigNumber;// Error!
System.out.println(bigNumber);
}
Fout! De compiler begrijpt dat je iets abnormaals probeert te doen door een grote pop (
int ) in een kleine (
short ) te schuiven. In dit geval is de compilatiefout een waarschuwing van de compiler: "Hé, weet je
absoluut zeker dat je dit wilt doen?" Als je het zeker weet, zeg je tegen de compiler:
"Alles is in orde. Ik weet wat ik doe!" Dit proces wordt expliciete typeconversie of vernauwing genoemd.
Vernauwing op Java
Om een vernauwende conversie uit te voeren, moet u expliciet aangeven naar welk type u uw waarde wilt converteren. Met andere woorden, je moet de vraag van de samensteller beantwoorden:
"Nou, in welke van deze kleine nestpoppen wil je deze grote nestpop zetten?" In ons geval ziet het er zo uit:
public static void main(String[] args) {
int bigNumber = 10000000;
short littleNumber = 1000;
littleNumber = (short) bigNumber;
System.out.println(littleNumber);
}
We geven expliciet aan dat we een int in een
korte variabele willen stoppen en dat we de verantwoordelijkheid nemen. Aangezien er expliciet een smaller type is aangegeven, voert de compiler de conversie uit. Wat is het resultaat? Console-uitvoer:
-27008 Dat was een beetje onverwacht. Waarom hebben we dat precies gekregen? Eigenlijk is het allemaal heel simpel. Oorspronkelijk was de waarde 10000000. Het werd opgeslagen in een
int- variabele, die 32 bits in beslag neemt. Dit is de binaire weergave:
We schrijven deze waarde in een
korte variabele, die maar 16 bits kan opslaan! Dienovereenkomstig worden alleen de eerste 16 bits van ons nummer daarheen verplaatst. De rest wordt weggegooid. Als resultaat krijgt de korte variabele de volgende waarde
wat in decimale vorm gelijk is aan -27008 Daarom vraagt de compiler u om te "bevestigen" door een expliciete vernauwende conversie naar een specifiek type aan te geven. Ten eerste laat dit zien dat je verantwoordelijkheid neemt voor het resultaat. En ten tweede vertelt het de compiler hoeveel ruimte moet worden toegewezen tijdens het converteren. Als we in het laatste voorbeeld immers een int-waarde toekennen aan een bytevariabele in plaats van een
short , dan zouden we maar 8 bits tot onze beschikking hebben, niet 16, en zou het resultaat anders zijn. Fractionele typen (
float en
double ) hebben hun eigen proces voor het beperken van conversies. Als u een factienummer probeert te casten naar een geheel getal, wordt het fractionele deel weggegooid.
public static void main(String[] args) {
double d = 2.7;
long x = (int) d;
System.out.println(x);
}
Console-uitgang:
2
char
U weet al dat
char wordt gebruikt om individuele tekens weer te geven.
public static void main(String[] args) {
char c = '!';
char z = 'z';
char i = '8';
}
Maar dit gegevenstype heeft verschillende kenmerken die belangrijk zijn om te begrijpen. Laten we nog eens kijken naar de tabel met waardebereiken:
Primitieve soort |
Grootte in het geheugen |
Waardebereik |
byte |
8 bits |
-128 tot 127 |
kort |
16 bits |
-32768 tot 32767 |
char |
16 bits |
0 tot 65536 |
int |
32 bits |
-2147483648 tot 2147483647 |
lang |
64 bits |
-9223372036854775808 tot 9223372036854775807 |
vlot |
32 bits |
(2 tot de macht van -149) tot ((2 - (2 tot de macht van -23)) * 2 tot de macht van 127) |
dubbele |
64 bits |
(-2 tot de macht van 63) tot ((2 tot de macht van 63) - 1) |
booleaans |
8 (indien gebruikt in arrays), 32 (indien niet gebruikt in arrays) |
waar of niet waar |
Het bereik 0 tot 65536 wordt aangegeven voor het
tekentype . Maar wat betekent dat?
Een teken staat immers niet alleen voor cijfers, maar ook voor letters, leestekens… Het punt is dat in Java
tekenwaarden worden opgeslagen in Unicode-indeling. Unicode kwamen we al tegen in een van de vorige lessen. U herinnert zich waarschijnlijk dat Unicode een tekencoderingsstandaard is die de symbolen van bijna alle geschreven talen van de wereld bevat. Met andere woorden, het is een lijst met speciale codes die bijna elk teken in elke taal vertegenwoordigen. De hele Unicode-tabel is erg groot en het is natuurlijk niet nodig om deze uit het hoofd te leren. Hier is een klein deel ervan:
![Verbreding en vernauwing van primitieve typen - 5]()
Het belangrijkste is om te begrijpen hoe tekens worden opgeslagen, en om te onthouden dat als je de code voor een bepaald teken kent, je dat teken altijd in je programma kunt produceren. Laten we het eens proberen met een willekeurig getal:
public static void main(String[] args) {
int x = 32816;
char c = (char) x ;
System.out.println(c);
}
Console-uitvoer: 耰 Dit is de indeling die wordt gebruikt om
tekens in Java op te slaan. Elk symbool komt overeen met een nummer: een 16-bits (twee bytes) numerieke code. In Unicode komt 32816 overeen met het Chinese karakter 耰. Noteer het volgende punt. In dit voorbeeld hebben we een
int- variabele gebruikt. Het neemt 32 bits geheugen in beslag, terwijl een
char er 16 in beslag neemt. Hier hebben we een
int gekozen , omdat ons nummer (32816) niet in een
kort past . Hoewel de grootte van een
teken (net als een
korte ) 16 bits is, zijn er geen negatieve getallen in het
tekenbereik , dus het "positieve" deel van het
tekenbereik is twee keer zo groot (65536 in plaats van 32767 voor het
korte type).
We kunnen een int gebruiken zolang onze code onder 65536 blijft. Maar als u een
int- waarde groter dan 65536 maakt, zal deze meer dan 16 bits in beslag nemen. En dit zal resulteren in een versmallende conversie
char c = (char) x;
de extra bits worden weggegooid (zoals hierboven besproken) en het resultaat zal vrij onverwacht zijn.
Speciale kenmerken van het toevoegen van tekens en gehele getallen
Laten we een ongebruikelijk voorbeeld bekijken:
public class Main {
public static void main(String[] args) {
char c = '1';
int i = 1;
System.out.println(i + c);
}
}
Console-uitvoer:
50 O_О Hoe logisch is dat? 1+1. Waar komt de 50 vandaan?! U weet al dat
char
waarden in het geheugen worden opgeslagen als getallen in het bereik van 0 tot 65536, en dat deze getallen een Unicode-weergave van een teken zijn.
Wanneer we een teken en een type met een geheel getal
![Verbreding en vernauwing van primitieve typen - 6]()
toevoegen , wordt het
teken geconverteerd naar het overeenkomstige Unicode-nummer. Toen we in onze code 1 en '1' toevoegden, werd het symbool '1' geconverteerd naar zijn eigen code, namelijk 49 (je kunt dit verifiëren in de bovenstaande tabel). Daarom is het resultaat 50. Laten we nogmaals onze oude vriend 耰 als voorbeeld nemen en deze proberen op te tellen bij een getal.
public static void main(String[] args) {
char c = '耰';
int x = 200;
System.out.println(c + x);
}
Console-uitvoer:
33016 We hebben al ontdekt dat 耰 overeenkomt met 32816. En als we dit getal en 200 optellen, krijgen we ons resultaat: 33016. :) Zoals je kunt zien, is het algoritme hier vrij eenvoudig, maar je moet het niet vergeten .
GO TO FULL VERSION