Zoeken…


Invoering

Dit onderwerp schetst enkele veelvoorkomende fouten die beginners op Java hebben gemaakt.

Dit omvat veel voorkomende fouten bij het gebruik van de Java-taal of het begrip van de runtime-omgeving.

Fouten in verband met specifieke API's kunnen worden beschreven in onderwerpen die specifiek zijn voor die API's. Strings zijn een speciaal geval; ze worden behandeld in de Java Language Specification. Andere details dan veelgemaakte fouten kunnen in dit onderwerp over tekenreeksen worden beschreven.

Valkuil: gebruik == om primitieve wrappers-objecten zoals Integer te vergelijken

(Deze valkuil is evenzeer van toepassing op alle primitieve wrapper-typen, maar we zullen het illustreren voor Integer en int .)

Wanneer u met Integer objecten werkt, is het verleidelijk om == te gebruiken om waarden te vergelijken, omdat u dat zou doen met int waarden. En in sommige gevallen lijkt dit te werken:

Integer int1_1 = Integer.valueOf("1");
Integer int1_2 = Integer.valueOf(1);

System.out.println("int1_1 == int1_2: " + (int1_1 == int1_2));          // true
System.out.println("int1_1 equals int1_2: " + int1_1.equals(int1_2));   // true

Hier hebben we twee Integer objecten met de waarde 1 en vergeleken (in dit geval hebben we er een gemaakt uit een String en een uit een int Letterlijk. Er zijn andere alternatieven). We zien ook dat de twee vergelijkingsmethoden ( == en equals ) beide true opleveren.

Dit gedrag verandert wanneer we verschillende waarden kiezen:

Integer int2_1 = Integer.valueOf("1000");
Integer int2_2 = Integer.valueOf(1000);

System.out.println("int2_1 == int2_2: " + (int2_1 == int2_2));          // false
System.out.println("int2_1 equals int2_2: " + int2_1.equals(int2_2));   // true

In dit geval levert alleen de vergelijking is equals het juiste resultaat.

De reden voor dit verschil in gedrag is dat de JVM een cache van Integer objecten onderhoudt voor het bereik -128 tot 127. (De bovenste waarde kan worden overschreven met de systeemeigenschap "java.lang.Integer.IntegerCache.high" of de JVM-argument "-XX: AutoBoxCacheMax = grootte"). Voor waarden in dit bereik retourneert Integer.valueOf() de waarde in het cachegeheugen in plaats van een nieuwe te maken.

Dus in het eerste voorbeeld retourneerden de aanroepen Integer.valueOf(1) en Integer.valueOf("1") dezelfde Integer instantie in de cache. In het tweede voorbeeld daarentegen hebben de Integer.valueOf(1000) en Integer.valueOf("1000") zowel nieuwe Integer objecten gemaakt als geretourneerd.

De operator == voor referentietypes voor referentiegelijkheid (dwz hetzelfde object). Daarom is in het eerste voorbeeld int1_1 == int1_2 true omdat de verwijzingen hetzelfde zijn. In het tweede voorbeeld is int2_1 == int2_2 onwaar omdat de referenties verschillen.

Valkuil: vergeten middelen vrij te maken

Telkens wanneer een programma een bron opent, zoals een bestand of netwerkverbinding, is het belangrijk om de bron vrij te maken zodra u klaar bent met het gebruik ervan. Soortgelijke voorzichtigheid is geboden als een uitzondering wordt gemaakt tijdens operaties met dergelijke middelen. Je zou kunnen stellen dat FileInputStream een finalizer heeft die de methode close() aanroept bij een gebeurtenis voor het verzamelen van afval; Omdat we echter niet zeker kunnen zijn wanneer een afvalinzamelcyclus wordt gestart, kan de invoerstroom computerbronnen voor onbepaalde tijd gebruiken. De bron moet in een gesloten finally gedeelte van een try-catch-blok:

Java SE 7
private static void printFileJava6() throws IOException {
    FileInputStream input;
    try {
        input = new FileInputStream("file.txt");
        int data = input.read();
        while (data != -1){
            System.out.print((char) data);
            data = input.read();
        }
    } finally {
        if (input != null) {
            input.close();
        }
    }
}

Sinds Java 7 is er een echt nuttige en nette verklaring geïntroduceerd in Java 7, met name voor dit geval, genaamd try-with-resources:

Java SE 7
private static void printFileJava7() throws IOException {
    try (FileInputStream input = new FileInputStream("file.txt")) {
        int data = input.read();
        while (data != -1){
            System.out.print((char) data);
            data = input.read();
        }
    }
}

De instructie try-with-resources kan worden gebruikt met elk object dat de interface Closeable of AutoCloseable implementeert. Het zorgt ervoor dat elke bron aan het einde van de instructie wordt afgesloten. Het verschil tussen de twee interfaces is dat de methode close() van Closeable een IOException Closeable die op de een of andere manier moet worden afgehandeld.

In gevallen waarin de bron al is geopend maar na gebruik veilig moet worden gesloten, kan deze worden toegewezen aan een lokale variabele in de try-with-resources

Java SE 7
private static void printFileJava7(InputStream extResource) throws IOException {
    try (InputStream input = extResource) {
        ... //access resource
    }
}

De lokale bronvariabele die is gemaakt in de try-with-resources-constructor is effectief definitief.

Valkuil: geheugenlekken

Java beheert geheugen automatisch. U hoeft het geheugen niet handmatig vrij te maken. Het geheugen van een object op de hoop kan worden vrijgegeven door een vuilnisman wanneer het object niet langer bereikbaar is met een levende draad.

U kunt echter voorkomen dat geheugen vrijkomt, door objecten bereikbaar te maken die niet langer nodig zijn. Of u dit nu een geheugenlek of geheugenverpakking noemt, het resultaat is hetzelfde - een onnodige toename van toegewezen geheugen.

Geheugenlekken in Java kunnen op verschillende manieren gebeuren, maar de meest voorkomende reden zijn eeuwige objectverwijzingen, omdat de vuilnisman geen objecten uit de heap kan verwijderen terwijl er nog steeds verwijzingen naar zijn.

Statische velden

Men kan een dergelijke verwijzing maken door klasse te definiëren met een static veld dat een verzameling objecten bevat, en te vergeten dat static veld op null nadat de verzameling niet langer nodig is. static velden worden beschouwd als GC-wortels en worden nooit verzameld. Een ander probleem zijn lekken in niet-heap-geheugen wanneer JNI wordt gebruikt.

Classloader-lek

Veruit het meest verraderlijke type geheugenlek is echter het classloader-lek . Een classloader bevat een verwijzing naar elke klasse die is geladen en elke klasse bevat een verwijzing naar de classloader. Elk object bevat ook een verwijzing naar zijn klasse. Daarom, als zelfs een enkel object van een klasse die door een classloader is geladen geen afval is, kan geen enkele klasse die die classloader heeft geladen, worden verzameld. Omdat elke klasse ook verwijst naar de statische velden, kunnen ze ook niet worden verzameld.

Accumulatielek Het voorbeeld van het accumulatielek kan er als volgt uitzien:

final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
final Deque<BigDecimal> numbers = new LinkedBlockingDeque<>();
final BigDecimal divisor = new BigDecimal(51);

scheduledExecutorService.scheduleAtFixedRate(() -> {
    BigDecimal number = numbers.peekLast();
    if (number != null && number.remainder(divisor).byteValue() == 0) {
        System.out.println("Number: " + number);
        System.out.println("Deque size: " + numbers.size());
    }
}, 10, 10, TimeUnit.MILLISECONDS);

scheduledExecutorService.scheduleAtFixedRate(() -> {
    numbers.add(new BigDecimal(System.currentTimeMillis()));
}, 10, 10, TimeUnit.MILLISECONDS);

try {
    scheduledExecutorService.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
    e.printStackTrace();
}

In dit voorbeeld worden twee geplande taken gemaakt. De eerste taak neemt het laatste nummer van een deque genaamd numbers , en, als het nummer deelbaar is door 51, wordt het nummer en de grootte van de deque afgedrukt. De tweede taak zet cijfers in de deque. Beide taken zijn gepland met een vaste snelheid en worden elke 10 ms uitgevoerd.

Als de code wordt uitgevoerd, ziet u dat de grootte van de deque permanent toeneemt. Hierdoor zal de deque uiteindelijk worden gevuld met objecten die al het beschikbare heap-geheugen verbruiken.

Om dit te voorkomen en tegelijkertijd de semantiek van dit programma te behouden, kunnen we een andere methode gebruiken om getallen uit de deque te nemen: pollLast . In tegenstelling tot de methode peekLast , retourneert pollLast het element en verwijdert het uit de deque, terwijl peekLast alleen het laatste element retourneert.

Valkuil: == gebruiken om strings te vergelijken

Een veelgemaakte fout voor Java-beginners is om de operator == gebruiken om te testen of twee tekenreeksen gelijk zijn. Bijvoorbeeld:

public class Hello {
    public static void main(String[] args) {
        if (args.length > 0) {
            if (args[0] == "hello") {
                System.out.println("Hello back to you");
            } else {
                System.out.println("Are you feeling grumpy today?");
            }
        }
    }
}

Het bovenstaande programma wordt verondersteld het eerste opdrachtregelargument te testen en verschillende berichten af te drukken wanneer dit niet het woord "hallo" is. Maar het probleem is dat het niet zal werken. Dat programma zal uitvoeren: "Voel je je vandaag chagrijnig?" ongeacht wat het eerste opdrachtregelargument is.

In dit specifieke geval wordt de String "hallo" in de stringpool geplaatst terwijl de String args [0] op de heap staat. Dit betekent dat er twee objecten zijn die hetzelfde letterlijke vertegenwoordigen, elk met zijn referentie. Aangezien == tests voor referenties, niet daadwerkelijke gelijkheid, zal de vergelijking meestal een fout opleveren. Dit betekent niet dat dit altijd zal gebeuren.

Wanneer u == gebruikt om tekenreeksen te testen, is wat u eigenlijk test of twee String objecten hetzelfde Java-object zijn. Helaas is dat niet wat stringgelijkheid betekent in Java. In feite is de juiste manier om tekenreeksen te testen de methode equals(Object) gebruiken. Voor een paar strings willen we meestal testen of ze uit dezelfde tekens in dezelfde volgorde bestaan.

public class Hello2 {
    public static void main(String[] args) {
        if (args.length > 0) {
            if (args[0].equals("hello")) {
                System.out.println("Hello back to you");
            } else {
                System.out.println("Are you feeling grumpy today?");
            }
        }
    }
}

Maar het wordt eigenlijk erger. Het probleem is dat == in sommige omstandigheden het verwachte antwoord zal geven. Bijvoorbeeld

public class Test1 {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "hello";
        if (s1 == s2) {
            System.out.println("same");
        } else {
            System.out.println("different");
        }
    }
}

Interessant is dat dit "hetzelfde" zal drukken, ook al testen we de snaren verkeerd. Waarom is dat? Omdat de Java-taalspecificatie (paragraaf 3.10.5: String Literals) bepaalt dat elke twee string >> literals << die uit dezelfde tekens bestaan, daadwerkelijk door hetzelfde Java-object worden vertegenwoordigd. Vandaar dat de test == waar geeft voor gelijke letterlijke waarden. (De letterlijke tekenreeksen worden "geïnterneerd" en toegevoegd aan een gedeelde "tekenreekspool" wanneer uw code wordt geladen, maar dat is eigenlijk een implementatiedetail.)

Om aan de verwarring toe te voegen, bepaalt de Java Language Specification ook dat wanneer je een compilatie-tijd constante uitdrukking hebt die twee string-literalen samenvoegt, dat equivalent is aan een enkele letterlijke. Dus:

    public class Test1 {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "hel" + "lo";
        String s3 = " mum";
        if (s1 == s2) {
            System.out.println("1. same");
        } else {
            System.out.println("1. different");
        }
        if (s1 + s3 == "hello mum") {
            System.out.println("2. same");
        } else {
            System.out.println("2. different");
        }
    }
}

Dit geeft "1. hetzelfde" en "2. verschillend" weer. In het eerste geval wordt de uitdrukking + geëvalueerd tijdens het compileren en vergelijken we één String object met zichzelf. In het tweede geval wordt het geëvalueerd tijdens de uitvoering en vergelijken we twee verschillende String objecten

Samenvattend, het gebruik van == om tekenreeksen in Java te testen is bijna altijd onjuist, maar het is niet gegarandeerd dat het een verkeerd antwoord geeft.

Valkuil: een bestand testen voordat u het probeert te openen.

Sommige mensen adviseren dat u verschillende tests op een bestand moet toepassen voordat u het probeert te openen, hetzij om een betere diagnose te bieden of om te gaan met uitzonderingen. Deze methode probeert bijvoorbeeld te controleren of het path overeenkomt met een leesbaar bestand:

public static File getValidatedFile(String path) throws IOException {
    File f = new File(path);
    if (!f.exists()) throw new IOException("Error: not found: " + path);
    if (!f.isFile()) throw new IOException("Error: Is a directory: " + path);
    if (!f.canRead()) throw new IOException("Error: cannot read file: " + path);
    return f;
}

U kunt de bovenstaande methode als volgt gebruiken:

File f = null;
try {
    f = getValidatedFile("somefile");
} catch (IOException ex) {
    System.err.println(ex.getMessage());
    return;
}
try (InputStream is = new FileInputStream(file)) {
    // Read data etc.
}

Het eerste probleem zit in de handtekening voor FileInputStream(File) omdat de compiler er nog steeds op staat dat we IOException hier vangen, of hoger op de stapel.

Het tweede probleem is dat controles die worden uitgevoerd door getValidatedFile niet garanderen dat de FileInputStream zal slagen.

  • Racevoorwaarden: een andere thread of een afzonderlijk proces kan het bestand hernoemen, het bestand verwijderen of leestoegang verwijderen nadat de getValidatedFile geretourneerd. Dat zou leiden tot een "gewone" IOException zonder het aangepaste bericht.

  • Er zijn randgevallen die niet onder die tests vallen. Op een systeem met SELinux in de modus "afdwingen" kan een poging om een bestand te lezen mislukken, ondanks dat canRead() true retourneert.

Het derde probleem is dat de tests niet efficiënt zijn. Bijvoorbeeld, het exists , isFile en canRead gesprekken zullen elk een syscall om de vereiste controle uit te voeren. Vervolgens wordt een ander syscall gemaakt om het bestand te openen, dat dezelfde controles achter de schermen herhaalt.

Kortom, methoden zoals getValidatedFile zijn misleidend. Het is beter om gewoon te proberen het bestand te openen en de uitzondering te verwerken:

try (InputStream is = new FileInputStream("somefile")) {
    // Read data etc.
} catch (IOException ex) {
    System.err.println("IO Error processing 'somefile': " + ex.getMessage());
    return;
}

Als u onderscheid wilt maken tussen IO-fouten die worden gegenereerd tijdens het openen en lezen, kunt u een geneste try / catch gebruiken. Als u betere diagnostiek voor open storingen wilde produceren, zou u de exists , isFile en canRead controles in de handler kunnen uitvoeren.

Valkuil: variabelen beschouwen als objecten

Geen Java-variabele vertegenwoordigt een object.

String foo;   // NOT AN OBJECT

Evenmin bevat een Java-array objecten.

String bar[] = new String[100];  // No member is an object.

Als u ten onrechte variabelen als objecten beschouwt, zal het feitelijke gedrag van de Java-taal u verbazen.

  • Voor Java-variabelen met een primitief type (zoals int of float ) bevat de variabele een kopie van de waarde. Alle kopieën van een primitieve waarde zijn niet te onderscheiden; dat wil zeggen dat er slechts één int waarde is voor de nummer één. Primitieve waarden zijn geen objecten en gedragen zich niet als objecten.

  • Voor Java-variabelen met een referentietype (een klasse of een arraytype) bevat de variabele een referentie. Alle exemplaren van een referentie zijn niet van elkaar te onderscheiden. Verwijzingen kunnen naar objecten verwijzen, of ze kunnen null wat betekent dat ze naar geen enkel object verwijzen. Het zijn echter geen objecten en ze gedragen zich niet als objecten.

Variabelen zijn in geen van beide gevallen objecten en bevatten in geen van beide gevallen objecten. Ze kunnen verwijzingen naar objecten bevatten, maar dat zegt iets anders.

Voorbeeldklasse

De voorbeelden die volgen gebruiken deze klasse, die een punt in 2D-ruimte vertegenwoordigt.

public final class MutableLocation {
   public int x;
   public int y;

   public MutableLocation(int x, int y) {
       this.x = x;
       this.y = y;
   }

   public boolean equals(Object other) {
       if (!(other instanceof MutableLocation) {
           return false;
       }
       MutableLocation that = (MutableLocation) other;
       return this.x == that.x && this.y == that.y;
   }
}

Een instantie van deze klasse is een object met twee velden x en y die het type int .

We kunnen veel instanties van de klasse MutableLocation . Sommige zullen dezelfde locaties in 2D-ruimte vertegenwoordigen; dat wil zeggen dat de respectieve waarden van x en y overeenkomen. Anderen zullen verschillende locaties vertegenwoordigen.

Meerdere variabelen kunnen naar hetzelfde object wijzen

 MutableLocation here = new MutableLocation(1, 2);
 MutableLocation there = here;
 MutableLocation elsewhere = new MutableLocation(1, 2);

In het bovenstaande hebben we here , there en elsewhere drie variabelen opgegeven die verwijzingen naar MutableLocation objecten kunnen bevatten.

Als je (ten onrechte) deze variabelen als objecten beschouwt, dan zul je de verklaringen waarschijnlijk verkeerd lezen door te zeggen:

  1. Kopieer de locatie "[1, 2]" naar here
  2. Kopieer de locatie "[1, 2]" naar there
  3. Kopieer de locatie "[1, 2]" naar een elsewhere

Daaruit zult u waarschijnlijk afleiden dat we drie onafhankelijke objecten in de drie variabelen hebben. In feite zijn er slechts twee objecten gemaakt door het bovenstaande. De variabelen here en there verwijzen eigenlijk naar hetzelfde object.

We kunnen dit aantonen. Uitgaande van de variabele aangiften zoals hierboven:

System.out.println("BEFORE: here.x is " + here.x + ", there.x is " + there.x +
                   "elsewhere.x is " + elsewhere.x);
here.x = 42;
System.out.println("AFTER: here.x is " + here.x + ", there.x is " + there.x +
                   "elsewhere.x is " + elsewhere.x);

Dit levert het volgende op:

BEFORE: here.x is 1, there.x is 1, elsewhere.x is 1
AFTER: here.x is 42, there.x is 42, elsewhere.x is 1

We hebben een nieuwe waarde aan here.x en deze heeft de waarde gewijzigd die we via there.x . Ze verwijzen naar hetzelfde object. Maar de waarde die we via elsewhere.x is niet gewijzigd, dus elsewhere moet naar een ander object verwijzen.

Als een variabele een object was, zou de toewijzing here.x = 42 daar niet veranderen. there.x

De operator voor gelijkheid test NIET dat twee objecten gelijk zijn

De operator gelijkheid ( == ) toepassen op referentiewaardetests als de waarden naar hetzelfde object verwijzen. Het test niet of twee (verschillende) objecten "intuïtief" zijn.

 MutableLocation here = new MutableLocation(1, 2);
 MutableLocation there = here;
 MutableLocation elsewhere = new MutableLocation(1, 2);

 if (here == there) {
     System.out.println("here is there");
 }
 if (here == elsewhere) {
     System.out.println("here is elsewhere");
 }

Dit zal afdrukken "hier is daar", maar het zal niet afdrukken "hier is elders". (De verwijzingen here en elsewhere zijn voor twee verschillende objecten.)

Als we daarentegen de methode equals(Object) gebruiken die we hierboven hebben geïmplementeerd, gaan we testen of twee MutableLocation instanties een gelijke locatie hebben.

 if (here.equals(there)) {
     System.out.println("here equals there");
 }
 if (here.equals(elsewhere)) {
     System.out.println("here equals elsewhere");
 }

Hiermee worden beide berichten afgedrukt. In het bijzonder here.equals(elsewhere) true omdat is voldaan aan de semantische criteria die we hebben gekozen voor gelijkheid van twee MutableLocation objecten.

Methodeaanroepen geven helemaal geen objecten door

Java-methode-aanroepen gebruiken pass by value 1 om argumenten door te geven en een resultaat terug te geven.

Wanneer u een referentiewaarde doorgeeft aan een methode, geeft u in feite een verwijzing door aan een object op waarde , wat betekent dat het een kopie van de objectreferentie maakt.

Zolang beide objectreferenties nog steeds naar hetzelfde object verwijzen, kunt u dat object vanuit beide referenties wijzigen, en dit is voor sommigen verwarring.

U bent echter niet een object langs referentie 2. Het onderscheid is dat als de objectreferentie-kopie wordt gewijzigd om naar een ander object te wijzen, de oorspronkelijke objectreferentie nog steeds naar het oorspronkelijke object verwijst.

void f(MutableLocation foo) {  
    foo = new MutableLocation(3, 4);   // Point local foo at a different object.
}

void g() {
    MutableLocation foo = MutableLocation(1, 2);
    f(foo);
    System.out.println("foo.x is " + foo.x); // Prints "foo.x is 1".
}

U geeft ook geen kopie van het object door.

void f(MutableLocation foo) {  
    foo.x = 42;
}

void g() {
    MutableLocation foo = new MutableLocation(0, 0);
    f(foo);
    System.out.println("foo.x is " + foo.x); // Prints "foo.x is 42"
}

1 - In talen zoals Python en Ruby heeft de term "pass by sharing" de voorkeur voor "pass by value" van een object / referentie.

2 - De term "pass by reference" of "call by reference" heeft een zeer specifieke betekenis in de terminologie van de programmeertaal. Dit betekent in feite dat u het adres van een variabele of een array-element doorgeeft, zodat wanneer de aangeroepen methode een nieuwe waarde aan het formele argument toewijst, de waarde in de oorspronkelijke variabele wordt gewijzigd. Java ondersteunt dit niet. Raadpleeg https://en.wikipedia.org/wiki/Evaluation_strategy voor een meer fulsome beschrijving van verschillende mechanismen voor het doorgeven van parameters.

Valkuil: combinatie van opdracht en bijwerkingen

Af en toe zien we StackOverflow Java-vragen (en C- of C ++ -vragen) die vragen over zoiets als dit:

i += a[i++] + b[i--];

evalueert tot ... voor enkele bekende begintoestanden van i , a en b .

In het algemeen:

  • voor Java is het antwoord altijd gespecificeerd 1 , maar niet voor de hand liggend en vaak moeilijk te achterhalen
  • voor C en C ++ is het antwoord vaak niet gespecificeerd.

Dergelijke voorbeelden worden vaak gebruikt in examens of sollicitatiegesprekken als een poging om te zien of de student of geïnterviewde begrijpt hoe expressie-evaluatie echt werkt in de programmeertaal Java. Dit is aantoonbaar legitiem als een "test van kennis", maar dat betekent niet dat je dit ooit in een echt programma zou moeten doen.

Ter illustratie is het volgende schijnbaar eenvoudige voorbeeld een paar keer verschenen in StackOverflow-vragen (zoals deze ). In sommige gevallen lijkt het een echte fout in iemands code.

int a = 1;
a = a++;
System.out.println(a);    // What does this print.

De meeste programmeurs (inclusief Java-experts) die deze verklaringen snel lezen , zouden zeggen dat het 2 uitvoert. In feite geeft het 1 . Lees dit antwoord voor een gedetailleerde uitleg waarom.

Het echte voordeel van deze en vergelijkbare voorbeelden is echter dat elke Java-instructie die zowel dezelfde variabele toewijst als bijwerkingen heeft, op zijn best moeilijk te begrijpen en in het slechtste geval ronduit misleidend zal zijn. Je moet voorkomen dat je code zoals deze schrijft.


1 - Modulo potentiële problemen met het Java-geheugenmodel als de variabelen of objecten zichtbaar zijn voor andere threads.

Valkuil: niet begrijpen dat String een onveranderlijke klasse is

Nieuwe Java-programmeurs vergeten vaak of begrijpen niet volledig dat de Java String klasse onveranderlijk is. Dit leidt tot problemen zoals die in het volgende voorbeeld:

public class Shout {
    public static void main(String[] args) {
        for (String s : args) {
            s.toUpperCase();
            System.out.print(s);
            System.out.print(" ");
        }
        System.out.println();
    }
}

De bovenstaande code zou opdrachtregelargumenten in hoofdletters moeten afdrukken. Helaas werkt het niet, de argumenten zijn niet gewijzigd. Het probleem is deze verklaring:

s.toUpperCase();

Je zou denken dat het aanroepen toUpperCase() s zal veranderen in een hoofdletterreeks. Dat doet het niet. Het kan niet! String zijn onveranderlijk. Ze kunnen niet worden gewijzigd.

In werkelijkheid retourneert de methode toUpperCase() een String object dat een toUpperCase() is van de String waarop u het toUpperCase() . Dit zal waarschijnlijk een nieuw String object zijn, maar als s al hoofdletters was, zou het resultaat de bestaande string kunnen zijn.

Dus om deze methode effectief te gebruiken, moet u het object gebruiken dat wordt geretourneerd door de methode-aanroep; bijvoorbeeld:

s = s.toUpperCase();

De regel "strings verandert nooit" is zelfs van toepassing op alle String methoden. Als je je dat herinnert, kun je een hele categorie fouten voor beginners vermijden.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow