Leer Java vanaf de grond af

Dus je wilt programmeren in Java? Dat is geweldig, en u bent bij ons aan het juiste adres. De Java 101-serie biedt een zelfgeleide inleiding tot programmeren in Java, beginnend met de basisprincipes en alle kernconcepten die u moet kennen om een ​​productieve Java-ontwikkelaar te worden. Deze serie is technisch, met veel codevoorbeelden om u te helpen de concepten gaandeweg te begrijpen. Ik ga ervan uit dat je al enige programmeerervaring hebt, alleen niet in Java.

Dit eerste artikel introduceert het Java-platform en legt het verschil uit tussen de drie edities: Java SE, Java EE en Java ME. Je leert ook over de rol van de Java Virtual Machine (JVM) bij het implementeren van Java-applicaties. Ik help je bij het opzetten van een Java Development Kit (JDK) op je systeem zodat je Java-programma's kunt ontwikkelen en uitvoeren, en ik zal je op weg helpen met de architectuur van een typische Java-applicatie. Ten slotte leert u hoe u een eenvoudige Java-app compileert en uitvoert.

Bijgewerkt voor Java 12 en de nieuwe JShell

Deze serie is bijgewerkt voor Java 12 en bevat een korte introductie tot het nieuwe jshell: een interactieve tool voor het leren van Java en het prototypen van Java-code.

download Download de code Download de broncode voor voorbeeldtoepassingen in deze tutorial. Gemaakt door Jeff Friesen voor JavaWorld.

Wat is Java?

U kunt Java zien als een algemene, objectgeoriënteerde taal die veel op C en C ++ lijkt, maar die gemakkelijker te gebruiken is en waarmee u robuustere programma's kunt maken. Helaas geeft deze definitie u niet veel inzicht in Java. In 2000 beschreef Sun Microsystems (de maker van het Java-platform) Java als volgt: 

Java is een eenvoudige, objectgeoriënteerde, netwerkbewuste, geïnterpreteerde, robuuste, veilige, architectuurneutrale, draagbare, krachtige, multithreaded, dynamische computertaal.

Laten we elk van deze definities afzonderlijk bekijken.

Java is een eenvoudige taal . Java was aanvankelijk gemodelleerd naar C en C ++, minus enkele mogelijk verwarrende functies. Aanwijzingen, overerving van meerdere implementaties en overbelasting van operators zijn enkele C / C ++ -functies die geen deel uitmaken van Java. Een functie die niet verplicht is in C / C ++, maar essentieel is voor Java, is een voorziening voor het ophalen van afval die automatisch objecten en arrays terugwint.

Java is een objectgeoriënteerde taal . Java's objectgeoriënteerde focus stelt ontwikkelaars in staat om Java aan te passen om een ​​probleem op te lossen, in plaats van ons te dwingen het probleem te manipuleren om aan taalbeperkingen te voldoen. Dit is anders dan een gestructureerde taal zoals C. Als voorbeeld, terwijl Java kunt u zich richten op spaarrekening voorwerpen, C moet u afzonderlijk denken over spaarrekening staat (een dergelijk evenwicht) en gedrag (zoals storting en opname).

Java is een netwerkvriendelijke taal . Java's uitgebreide netwerkbibliotheek maakt het gemakkelijk om te gaan met Transmission Control Protocol / Internet Protocol (TCP / IP) netwerkprotocollen zoals HTTP (HyperText Transfer Protocol) en FTP (File Transfer Protocol), en vereenvoudigt het maken van netwerkverbindingen. Bovendien hebben Java-programma's toegang tot objecten via een TCP / IP-netwerk, via Uniform Resource Locators (URL's), met hetzelfde gemak als wanneer u er toegang toe zou hebben vanuit het lokale bestandssysteem.

Java is een geïnterpreteerde taal . Tijdens runtime wordt een Java-programma indirect uitgevoerd op het onderliggende platform (zoals Windows of Linux) via een virtuele machine (die een softwareversie is van een hypothetisch platform) en de bijbehorende uitvoeringsomgeving. De virtuele machine vertaalt de bytecodes van het Java-programma (instructies en bijbehorende gegevens) naar platformspecifieke instructies door middel van interpretatie. Interpretatie is het uitzoeken wat een bytecode-instructie betekent en vervolgens gelijkwaardige "ingeblikte" platformspecifieke instructies kiezen om uit te voeren. De virtuele machine voert vervolgens die platformspecifieke instructies uit.

Interpretatie maakt het gemakkelijker om fouten in Java-programma's op te sporen, omdat er tijdens runtime meer informatie over compilatie beschikbaar is. Interpretatie maakt het ook mogelijk om de koppelingsstap tussen de onderdelen van een Java-programma uit te stellen tot runtime, wat de ontwikkeling versnelt.

Java is een robuuste taal . Java-programma's moeten betrouwbaar zijn omdat ze worden gebruikt in zowel consumenten- als bedrijfskritische toepassingen, variërend van Blu-ray-spelers tot voertuignavigatie- of luchtcontrolesystemen. Taalfuncties die Java robuust maken, zijn onder meer declaraties, controle van duplicaattypen tijdens compilatie en runtime (om problemen met niet-overeenkomende versies te voorkomen), true arrays met automatische grenscontrole en het weglaten van pointers. (Zie "Elementaire Java-taalfuncties" om aan de slag te gaan met Java-taaltypen, literals, variabelen en meer.)

Een ander aspect van Java's robuustheid is dat lussen moeten worden bestuurd door Booleaanse expressies in plaats van integer-expressies waarbij 0 onwaar is en een niet-nulwaarde waar is. Java staat bijvoorbeeld geen lus in C-stijl toe, while (x) x++;omdat de lus mogelijk niet eindigt waar verwacht. In plaats daarvan moet u expliciet een Booleaanse uitdrukking opgeven, zoals while (x != 10) x++;(wat betekent dat de lus wordt uitgevoerd totdat xgelijk is aan 10).

Java is een veilige taal . Java-programma's worden gebruikt in netwerkomgevingen / gedistribueerde omgevingen. Omdat Java-programma's kunnen migreren naar en kunnen worden uitgevoerd op de verschillende platforms van een netwerk, is het belangrijk om deze platforms te beschermen tegen schadelijke code die virussen kan verspreiden, creditcardgegevens kan stelen of andere kwaadaardige handelingen kan verrichten. Java-taalfuncties die robuustheid ondersteunen (zoals het weglaten van pointers) werken met beveiligingsfuncties zoals het Java Sandbox-beveiligingsmodel en codering met openbare sleutels. Samen voorkomen deze functies dat virussen en andere gevaarlijke code grote schade aanrichten op een nietsvermoedend platform.

In theorie is Java veilig. In de praktijk zijn verschillende beveiligingsproblemen opgespoord en uitgebuit. Als gevolg hiervan blijven Sun Microsystems en Oracle nu beveiligingsupdates uitbrengen.

Java is een architectuurneutrale taal . Netwerken verbinden platforms met verschillende architecturen op basis van verschillende microprocessors en besturingssystemen. Je kunt niet verwachten dat Java platformspecifieke instructies genereert en deze instructies "begrijpt" door allerlei platforms die deel uitmaken van een netwerk. In plaats daarvan genereert Java platformonafhankelijke bytecode-instructies die voor elk platform gemakkelijk te interpreteren zijn (via de implementatie van de JVM).

Java is een draagbare taal . Architectuurneutraliteit draagt ​​bij aan draagbaarheid. De draagbaarheid van Java omvat echter meer dan platformonafhankelijke bytecode-instructies. Bedenk dat de grootte van het type integer niet mag variëren. Het 32-bits integer-type moet bijvoorbeeld altijd ondertekend zijn en 32 bits bezetten, ongeacht waar het 32-bits gehele getal wordt verwerkt (bijvoorbeeld een platform met 16-bits registers, een platform met 32-bits registers of een platform met 32-bits registers). met 64-bits registers). Java's bibliotheken dragen ook bij aan draagbaarheid. Waar nodig bieden ze typen die Java-code op de meest draagbare manier verbinden met platformspecifieke mogelijkheden.

Java is een krachtige taal . Interpretatie levert een prestatieniveau op dat doorgaans meer dan voldoende is. Voor applicatiescenario's met zeer hoge prestaties gebruikt Java just-in-time-compilatie, die geïnterpreteerde bytecode-instructiereeksen analyseert en vaak geïnterpreteerde instructiereeksen compileert tot platformspecifieke instructies. Daaropvolgende pogingen om deze bytecode-instructiereeksen te interpreteren, resulteren in de uitvoering van gelijkwaardige platformspecifieke instructies, wat resulteert in een prestatieverbetering.

Java is een taal met meerdere threads . Om de prestaties te verbeteren van programma's die meerdere taken tegelijk moeten uitvoeren, ondersteunt Java het concept van threaded uitvoering . Een programma dat bijvoorbeeld een grafische gebruikersinterface (GUI) beheert terwijl het wacht op invoer van een netwerkverbinding, gebruikt een andere thread om het wachten uit te voeren in plaats van de standaard GUI-thread voor beide taken te gebruiken. Hierdoor blijft de GUI responsief. Met de synchronisatieprimitieven van Java kunnen threads gegevens veilig onderling communiceren zonder de gegevens te beschadigen. (Zie threaded programmeren in Java dat elders in de Java 101-serie wordt besproken.)

Java is een dynamische taal . Omdat onderlinge verbindingen tussen programmacode en bibliotheken dynamisch plaatsvinden tijdens runtime, is het niet nodig om ze expliciet te koppelen. Als gevolg hiervan hoeft een ontwikkelaar alleen het bijgewerkte programma of de bijgewerkte bibliotheek te distribueren wanneer een programma of een van zijn bibliotheken evolueert (bijvoorbeeld voor een bugfix of prestatieverbetering). Hoewel dynamisch gedrag ertoe leidt dat er minder code wordt gedistribueerd wanneer er een versiewijziging optreedt, kan dit distributiebeleid ook leiden tot versieconflicten. Een ontwikkelaar verwijdert bijvoorbeeld een klassetype uit een bibliotheek of geeft het een andere naam. Wanneer een bedrijf de bijgewerkte bibliotheek distribueert, zullen bestaande programma's die afhankelijk zijn van het klassetype, mislukken. Om dit probleem aanzienlijk te verminderen, ondersteunt Java een interfacetype, dat is als een contract tussen twee partijen. (Zie interfaces, typen en andere objectgeoriënteerde taalfuncties die elders in de Java 101-serie worden besproken.)

Het uitpakken van deze definitie leert ons veel over Java. Het belangrijkste is dat het laat zien dat Java zowel een taal als een platform is. Later in deze tutorial leer je meer over Java-platformcomponenten, namelijk de virtuele Java-machine en de Java-uitvoeringsomgeving.

Drie versies van Java: Java SE, Java EE en Java ME

Sun Microsystems bracht in mei 1995 de Java 1.0 Software Development Kit (JDK) uit. De eerste JDK werd gebruikt om desktoptoepassingen en applets te ontwikkelen, en Java evolueerde vervolgens naar het programmeren van bedrijfsservers en mobiele apparaten. Het opslaan van alle benodigde bibliotheken in een enkele JDK zou de JDK te groot hebben gemaakt om te distribueren, vooral omdat de distributie in de jaren negentig werd beperkt door kleine cd's en lage netwerksnelheden. Omdat de meeste ontwikkelaars niet elke laatste API nodig hadden (een ontwikkelaar van desktoptoepassingen zou nauwelijks toegang moeten hebben tot Java-API's voor ondernemingen), heeft Sun Java in drie hoofdedities verwerkt. Deze werden uiteindelijk bekend als Java SE, Java EE en Java ME:

  • Java Platform, Standard Edition (Java SE) is het Java-platform voor het ontwikkelen van client-side applicaties (die op desktops draaien) en applets (die draaien in webbrowsers). Houd er rekening mee dat applets om veiligheidsredenen niet langer officieel worden ondersteund.
  • Java Platform, Enterprise Edition (Java EE ) is het Java-platform dat bovenop Java SE is gebouwd en dat uitsluitend wordt gebruikt om bedrijfsgerichte servertoepassingen te ontwikkelen. Servertoepassingen omvatten Java-servlets , Java-programma's die lijken op applets, maar op een server worden uitgevoerd in plaats van op een client. Servlets voldoen aan de Java Servlet API.
  • Java Platform, Micro Edition (Java ME) is ook bovenop Java SE gebouwd. Het is het Java-platform voor het ontwikkelen van MIDlets , Java-programma's die op mobiele informatieapparaten worden uitgevoerd, en Xlets , Java-programma's die op embedded apparaten worden uitgevoerd.

Java SE is het basisplatform voor Java en vormt de focus voor de Java 101-serie. Codevoorbeelden zijn gebaseerd op de meest recente versie van Java op het moment van schrijven, Java 12.

Het Java-platform en JVM

Java is zowel een programmeertaal als een platform voor het uitvoeren van gecompileerde Java-code. Dit platform bestaat voornamelijk uit de JVM, maar bevat ook een uitvoeringsomgeving die de uitvoering van de JVM op het onderliggende (native) platform ondersteunt. De JVM bevat verschillende componenten voor het laden, verifiëren en uitvoeren van Java-code. Figuur 1 laat zien hoe een Java-programma op dit platform wordt uitgevoerd. 

Jeff Friesen

Bovenaan het diagram staat een reeks programmaklassebestanden, waarvan er één wordt aangeduid als het hoofdklassebestand. Een Java-programma bestaat in ieder geval uit het hoofdklassebestand, het eerste klassebestand dat moet worden geladen, geverifieerd en uitgevoerd.

De JVM delegeert het laden van klassen naar de component classloader. Classloaders laden klassebestanden uit verschillende bronnen, zoals bestandssystemen, netwerken en archiefbestanden. Ze isoleren de JVM van de fijne kneepjes van het laden in de klas.

Een geladen klassebestand wordt in het geheugen opgeslagen en weergegeven als een object dat op basis van de Classklasse is gemaakt. Eenmaal geladen, verifieert de bytecode-verificateur de verschillende bytecode-instructies om er zeker van te zijn dat ze geldig zijn en de beveiliging niet in gevaar brengen.

Als de bytecodes van het klassebestand niet geldig zijn, wordt de JVM beëindigd. Anders interpreteert de interpretercomponent de bytecode met één instructie tegelijk. Interpretatie identificeert bytecode-instructies en voert gelijkwaardige native instructies uit.

Sommige bytecode-instructiereeksen worden vaker uitgevoerd dan andere. Wanneer de interpreter deze situatie detecteert, compileert de JVM's just-in-time (JIT) -compiler de bytecode-reeks naar native code voor snellere uitvoering.

Tijdens de uitvoering komt de interpreter doorgaans een verzoek tegen om de bytecode van een ander klassebestand uit te voeren (behorend tot het programma of tot een bibliotheek). Wanneer dit gebeurt, laadt de classloader het class-bestand en verifieert de bytecode-verifier de bytecode van het geladen class-bestand voordat het wordt uitgevoerd. Ook tijdens de uitvoering kunnen bytecode-instructies de JVM vragen om een ​​bestand te openen, iets op het scherm weer te geven, een geluid te maken of een andere taak uit te voeren die samenwerking met het native platform vereist. De JVM reageert door zijn Java Native Interface (JNI) -brugtechnologie te gebruiken om te communiceren met het native platform om de taak uit te voeren.