4.4. Tekststrenge (klassen String)

Figur 4-9. s refererer ikke til noget

Vi kommer nu til de mest brugte objekter, nemlig tekststrenge (af typen String). En variabel, der refererer til strenge erklæres ved at skrive


String s;

Nu har vi defineret, at s er en variabel, der kan referere til objekter af typen String, men den refererer endnu ikke til nogen konkret streng. Lad os tildele s en værdi:

Figur 4-10. s refererer nu til en streng


s = "Ude godt";

Nu er situationen som vist på figuren til højre. Vi kan bruge s i vores program, f.eks. til at skrive ud på skærmen:


System.out.println("Strengen s indeholder: "+s);

Nu kan vi spørge streng-objektet om forskellige ting. For eksempel kan vi kalde metoden length(), der svarer til spørgsmålet "hvor lang er du?". Strengen vil svare med tallet 8:


...
int strengensLængde;
strengensLængde = s.length();
System.out.println("s er "+strengensLængde+" tegn lang");
...

s er 8 tegn lang

Vi kunne også springe mellemvariablen over og skrive:


System.out.println("s er "+s.length()+" tegn lang");

Metoden toUpperCase() svarer til spørgsmålet "hvordan ser du ud med store bogstaver?":


System.out.println("s med store bogstaver: "+s.toUpperCase());

s med store bogstaver: UDE GODT

Herunder ses nogle af metoderne, man kan kalde på strenge. I kursiv til højre står spørgsmålene, som de svarer til.

Nogle af String-klassens metoder. En mere fuldstændig oversigt kan findes i Afsnit 4.7.3.

char charAt (int indeks) "hvilket tegn er der på plads nummer x?"Returnerer tegnet på det angivne indeks. Indeks tæller fra 0.

String replace (char gammeltTegn, char nytTegn) "hvad hvis tegn x erstattes med y?"Returnerer en ny streng, som er identisk med denne streng, bortset fra at alle forekomster af gammeltTegn er erstattet med nytTegn.

String substring (int startindeks) "hvad er delstrengen fra x?"Returnerer en ny streng, som er en del af denne streng. Delstrengen starter ved startindeks og går til slutningen.

String substring (int startindeks, int slutindeks) "hvad er delstrengen fra x til y?"Returnerer en ny streng, som er en del af denne streng. Delstrengen starter ved startindeks og slutter ved slutindeks (til og med slutindeks-1).

String toLowerCase () "hvordan ser du ud med små bogstaver?"Returnerer en ny streng, som er identisk med denne streng, bortset fra at alle store bogstaver er erstattet med små.

String toUpperCase () "hvordan ser du ud med store bogstaver?"Returnerer en ny streng, som er identisk med denne streng, bortset fra at alle små bogstaver er erstattet med store.

boolean equals (String str) "er det samme indhold?"Returnerer sand, hvis denne streng indeholder den samme tegnsekvens som str, ellers falsk.

int length () "hvad er din længde?"Returnerer længden af (antal tegn i) strengen.

int indexOf (String str) "hvor er delstrengen x?"Returnerer indekset på den første forekomst af str som delstreng. Hvis str ikke er en delstreng, returneres -1.

Herunder ses et eksempel, hvor nogle af metoderne er afprøvet:


// Strengeleg.java
// Viser brugen af String-klassen og dens metoder.
public class Strengeleg
{
  public static void main(String[] args)
  {
    String s;
    s = "Ude godt";
    System.out.println("Strengen s indeholder: "+s);
    System.out.println("s er "+s.length()+" tegn lang");
    System.out.println("s med store bogstaver: "+s.toUpperCase());
    System.out.println("Tegnet på plads nummer 2 er: "+s.charAt(2));
    System.out.println("Det første g er på plads nummer: "+s.indexOf("g"));
  }
}

Strengen s indeholder: Ude godt
s er 8 tegn lang s med store bogstaver: UDE GODT
Tegnet på plads nummer 2 er: e
Det første g er på plads nummer: 4

4.4.1. Strenge er uforanderlige

De fleste objekter tillader, at deres data ændres, enten ved at man direkte har adgang til deres variabler eller gennem kald af metoder. String-objekter er derimod indrettet sådan, at når de først er oprettet, så kan de ikke ændres (det giver Java mulighed for at spare hukommelse ved at slå streng-objekter med fælles indhold sammen til én streng). I stedet for at ændre indholdet af strengen returnerer String-objekters metoder altid en anden streng, som er resultatet af ændringen.

Når vi skal ændre i et Point-objekt, f.eks. så dets x og y er (1,1), skriver vi:


  p.move(1,1);              // p forandres

Kalder man derimod en metode på et String-objekt, bliver den ikke ændret:


  s.replace('d','f');      // s forandres ikke

replace()-metoden giver en ny streng tilbage til os, hvor alle 'd'-tegn er erstattet med 'f', men den bliver smidt væk med det samme, da vi ikke bruger returværdien. I stedet kunne vi skrive:


  String s2;
  s2 = s.replace('d','f');  // s forandres ikke, men s2 husker resultatet

Nu bliver resultat-strengen gemt vha. s2 (s er som sagt uforandret).

Her ses samlet et eksempel på strenges uforanderlighed:


// Strengeleg2.java
public class Strengeleg2
{
  public static void main (String[] args)
  {
    String s1;
    String s2;
    String s3;
    String s4;

    s1 = "Ude godt, men hjemme bedst.";
    s2 = s1.toUpperCase();          // kald toUpperCase() på s1
    s3 = s2.replace('E', 'X');        // kald replace() på s2
    s4 = s3.substring(4, 16);        // kald substring() på s3

    System.out.println ("s1: " + s1);  // s1 er uændret af toUpperCase()-kald
    System.out.println ("s2: " + s2);  // s2 er uændret af replace()-kaldet
    System.out.println ("s3: " + s3);  // s3 er uændret af s3.substring(4, 20)
    System.out.println ("s4: " + s4);  // s4 er resultatet af substring()-kaldet
 }
}

s1: Ude godt, men hjemme bedst.
s2: UDE GODT, MEN HJEMME BEDST.
s3: UDX GODT, MXN HJXMMX BXDST.
s4: GODT, MXN HJ

Variablerne s1, s2, s3 og s4 får tildelt en reference til hvert sit strengobjekt, og derefter ændrer deres indhold sig ikke, uanset hvilke metoder der kaldes på objekterne.

Bemærk, at selvom streng-objekterne i sig selv er uforanderlige, kan streng-variablerne godt ændres:


  s = s.replace('d','f');  // sæt s til at referere resultatet af replace()

Forskellen mellem en metode, der ændrer på det objekt, den bliver kaldt på og en metode, der returnerer en værdi, kan være svær at forstå i starten, men det kommer i takt med, at du programmerer selv.

4.4.2. Man behøver ikke bruge new til String-objekter

De andre klasser, vi har set indtil nu, har vi brugt til at skabe nye objekter med. Når vi skulle lave et nyt Point-objekt, kaldte vi dens konstruktør vha. new, f.eks.:


  Point p;
  p = new Point(0,0);

Lige netop med strenge behøves det ikke. Her skriver man typisk:


  String s;
  s = "Ude godt";

Man kan godt skrive:


  s = new String("Ude godt");

I det sidste tilfælde skabes et nyt String-objekt, som også indeholder teksten "Ude godt", så der i lageret er to strenge med samme indhold, hvilket er unødvendigt. Netop fordi strenge ikke kan ændres, når de først er skabt, har man aldrig brug for kopier. Hvorfor skulle man lave en kopi, der altid vil være helt den samme som originalen?

4.4.3. Navnesammenfald for metoder

I tabellen over Strings metoder er der en, der er nævnt to gange; substring(). Den findes i to varianter: substring(int startindeks) og substring(int startindeks, int slutindeks).

Hvilken variant der kaldes i Strengeleg2.java ved tildelingen af s4 kan man se ud fra, hvilke parameterlister der passer sammen. I dette tilfælde den metode med to parametre.

Så længe der er forskel på antallet af parametre, er det simpelt nok, ellers må man kigge på typerne af parametrene.

4.4.4. At sætte strenge sammen med +

Operatoren + bruges ikke kun til at lægge tal sammen. Hvis enten højre- eller venstre-siden er en streng, bliver + opfattet som: "konverter begge sider til strenge, og sæt dem i forlængelse af hinanden til en samlet streng".

Hvis man f.eks. skriver:


  Point p;
  p=new Point(1,1);
  System.out.println("Svaret er: "+p);

Svaret er: java.awt.Point[x=1,y=1]

Sker der i computeren nogenlunde følgende:


  String s1;
  String s2;
  String s3;
  s1 = "Svaret er: ";
  s2 = p.toString();  // toString() er en metode alle objekter har
  s3 = s1 + s2;
  System.out.println(s3);

toString() laver en streng-repræsentation af et objekt. Alle objekter har en toString()-metode, og oversætteren sætter kode ind, der kalder toString(), hvis den møder et + mellem en streng og en anden slags objekt.

Alle de simple typer kan også laves om til strenge med +:


  int i;
  i = 42;
  System.out.println("Svaret er: "+i);

Java kigger ikke på indholdet af strengene, så "2" (som streng) + 3 (som tal) giver "23" (som streng). Man kan altså bruge operatoren + til et lille trick: For at få noget repræsenteret som en streng kan man sammensætte det med en tom streng:


  String s;
  int i;
  i=42;
  s=""+i; // nu refererer s til strengen "42"

Man kan derimod ikke skrive:


  s=i; // sprogfejl: konverterer ikke automatisk fra int til String. 

...eller...


  i=s+1; // sprogfejl: konverterer ikke automatisk fra String til int.

... selvom s er "42".

4.4.5. Sammenligning

Umiddelbart kunne man fristes til at sammenligne to strenge med == ligesom med de simple typer. Det går ofte (men ikke altid) godt:


    s1 = "Hej verden";
    s2 = s1;
    if (s1 == s2) System.out.println("s1 og s2 er ens."); // forkert!
    else System.out.println("s1 og s2 er IKKE ens.");

s1 og s2 er ens.

Imidlertid sammenligner == referencerne til (adresserne på) objekterne, ikke indholdet af dem. Sammenligningen s1==s2 går godt fordi s1 og s2 refererer til samme objekt.

Derfor vil det gå galt hvis s1 og s2 refererer til to objekter forskellige steder i hukommelsen, selvom de har samme indhold:


    s1 = "Hej verden";
    s2 = "Hej "+"verden";
    if (s1 == s2) System.out.println("s1 og s2 er ens."); // forkert!
    else System.out.println("s1 og s2 er IKKE ens.");

s1 og s2 er IKKE ens.

I stedet bør man kalde equals()-metoden, dvs. spørge et af objekterne "har du samme indhold som dette objekt?" og give det andet objekt som parameter:


    if (s1.equals(s2)) System.out.println("s1 og s2 er ens."); // korrekt
    else System.out.println("s1 og s2 er IKKE ens.");

s1 og s2 er ens.

Dette gælder i virkeligheden ikke kun strenge. Alle objekter har en equals()-metode, som kan bruges til at afgøre, om to objekter er ens, og den bør man bruge i stedet for ==.

Sammenligning af adresser på objekter sker med ==

Sammenligning af objekters indhold sker med equals()-metoden

4.4.6. Opgaver

  1. Skriv et program, der finder positionen af det første mellemrum i en streng (Vink: Brug metoden indexOf(" ")).

  2. Skriv et program, der fjerner det første ord i en sætning (indtil første mellemrum).

  3. Skriv et program, der finder og fjerner alle forekomster af ordet "måske" fra en tekst.

  4. Skriv et program, der finder og fjerner alle forekomster af ordet "måske" fra en tekst, uanset om det er skrevet med store eller små bogstaver.

  5. Skriv et program, der tæller antallet af kommaer i en tekst.

  6. Skriv et program, der undersøger, om en tekst er et palindrom, dvs. med samme stavning forfra og bagfra (som f.eks. "regninger", "russerdressur", "vær dog god ræv").(vink: træk de enkelte tegn ud af strengene med substring(n,n+1) eller med charAt(n), som er beskrevet i apendikset, og kræver at du også bruger variabler af typen char).

  7. Udvid programmet til at tage højde for store/små bogstaver, tegnsætning og mellemrum, sådan at de følgende palindromer også genkendes: "Selmas lakserøde garagedøre skal samles" og "Åge lo, da baronesse Nora bad Ole gå".