Make your own free website on Tripod.com
String'lere dair

Stringler ile ilgili yaptigim aci bir hata nedeniyle bu yaziyi yazma ihtiyaci hissettim. bu bilgileri cogu yerde bulabilirisiniz, ama aralarda bir kac ilginc nokta oldugunu saniyorum.

String, malumunuz Java'nin ve genel olarak tum dillerin icinde ozel yeri olan bir kavramdir. String temelde verinin insanin anlayabilecegi ya da gorebilecgi hale donusturulmesi amaci ile kullinilan ozel bir veri ifade bicimidir. Bir harf, kelime, cumle hatta koca bir kitap tek bir string ile ifade edilebilir. En basit anlamiyla string, sonlu karakter dizisi olarak tarif edilebilir. C dilinde stringleri ifade etmek icin herhangi bir ayri ifade yerine char turunden isaretciye sonunda "null" bulunan bir dizi kullanilir. Bu kullanimda hiz ve bellek acisindan avantaj saglasa da programcilik acisindan kullaniciya hic bir standart erisim, degistirme imkani saglamadigi gibi hataya son derece meyilli kod yazimina neden olur. Ayrica C Stringleri sadece karakterlerden olusabilir ve eger karakterleriniz (mesela cince harfler) ASCII disi ise pek cok takla atmaniz gerekir.

Bu nedenle Java'da performanstan fedakarlik edilip uygulamanin yonetilebilirligini arttirmak icin Stringler birinci sinif nesne olarak tasarlanmistir. Bu nesnenin adi da haliyle "String" dir. Stringler kendi iclerinde char  cinsinden bir dizi, dizinin boyunu belirten bir integer ve simdi bahsetmeyecegim baska uc int deger daha  tasirlar.  Ancak Stringler kod icinde cok sIklikla kullanildiklarindan sirf onlara ozel bazi kolayliklar saglanmistir. Bu yazida hem baslangic, hem de orta seviye diyebilecegim sekilde Stringler'e iliskin bir kac ufak bilgi vermeye calisacagim. yanlislari lutfen bildirin.

Stringlerin olusturulmasi:

Stringleri tanimlamanin cesitli yontemleri vardir. Bunlardan en kolayi su sekildedir:

String isim = "Ahmet";

Bu sekil bir atama sadece String'lere hastir.  Java derleyicisi bu sekil bir satir gorunce icerigi "Ahmet" olan bir String nesnesi olusturacak kodu uretir. Baska bir yontem ise madem String bir nesne deyip String constructor'u kullanmaktir.

String isim = new String("Ahmet");

Burada ilk uyari geliyor, ikinci yontemi kullanmamayi tercih edin! Bunun nedenini birazdan aciklayacagim. Ama devam edelim. String atama ile ilgili ozellikler,

String isim;


Bu durumda bir nesne referansi olusturulur. Ama bu referans null degerine sahiptir. Eger uzerinde herhangi bir islem yapmaya kalkarsaniz derleyiciniz sizi uyarir, ornegin:

String isim;
char ilkharf = isim.charAt(0);

Eger Eclipse ya da Idea benzeri ileri seviye bir yazilim gelistirme araci kullaniyorsaniz daha siz uygulamanizi derlemeden yazilim sizi "isim might not have been initialized" seklinde uyarir. notepad kullanan talihsizlerdenseniz bunu hata olarak derleme sirasinda gorursunuz.
Cunku hicbir nesneyi gostermeyen bir referans uzerinden islem yapmaya calisiyorsunuz.  (C++'ta ise benzeri hata uyarilari dikkate elmazsaniz calisma aninda patlar..)

String isim="";

Burada ise durum farkli, bu kod satiri calistirildiginda gercekten isim Stringi olusturulur icindeki char dizisinde veri yer almaz, ama uzerinde islem yapilabilir. mesela boyunu ogrenmek isterseniz "0" cevabini alirsiniz.
Java stringleri Unicode kodlanmistir. iclerindeki Stringi olusturan char tipindeki veriler 16 bitliktir ve asagi yukari muhtemel tum alfabeleri icerisinde barindirir. Bu ozel verieleri kod icinde ya da disaridaki kaynaklarda tanimlamak isterseniz (Mesela turkce karakterleri) su sekilde ifade edebilirsiniz (Not: konsolda turkce cikis vermeyebilir cunku konsol isletim sisteminin o anki kodlamasini kullanir.)

//"siraci ve bozaci", turkce karakterler ile.
String isim = "\u015f\u0131rac\u0131 ve bozac\u0131";

Yani unicode karakterleri \uxxxx seklinde belirtebiliyoruz.

Atama, kiyaslama

En basit atama referans atamasi seklindedir.

        String isimA = "Ahmet";
        String isimB = isimA;
        System.out.println("IsimB=" + isimB);

Bu durumda ekranda IsimB="Ahmet" yazdigini goreceksiniz. Cunku IsimB referansi'na IsimA referansi esitlenmistir. Artik her ikisi de ayni nesneyi, yani icerigi "Ahmet" olan String nesnesini gosteriyordur.

Simdi esitlik testi. Sadece Java'ya yeni baslayanlarin degil her seviyden yazilimcinin ara ara dustukleri en bilindik hata Nesne, ozellikle String esitlik denetimidir.

String isimA = "Ahmet"
String isimB = new String("Ahmet");
if(isimA == isimB)
  System.out.println("isimler ayni");
else
  System.out.println("isimler farkli");


Kodu calistirdiginizda ekranda "isimler farkli" yazdigini goreceksiniz. Oysa her ikisi de "Ahmet" kelimesini gosteriyor. Tabiki hata basit, "==" islemi sadece ilkel degiskenler ve nesne referanslarinin ayni olup olmadigini kontrol eder. Nesnelerin ayni olup olmadigini degil!! Yukaridaki kod isimA ve isimB icin iki ayri nesne olusturur. isimA ve isimB farkli referans degerlerine sahiptir. Bu sekilde if satiri sadece referanslarin ayni olmadigini denetler ve ayni olmadiklarindan esit degil sonucunu verir. Nesnelerin iceriklerini kiyaslamak icin tum nesnelerin Object nesnesinden kalitim ile aldiklari "equals()" metodu kullanilir. Kodda ucuncu satiri asagidaki sekilde degistirince "isimler ayni" cevabini alacagiz.
..
if(isimA.equals(isimB))
..

Ama bu kadar kolay degil, kodu equal olmadan ama asagidaki gibi yazip calistirirsaniz (ikinci nesneyi new olmadan olusturup)

String isimA = "Ahmet";
String isimB = "Ahmet";
if(isimA == isimB)
  System.out.println("isimler ayni");


Sistemin "Isimler Ayni" cevabini verecektir. Oysa az evvel iki ayri nesne olusturuldugundan ve referanslarin farkli oldugundan dem vuruyorduk. Bunun nedeni bir java sanal makinesi ic optimizasyonu, eger ayni skop icinde ( mesela ayni metod ya da blok  icinde) Ayni String degeri dogrudan esitleme ile birden fazla referansa atanilirsa String nesne olusturma ve yer masrafina neden olmamak icin tek nesne olusturulur ve referanslar bu ayni nesneyi gosterdiginden ayni degere sahip olur, haliyle tehlikeli ve yanlis esitleme kodumuz sans eseri dogru calisir. Dikkat.

Not: == kullanimi yanlis diye bir sey yok, yeri geldiginde nesne referanslarinin esitligi de kontrol edilir. String.equals() kodunu incelerseniz ilk yapilan islemin referans esitliginin testi oldugu gorulur. Ayrica bazi durumlarda, ornegin sinif icinde static final tanimlanan Stringlerin esitlendigi String nesnelerinin de esitligi equals() yerine hiz acisindan dogrudan referans kiyaslamasi ile tespit edilebilir. Ornegin asagidaki ornegi inceleyin..

public class StringTest
{
    public static final String BASARI="BASARI";
    public static final String HATA="HATA";

    public static void main(String[] args)
    {
        String sonuc1 = StringTest.BASARI;
        String sonuc2 = StringTest.BASARI;
        if(sonuc1==sonuc2)
        {
            System.out.println("Sonuclar ayni..");
        }          
    }
}

Goruldugu gibi BASARI ve HATA Stringleri statik ve final tanimlanmis. Bunlarin referanslari ve icerikleri uygulamanin calismasi boyunca degismez. Bu nedenle equals() yerine == ile referans esitlik tercih edilebilir. Gene de garantiye almak icin nesnelerin ic esitligini test etmek icin equals() kullanmayi oneririm. Ayrica yukaridaki yontemde string yerine enum tipi yapilar da tercih edilebilir


Operator Overloading ve String'lerin degismezligi.

Java dilinde C++ ve C# dillerinde kullanilan nesneler icin operator overloading ozelligi yer almaz. Yani nesneler uzerinde +, - ==  gibi operatorleri kullanamazsiniz.  Java'yi tasarlayanlarin bu konuda gerekceleri var, ama bu konunun disinda.  Genede bunun javada bir istisnasi var, o da Stringler icin + islemi. Stringler icinde ulama (concatenation, append) islemi cok sik kullanildigi icin bu konuda bir istisna yapmaya gerek gormusler. Ornegin;

String isim = "Ahmet" + " Davul";


dedigimizde isim referansi icerigi "Ahmet Davul" olan bir String nesnesini gosterecektir. Simdi aci gercegi ifsa etmenin vakti geldi, Stringler icerigi degistirilemez (immutable) nesnelerdir. Takla atsaniz String Nesnesinin icindeki bir karakteri bile degistiremezsiniz. Asagidaki kodu inceleyelim.

String ad = "Ahmet";
String soyad = " Davul";
String ad = ad + soyad;

Islem sonunda ad nesnesi gercekten "Ahmet Davul" icerigine sahip bir String nesnesini gosterecektir ama arka planda su islem gerceklesir:

1- icerigi "Ahmet" olan bir String nesnesi olustur, nesne referansini ad'a ata.
2- icerigi "Davul" olan bir String nesnesi olustur, nesne referansini soyad'a ada..
3- icerigi ad'in icerigi olan "Ahmet", soyad'in icerigi olan "Davut"'u al, bunun birlesimi olan icerigi "Ahmet Davul" olan yani bir String nesnesi olustur. ve referansini ad'a ata.
4- Icerigi "Ahmet" olan String nesnesi artik Cop toplayici tarafindan yok edilecektir. Cunku hicbir nesne referansi artik o nesneyi gostermiyor.

Simdi yazi basindaki uyarinin nedeni ortaya cikiyor, bir String nesnesini

String isim = new String("Ahmet");

seklinde olusturursaniz sistem arka planda su islemi yapar,
1- icerigi "Ahmet" olan bir String nesnesi olustur. Refransi X olsun.
2- yeni bir String nesnesi olustur ve referansi X olan nesnenin icerigini kopyala. Yeni String nesnesini isim referansina ata. X  referansini null yap cop toplayici zamani geldiginde silsin.

Bu cok zahmetli bir is. Gercekten gerekmedigi taktirde bir String nesnesini bu sekilde olusturmayin. Daima String = "Ahmet"; yontemini tercih edin.

Yeni bir oneri daha, Stringler uzerinde + islemini abartili bicimde kesinlikle kullanmayin. mesela elimizde bir dizi String var ve biz bunu ardi ardina ulamak ve tek bir string yapmak istiyoruz.

        String[] kelimeler = {"Ahmet", " Davul", ", nasilsin", " iyimisin?"};
        String cumle = "";
        for (int i = 0; i < kelimeler.length; i++)
            cumle = cumle + kelimeler[i];
        System.out.println("Cumle=" + cumle);
.
Burada yapilan islem kelimelerin teker teker cumle Stringine ulanmasi gibi goruluyor. Ama ne yazik ki Stringler icerigi degistirilemez oldugundan dongu icinde her defasinda yeni bir string olusturuluyor. Eger cok sayida kelimeniz olsaydi bu yaziliminizin performansini cok ciddi sekilde etkileyebilirdi. Sozun ozu, Stringler manipulasyon amaciyla kullanmamaya calisin, String sayisinin cok fazla oldugu durumlarda ulama icin String ve + islemi kullanmayin!!

Tabi bu + ifadesini hic kullanmayin demek degil, bilakis cogu yerde kullanmak mecburi olarak bile gorulebilir. Basit konsol cikislari, iki uc  stringi birbirine ulamak gayet normal seylerdir. Sadece + islemini ozellikle perfomansin gerektigi yerlerde tercih etmeyin. Cunku bunun icin imdadimiz yetisen baska bir nesne var, StringBuffer. StringBuffer String'e benzeyen ama ozellikle uzerinde degisiklik yapmaya musait olan bir nesnedir. Yukaridaki kodu asagidaki sekilde yazsaydik performans acisindan gayet iyi sonuclar elde edebilirdik.

        String[] kelimeler = {"Ahmet", " Davul", ", nasilsin", " iyimisin?"};
        StringBuffer cumle = new StringBuffer();
        for (int i = 0; i < kelimeler.length; i++)
            cumle.append(kelimeler[i]);
        System.out.println("Cumle=" + cumle);

Goruldugu gibi artik String'e ozel + isareti yerine StringBuffer'in append yani ekleme metodunu kullaniyoruz. StringBuffer yontemi digerinden kelime sayisi arttikca yerine gore 50-100 kat, daha hizli calisabilir. Deneyip gorebilirsiniz. cunku nesne olusturma islemi tum nesneye yonelik dillerde zaman alan bir islemdir. Ayni sey String icindeki karakterleri cok sIklikla degistirmeniz gereken durumlar icin de gecerlidir.

Konu biterken bir soru...
asagidaki kodda kac String nesnesi olusturulur?

String isimA = "Ahmet";
String isimB =
isimA;
String isimC = isimA + isimB;

toString() metodu

public String toString() metodu Java'da tum metodlarin turedigi en tepedeki sinif olan Objet'in bir metodudur. Herhangi bir nesne icinde bu metodu yazarsaniz nesnenin iceriginin size nasil gorunmesine iliskin bir kod yazmaniz beklenir. Mesela

public class StringTest
{
    private String ad = "Ahmet";
    private String soyad = "Davul";

    public String toString()
    {
        return "Adi:" + ad + " Soyadi:" + soyad;
    }

    public static void main(String[] args)
    {
        StringTest test = new StringTest();
        System.out.println("to String sonucu: " + test.toString());
    }
}

Sonucta ekranda "to String sonucu: Adi:Ahmet Soyadi:Davul" yazdigini goreceksiniz. Deneme icin to String metodunu silin ve tekrar calistirin. Bu defa sonuc olarak "to String sonucu: StringTest@f6a746" ya da benzeri bir 16'lik rakam goreceksiniz. Bunun nedeni su, Object sinifi icinde yer alan toString metodu bos degildir. Eger siz bir nesne icinde toString metodunu yazmasaniz Object icindeki toString() metodu cagrilir. Bunun icerigi ise su sekildedir: yani sinif adi ve hashcode degeri ekrana yazilir.

    public String toString()
    {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

Not: yukaridaki ornekten test.toString yerine sadece test yazsak ta ayni sonucu gorurduk. Bu + isaretinin sagladigi bir ozellik saniyorum, otomatik olarak toString metodunu cagiriyor.
toString() metodu genellikle test ve log alma amaciyla kullanilir. Yazilimlarinizda nesnelerin icerigini debug yapmadan log araclarina ya da dogrudan konsola println(..toString()) ile bastirarak inceleyebilirsiniz. toString() metodunun icerigini yazmak ise size kalmis, bu konuda kisitlayici bir sey tok. String dondursun yeter. Bazi IDE'ler (idea gibi) ve Eclipse Plug-inleri hazir toString olusturucu araclara sahiptir, onlari kullanip amelelikten kurtulabilirsiniz.
Java'da bazi nesneler icinde hazir toString metodu bulundurur. Ornegin Collection nesneleri ya da StringBuffer gibi. bunu deneyip gorebilirsiniz.

Bazi Donusumler

Cok karsilasilan bir konu int, char , long gibi  ilkel tiplerin String'e ve String'den donusumudur. Bunun icin ilkellerin  nesne karsiliklarindan yararlanilir.

        int i = Integer.parseInt("1000");
        int j = Integer.parseInt("1000", 2);
        System.out.println("i:" + i + " j:" + j);

Sonuc olarak i:1000, j:8 elde edilir. metod icindeki ikinci paramtre yazilmazsa 10'luk tabanda, parametre yazilirsa o tabanda donusum yapilmaya calisilir. Eger string icindeki degerin integer harici bir deger ise sistem  sayi bicimleme istisnasi (NumberFormatException) gonderir. isterseniz bu istisnayi yakalayan bir try-catch blogu duzenleyebilirsiniz. tamamen sizin tasariminiza kalmis.

        try
        {
            int i = Integer.parseInt("ahmet");
        } catch (NumberFormatException e)
        {
            System.out.println("Olmadi.. integer olmuyor bu string..");
        }

Bunun tersi icin ise yani ilkellerin String karsiliklari icin String.valueOf(..) metodlarini kullanabilirsiniz.

String icindeki karakterleri dizi seklinde elde etmek icin ise toCharArray() metodu kullanilabilir..

String kelime = "armut"
char[] harfler = kelime.toCharArray();

Temel Stringlere iliskin aklima gelenler bu kadar.. Bu arada yukaridaki sorunun cevabi "2".
String kodunu JSDK kodlarina bakarak incelemenizi oneririm. ozellikle HashCode ve Equals metodlarina bakiniz.  Ve tabiki asil kaynak olan J2SDK dokumani,

http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html


ve bilgilendirici String ornekleri icin Java Developers Almanac'i mutlaka ziyaret edin..

http://javaalmanac.com/egs/java.lang/pkg.html#Strings

Yazi ile ilgili hata ya da elestirileri anet haber grubu java grubundaki ilgili ilmege yazabilirsiniz.

ff, Haziran 2004

[Ana Sayfa]