stanford-oval/wikipedia huggingface dataset:
Java
Ominaisuudet > Tiedostonkäsittely Javassa

Javassa tiedostoon kirjoittaminen ja sieltä lukeminen on melko yksinkertaista. Java tarjoaa näitä toimintoja varten käyttäjälleen useita eri kirjastoja ja metodeja, joita Java-ohjelmoijat pääsevät vapaasti käyttämään. FileWriter on yksi java.io kirjaston tarjoama luokka, jota voi hyödyntää tiedoston kirjoittamiseen tai lukemiseen. Tämän jälkeen voidaan käyttää write-metodia tekstin kirjoittamiseen. Esimerkiksi jos halutaan luoda ja kirjoittaa tekstiä tiedostoon "tiedosto.txt", tapahtuu se seuraavanlaisesti.
try (FileWriter kirjoittaja = new FileWriter("tiedosto.txt")) {
    kirjoittaja.write("Kirjoitetaan tekstiä tiedostoon.");
} catch (IOException e) {
    // Poikkeukset tähän
}
Java pakottaa käyttäjänsä lisäämään poikkeuksenkäsittelyn, jota esimerkiksi Pythonissa ei tarvitse tehdä. Tällä varmistetaan se, että tiedostojen puuttumiset, käyttöoikeusongelmat sekä muut odottomattomat ongelmat otetaan huomioon. Kuten yllä olevasta koodista huomaa, tapahtuu tiedostoon kirjoittaminen try-lausekkeen sisällä.
Olion lisääminen tiedostoon tapahtuu Javassa hieman erilailla, kuin esimerkiksi tavallisen tekstin kanssa toimiessa. Tämä johtuu siitä, että oliot usein sisältävät tietoa, joka ei yksinkertaisesti ole kirjoitettavissa tekstinä, vaan se on tallennettava binäärimuodossa. Javassa ohjelmoija joutuu käyttämään ObjectOutputStream - toimintoa, jos haluaa lisätä sen tiedostoon ja vastaavasti sen voi lukea sieltä ObjectInputStream-luokkien avulla. Tässä on yksinkertainen esimerkki siitä, miten olion tallentaminen tiedostoon tapahtuu.
Henkilo henkilo = new Henkilo("Matti Meikäläinen", 30);
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("tiedosto.txt"))) {
            objectOutputStream.writeObject(henkilo);
        } catch (IOException e) {
            // Poikkeukset tähän
        }
Ominaisuudet > Muistinhallinta

Javan muistinhallinta on automaattista. Java käyttää automaattista roskienkeräintä (eng. automatic garbage collector) muistinhallintaan. Sen tarkoitus on hallita objektin muistinkäyttöä sen käyttöiän aikana.
Tämä poikkeaa esimerkiksi C- ja C++-kielten käytännöistä, joissa ohjelman muistinhallinta on manuaalista ja siten ohjelmoijan vastuulla. Manuaalisen muistinhallinnan ohjelmointikielissä on mahdollista saada aikaan muistivuotoa, eli tilanteen, jossa ohjelma varaa muistia toimintojaan varten, mutta ei vapauta sitä tarpeen ja ohjelman päättyessä.
Javan roskienkerääjän tarkoitus on ehkäistä muistivuotoa. Vaikka roskienkeräys ei eliminoi muistivuotoja täysin, se vähentää niitä merkittävästi, kuin myös niiden ehkäisemiseen kuluvia resursseja ohjelmistokehittäjiltä.
Roskienkerääjä ajaa jaksollisesti taustalla "roskienkeräyksen" (eng. garbage collection), joka pyrkii tunnistamaan muistissa olevat "kuolleet" objektit, joihin ei tulla enää viittaamaan ohjelmassa, vapauttamaan näiden objektien viemä tila muistista ja uudelleenorganisoida "elävät" objektit muistinkäytön ja tulevien roskienkeräysten optimoimiseksi.
Roskienkerääjätyyppejä on useita erilaisia toteutuksia. Käytettävä roskienkerääjä riippuu käytettävissä olevista laitteiston resursseista sekä suorituskykyvaatimuksista. Roskienkerääjätyypit Javassa:
Serial Garbage Collector - Suorittaa kaiken toiminnan yhdellä säikeellä. Omaa suuremmat taukoajat, mutta käyttää vähän resursseja. Sopii parhaiten yhden prosessorin systeemeihin.
Parallel Garbage Collector - Samankaltainen Serial Garbage Collectorin kanssa, mutta käyttää useita säikeitä.
Concurrent Mark Sweep (CMS) Garbage Collector (Hylätty JDK 9:ssä, poistettu JDK 14:ssä) - Vähentää roskienkeräyksen taukoaikoja suorittamalla toimintojaan sovelluksen toimiessa.
Garbage First (G1) Garbage Collector (Oletuksena JDK:n versiosta 9) - Parannus CMS GC:stä, syrjäyttää tämän. G1 sopii parhaiten usean prosessorin laitteisiin suurella muistikapasiteetilla.
ZGC (Kokeellinen JDK 11:ssä, tuotanto JDK 15:ssä) - Erittäin matalan viiveen roskankeräärä, skaalattavissa usean teratavun "keon" (eng. heap) sovelluksiin. ZGC:n sisäinen toteutus ja käyttäytyminen poikkeavat merkittävästi muista roskankerääjistä.
Ominaisuudet > Tyyppimuunnokset

Java on staattisesti tyypitetty kieli. Välillä saattaa tulla eteen tilanne, jossa on tarve tehdä tyyppimuunnos. Tyyppimuunnoksella tarkoitetaan prosessia, jossa muutetaan muuttujan tai arvon tietotyyppi toiseksi tietotyypiksi. Tyyppimuunnos voi tapahtua automaattisesti tai manuaalisesti.
Jos tyyppimuunnoksessa tietotyypit ovat yhteensopivia, ja tavoiteltu tietotyyppi on laajempi kuin alkuperäinen tietotyyppi, tekee Java tyyppimuunnoksen automaattisesti. Esimerkiksi ”int”-tyypin muuttuja  voidaan muuttaa ”long”-tyyppiseksi muuttujaksi, sillä ”long”-tietotyypissä on tarvittava määrä muistia ”int”-tyypin muuttujan säilymiseen. Lisäksi Java muuttaa automaattisesti kokonaislukuarvon ”int” liukulukuarvoksi ”float”, kun laskee näiden kahden jakolaskun.
Jos tyyppimuunnoksessa ei voida taata täydellistä datan säilymistä, ei automaattista tyyppimuunnosta voida tehdä. Kun tyyppimuunnos ei tapahdu automaattisesti, voi tyyppimuunnoksen tehdä manuaalisesti käyttämällä tyyppimuunnosoperaattoria. Operaation nimi on pakotettu tyyppimuunnos. Esimerkiksi, jos haluat muuttaa muuttujan, jonka tietotyyppi on ”float” tietotyypiksi ”int”, voisit käyttää tyyppimuunnosoperaattoria seuraavalla tavalla: (int) float-muuttuja. Haluttu tietotyyppimuuttuja siis sijoitetaan sulkeisiin, jonka jälkeen kirjoitetaan alkuperäinen, muutettava muuttuja. Java pyrkii tekemään tyyppimuunnoksen, vaikkei se olisi kyseisessä tilanteessa mahdollista. Tällainen tilanne voi aiheuttaa ongelmia myöhemmässä vaiheessa, viimeistään ohjelman ajovaiheessa.
Tyyppimuunnos auttaa käsittelemään erilaisia tietotyyppejä ja tehostaa ohjelman suorituskykyä. Tyyppimuunnosta on kuitenkin käytettävä tarkkaavaisuutta vaatien, sillä sen väärinkäyttö voi johtaa virheisiin. Tyyppimuunnoksen yhteydessä esiintyviä yleisimpiä virheitä ovat tyyppimuunnoksen tyyppien yhteensopimattomuus sekä muunnoksen aiheuttamat tietojen menetys tai vääristyminen. Tyyppimuunnoksen käytön tarpeellisuutta onkin tarpeen harkita tapauskohtaisesti.
Ominaisuudet > Suorituskyky

Javan ohjelmointikieli nähdään laajalti suorituskyvyltään hitaampana kuin muut nopeammat kielet, kuten C ja C++. Ongelma johtuu perustavanlaatuisesta erosta kielien rakenteessa: C ja C++ suorittavat ohjelman suoraan tietokoneen prosessorilta. Java taas avaa virtuaalikoneen (Java virtual machine, JVM) ohjelmaa varten, johtaen merkittävään suorituskyvyn menetykseen.
Java kehittää JVM:n suorituskykyä ja optimointia jatkuvasti uusissa versioissa. Vuonna 1997 Java alkoi käyttämään ajonaikaista kääntöä (Just-in-Time compilation, JIT), joka toi merkittävän parannuksen JVM:n suorituskyvyssä.
Java-ohjelman suorituskyky on siis olennaisesti sidottu paitsi itse ohjelman optimointiin myös käytössä olevaan Javan versioon. Uudemmat versiot Javan virtuaalikoneesta käyttävät tietokoneen prosessointikykyä tehokkaammiin ja ohjelman suorittaminen voi nopeutua merkittävästi vanhemmista versioista. Tämän takia Java-ohjelman suorituskykyä testattaessa on olennaista raportoida käytössä olevan JVM:n versio, myyjä, käyttöjärjestelmä sekä laitearkkitehtuuri.
Ominaisuudet > Virheidenkäsittely

Java ohjelmointi kielenä tarjoaa laajan virhekontrolli mekanismin. Yksi päätavoista, jolla tämä saavutetaan on poikkeuksen käsittely systeemi (exception). Kun virhe tapahtuu ohjelman aikana, Java voi implementoida poikkeuksen, jossa objekti kuvastaa virhettä. Tämä systeemi mahdollistaa ohjelman käsittelemään virheet jäsennellysti eikä niin, että ohjelma kaatuisi tai tekisi hankalaksi ohjelman ajamista.
Javassa, poikkeukset käsitellään käyttäen try-catch-finally-block systeemiä. Try-lause sisältää koodia, joka saattaa poikkeuksen ja Catch-lause sisältää koodin, joka käsittelee poikkeuksen. Finally-lause on näiden jälkeinen toimenpide, joka tekee resurssien putsauksen, kun aiemmat lauseet ovat ajettu.
Java tarjoaa myös mekanismin, jossa throw ja catch menetelmät toteutetaan eri tavalla, jossa ohjelmoija määrittelee niiden toimenpiteet. Tämä antaa ohjelmoijille hyvän mahdollisuuden tehdä joustavaa virheiden käsittelyä Javan-ohjelmointi kielen normaalien poikkeuskäsittelyn toimenpiteiden lisäksi.
Javan ohjelmointikielellä on myös toinen tapa käsitellä virheitä ja se perustuu type-safe mekanismiin. Javan ohjelmointkieli on vahvasti ”näppäilty” kieli mikä tarkoittaa sitä, että muuttujien tyyppi on oltava täysin ilmoitettu siinä ajassa, kun sitä määritellään. Tämä helpottaa virheissä kuten tyyppien yhteensopimattomuudessa sekä null-osoittimien poikkeuksissa.
Yleisesti Java-ohjelmointikielen virheidenkäsittely mekaniikat ovat luotettavia ja näiden avulla pystytään ohjelmoimaan vakaata ja helposti käsiteltävää koodia ohjelmistoihin.
Ominaisuudet > Javan tarjoamia kirjastoja

Java tarjoaa laajan skaalan erilaisia kirjastoja helpottamaan ohjelmoijien työtä. Kirjastoista yleisin ja käytetyin on Javan standardikirjasto (eng. Java Standard Library) joka pitää sisällään useita tärkeitä kirjastoja, kuten esimerkiksi java.lang, jo aiemmin mainittu java.util ja java.math. Javan kirjastot saadaan dynaamisesti käyttöön ajon aikana sisällyttämällä kirjastot koodin alkuun komennolla "import" ja kutsumalla kirjastojen sisältämiä komentoja osana koodia. Näin Javassa saadaan käyttöön hyvin laaja skaala komentoja vain parilla lisäsanalla.
Ominaisuudet > Ulkoiset Java kirjastot

Java tarjoaa myös mahdollisuuden käyttää ulkoisia kirjastoja. Kirjastot on luotu helpottamaan sovellusten kehitystä tarjoamalla erilaisia työkaluja ja valmiiksi kirjoitettua koodia. Niitä voidaan lisätä omaan ohjelmaan sen toiminnallisuuden laajentamiseksi tai koodin ongelmakohtien ratkaisemiseksi. Kirjastojen tarkoitus on mahdollistaa ohjelmoijan keskittyminen sovelluksen rakenteeseen matalatasoisen toteutuksen sijasta.
Esimerkkikirjastoja:
Spring on kevyt mutta tehokas Java-sovelluskehityskehys, jota käytetään yleisesti JEE (Java Enterprise Edition) -sovelluksissa. Se tarjoaa erilaisia ​​moduuleja, kuten Spring Security, Spring MVC, Spring Batch, Spring ORM, Spring Boot ja Spring Cloud. Lisäksi Spring tukee Dependency Injection -integraatiota, mikä mahdollistaa riippuvuuksien ratkaisemisen joustavasti. Spring pystyy myös ratkaisemaan riippuvuudet, sillä se tukee Dependency Injection -integraatiota.
Hibernate helpottaa tietokantaan pääsyä ja hallintaa sekä tarjoaa ominaisuuksia, kuten SQL-kyselyjen käsittelyä, välimuistitusta ja suorituskyvyn optimointia. Hibernate mahdollistaa myös oletusarvoista SQL-tyyppien ohittamista, Java Enumien mappamisen sarakkeisiin sekä yhden ominaisuuden mappamisen useaan sarakkeeseen.
Grails tarjoaa yhtenäisen kehitysympäristön, joka hyödyntää olemassa olevia Java-teknologioita, kuten Hibernatea ja Springiä, yhden käyttöliittymän alla. Grails tarjoaa myös dokumentaatiota tärkeimmistä osista, kuten pysyvyyskehyksestä, GSP-templaattikielen käytöstä ja dynaamisista tag-kirjastoista. Lisäksi Grails tarjoaa esimerkkisovelluksia, kehysarkkitehtuurin laajennettavuuden sekä tuen Ajaxille.
Play on nykyaikainen ja kevyt web-kehys, joka keskittyy kehittäjien tuottavuuteen ja tehokkuuteen. Se tukee RESTful-arkkitehtuuria, sisältää yksikkötestauksen tukemisen JUnitin ja Seleniumin avulla, tarjoaa monia sisäänrakennettuja elementtejä ja tukee asynkronista I/O:ta.
Erot muihin kieliin

Ohjelmointikieliä on paljon ja monet niistä jakavatkin samanlaisia ominaisuuksia. C-kieli on rakenteellista ja proseduraalista, kun taas sen jälkeläinen C++ tarjoaa laajemman valikoiman ominaisuuksia ja kielen toiminnallisuus pohjautuu huomattavasti enemmän oliopohjaiseen ohjelmointiin kuten Java. JavaScriptiä taas kuvaillaan enemmän C:n kaltaisena.
Java-ohjelmointi pohjautuu hyvin vahvasti useiden eri luokkien, metodien, olioiden ja niille määritettyjen funktioiden suorittamiseen. Filosofiassaan kieli noudattaa SOLID-periaatetta.
Erot muihin kieliin > Java ja JavaScript

Vaikka kielet Java ja JavaScript jakavatkin osittain saman nimen, ovat ne hyvin erilaisia ohjelmointikieliä toimivuudeltaan ja ajatusmaailmaltaan. Java kääntyy Java-bittikoodiksi, jonka takia Javaa suoritetaan useimmiten JVM (Java Virtual Machine) sisällä, kun taas JavaScript suoritetaan selaimen puolella. Muuttamalla koodin Java-biteiksi, Javan oma virtuaalikone mahdollistaa paremman turvallisuuden perustasolla ilman ylimääräistä koodia. JavaScriptiä käytetäänkin usein front end-suunnittelussa ja Javaa back end-suunnittelussa.
Java on kieli, joka tarkistaa static-tietotyyppejä ja JavaScript dynaamisia. Javan kirjoitus voi olla hieman työläämpää, koska siinä voi joutua kirjoittamaan enemmän ja pitämään huolta suunnittelun ohella, että tietotyyppi on määritelty oikein. JavaScript on kielenä nopeampi. Sen scripti tarkastetaan sillä hetkellä, kun sitä suoritetaan. JavaScriptin koodia on helpompi ymmärtää, sillä se kirjoitetaan yksinkertaisena tekstinä. Sitä ei ole käännetty kuten Javassa tehdään.
JavaScriptiä käytetään pääosin nettiselainten suunnitteluun ja toteutukseen ja sen päätehtävä on tehdä sivuista vuorovaikutteisempia kuin perinteisesti koodatut yksinkertaiset sivut.
Javan suunnittelu ja kirjoitus vaatii JDK:n (Java Development Kit), joten sitä voidaan mieltää hieman edistyksellisempänä ohjelmointikielenä.
Uuden luokan tekeminen ja käyttäminen. Luokan rakentaja ja metodien yksinkertaista käyttöä.

Luokka, eli olio. Tarkoitus on saada koodi selkeämmin hallittavaan pakettiin, että koodi olisi helpommin muokattavissa ja luettavissa.
Ohessa lyhyt esimerkki kahteen tiedostoon rakennetusta ohjelmasta, missä käytetään uutta luokkaa, rakentajaa ja sen metodeita.
Uuden luokan tekeminen ja käyttäminen. Luokan rakentaja ja metodien yksinkertaista käyttöä. > Pää tiedosto

Esimerkki.
package com.example;

public class App 
{
    public static void main( String[] args ){
        Animal siili1 = new Animal();   // Luodaan siili1 niminen olio. Rakentajaa käytetään ilman parametrejä,
                                        // jolloin eläin saa vakio parametrit.
        Animal siili2 = new Animal("Pekka",9);  //Luodaan siili2 niminen olio ja käytetään paramerillistä rakentajaa.
        System.out.println("Siilin1 nimi: " + siili1.getName());//Tulostetaan siili1 nimi
        System.out.println("Siilin2 ikä: " + siili2.getAge());  //Tulostetaan siili2 ikä
        siili1.setName("Maija");                                //Siilin nimeä voidaan myös vaihtaa lennosta. 
        System.out.println(siili1.getName());
        siili1.printAnimalSpecs();                              //Tulostetaan siilin1 tiedot käyttäen metodia.
        siili2.printAnimalSpecs();                              //Tulostetaan siilin2 tiedot käyttäen metodia.
    }
}
Olioita voidaan luoda tarpeen mukaan.
Uuden luokan tekeminen ja käyttäminen. Luokan rakentaja ja metodien yksinkertaista käyttöä. > Luokkatiedosto. Luokan ja metodien tekeminen

Esimerkki.
package com.example;

public class Animal {
    public String name = "Heikki";      //Asetettu String tyyppinen nimimuuttuja "Heikki"
    public int age = 15;                //Asetettu int tyyppinen ikämuuttuja 15.

    //Voidaan tehdä useampi rakentaja, kunhan sisään tulevat parametrit eroavat toisistaan.
    public Animal(){        //Rakentaja ilman parametrejä. Nimeksi jää tässätapauksessa nimi "Heikki" ja ikä 15, ellei niitä erikseen muuteta
    }
    public Animal(String name,int ika){ //Rakentaja parametreillä.
        this.name = name;               //Sanaa this.name kertoo kääntäjälle, että käytetään tämän luokan muuttujaa name.
        age = ika;                      //Sama voidaan tehdä myös, jos sisään tulevan parametrin nimeä muutetaan erinimiseksi.
    }
    public String getName(){            //saadaan haettua Animal olion nimi.
        return name;
    }
    public void setName(String name){   //voidaan asettaa Animal olion nimi.
        this.name = name;
        return;
    }
    public int getAge(){                //Voidaan hakea Animal olion ikä.
        return age;
    }
    public void setAge(int age){        //Voidaan asettaaa Animal olion ikä.
        this.age = age;
        return;
    }
    public void printAnimalSpecs(){//Tulostetaan siilin tiedot.
        System.out.println("Nimi: " + name + " ikä: " + age);
    }
}
Tuloste:
Siilin1 nimi: Heikki
Siilin2 ikä: 9
Maija
Nimi: Maija ikä: 15
Nimi: Pekka ikä: 9

Metodeita tehdään sen mukaan, että mitä tietoja halutaan saada luokasta ulos, tai luokkaan sisään.
Uuden luokan tekeminen ja käyttäminen. Luokan rakentaja ja metodien yksinkertaista käyttöä. > Singleton

Singleton on Javassa käytetty suunnittelumalli, jonka avulla voidaan varmistaa, että singelton-luokassa on vain yksi olio (luokan ilmentymä) kerrallaan. Avaintekijöinä singleton-luokan luomisessa toimii yksityinen luokkamuuttuja, yksityinen rakentaja ja julkinen metodi siihen käsiksi pääsemiseen. Luokan rakentaja määritellään yksityiseksi, jotta uusien olioiden luominen luokan ulkopuolella voidaan estää, ja yksityisellä luokkamuuttujalla (määrittelyllä private static) päästään käsiksi luotuun olioon. Julkisella luokkametodilla (määrittelyllä public static) luodaan pääsy yksityiseksi määriteltyyn olioon. Nimenomaan tämän julkisen luokkametodin sisällä on toiminnallisuus, jolla useampien olioiden luominen luokan ulkopuolella voidaan estää.
Yksinkertainen esimerkki singleton-luokan luomisesta voi olla esimerkiksi seuraavanlainen:
public class singletonExample{
    
    //yksityinen luokkamuuttuja, jonka arvoksi määritellään aluksi "null"
    private static Storage storage = null;
    
    //yksityinen rakentaja
    private Storage(){
    }
    
    //Luokkametodi, joka tarkistaa onko luokkamuuttuja Storage jo tehty.
    //Jos Storagen arvo on edelleen "null" eli sitä ei ole vielä luotu, luodaan
    //se tässä metodissa ja palautetaan se. Jos Storage on jo olemassa, 
    //palauttaa metodi kyseisen valmiiksi olemassa olevan Storagen.
    public static Storage getInstance(){
        if(storage == null){
            storage == new Storage();
        }
        return storage;
    }
}
Singleton-luokan ongelmana on kuitenkin esimerkiksi sen globaalia muuttujaa muistuttava luonne. Kaikilla luokilla on pääsy muuttujaan julkisen instanssin vuoksi, mutta luokkien välisiä riippuvuuksia on vaikea määrittää, koska singelton-luokan yksityisellä rakentajalla ei ole niitä. Luokilla kuitenkin on riippuvuus, jos toinen luokka käyttää toisen instanssia.
Singleton-luokkaa käytetään usein tietokantoja käsittelevissä ohjelmissa. Käytännön näkökulmasta singelton luokka toimii niin, että koko applikaatio, ohjelma tms. käyttää vain yhtä ja samaa tietokannan ilmentymää sen kaikissa toiminnoissa. Tämä ehkäisee virheiden ja poikkeusten esiintymistä, koska aina luotaessa uusi ilmentymä nämä ovat mahdollisia. Lisäksi saman ilmentymän käyttäminen nopeuttaa tiedonhakua ja sen palauttamista tietokannasta.
Säikeet

Javassa voi käynnistää käyttöjärjestelmän tukemana tavallista ohjelmaprosessia kevyempiä säikeitä. Säikeen käyttämät resurssit kuuluvat säikeen käynnistävälle prosessille. Yksittäinen prosessi voi käynnistää yhden tai suuren määrän säikeitä yhdessä tai useammassa suoritinytimessä käyttöjärjestelmä- ja resurssirajoitteiden puitteissa. Säie on yksi ohjelman suoritusketjuista, joka voi toimia samanaikaisesti muiden säikeiden kanssa. Säikeiden käyttö sallii moniajo-ohjelmoinnin, joka voi parantaa ohjelman suorituskykyä. Javan Thread -luokkaa käytetään säikeiden luomiseen. Kullakin säikeellä on oma suorituskaista, joka voi toimia samanaikaisesti muiden säikeiden kanssa.
Esimerkki.
Oheinen koodiesimerkki luo 5 säiettä ja jokainen niistä tulostaa aloitus- ja lopetusaikansa. Säikeet tallentavat aloitusajan, odottavat satunnaisen ajan ja tallentavat lopetusaikansa. Ohjelma käyttää Thread-luokan sleep -metodia odottamiseen. Taulukkoon tallennetut säikeet käynnistetään for-silmukan avulla. Säikeet käynnistetään samanaikaisesti, mutta niiden suoritusjärjestys ei ole ennalta määrätty. Lopuksi odotetaan, että kaikki säikeet ovat suorittaneet loppuun ennen kuin ohjelma päättyy. Tämä tehdään Thread-luokan join-metodilla, joka pysäyttää nykyisen säikeen suorituksen, kunnes liittyvä säie on suorittanut loppuun. Ilman join-metodia ohjelma saattaisi lopettaa liian aikaisin, kun jotkut säikeet vielä suorittavat.
import java.util.Date;
import java.text.SimpleDateFormat;

public class Saietesti {
    public static void main(String[] args) throws InterruptedException {
        Thread[] saikeet = new Thread[5]; // luodaan taulukko säikeitä varten
        for (int i = 0; i < saikeet.length; i++) {
            final int j = i + 1; // säikeiden numerointi
            saikeet[i] = new Thread(() -> { // luodaan uusi säie
                String aloitettu = new SimpleDateFormat("HH:mm:ss.SSS").format(new Date()); // tallennetaan aloitusaika
                System.out.printf("Säie %d aloitettu: %s\n", j, aloitettu); // tulostetaan aloitusaika
                try {
                    int odota = (int) (Math.random() * 9000 + 1000); // arvotaan odotusaika väliltä 1-10 sekuntia
                    Thread.sleep(odota); // nukutetaanaan säiettä odotusajan verran
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String lopetettu = new SimpleDateFormat("HH:mm:ss.SSS").format(new Date());  // tallennetaan lopetusaika
                System.out.printf("Säie %d lopetettu: %s\n", j, lopetettu); // tulostetaan lopetusaika
            });
            saikeet[i].start(); // käynnistetään säie
        }
        for (Thread saie : saikeet) {
            saie.join(); // odotetaan, että kaikki säikeet ovat suorittaneet loppuun ennen kuin ohjelma päättyy
        }
    }
}
> java Saietesti
Säie 4 aloitettu: 20:38:42.394
Säie 5 aloitettu: 20:38:42.397
Säie 2 aloitettu: 20:38:42.397
Säie 3 aloitettu: 20:38:42.397
Säie 1 aloitettu: 20:38:42.405
Säie 1 lopetettu: 20:38:43.808
Säie 5 lopetettu: 20:38:47.258
Säie 2 lopetettu: 20:38:49.158
Säie 4 lopetettu: 20:38:49.985
Säie 3 lopetettu: 20:38:52.153