Begrijpen van gegevenstypen in Java

De auteur heeft het Vrij en Open Source Fonds geselecteerd om een donatie te ontvangen als onderdeel van het programma Schrijf voor Donaties.

Introductie

Java is een statisch getypeerde programmeertaal. Dit betekent dat wanneer je een variabele creëert, je ook zijn gegevenstype moet specificeren, dat is het type informatie dat het opslaat. Dit staat in contrast met dynamisch getypeerde talen, zoals PHP. Bij dynamisch getypeerde talen hoef je het gegevenstype van een variabele niet te specificeren, wat misschien een opluchting lijkt.

Echter, het kennen van de gegevenstypen en ze op de juiste manier gebruiken stelt ontwikkelaars in staat om hun code te optimaliseren omdat elk gegevenstype specifieke resourcevereisten heeft. Bovendien, als je één gegevenstype specificeert en probeert een ander type op te slaan, zoals per ongeluk, zul je de code niet kunnen compileren. Dus, met statisch getypeerde talen, kun je fouten detecteren zelfs voordat er enige testen zijn uitgevoerd.

Java heeft twee gegevenstypen: primitief en referentie (ook bekend als niet-primitief). In deze handleiding zul je variabelen gebruiken om informatie op te slaan en te gebruiken in een Java-programma, zodat je meer te weten komt over enkele veelgebruikte gegevenstypen in Java. Dit is geen uitputtend overzicht van alle gegevenstypen, maar deze gids zal je helpen vertrouwd te raken met de beschikbare opties in Java.

Vereisten

Om deze handleiding te volgen, heb je nodig:

Primitieve Types

Java primitieve types zijn de eenvoudigste en meest fundamentele gegevenstypen in Java. Ze vertegenwoordigen rauwe waarden zoals getallen en tekens. De meest gebruikte primitieve gegevenstypen zijn int (gehele getallen), boolean (boolean-waarden) en char (tekens). De overige typen zijn te vinden in de officiële Java gegevenstypen documentatie.

Gehele Getallen

Gehele getallen kunnen zowel negatieve als positieve gehele getallen zijn. In Java gebruik je int om ze op te slaan. int kan voldoende grote getallen bevatten voor de meeste doeleinden: van -2.147.483.648 tot 2.147.483.647.

Laten we kijken hoe int wordt gebruikt in een voorbeeld:

int theAnswer = 42;

Primitieve typen beginnen altijd met een kleine letter (int). Java-syntaxisregels vereisen dat je eerst het gegevenstype opgeeft (int) en vervolgens de naam ervan (hetAntwoord). Daarna wijs je de waarde 42 toe met het is-gelijk-teken (=) aan de variabele.

Ongeacht het gegevenstype gebruik je een variabele door rechtstreeks de naam op te geven zonder enige speciale tekens ervoor te plaatsen. Dit komt omdat Java het kan herkennen als een variabele.

Opmerking: De naam van de variabele theAnswer en alle andere variabelen in deze zelfstudie zijn geschreven in Camelcase. Hoewel er geen strikte vereiste is om het te gebruiken, is dit de geaccepteerde naamgevingsconventie in Java.

Zodra je de variabele hebt gedeclareerd, kun je deze gebruiken door ernaar te verwijzen in een methode zoals deze:

int theAnswer = 42;
System.out.println("The answer to all questions is " + theAnswer);

In de tweede regel druk je theAnswer af naar de console met behulp van de ingebouwde methode println uit het pakket System.out. Dit is de eenvoudigste manier om een variabele te testen om ervoor te zorgen dat deze zoals verwacht is gedeclareerd.

Om deze code in actie te zien, gebruik je de Java Shell-tool. Nadat je Java hebt geïnstalleerd, open je een terminal of opdrachtprompt op je lokale computer en typ je jshell:

  1. jshell

Je uitvoer zal er ongeveer als volgt uitzien:

Output
| Welcome to JShell -- Version 11.0.16 | For an introduction type: /help intro jshell>

Je kunt de codevoorbeelden uit deze zelfstudie in de console plakken. Als je klaar bent, kun je jshell afsluiten door /exit in te typen.

Om int te declareren en te gebruiken, plak je de volgende regels in de jshell-console:

  1. int theAnswer = 42;
  2. System.out.println("The answer to all questions is " + theAnswer);

Je zult de volgende uitvoer zien:

Output
theAnswer ==> 42 The answer to all questions is 42

Deze uitvoer bevestigt dat je de int-variabele theAnswer correct hebt ingesteld op 42 (theAnswer ==> 42). Je hebt ook succesvol theAnswer gebruikt door het door te geven aan een methode, en de methode produceerde de verwachte variabele waarde.

Boolean

Boolean waarden zijn true of false. In Java, gebruik je boolean om ze op te slaan. Bijvoorbeeld, laten we een boolean variabele aanmaken die aangeeft of Java leuk is:

boolean isJavaFun = true;

Je definieert de variabele isJavaFun als true. De alternatieve boolean waarde is false.

Met de bovenstaande variabele kan je de zin Java is fun: true printen als volgt:

  1. boolean isJavaFun = true;
  2. System.out.println("Java is fun: " + isJavaFun);

Het uitvoeren van deze regels in jshell zal de volgende uitvoer produceren:

Output
isJavaFun ==> true Java is fun: true

Vergelijkbaar met het int voorbeeld, zal de methode println het argument dat in de haakjes is opgegeven printen. Het plus-teken (+) concateneert of voegt de string “Java is fun: ” samen met de variabele isJavaFun zodat het in werkelijkheid slechts één argument is — de string, Java is fun: true.

Karakters

Om een enkel alfanumeriek karakter op te slaan, gebruik je char. Bijvoorbeeld:

char firstLetter = 'a';

Merk op dat de letter a wordt omringd door enkele aanhalingstekens. Enkele aanhalingstekens kunnen alleen worden gebruikt voor char waarden. Dubbele aanhalingstekens worden gebruikt voor strings, zoals je later zult leren.

char lijkt niet echt een nuttig type te zijn, omdat het niet waarschijnlijk is dat je een variabele nodig hebt toegewezen aan een enkel karakter. Echter, char wordt gebruikt als het bouwsteen voor klassen van karakterreeksen zoals String, die in feite een verzameling van char-waarden zijn.

Zoals je hebt gezien in dit gedeelte, is de declaratie en het gebruik van variabelen van het primitieve type eenvoudig omdat ze eenvoudige waarden zoals integers vertegenwoordigen. Deze waarden zijn klaar voor gebruik en vereisen geen aanvullende bewerkingen zoals het maken van objecten, het aanroepen van methoden, enzovoort.

Referentietypen

In de eerste tutorial in deze serie, Hoe schrijf je je eerste programma in Java, heb je geleerd dat Java-code is georganiseerd in klassen en dat deze klassen worden gebruikt als sjablonen om objecten te maken. Wanneer dergelijke objecten aan variabelen worden toegewezen, wijs je naar of verwijs je naar deze objecten. In deze gevallen worden de variabelen geclassificeerd als referentietypen. Deze variabelen worden ook wel niet-primitief genoemd omdat variabelen van het primitieve type niet naar objecten kunnen wijzen.

Objecten zijn krachtig omdat ze geavanceerde eigenschappen hebben en in staat zijn om acties uit te voeren wanneer je hun methoden activeert. Zonder variabelen die naar hen wijzen, zijn deze objecten ontoegankelijk en praktisch onbruikbaar. Daarom zijn variabelen van het referentietype essentieel voor Java en objectgeoriënteerd programmeren in het algemeen.

Opmerking: Referentietypen wijzen naar objecten die zijn gemaakt van klassen. Om verwarring te voorkomen, zullen het referentietype en het gemaakte object in de volgende voorbeelden van dezelfde klasse zijn.

Echter, in complexe programma’s is dit zelden het geval. In Java is een interface een groep vereisten voor een specifiek gedrag, en deze vereisten kunnen worden vervuld door een of meer klassen. Een klasse die voldoet aan de vereisten van een interface wordt gezegd deze interface te implementeren. Daarom is het in complexe programma’s gebruikelijk om een variabele te declareren met het referentietype van een interface. Op deze manier specificeer je het gedrag dat je variabele moet vertonen zonder het te koppelen aan een concrete implementatie van dit gedrag. Hierdoor kun je eenvoudig wijzigen naar welke implementatie je variabele verwijst zonder de manier waarop de variabele wordt gebruikt te veranderen. Dit complexe concept maakt deel uit van een meer gevorderd onderwerp over overerving en polymorfisme, wat een aparte tutorial zal zijn in onze Java-serie.

Terwijl er slechts enkele primitieve types zijn, zijn referentietypes praktisch onbeperkt omdat er geen limiet is aan het aantal klassen (en interfaces), en elke klasse staat voor een referentietype. Er zijn veel ingebouwde klassen in Java die essentiële functionaliteit bieden. De meest gebruikte bevinden zich in het kernpakket java.lang. Je zult er enkele van bespreken in deze sectie.

De klasse String

De klasse String vertegenwoordigt een combinatie van tekens die een string vormen. Om een String of een andere referentietypevariabele te declareren, geef je eerst het type ervan op, gevolgd door de naam. Daarna wijs je er een waarde aan toe met het is-gelijkteken. Tot nu toe lijkt het op werken met primitieve types. Referentietypes wijzen echter naar objecten, dus je moet een object maken als er nog geen is gemaakt. Hier is een voorbeeld:

String hello = new String("Hello");

hello is de naam van de variabele met het referentietype String. Je wijst het toe aan een nieuw String-object. Het nieuwe String-object wordt gemaakt met het new-trefwoord samen met de naam van de klasse – in dit geval String. De klasse String begint met een hoofdletter. Volgens conventie beginnen alle klassen en dus referentietypes met een hoofdletter.

Elke klas heeft een speciale methode genaamd een constructor die wordt gebruikt voor het maken van nieuwe objecten. U kunt deze constructor oproepen door haakjes (()) toe te voegen aan het einde van de klassenaam. De constructor kan parameters accepteren, zoals in het bovenstaande voorbeeld, waarbij de parameter "Hallo" wordt toegepast op de constructor voor String.

Om te bevestigen dat de variabele hello zich gedraagt zoals verwacht, moet u deze opnieuw doorgeven aan de methode println als volgt:

  1. String hello = new String("Hello");
  2. System.out.println(hello);

Het uitvoeren van deze regels in jshell zal de volgende output produceren:

Output
hello ==> "Hello" Hello

Deze keer bevestigt de uitvoer dat de variabele hello is ingesteld op Hallo. Daarna wordt dezelfde Hallo afgedrukt op een nieuwe regel, waarbij wordt bevestigd dat de methode println() deze heeft verwerkt.

Wrapper Klassen

In het vorige gedeelte heb je gewerkt met het referentietype String, dat vaak wordt gebruikt. Andere populaire referentietypes zijn de zogenaamde wrappers voor primitieve types. Een wrapperklasse wikkelt of bevat primitieve gegevens, vandaar de naam. Alle primitieve typen hebben wrapper-tegenhangers, en hier zijn enkele voorbeelden:

  • Integer: om int waarden in te pakken.
  • Character: om char waarden in te pakken.
  • Boolean: om boolean waarden in te pakken.

Deze wrappers bestaan zodat je een eenvoudige primitieve waarde kunt upgraden naar een krachtig object. Elke wrapper heeft kant-en-klare methoden die verband houden met de waarden waarvoor deze is ontworpen om op te slaan.

Als voorbeeld ga je Integer verkennen. In het vorige gedeelte heb je een String-object gemaakt met het new-trefwoord. Sommige klassen bieden echter, en moedigen zelfs aan, het gebruik van speciale methoden om objecten van hen te verkrijgen, en Integer is er een van. In het geval van Integer gaat het gebruik van een speciale methode voornamelijk over optimalisatie van middelen, maar in andere gevallen kan het gaan om het vereenvoudigen van de constructie van complexe objecten.

In het volgende voorbeeld maak je bijvoorbeeld een Integer-variabele genaamd theAnswer met de waarde 42 met behulp van de valueOf-methode:

  1. Integer theAnswer = Integer.valueOf(42);
  2. System.out.println(theAnswer);

In jshell krijg je de volgende output:

Output
theAnswer ==> 42 42

Door de Integer-methode valueOf(42) aan te roepen, geef je Java de opdracht om je een object met deze waarde te geven. Achter de schermen controleert Java of er al een object met zo’n waarde in de cache staat. Als dat het geval is, wordt het object gekoppeld aan de variabele theAnswer. Als dat niet het geval is, wordt er een nieuw object gemaakt voor de variabele theAnswer.

Veel ingebouwde klassen bieden dergelijke methoden om prestatieredenen, en het gebruik ervan wordt aanbevolen, zo niet verplicht. In het geval van Integer zou je nog steeds een object kunnen maken met het new-trefwoord, maar je krijgt een verouderingswaarschuwing.

Naast String en wrappers, zijn er ook andere nuttige ingebouwde referentietypes, die je kunt vinden in de samenvatting van het pakket java.lang. Om enkele van deze meer geavanceerde referentietypes volledig te begrijpen, is aanvullende uitleg of voorkennis vereist. Daarom zullen we enkele ervan behandelen in onze volgende tutorials uit de Java-serie.

Literals

Literals vertegenwoordigen vaste waarden die direct in de code kunnen worden gebruikt en dus zowel aan primitieve als aan referentietypes kunnen worden toegewezen. Er zijn een paar soorten literals, die als volgt kunnen worden gecategoriseerd.

Primitieve Type Literals

Je hebt al een paar literals gebruikt in de sectie over primitieve types. Voor elk primitief type is er een literal, zoals die uit onze voorbeelden: 42, 'a', en true. Gehele getallen zoals 42 zijn gehele literals. Op dezelfde manier zijn karakters zoals 'a' karakter literals, en true en false zijn booleaanse literals.

Primitive typen literals kunnen ook worden gebruikt om waarden te creëren voor referentietypen. De int literal werd gebruikt bij het creëren van een Integer object met de code Integer.valueOf(42). Er is ook een verkorte notatie voor dat, en je kunt de waarde rechtstreeks toewijzen zoals dit:

Integer theAnswer = 42;

42 is een integer literal, net als elk geheel getal, en je kunt het rechtstreeks toewijzen aan de variabele theAnswer zonder enige extra statements. Het is gebruikelijk om een Integer op deze manier te declareren omdat het handig is.

Deze verkorte benadering werkt ook voor andere primitieve typen literals en hun tegenhangers referentietypen zoals Boolean, bijvoorbeeld:

Boolean isFun = true;

true is de literal, die direct wordt toegewezen aan de variabele isFun van het type Boolean. Er is ook een false literal, die je op dezelfde manier kunt toewijzen.

De String Literal

Er is ook een speciale literal voor het String referentietype, en het wordt herkend door de dubbele aanhalingstekens rond de waarde. In dit voorbeeld is het "Hello, World!":

String helloWorld = "Hello, World!";

Het gebruik van literals is eenvoudiger en korter, en daarom geven veel programmeurs er de voorkeur aan. Je kunt echter nog steeds een String variabele declareren met een nieuw String object, zoals je al hebt gedaan in het gedeelte voor referentietypen.

De Nulwaarde

Er is nog een belangrijke constante: null, die de afwezigheid van een waarde of het niet-bestaan van een object vertegenwoordigt. Null stelt je in staat om een verwijzingstype te maken en het te wijzen op null in plaats van naar een object te wijzen. null kan worden gebruikt voor alle verwijzingstypen, maar niet voor enig primitief type.

Er is één voorbehoud bij de null constante: je kunt variabelen ermee declareren, maar je kunt deze variabelen niet gebruiken totdat je deze opnieuw toewijst aan een geschikte, niet-nul waarde. Als je probeert een verwijzingstype variabele te gebruiken met een null waarde, krijg je een foutmelding. Hier is een voorbeeld:

  1. String initiallyNullString = null;
  2. System.out.println("The class name is: " + initiallyNullString.getClass());

Als je deze code probeert uit te voeren in jshell, zie je een fout vergelijkbaar met de volgende:

Output
initiallyNullString ==> null | Exception java.lang.NullPointerException | at (#4:1)

Afhankelijk van je besturingssysteem en Java-versie kan je uitvoer verschillen.

De fout java.lang.NullPointerException wordt gegenereerd omdat je probeert de String methode getClass() (die de naam van de klasse retourneert) aan te roepen op de variabele initiallyNullString (die naar een null-object wijst).

Let op: Voor eenvoud noemen we java.lang.NullPointerException een fout, ook al is het technisch gezien een uitzondering. Voor meer informatie over uitzonderingen en fouten, bekijk de tutorial, Exception Handling in Java.

Om de fout aan te pakken, moet je de waarde van initiallyNullString opnieuw toewijzen zoals dit:

  1. String initiallyNullString = null;
  2. initiallyNullString = "not null any longer";
  3. System.out.println("The class name is: " + initiallyNullString.getClass());

De nieuwe, verbeterde code zal de volgende uitvoer weergeven:

Output
initiallyNullString ==> null initiallyNullString ==> "not null any longer" The class name is: class java.lang.String

De bovenstaande uitvoer laat zien hoe initiallyNullString eerst null is, dan wordt het een nieuw String-object dat "niet langer null" bevat. Vervolgens, wanneer de methode getClass() wordt aangeroepen op het geïnstantieerde object, krijg je java.lang.String, waarbij String de klassenaam is en java.lang zijn pakket. Ten slotte wordt een volledige, betekenisvolle boodschap afgedrukt: "De klassenaam is: class java.lang.String".

Zulke declaraties van null-waarden zijn gebruikelijker voor verouderde code. Ze worden gebruikt om eerst een variabele te maken en later de echte waarde toe te kennen, meestal via een logica die de laatste bepaalt. Echter, sinds Java versie 8 is er een nieuw referentietype genaamd Optioneel, dat meer geschikt is voor gevallen waarin null eerder is gebruikt.

Lokale Variabelen Type Inferentie

Tot nu toe heb je enkele van de gebruikelijke gegevenstypen in Java gebruikt om variabelen te definiëren. Echter, Java 10 introduceerde een nieuwe functie genaamd lokale variabelentype-inferentie, waarmee je het sleutelwoord var kunt gebruiken voor een nieuwe variabele. Met deze functie zal Java automatisch (dat wil zeggen, automatisch raden) het gegevenstype afleiden uit de lokale context. Type-inferentie is controversieel omdat het in contrast staat met de eerder uitgelegde uitgebreidheid van het definiëren van variabelen. De voor- en nadelen van zo’n functie zijn betwistbaar, maar het feit is dat andere statisch getypeerde talen, zoals C++, type-inferentie ondersteunen.

In ieder geval kan type-inferentie het gebruik van gegevenstypen niet volledig vervangen omdat het alleen werkt met lokale variabelen, dat wil zeggen variabelen binnen een methode. Laten we een voorbeeld bekijken met var:

  1. var hello = "Hello";
  2. System.out.println(hello);

Je declareert de variabele hello met het sleutelwoord var om Java aan te geven het gegevenstype te detecteren. Daarna druk je het af naar de console op de gebruikelijke manier om te bevestigen dat het zoals verwacht werkt:

Ouput
hello ==> "Hello" Hello

Dit voorbeeld zal werken zolang je Java-installatie (meer specifiek, de JDK) versie 10 of hoger is. Het sleutelwoord var wordt niet ondersteund in oudere versies.

Het type-inferentieproces vindt plaats tijdens het compilatieproces, dat wil zeggen wanneer je de code compileert. Het compilatieproces zet de broncode om naar machinetaal en past verschillende optimalisaties toe, waaronder type-inferentie. Dit zorgt ervoor dat de juiste hoeveelheid systeemgeheugen beschikbaar is voor de geïnferieerde variabelen. Dus, de machinetaal die je uitvoert na het compileren is volledig geoptimaliseerd, alsof je handmatig alle datatypes hebt gespecificeerd.

In dit voorbeeld werkt het var-sleutelwoord omdat de variabele lokaal is, en het var-datatype werkt alleen met lokale variabelen. Lokale variabelen zijn gedefinieerd binnen methoden en zijn alleen toegankelijk binnen de methoden, daarom worden ze “lokaal” genoemd.

Om te laten zien dat var alleen kan worden gebruikt voor lokale variabelen, probeer het buiten de hoofdmethode te plaatsen, zoals dit:

  1. public class Hello {
  2. var hello = "Hello";
  3. public static void main(String[] args) {
  4. // example code
  5. }
  6. }

Als je de bovenstaande code plakt in jshell, krijg je de volgende foutmelding:

Output
| Error: | 'var' is not allowed here | var hello = "Hello"; | ^-^

var is daar niet toegestaan omdat hello buiten een methode staat en het niet langer als lokaal wordt beschouwd. Dus, type-inferentie werkt niet voor niet-lokale variabelen omdat de context niet betrouwbaar kan worden gebruikt om het datatype te detecteren.

Hoewel het gebruik van var uitdagend kan zijn en niet vereist is, zul je er waarschijnlijk mee te maken krijgen, dus het is handig om ervan op de hoogte te zijn.

Gereserveerde Sleutelwoorden

Bij het declareren van variabelen in Java is er nog een belangrijke regel om te kennen. Er zijn gereserveerde trefwoorden die je niet kunt gebruiken als variabelennamen. Bijvoorbeeld, je kunt geen primitief van het type int declareren en het new noemen zoals dit:

  1. int new = 1;

Als je dit voorbeeld probeert, zul je compilatiefouten krijgen omdat new een gereserveerd trefwoord is.

Output
| Error: | '.class' expected | int new = 1; | ^ | Error: | <identifier> expected | int new = 1; | ^ | Error: | '(' or '[' expected | int new = 1; | ^ | Error: | unexpected type | required: value | found: class | int new = 1; | ^--^ | Error: | missing return statement | int new = 1; | ^----------^

Het trefwoord new wordt gebruikt voor het maken van nieuwe objecten en Java verwacht het niet op deze positie. In de lijst met fouten in de vorige uitvoer, is het eerste deel het belangrijkst:

Output
| Error: | '.class' expected | int new = 1; | ^

De fout '.class' verwacht betekent dat wanneer je het trefwoord new gebruikt, Java verwacht dat er een klasse zal volgen. Op dit punt is Java niet in staat om de verklaring te interpreteren en volgen de rest van de fouten.

De rest van de gereserveerde trefwoorden, zoals abstract, continue, default, for, en break, hebben ook specifieke betekenissen in Java en kunnen niet worden gebruikt als variabelennamen. De volledige lijst van gereserveerde trefwoorden is te vinden op de Java Language Keywords pagina. Zelfs als je niet alle gereserveerde trefwoorden kunt onthouden, kun je compilatiefouten gebruiken om het probleem te identificeren.

Conclusie

In deze tutorial heb je geleerd over primitieve en referentietypen in Java, wat een complex maar essentieel onderwerp is. Neem de tijd om ermee te oefenen en ga de voorbeelden meer dan eens door. Probeer enkele van de datatypen en waarden te wijzigen. Let op wanneer fouten worden gegenereerd en wanneer niet om een gevoel te ontwikkelen voor succesvolle uitvoering van de code.

Voor meer over Java, bekijk onze serie Hoe te coderen in Java.

Source:
https://www.digitalocean.com/community/tutorials/understanding-data-types-in-java