Java-kompilering

Fra TermvaktWiki
Revisjon per 7. jan 2008 kl. 15:01 av Siljebj (Diskusjon | bidrag)
Gå til: navigasjon, søk

Innhold

Kort innføring i Java-kompilering

Anbefalte bakgrunnskunnskaper. Denne veiledningen forutsetter at du kjenner til følgende:

  • Innlogging og vindusbehandling på en linuxmaskin
  • Hva et terminalvindu er, og hvordan du angir en kommando i terminalvinduet (bash)
  • Enkel filbehandling
  • Ett tekstredigeringsprogram i linux (Emacs, Linux/vi)

Om kompilering

Kompilering er navet på prosessen som oversetter kildekode til maskinkode. Kildekode betegner formatet som vi skriver programmet i, men maskinkode er en sekvens av enere og nuller som datamaskinen forstår. Denne veiledningen forklarer hvordan man kompilerer Java-, C- og C++-programmer. Den tar i tillegg for seg vannlige feilmeldinger ved kompilering og gir forslag på hvordan feilen kan løses. Veiledningen er todelt. Del en tar for seg kompilering av javaprogrammer, mens del to tar for seg kompilering av C- og C++-programmer. Hver del er igjen delt opp i en enkel teoretisk og en praktisk del.

Teori

Java er et programmeringsspråk som må kompileres, men hovedforskjellen fra tradisjonelle språk er at Java blir kompilert ned til bytecode. Denne bytecoden er plattformuavhengig. Det vil si at den kan kjøres både på Windos, Unix, Linux, Mac OS X, BeOS osv. ved hjelp av en bytecodetolker. Under kan man se en figur over hvordan det ser ut.

Obj2.gif

Man ser her at hver enkel prosessorarkitektur og/eller operativsystem har sin unike JVM. JVM står for Java Virtuel Machine. Det er denne maskinene som oversetter fra Java bytecode til nuller og ett-tall (0100101010100) som maskinene du sitter på skjønner.

Kompilering og kjøring i praksis

Det finnes to hovedkompilatorer for Java. Den ene er laget av Sun selv og heter javac og den andre heter jikes og er laget av IBM. Jeg kommer i denne veiledning til å bruke javac, siden det er den som blir brukt i kurs her på UiO. Hovedforskjellen mellom disse to kompilatorene er at jikes har høyere ytelse og er Open Source. Her kan du lese mer om jikes. http://oss.software.ibm.com/developerworks/opensource/jikes/

Så tilbake til Sun sin javac. Jeg skal nå vise et par eksempler på hvordan man kompilere små programmer i java og hvordan man kan finne kompileringsfeil. Vi bruker selvfølgelig det klassiske «Hello World!»-programmet.


/** 
 * The HelloWorldApp class implements an application that
 * simply displays "Hello World!" to the standard output.
 */
class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello World!"); //Display the string.
    }
}

Vi skal nå kompilere hele dette programmet. Siden det er et javaprogram det er snakk om, må navnet på fila ha samme navn som den klassen main() ligger i. Filen vi skal kompilere vil da hete HelloWorldApp.java. For å kompilere filen skriver du følgende kommando i et terminalvindu:

isolde: ~-1>javac HelloWorldApp.java
isolde: ~-1>

Hvis kompileringen var feilfri(Dvs. at ingen feilmelding er kommet opp i terminalvinduet) så er det nå blitt laget en ny fil med navn HelloWorldApp.class Den nye filen inneholder bytecoden. For å kjøre programmet skriver du følgende inn i terminalvinduet:

isolde: ~-1>java HelloWorldApp
Hello World!
isolde: ~-1>

Som du ser er nå programmet blitt kjørt av JVM.

Kompileringsfeil

I tillegg til at ”javac” programmet oversetter kildekoden til bytekode, så sjekker den at kildekoden er syntaktisk korrekt.(Syntaks er regler for hvordan en kan lage setninger el. deler av setninger på et gitt språk) Hvis kildekoden ikke er syntaktisk korrekt vil du få en melding om dette, samt at kompileringen vil stoppe opp. La oss legge inn en feil i HelloWorldApp.java-programmet og se hva som skjer når vi kompilerer.

/** 
 * The HelloWorldApp class implements an application that@]\\
 * simply displays "Hello World!" to the standard output.@]\\
 */
class HelloWorldApp {
    public static void main(String[] args) {
        System.println("Hello World!"); //Display the string.
    }   //    +- Her mangler out.
}

Vi skal nå se hva vår kompilator sier da:

isolde: ~-1> javac HelloWorldApp.java
HelloWorldApp.java:6: cannot resolve symbol
symbol  : method println (java.lang.String)
location: class java.lang.System
    System.println("Hello World!"); //Display the string.
          ^
1 error

Som vi ser så sier kompilatoren i fra om at noe er galt. 6-tallet refererer til hvilken linje feilen befinner seg på. Dette er til stor hjelp når man feilsøker programmer. I større programmer eller programmer med veldig mange feil, er det ikke alltid kompilatoren klarer å gi så presise feilmeldinger.

Ubalanserte parenteser (eksempel {{}, {{, {{}{, osv er alle ubalanserte, fordi det ikke er like mange høyreparenteser som venstreparenteser.), samt manglende semikolon på slutten av en linje kan ofte være opphav til flere upresise og misvisende feilmeldinger. Det er derfor en god regel og alltid sjekke dette når man ikke forstår en feil. (Tips: Bruk linjeskift og innrykk på en konsekvent måte som gjør det lett å se slike feil.)

Kjøretidsfeil

Selv om du får kompilert, kan programmet fortsatt inneholde en god del feil som først oppdages ved kjøring. (Dvs. når du gir kommandoen ”java [=HelloWorldApp=]”). Disse feilene er ofte mindre informative og dermed vanskeligere å tolke en kompileringsfeil. Vi tar en titt på noen av de vanligste kjøretidsfeilene. La oss ta utgangspunkt i følgende program:

 1: /* The HelloWorldApp class implements an application that
 2:  simply displays "Hello World!" to the standard output.
 3:  */
 4: class HelloWorldApp {
 5:     public static void main(String[] args) {
 6:
 7:         int i = 0;
 8:         String ord[] = new String[4];
 9:
10:         // Fill array with information
11:         ord[0] = new String("Hello ");
12:         ord[1] = new String("World");
13:         ord[2] = "!"; // This is just a shorthand notation 
14:                       // for 'new String "!"; '
15:
16:         // Print the number of characters in ord[1]
17:         System.out.println(ord[1].length());
18:  
19:         // Print the ord array
20:         for(; i < 4; i++){
21:             System.out.println(ord[i]); //Display the string.
22:         }
23:     }
24: }


Programmet kompilerer og kjører uten problemer i sin nåværende form:

isolde: ~-1> javac HelloWorldApp.java
isolde: ~-1> java HelloWorldApp
5
Hello
World
!
null
isolde: ~-1>

La oss så fremprovosere noen kjøretidsfeil:

ArrayIndexOutOfBoundsException

Ved å endre linje 15 til ”for(;i < 5;i++)” får vi følgende feil:

isolde: ~-1> javac HelloWorldApp.java
isolde: ~-1> java HelloWorldApp
5
Hello
World
!
null
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4
        at HelloWorldApp.main(HelloWorldApp.java:21)
isolde: ~-1>

Feilen angir at en det er blitt referert til en array med en ugyldig indeksverdi. Indeksen er enten negativ eller større eller lik størrelsen av arrayen. Tallet 4 i feilmeldingen angir den ugyldige indeksverdien og tallet 21 angir hvilken linje i HelloWorldApp.java som fremprovoserte feilen. Vi vet altså at det var ”System.out.println(ord[4]);” som er kilden til feilen, og det er ikke så vanskelig å forstå at ord[4] refererer til en plassering i arrayen som ikke finnes. Arrayen ble via linje 8 gitt en størrelse på 4 elementer, som tilsvarer at ord[0], ord[1], ord[2] og ord[3] er gyldige referanser.(NB! Det er lett å glemme/overse at 4 egentlig refererer til element 5 siden tellingen av elementer i arrayen starter med 0.)


NullPointerException

Ved å endre linje 17 til ”System.out.println(ord[3].length());” får vi følgende feil:

isolde: ~-1> javac HelloWorldApp.java
isolde: ~-1>  java HelloWorldApp
Exception in thread "main" java.lang.NullPointerException
        at HelloWorldApp.main(HelloWorldApp.java:17)
isolde: ~-1>

Feilen angir at det er gjort et forsøk på å bruke ”null” i et tilfelle hvor et objekt var påkrevd. Tallet 17 angir hvilken linje som fremprovoserte feilen. For å forstå denne feilen er det nødvendig å forstå pekere og objekter.

Linje 8 medfører at det blir laget en array med plass til 4 pekere. Det er altså ikke meningen at arrayen skal inneholde selve teksten, noe som enn lett kan tro at skulle være tilfellet ved å se på linjene 11-13. Hvis ikke selve arrayen inneholder tekstene, hva er det da som skjer, hvor blir tekstene av?

Arrayen kan sammenlignes med et kartotek for et bibliotek. Når biblioteket mottar en tekst/bok så legges ikke selve boken inn i kartoteket, men de finner en ledig hylleplass og noterer hyllenummer og seksjon der boken blir plassert på et kartotekkort, og så legges kortet i kartoteket.

Litt forenklet kan vi si at det er det samme som skjer i en PC. Linje 8 medfører i bibliotekverdenen at det opprettes et kartotek med fire blanke kartotekkort. Linjene 11-13 tilsvarer 3 bokleveranser til biblioteket. Når bøkene er registrert og plassert er 3 av de fire kortene i kartoteket fylt ut med plasseringsinformasjon.

Hvis vi trekker samme historie inn i PC-terminologi så skjer følgende: Linje 8 setter av 8 minneplasser hvor hver plass har mulighet for å lagre en adresse til en annen plassering i minne(en peker). Når vi utfører linjen 11 søker vi etter en ledig plass i minne til teksten ”Hello”. (et tekstobjekt.). Når så en stor nok ledig plass er funnet, noteres startadressen til denne ledige plassen i arrayplassen angitt av indeksen [i]. I linje 11 ser vi at startadressen skal lagres i arrayplassen 0. Tilsvarende ting skjer for linje 12 og 13. Når disse linjene er ferdig utført inneholder plassene 0, 1, 2 startadresseinformasjon til henholdsvis tekstene; ”Hello”, ”World” og ”!”. Den siste plassen (3) inneholder fortsatt ingen referanse til et objekt.

Det som mangler nå er å forstå hvorfor linje 17 feiler. I bibliotekverdenen tilsvarer linje 17 at en person kommer inn i biblioteket, og spør etter hvor mange sider boken som er registrert på kartotekkort nr 3 har. Når så bibliotekaren henter kort nr 3 finner han ut at kortet er blankt og må dessverre si at boken mangler.(NullPointerException) Så tilbake til PC terminologien. Linje 17 gir beskjed at datamaskinen skal lese adressen registrert i arrayens plass 3 og deretter gå til denne adressen, hente ut hvor lang teksten var, men som vi husker er det ikke blitt registrert noen adresse i plass 3. Det er altså på grunn av at det ikke eksisterer en peker i plass 3 til et objekt at vi får feilen NullPointerException. Løsningen kunne for eksempel ha vært å legge til følgende linje rett etter linje 13:


ord[3] = new String("Nå er alt i orden");

Ekstern informasjon

Hjemmesiden til Sun sin javac kompilator

Hvordan java programmering fungerer

Man side for java

Man side for javac

Man side for jikes

Personlige verktøy
Navnerom
Varianter
Handlinger
Navigasjon
Kategorier
Programvare
Andre
Translate
Verktøy