Ohjelmiston tuotannossa yksi keskeinen ongelma on ohjelman osien, erilaisten ohjelmatiedostojen hallinta: missä mikin osa sijaitsee, miten osat löydetään.
Javassa ohjelmatiedostot eli class-tiedostot kerätään ns. pakkauksiin (package). Itse pakkaukset voidaan järjestää puumaiseen tiedostorakenteeseen. Pakkaus ei sinänsä liity olio-ohjelmointiin. Se on käytännön ohjelmistotuotannon väline!
Yhteen pakkaukseen on tapana kerätä luokkia (ja rajapintaluokkia), jotka tavalla tai toisella liittyvät toisiinsa. Jos luokkia on paljon, pakkaukset voidaan jäsennellä alipakkauksiksi, jne.
Myös Java-kielen valmiit välineet on toteutettu pakkauksina: Esimerkiksi pakkaus java muodostuu alipakkauksista java.lang, java.util, java.io, ...
Pakkaus muodostaa "nimiavaruuden", jonka nimien näkyvyys pakkauksen ulkopuolelle voidaan sallia tai kieltää. (Myös pakkauksen sisällä nimien näkyvyys luokkien välillä on säädeltävissä, kts. 4.6)
Käännösyksikkö (compilation unit) on tiedosto, joka voi sisältää (tässä järjestyksessä!):
Huom: Käännösyksikössä voi olla vain yksi luokka, jolle on määritelty näkyvyydeksi public!
Kääntäjä tuottaa jokaisesta käännösyksikön sisältämästä Java-kielisestä luokkamäärittelystä erillisen Bytecode-kielisen class-tiedoston. Ja pakkauksiin siis kerätään nimenomaan näitä class-tiedostoja.
package paketti;ilmaisee, että käännösyksikön sisältämät luokat on käännettyinä määrä sijoittaa pakkaukseen paketti. Useissa käännösyksiköissä voi olla sama pakkauksen nimi.
Huom: package-ilmaus ei automaattisesti vie syntyviä class-tiedostoja pakkausta vastaavaan hakemistoon!
Käännösyksikön kuuluminen alipakkaukseen ilmaistaan:
package paketti.alipaketti;Pakkauksen kaikki luokat saavat "etunimekseen" pakkauksen nimen! Esimerkiksi käännösyksiköiden
package pak;
public class Aapeli {
...
}
ja
package pak;
public class Beepeli {
...
}
luokkien koko nimet ovat pak.Aapeli ja pak.Beepeli.
class sovellutututus {
...
pak.Aapeli x = new pak.Aapeli();
...
}
Näin voi menetellä, jos pakkauksen välineitä ei käytetä paljon, ja
jos ohjelmaa ei ole tarkoitus ylläpitää!
Käytännössä on järkevää käyttää aina import-ilmauksia. Ne kirjoitetaan käännösyksikön alkuun mahdollisen package-ilmauksen jälkeen. Näin heti käännösyksikön alusta näkee, mitä pakkauksia luokissa käytetään.
Esimerkki: Käännösyksikkö olkoon seuraavanlainen:
package omapaketti;
import pak1.Luo1;
import pak2.*;
import pak3.pak4.*;
class Oma1 { ... }
class Oma2 {
...
pak5.Kissa a = new pak5.Kissa();
...
}
Tässä
import java.lang.*;
Tulevaisuuden visioissa Java-pakkaukset on haluttu nähdä koko Internetin nimiavaruudessa. Tähän tarkoitukseen on suositeltu ns. domain-nimestä johdettujen nimien käyttöä pakkauksen nimen alkuosana.
Esimerkiksi kun jokin itse tehty luokka - vaikkapa MinunIkiomaLuokkani - annettaisiin maailmanlaajuiseen käyttöön, siihen voitaisiin viitata vaikkapa nimellä:
fi.helsinki.cs.wikla.MinunIkiomaLuokkaniHuom. Näin asiat eivät siis vielä ole! Mahtanevatko koskaan ollakaan?
Pakkaukset voitaisiin sijoittaa myös tietokantaan tai vaikkapa ripotella "ympäri internettiä" kunhan vain käytettävä Java-toteutus tietää, mistä etsiä!
Jos pakkaukset on tarkoitettu pysyvään käyttöön, CLASSPATH-muuttujaa voi täydentää pysyvästi.
Esimerkki:
export CLASSPATH="$CLASSPATH:$HOME/classes"
Nyt Java-toteutus osaa etsiä pakkauksia myös kotihakemiston alihakemistosta classes.
package kalut;
public class Koe {
public int x, y;
}
Käännetään luokka ja sijoitetaan käänöksen tulos
Koe.class hakemistoon classes/kalut.
import kalut.*;
class Sov {
public static void main(String[] args) {
Koe a = new Koe();
a.x = 5; a.y = 6;
System.out.println(a.x+" "+a.y);
}
}
Tässä annetaan lupa hakea mikä tahansa julkinen luokka pakkauksesta
kalut. (Myös pelkkä import kalut.Koe; riittäisi
tässä tapauksessa.)
Takaisin luvun 4 sisällysluetteloon.