Programmering

Pakker og statisk import i Java

I min forrige Java 101 tutorial lærte du, hvordan du bedre organiserer din kode ved at erklære referencetyper (også kendt som klasser og grænseflader) som medlemmer af andre referencetyper og -blokke. Jeg viste dig også, hvordan du bruger indlejring for at undgå navnekonflikter mellem indlejrede referencetyper og referencetyper på øverste niveau, der har samme navn.

Sammen med indlejring bruger Java pakker til at løse problemer med samme navn i referencetyper på øverste niveau. Brug af statisk import forenkler også adgangen til de statiske medlemmer i pakkede topniveau referencetyper. Statisk import gemmer dig tastetryk, når du får adgang til disse medlemmer i din kode, men der er et par ting at passe på, når du bruger dem. I denne vejledning vil jeg introducere dig til at bruge pakker og statisk import i dine Java-programmer.

download Hent koden Download kildekoden for eksempel applikationer i denne Java-tutorial. Oprettet af Jeff Friesen til JavaWorld.

Emballagereferenstyper

Java-udviklere grupperer relaterede klasser og grænseflader i pakker. Brug af pakker gør det lettere at lokalisere og bruge referencetyper, undgå navnekonflikter mellem samme navngivne typer og kontrollere adgang til typer.

I dette afsnit lærer du om pakker. Du finder ud af, hvad pakker er, lær om pakke og importere erklæringer og udforske de ekstra emner beskyttet adgang, JAR-filer og typesøgninger.

Hvad er pakker i Java?

I softwareudvikling organiserer vi ofte varer i henhold til deres hierarkiske forhold. For eksempel viste jeg i den foregående vejledning, hvordan man erklærer klasser som medlemmer af andre klasser. Vi kan også bruge filsystemer til at indlejre mapper i andre mapper.

Brug af disse hierarkiske strukturer hjælper dig med at undgå navnekonflikter. For eksempel er det i et ikke-hierarkisk filsystem (en enkelt mappe) ikke muligt at tildele det samme navn til flere filer. I modsætning hertil lader et hierarkisk filsystem filer med samme navn eksistere i forskellige kataloger. Tilsvarende kan to omsluttende klasser indeholde samme navngivne indlejrede klasser. Navnekonflikter findes ikke, fordi emner er opdelt i forskellige navneområder.

Java giver os også mulighed for at opdele referencetyper på topniveau (ikke-indlejrede) i flere navneområder, så vi bedre kan organisere disse typer og forhindre navnekonflikter. I Java bruger vi pakkens sprogfunktion til at opdele referencetyper på øverste niveau i flere navneområder. I dette tilfælde a pakke er et unikt navneområde til lagring af referencetyper. Pakker kan gemme klasser og grænseflader samt underpakker, hvilke pakker er indlejret i andre pakker.

En pakke har et navn, som skal være en ikke-reserveret identifikator; for eksempel, java. Medlemadgangsoperatøren (.) adskiller et pakkenavn fra et underpakkenavn og adskiller et pakke- eller underpakningsnavn fra et typenavn. For eksempel er to-medlems adgangsoperatører i java.lang.System separat pakke navn java fra lang underpakke navn og separat underpakke navn lang fra System skriv navn.

Referencetyper skal erklæres offentlig at være tilgængelig uden for deres pakker. Det samme gælder for konstanter, konstruktører, metoder eller indlejrede typer, der skal være tilgængelige. Du kan se eksempler på disse senere i vejledningen.

Pakkeerklæringen

I Java bruger vi pakkeerklæring at oprette en pakke. Denne erklæring vises øverst i en kildefil og identificerer den pakke, som kildefiltyperne hører til. Den skal svare til følgende syntaks:

 pakke identifikator[.identifikator]*; 

En pakkeerklæring starter med det reserverede ord pakke og fortsætter med en identifikator, der eventuelt efterfølges af en periodesepareret sekvens af identifikatorer. Et semikolon (;) opsiger denne erklæring.

Den første (længst til venstre) identifikator navngiver pakken, og hver efterfølgende identifikator navngiver en underpakke. For eksempel i pakke a.b;, alle typer, der er angivet i kildefilen, hører til b underpakning af -en pakke.

Pakke / underpakning navngivningskonvention

Efter konvention udtrykker vi et pakke- eller underpakningsnavn med små bogstaver. Når navnet består af flere ord, vil du muligvis bruge et stort stort bogstav med undtagelse af det første. for eksempel, generalLedger.

En sekvens af pakkenavne skal være unik for at undgå kompileringsproblemer. Antag for eksempel, at du opretter to forskellige grafik pakker, og antag, at hver grafik pakken indeholder en Trekant klasse med en anden grænseflade. Når Java-kompileren støder på noget som det nedenfor, skal det bekræfte, at Trekant (int, int, int, int) konstruktør eksisterer:

 Trekant t = ny Trekant (1, 20, 30, 40); 

Trekantsafgrænsningsboks

Tænk på Trekant konstruktør som angiver en afgrænsningsboks, hvor trekanten skal tegnes. De to første parametre identificerer feltets øverste venstre hjørne, og de to andre parametre definerer feltets omfang.

Compileren søger i alle tilgængelige pakker, indtil den finder en grafik pakke, der indeholder en Trekant klasse. Hvis den fundne pakke indeholder det relevante Trekant klasse med en Trekant (int, int, int, int) konstruktør, alt er i orden. Ellers hvis fundet Trekant klasse har ikke en Trekant (int, int, int, int) konstruktør, rapporterer compileren en fejl. (Jeg siger mere om søgealgoritmen senere i denne vejledning.)

Dette scenario illustrerer vigtigheden af ​​at vælge unikke sekvenser for pakkenavn. Konventionen ved valg af en unik navnesekvens er at vende dit internetdomænenavn og bruge det som et præfiks for sekvensen. For eksempel ville jeg vælge ca. javajeff som mit præfiks fordi javajeff.ca er mit domænenavn. Jeg ville derefter specificere ca.javajeff.graphics.Triangle adgang Trekant.

Komponenter for domænenavne og gyldige pakkenavne

Komponenter for domænenavne er ikke altid gyldige pakkenavne. Et eller flere komponentnavne starter muligvis med et ciffer (3D.com), indeholder en bindestreg (-) eller en anden ulovlig karakter (ab-z.com), eller være et af Java's reserverede ord (short.com). Konventionen dikterer, at du præfikser cifferet med en understregning (com._3D), udskift den ulovlige karakter med en understregning (com.ab_z), og efterfølger det reserverede ord med en understregning (com.short_).

Du skal følge et par regler for at undgå yderligere problemer med pakkeerklæringen:

  1. Du kan kun erklære én pakkeerklæring i en kildefil.
  2. Du kan ikke gå foran pakkeerklæringen med noget bortset fra kommentarer.

Den første regel, som er et specielt tilfælde af den anden regel, findes, fordi det ikke giver mening at gemme en referencetype i flere pakker. Selvom en pakke kan gemme flere typer, kan en type kun tilhøre en pakke.

Når en kildefil ikke erklærer en pakkeerklæring, siges kildefilens typer at høre til unavngiven pakke. Ikke-trivielle referencetyper gemmes typisk i deres egne pakker og undgår den ikke-navngivne pakke.

Java-implementeringer kortlægger pakke- og underpakkenavne til samme navngivne mapper. For eksempel vil en implementering kortlægge grafik til en mappe med navnet grafik. I tilfælde af pakken a.b, det første bogstav, -en vil kortlægge til et navngivet katalog -en og b ville kortlægge til en b underkatalog til -en. Compileren gemmer de klassefiler, der implementerer pakkens typer i den tilsvarende mappe. Bemærk, at den ikke-navngivne pakke svarer til den aktuelle mappe.

Eksempel: Pakning af et lydbibliotek i Java

Et praktisk eksempel er nyttigt til fuldt ud at forstå pakke udmelding. I dette afsnit demonstrerer jeg pakker i sammenhæng med et lydbibliotek, der lader dig læse lydfiler og hente lyddata. For kortfattethed præsenterer jeg kun en skeletversion af biblioteket.

Lydbiblioteket består i øjeblikket kun af to klasser: Lyd og WavReader. Lyd beskriver et lydklip og er bibliotekets hovedklasse. Liste 1 viser kildekoden.

Liste 1. Eksempel på pakkeerklæring (Audio.java)

 pakke ca. javajeff.audio; offentlig endelig klasse Audio {private int [] prøver; privat int sampleRate; Audio (int [] samples, int sampleRate) {this.samples = samples; this.sampleRate = sampleRate; } public int [] getSamples () {return samples; } public int getSampleRate () {returner sampleRate; } offentlig statisk lyd newAudio (String-filnavn) {hvis (filnavn.tilLowerCase (). slutter med (". wav")) returnerer WavReader.read (filnavn); ellers returnere null; // ikke-understøttet format}} 

Lad os gennemgå Listing 1 trin for trin.

  • Det Audio.java i Listing 1 gemmer Lyd klasse. Denne fortegnelse begynder med en pakkeerklæring, der identificerer ca. javajeff.audio som klassens pakke.
  • Lyd erklæres offentlig så det kan henvises til uden for pakken. Det er også erklæret endelig så det ikke kan udvides (betyder, underklasseret).
  • Lyd erklærer privatprøver og sampleRate felter til lagring af lyddata. Disse felter initialiseres til de værdier, der sendes til Lyds konstruktør.
  • Lydkonstruktør erklæres pakke-privat (hvilket betyder, konstruktøren er ikke erklæret offentlig, privat, eller beskyttet), så denne klasse ikke kan instantieres uden for pakken.
  • Lyd præsenterer getSamples () og getSampleRate () metoder til returnering af et lydklips prøver og samplingsfrekvens. Hver metode erklæres offentlig så det kan kaldes udefra Lyd's pakke.
  • Lyd afsluttes med en offentlig og statisknewAudio () fabriksmetode til returnering af en Lyd objekt svarende til filnavn argument. Hvis lydklippet ikke kan opnås, nul returneres.
  • newAudio () sammenligner filnavnudvidelse med .wav (dette eksempel understøtter kun WAV-lyd). Hvis de matcher, udføres det returner WavReader.read (filnavn) at returnere en Lyd objekt med WAV-baserede lyddata.

Liste 2 beskriver WavReader.

Liste 2. WavReader hjælperklassen (WavReader.java)

 pakke ca. javajeff.audio; final class WavReader {statisk lydlæsning (strengfilnavn) {// Læs indholdet af filnavnet og behandl det // til en matrix af prøveværdier og en prøvehastighed // værdi. Hvis filen ikke kan læses, skal du returnere null. For // kortfattethed (og fordi jeg endnu ikke har diskuteret Javas // fil I / O API'er), præsenterer jeg kun skeletkode, der // altid returnerer et lydobjekt med standardværdier. returner ny lyd (ny int [0], 0); }} 

WavReader er beregnet til at læse en WAV-fils indhold i en Lyd objekt. (Klassen bliver til sidst større med yderligere privat felter og metoder.) Bemærk, at denne klasse ikke er erklæret offentlig, hvilket gør WavReader tilgængelig for Lyd men ikke at kode uden for ca. javajeff.audio pakke. Tænke på WavReader som en hjælperklasse, hvis eneste grund til eksistens er at tjene Lyd.

Udfør følgende trin for at opbygge dette bibliotek:

  1. Vælg en passende placering i dit filsystem som det aktuelle bibliotek.
  2. Lave en ca / javajeff / lyd underkataloghierarki i den aktuelle mappe.
  3. Kopier lister 1 og 2 til filer Audio.java og WavReader.java, henholdsvis; og gem disse filer i lyd underkatalog.
  4. Forudsat at den aktuelle mappe indeholder ca. underkatalog, udfør javac ca / ​​javajeff / audio / *. java for at kompilere de to kildefiler i ca / javajeff / lyd. Hvis alt går godt, skal du opdage Audio.class og WavReader.class filer i lyd underkatalog. (Alternativt kan du i dette eksempel skifte til lyd underkatalog og udføre javac * .java.)

Nu hvor du har oprettet lydbiblioteket, vil du gerne bruge det. Snart ser vi på et lille Java-program, der demonstrerer dette bibliotek. Først skal du lære om importerklæringen.

Java's importerklæring

Forestil dig at skulle specificere ca.javajeff.graphics.Triangle for hver forekomst af Trekant i kildekoden gentagne gange. Java leverer importopgørelsen som et praktisk alternativ til udeladelse af lange pakkeoplysninger.

Importopgørelsen importerer typer fra en pakke ved at fortælle kompilatoren, hvor den skal søges ukvalificeret (intet pakkepræfiks) skrivnavne under kompilering. Det vises nær toppen af ​​en kildefil og skal svare til følgende syntaks:

 importere identifikator[.identifikator]*.(type Navn | *); 

En importerklæring starter med reserveret ord importere og fortsætter med en identifikator, der eventuelt efterfølges af en periodesepareret sekvens af identifikatorer. Et typenavn eller en stjerne (*) følger, og et semikolon afslutter denne erklæring.

Syntaksen afslører to former for importopgørelsen. For det første kan du importere et enkelt navn, som identificeres via type Navn. For det andet kan du importere alle typer, som identificeres via stjernen.

Det * symbol er et jokertegn, der repræsenterer alle ukvalificerede typenavne. Det fortæller kompilatoren at kigge efter sådanne navne i den yderste pakke i importopgørelsens pakkesekvens, medmindre typenavnet findes i en tidligere søgt pakke. Bemærk, at brug af jokertegnet ikke har en præstationsstraff eller fører til opblødning af kode. Det kan dog føre til navnekonflikter, som du vil se.

For eksempel, import ca. javajeff.graphics.Triangle; fortæller kompilatoren, at en ukvalificeret Trekant klasse findes i ca. javajeff. grafik pakke. Tilsvarende noget lignende

 import ca.javajeff.graphics. *; 

fortæller kompilatoren at se i denne pakke, når den møder en Trekant navn, a Cirkel navn eller endda et Konto navn (hvis Konto er ikke allerede fundet).

Undgå * i multi-udvikler projekter

Når du arbejder på et multi-udvikler-projekt, skal du undgå at bruge * wildcard, så andre udviklere nemt kan se, hvilke typer der bruges i din kildekode.