~BöLÜM-3~

 

Başlangıç Durumuna Getirme ve Temizlik İşlemleri

Uygulamalardaki  meydana gelen hataların önemli sebeblerinden  biri, objelerin yanlış biçimde başlangıç durumlarına getirilmesi diğeri ise  temizlik işleminin doğru dürüst    yapılmamasındır.Uygulamayı yazan  kişi bilmediği kütüphaneye ait objeleri yanlış başlangıç durumuna getirmesinden ötürü çok sıkıntılar yaşanabilir. Diğer bir husus ise temizliktir ,  oluşturulmuş ve kullanılmayan objelerin ,sistem kaynaklarında gereksiz yere var olması ile ortaya çıkan bir problemdir. Bu problem sistemin çökmesine sebep olabilecek kadar ciddidir.  Bu bölümde Java nın bu sorunları nasıl ele aldığını inceliyeceğiz.

Başlangıç durumuna getirme işlemi (initialization) ve yapılandırıcılar (constructor)

Bir objenin başlangıç durumuna getirilme işlemini , bir sanatçının sahneye çıkmadan evvelki yaptığı son hazırlık gibi düşünebilirsiniz . Oluşturulacak olan obje kullanıma sunulmadan evvel bazı bilgilere ihtiyaç duyabilir veya bazı işlemleri gerçekleştirmesi gerekebilir.Tüm bu işlemlerin gerçekleştiği yer bir çeşit metod diyebileceğiz yapılandırıcılardır (constructor) , ama şunu hemen belirtelim yapılandırıcılar normal metodlardan çok farklıdırlar .

Şimdi biraz düşünelim , elimizde öyle bir metod olacak ki , biz bu metodun içersinde objemiz kullandılmadan evvel gereken işlemleri yapacağız  yani objemizi başlangıç durumuna getireceğiz , ayrıca  Java bu metodu obje oluşturulmadan hemen önce otomatik olacak çağırabilecek. 

Diğer bir problem ise metodun ismidir . Bu metodun ismi öyle olmalıdır ki , bizim verebileğimiz metod isimleri ile çakışmamalı , artı , Java nın bu metodu otomatik olarak çağrıcağı düşünülürse , bu metod isminin Java tarafından daha evvelden biliniyor olması gerekmektedir. 

Bu problemlere ilk çözüm C++ dilinde bulunmuştur.Çözüm şöyledir , yapılandırıcı ile sınıfın isminin bire bir aynı olmasıdır(büyük küçük harf dahil),  böylece Java hangi metodu çağırabileceğini sınıfın isminden tahmin edebilecektir. Ufak bir uygulama üzerinde açıklarsak.

ör-yapılandırıcı-1

class KahveFincani {   

  public KahveFincani() { 
    System.out.println("KahveFincani..");
  } 
}

public class YapilandirciBasitOrnek {  
  public static void main(String[] args) {
    for(int i = 0; i < 10; i++) 
      new KahveFincani(); 
    } // for
} 

Bu örneğimizde ardı ardına 10 adet KahveFincani objesi oluşturuyoruz . Dikkat ederseniz KahveFincani objelerini oluşturmadan evvel (ellerimizi Kahvefincalarının üzerine götürmeden) , objelerimiz ekrana kendilerini tanıtan ifadeler yazdılar kısacası objelerimize ilk değerleri güvenli bir şekilde verilmiştir. 

Dikkat edilmesi gereken ikinci husus ise yapılandırıcının(constructor) ismidir. Yapılandırıcının(constructor) ismi içinde bulunduğu sınıf ismi ile bire bir aynıdır. Hatırlarsanız normalde metod isimleri bir fiil cümlesi içermeliydi (dosyaAc() , openFile() , dosyaOku() , readFile() , dosyaYaz() , arabaSur() ....vb ..) ama yapılandırıcılar bu kuralın da dışındadır .Uygulamımızın çıktısı aşağıdaki gibidir. 

KahveFincani..
KahveFincani..
KahveFincani..
KahveFincani..
KahveFincani..
KahveFincani..
KahveFincani..
KahveFincani..
KahveFincani..
KahveFincani..

Yapılandırıcılar , normal metodlar gibi parametre alabilirler.

ör-yapılandırcı-2

class YeniKahveFincani {

  public YeniKahveFincani(int adet ) {
      System.out.println(adet + " adet YeniKahveFincani");
  } 
}

public class YapilandirciBasitOrnekVersiyon2 { 
  public static void main(String[] args) {
    for(int i = 0; i < 10; i++) 
      new YeniKahveFincani( i );
     } //for
} 

Gönderdiğimiz parametre sayesinde objemizin nasıl oluşacağını belirtte bilmekteyiz.Örneğimizde olduğu gibi YeniKahveFincani objemizin , oluşurken kaç adet olacağını söyliye biliyoruz.Uygulamamızın çıktısı aşağıdaki gibidir .

0 adet YeniKahveFincani
1 adet YeniKahveFincani
2 adet YeniKahveFincani
3 adet YeniKahveFincani
4 adet YeniKahveFincani
5 adet YeniKahveFincani
6 adet YeniKahveFincani
7 adet YeniKahveFincani
8 adet YeniKahveFincani
9 adet YeniKahveFincani

Yapılandırıcılar(constructor) , normal metodlarda kullandığımız değer döndürme mekanizmasına sahip değillerdir. Yapılandırıcı metodlardan herhangi bir şekilde değer döndürülemez , bu değer döndürülemez ibaresi normal metodlardaki void ifadesine karşılık gelmez . Yapılandırıcılardan bir şekilde çıkmak istiyorsanız return anahtar kelimesini kullanabilirsiniz.

Bir ismi birden çok metod için kullanmak- Adaş Metodlar-(Overloading)

İyi bir uygulama yazmak herzaman zaman iyi bir takım çalışması gerektirir.Takım çalışmasının önemli kurallarından biri ise bir kişinin yazdığı kodu diğer kişininde kolaylıkla anlayabilmesinden geçer.Uygulamalardaki metod isimlerinin , metod içersinde yapılan iş ile uyum göstermesi önemlidir. Bu sayade bir başka kişi sadece metod ismine bakarak , içersinde oluşan olayları anlayabilme şansına sahiptir.

Örneğin elimizdeki bulunan muzik , resim ve text formatındaki dosyaları açmak için metodlar yazmak istersek, bu metodların isimlerinin ne olması gerekir ?  muzik dosyasını açan metodun ismi muzikDosyasiAc() , resim dosyası için resimDosyasıAc() ,text dosyasını açmak için ise textDosyasınıAc() gibi üç ayrı metod ismi kulanmak acaba ne kadar akıllıca ve pratik olur . Sonuçta işlem sadece dosya açmaktır , dosyanın türü sadece bir ayrıntıdır. Bir ismi birden çok metod için kullanmak(method overloading) size bu imkanı verebilmektedir , aynı tür işleve sahip olan metodların , isimlerininde aynı olabilme özelliği , bizi isim bulma sıkıntısından kurtarmaktadır. 

ör-metod-overloading-1

class MuzikDosyasi {
    String m_tur = "Muzik Dosyasi" ;
}

class ResimDosyasi {
    String r_tur = "Resim Dosyasi" ;
}

class TextDosyasi {
    String t_tur = "Text Dosyasi" ;
}

public class MetodOverloadingDemo1 {
    public void dosyaAc(MuzikDosyasi md) {
	System.out.println( "Tur =" + md.m_tur );
    }

    public void dosyaAc(ResimDosyasi rd) {
	System.out.println( "Tur =" + rd.r_tur );
    }

    public void dosyaAc(TextDosyasi td) {
	System.out.println( "Tur =" + td.t_tur );
    }

    public static void main(String[] args) { 
	      MetodOverloadingDemo1 mod1 = new MetodOverloadingDemo1();
	      MuzikDosyasi  md = new MuzikDosyasi();	
	      ResimDosyasi  rd = new ResimDosyasi();	
	      TextDosyasi   td = new TextDosyasi();

	      mod1.dosyaAc(md);
	      mod1.dosyaAc(rd);
	      mod1.dosyaAc(td);
    }
}

Uygulamanın çıktısı 

Tur =Muzik Dosyasi
Tur =Resim Dosyasi
Tur =Text Dosyasi

Yukarıdaki örneğimizde görüldüğü gibi , aynı tür işlemleri yapan metodların isimleri aynıdır , peki Java aynı isimde olan bu üç metodu nasıl birbirinden ayırt etmekte ? 

Adaş metodlar nasıl ayırt edilir ?

Java aynı isimde olan metodlar (overloaded methods) nasıl ayırt edebilmektedir ? Cevap olarak parameterlerini göre diyebiliriz ....... , konuyu biraz daha açarsak , her metodun kendisine özel/tek  parametresi veya parametre listesi olmak zorundadır.

ör-metod-overloading-1

public class MetodOverloadingDemo2 {


    public int toplamaYap(int a , int b){
	int sonuc = a + b ;
	System.out.println("sonuc - 1  = " + sonuc);
	return sonuc ;
    }

    public void  toplamaYap(int a , double b){
	double sonuc = a + b ;
	System.out.println("sonuc - 2 = " + sonuc);
    }

    public double  toplamaYap(double a , int b){
	double sonuc = a + b ;
	System.out.println("sonuc - 3= " + sonuc);
	return sonuc ;
    }

    public static void main(String[] args) { 
	      MetodOverloadingDemo2 mod2 = new MetodOverloadingDemo2();
	      mod2.toplamaYap(3,4);
	      mod2.toplamaYap(3,5.5);
	      mod2.toplamaYap(6.8,4);
    }

}

Bu örneğimizde , üç adet toplamaYap() metodlarının, parametreleri birbirinden bir şekilde farklıdır. ilk toplamaYap() metodumuz iki adet int tipinde değişken alarak diğer adaş metodlardan ayrılmaktadır.

Geriye kalan iki adet  toplamaYap() metodu ise aynı tip parametreler almaktadır , double tipi ve int tipi , bu iki metodu birbirinden farklı kılan , parametrelerin sırasıdır. Uygulamanın çıktısı aşağıdaki gibidir.

sonuc - 1 = 7
sonuc - 2 = 8.5
sonuc - 3= 10.8
Dönüş değerlerine göre adaş metodlar ayırt edilebilir mi ?

Akıllarda şöyle bir soru belirebilir : "Adaş metodlar dönüş tiplerine göre  ayırt edilebilir mi ?",inceliyelim :

gösterim-1

void toplamaYap();
double toplamaYap();

Elimizde iki adet aynı isimde , aynı işlemi yapan , fakat biri değer döndürmeyen(void) diğeri ise double tipinde değer döndüren metodların olduğunu varsayalım.  

gösterim-2

double y = toplamayap() ;

gösterim-2  için , Java bu metodlardan hangisini seçeçeğini tahmin edebilir (double toplamaYap() ).Peki aşağıdaki gibi  bir durum için nasıl bir yol izlenmesi gerekir ? 

gösterim-3

toplamayap() ;

Değer döndüren bir metodu kesinkes döndürdüğü tipe karşılık gelen değişkene atama  zorunluluğu olmadığı hatırlatalım . Kısacası bu koşulda Java hangi metodu cağrıcağını bilebilecektir.Bu yüzden dönüş tiplerine göre metodların ayırt edilmesi Java dilinde kabul görmez. Metodların ayırt edilmesini sağlayan tek şey parametrelerindeki farklılıktır.

Varsayılan yapılandırıcılar (Default constructors)

Eğer uygulamamıza herhangi bir yapılandırıcı koymasak Java bu işlemi kendi otomatik olarak yapmaktadır . Varsayılan yapılandırcıları  ( parametresiz yapılandırcılar , default constructor veya "no-args" constructor )  içi boş bir metod olarak düşünebilirsiniz. 

ör-default-constructor-1

class Kedi {
  int i;
}

public class VarsayilanYapilandirici {
   public static void main(String[] args) {
     Kedi kd = new Kedi(); //Varsayilan yapilandirici cagrildi
   }
} 

Javanın yerleştirmiş olduğu varsayılan yapılandırıcı açık bir şekilde gözükmemekdir.Açık sekilde görmek istersek.

ör-default-constructor-2

class Kedi { 
  int i;
  /* varsayailan yapilandirici
  Bu yapilandiriciyi eger biz koymasaydik 
  java bizim yerimize zaten koyardi */
  public Kedi() {}
}

public class VarsayilanYapilandirici {
  public static void main(String[] args) {
	Kedi kd = new Kedi(); //Varsayilan yapilandirici cagrildi
  }
} 
Büyünün bozulması

Eğer kendimiz yapılandırıcı metodlar yazarsak , Java bizden varsıyılan yapılandırıcı desteğini çekecektir.Bunun sebebi şöyledir ,eğer bir sınıfa ait herhangi bir yapılandırıcı belirtmez isek Java devreye girip bizim adımıza varsayılan bir yapılandırıcı koyar , eğer -ki biz kendimize ait özel yapılandırıcılar tanımlarsak Java ya " ben ne yaptığımı biliyorum, lütfen karışma" demiş oluruz .Bu durumda olası tüm yapılandırıcıları bizim yazmamız gerekir , şöyle ki ... 

ör-default-constructor-3

class Araba {
	int kapi_sayisi;
	int vites_sayisi ;

	public Araba(int adet) {
		kapi_sayisi = adet ;
	}

	public Araba(int adet , int sayi) {
		kapi_sayisi =  adet ;
		vites_sayisi = sayi  ;
	}
}



public class VarsayilanYapilandiriciVersiyon2 { 
	public static void main(String[] args) {
		//Araba ar = new Araba();//!Hata! böyle bir yapılandırıcı artık yok;
		Araba ar1 = new Araba(2);
		Araba ar2 = new Araba(4,5);
	}
} 

Artık Araba sınıfını parametresiz yapılandırıcı (default constructor) ile çağıramayız veya çağırmak istiyorsak bunu kendimiz yazmak zorundayız. 

this anahtar kelimesi

this anahtar kelimesi , içinde bulunulan objeye ait bize bir referans döner.Bu referans sayesinde objelere ait global değişkenlere erişme fırsatı buluruz , şöyle ki..

ör-this-1

public class TarihHesaplama {
    int gun , ay , yil ;

    public void gunEkle(int gun) {
         this.gun += gun ;
    }    

    public void gunuEkranaBas() {
	System.out.println("Gun = " + gun);
    }    

    public static void main(String[] args) {
	TarihHesaplama th = new TarihHesaplama();
	th.gunEkle(2);
	th.gunEkle(3);
	th.gunuEkranaBas();
    }
}

gunEkle() metodu sayesinde , parametre olarak gönderdiğimiz değer , global olan gun değişkeninin değerini arttırmaktadır .  

Objeye ait global değişkeler ,  içinde bulundukları objeye ait  değişkenler demektir ve  obje içersindeki her metod tarafından erişilebilirler . Yerel(local) değişkenler metodların içersinde tanımlanırlar ve ancak tanımlandığı metod içersinden erişilebilirler.

Dikkat ederseniz , gun ismini hem global hemde yerel değişken adı olarak kullandık .Burada herhangi bir yanlışlık yoktur çünkü bunlardan biri objeye ait  değişken diğeri ise gunEkle() metoduna ait yerel (local) değişkendir. Bizim gönderdiğimiz değer gunEkle() metodunun yerel değişkeni sayesinde objenin global olan değişkene eklenmektedir. En sonunda gunuEkranaBas() metodu ile global olan gun değişkeninin değerini görebilmekteyiz .

Özet olarak , gunEkle() metodunun içersinde kullandığımız this.gun ifadesi ile  TarihHesaplama objesinin global olan değişkenine erişebilmekteyiz.Uygulamamızın çıktısı aşağıdaki gibidir.

Gun = 5

Peki , gunEkle() metodunun içersinde this.gun ifadesi yerine sadece gun ifadesi kullansaydık sonuç nasıl değişirdi ? 

ör-this-2

public class TarihHesaplama2 { 
    int gun , ay , yil ;

    public void gunEkle(int gun) {
         gun += gun ;
    }    

    public void gunuEkranaBas() {
	System.out.println("Gun = " + gun);
    }    

    public static void main(String[] args) {
	TarihHesaplama2 th = new TarihHesaplama2();
	th.gunEkle(2);
	th.gunEkle(3);
	th.gunuEkranaBas();
    }
}

Uygulamamızın çıktısı aşağıdaki gibidir.

Gun = 0

Global değişken olan gun değişkenine herhangi bir değer ulaşmadığı için sonuç sıfırdır.

Metod cağrımlarımda this kullanımı

gösterim-4

class Uzum {

  void sec() { /* ... */ }

  void cekirdeginiCikar() { sec(); /* ... */ }

}

Bir metodun içersinde diğer metodu çağırmak gayet basittir ve açıktır , ama sahne arkasında derleyici (compiler), çağrılan bu metodun önüne this anahtar kelimesini gizlice sizin yerinize yerleştirmektedir.Yani bizim ekstradan this.sec() dememimiz fazla bir anlamı yoktur. 

Aşağıdaki örneğimiz this anahtar kelimesi ile içinde bulunduğumuz objeye ait nasıl referans aldığımızı çok net bir biçimde göstermektedir.

ör-this-referans

public class Yumurta {

  int toplam_yumurta_sayisi = 0;  

  Yumurta sepeteKoy() {
    toplam_yumurta_sayisi++;
    return this;
  }

  void goster() {
    System.out.println("toplam_yumurta_sayisi = " + toplam_yumurta_sayisi);
  }

  public static void main(String[] args) {
    Yumurta y = new Yumurta();
    y.sepeteKoy().sepeteKoy().sepeteKoy().goster();
  }
}

sepeteKoy() metodu Yumurta tipinde değer geri döndürmektedir. return this diyerek , oluşturulmuş objenin kendisine ait bir referans değeri geri döndürüyoruz. sepeteKoy() metodu her çağrıldığında global değişken olan toplam_yumurta_sayisi  değeri bir artmaktadır.Uygulamanın çıktısı aşağıdaki gibidir.

toplam_yumurta_sayisi = 3
Bir yapılandırıcıdan diğer bir yapılandırıcıyı çağırmak

Bir yapılandırıcıdan diğer bir yapılandırıcı çağırmak this anahtar kelimesi ile mümkündür.

ör-this-yapılandırıcı

public class Tost {    

    int sayi ;
    String malzeme ;

    public Tost() {
	this(5);
	//this(5,"sucuklu"); !Hata!-iki this kullanilamaz
	System.out.println("parametresiz yapilandirici");
    }

    public Tost(int sayi) {
	this(sayi,"Sucuklu");
	this.sayi =  sayi ;
	System.out.println("Tost(int sayi) " );
    }

    public Tost(int sayi ,String malzeme) {
       this.sayi = sayi ;
       this.malzeme = malzeme ;
       System.out.println("Tost(int sayi ,String malzeme) " );
    }

    public void siparisGoster() {
	//this(5,"Kasarli"); !Hata!-sadece yapilandiricilarda kullanilir
	System.out.println("Tost sayisi =" + sayi + " malzeme =" + malzeme );
    }

    public static void main(String[] args) {
	Tost t = new Tost();
	t.siparisGoster();
    }
}
 

Bir yapılandırıcıdan, this ifadesi ile diğer bir yapılandırıcıyı çağırırken dikkat edilmesi kurallar aşağıdaki gibidir .

  • Yapılandırıcı içersinden , diğer bir yapılandırıcıyı çağırırken this ifadesi ( this(sayi,"Sucuklu") ) , her zaman ilk satırda yazılmalıdır.

  • Her zaman yapılandırıcılar içersinden this ifadesi ile başka bir yapılandırıcı çağrılır.

  • Yapılandırıcılar içersinde birden fazla this ifadesi ile başka yapılandırıcı çağrılamaz.

Uygulamamızın çıktısı aşağıdaki gibidir 

Tost(int sayi ,String malzeme)
Tost(int sayi)
parametresiz yapilandirici
Tost sayisi =5 malzeme =Sucuklu

Statik değişkenler (sınıf değişkenleri)

Sadece global olan değişkenlere statik özelliğini verebiliriz  , yerel değişkenlerin statik olma özellikleri yoktur. Global değişkenler tür olarak iki çeşit olabilir , statik olan global değişkenler ve objeye ait global değişkenler.Statik değişkenler, bir sınıfa ait olan tüm objeler için aynı hafıza alanında bulunurlar.

ör-statik-degisken-1 

public class StatikDegisken {    

    public static int  x ; 
    public int  y ; 


    public static void ekranaBas(StatikDegisken sd ) {
       System.out.println("StatikDegisken.x = " + sd.x + " StatikDegisken.y = " + sd.y );
    }
    

    public static void main(String args[]) {

	  StatikDegisken sd1 = new StatikDegisken();
	  StatikDegisken sd2 = new StatikDegisken();
	  x = 10 ;
	  // sd1.x = 10 ; // x = 10 ile ayni etkiyi yapar
	  // sd2.x = 10 ; // x = 10 ile ayni etkiyi yapar
	  sd1.y = 2 ;
	  sd2.y = 8; 
	  ekranaBas(sd1);
	  ekranaBas(sd2);
    }
}

 

StatikDegisken tipinde iki adet obje oluşturduk , daha sonra StatikDegisken sınıfına ait statik x değişkenine  10 değerini atadık. Artık oluşturulacak olan tüm StatikDegisken tipindeki objeler için bu x değeri ortaktır . 

Şekil-1

Statik metodlar

Statik metodlar (sınıf metodları) , objelerden bağızsız metodlardır , yani statik bir metodu çağırmak için herhangi bir obje oluşturmak zorunda değilizdir. Statik olmayan metodlardan (objeye ait metodlar)  , statik metodları rahatlıkla çağırılabilmesine karşın statik metodlardan obje metodlarını doğrudan çağıramayız.

ör-statik-1

public class StatikTest {   

       public static void hesapla(int a , int b) {
	   /*static metod dogrudan 
	    obje metodunu cagiramaz */
	    //islemYap(a,b);   // !Hata!  
       }	

       public void islemYap(int a , int b) {
	   /*dogru , obje metodu static bir metodu cagirabilir*/  
	   hesapla(a,b); 
       }

}

 

Bir metodun statik mi yoksa obje metodu mu olucağını neye göre karar vereceğiz

Bir metodu  statik olarak tanımlamamızın tek sebebi , obje oluşturmadan dahi o metodu çağırabiliyor olmamız mı ?  

Uygulama geliştiren kişilerin kafasında her zaman var olan bir soru , acaba hangi metodu statik tanımlasam , hangisini obje metodu tanımlasam . Bu sorunun cevabı , sınıf metodları ile obje metodları arasındaki farkı iyi bilmekte gizlidir.

ör-statik-2

public class MutluAdam {

    private String ruh_hali = "Mutluyum" ;  

    public void ruhHaliniYansit() {
        System.out.println( "Ben  " + ruh_hali );
    }

    public void tokatAt() {
        if( ruh_hali.equals("Mutluyum" ) ) {
            ruh_hali = "Sinirlendim";
        } 
    }

    public void kucakla() {
        if( ruh_hali.equals( "Sinirlendim" ) ) {
            ruh_hali = "Mutluyum";
	} 
    }

     public static void main(String[] args) { 
	MutluAdam obj1 = new MutluAdam();
	MutluAdam obj2 = new MutluAdam();
	obj1.ruhHaliniYansit();
	obj2.ruhHaliniYansit();
      obj1.kucakla();
	obj2.tokatAt();
	obj1.ruhHaliniYansit();
	obj2.ruhHaliniYansit();
     }
}

 

ruhHaliniYansit() , tokatAt() , kucakla() metodlarının hepsi obje metodlarıdır yani bu metodları çağırmak için MutluAdam objesi oluşturmalıyız.  

MutluAdam tipindeki obj1 ve obj2 değişkenlerine MutluAdam objelerini bağladığımız  anda bu iki objenin ruh halleri aynıdır fakat zaman geçtikçe her objenin kendine ait ruh hali değişti. obj1 değişenine bağlı olan MutluAdam objesini kucakladim , böylece mutlu olmaya devam etti ama obj2 değişkenine bağlı MutluAdam objesine tokat attığım zaman mutlu olan ruh hali değişti ve sinirlendi.

Obje metodları , objenin durumuna ilişkin işlemleri yapmak için kulanılılar ama statik metodlar (sınıf metodları) objelerin durumuna ilişkin genel bir işlem yapmazlar , şöyle ki ....

ör-statik-3

public class Toplama {   

    public static double topla(double a , double b ) {
	double sonuc = a + b ;
	return sonuc ;
    }
}

Bu örneğimizde görüldüğü üzere topla() metodunun misyonu sadece kendisine gelen iki double değerini toplamak ve sonucu geri döndürmektir. Objenin durumu ile ilgili herhangi bir görev üslenmediği için statik metod (sınıf metodu) olarak tanımlanması gayet mantıklıdır.

Sonuç olarak eğer bir metod , objenin durumuna ilişkin bir misyon yükleniyorsa o metodu obje metodu olarak tanımlamamız en uygunudur , ama eğer bir metod sadece atomik işlemler için kullanılacaksa ve objenin durumuna ilişkin bir misyon yüklenmemiş ise o metodu da rahatlıkla statik metod (sınıf metodu) olarak tanımlıyabiliriz. Uygulamalarınızda çok fazla statik metod kullanıyorsanız , stratejinizi baştan bir kez daha gözden geçirmenizi tafsiye ediyorum.

Temizlik İşlemleri : finalize() ve çöp toplayıcı (Garbage Collector)

Yapılandırıcılar sayesinde objelerimiz oluşturulmadan evvel nasıl başlangıç posizyonlarını verdiğimizi belirtmiştik, peki oluşturulan bu objeler daha sonradan nasıl hafızadan silinmektedir . Java dilinde , C++ dilinde olduğu gibi oluşturulan objelerimizi işleri bitince yok etme özgürlüğü kodu yazan kişinin elinde değildir. Bir objenin gerçekten çöp olup olmadığını karar veren mekanizma  çöp toplayıcısıdır (garbage collector).

Uygulamada bir obje kullanılmıyorsa çöp toplayıcısı(Garbage Collector) tarafından hafızadan silinir. Bu işlem kodu yazan kişi bakımından büyük bir rahatlık oluşturmaktadır , çünkü uygulamalardaki en büyük hataların ana kaynağı  temizliğin doğru dürüst yapılamamasıdır.

finalize() metodu

finalize() metodunun iki işlevi vardır . Birincisi , eğer javada yerel metodlar (native methods) kullanılırsak ( Java kodu olmayan satırları Java dilinde kullanma ) -ki sonuçta çöp toplayıcısı(Garbage Collector) sadece new ile oluşturulmuş olan objeleri hafızan nasıl silineceğini bilmektedir.Javanın içersinde  şu an için iki programlama dili desteklenmektedir bunlar C ve C++ dır. Örneğin Javanın içersinde C diliyle malloc() fonksiyonunu kullanarak  hafızada bir yer alalım , aynı şekilde alınan bu hafıza alanını geri vermek istersek C dilindeki free() fonksiyonunu kullanmamız gerekecektir ,işte bu free()  fonksiyonunu finalize() metodunun içersine yazarak , alınmış olan hafıza alanı geri verebilmekteyiz.

İkinci işlev olarak , çöp toplayıcı(garbage collector) objeyi hafızadan silmeden hemen önce finalize() metodunu çağırır , aynı idam mahkumunun son isteği gibi finalize() metodunda  obje yapması gereken son işlemleri belirtir. Başka bir örnek verirsek , bir çizim programı yaptık ve elimizde ekrana çizgi çizin bir objemiz var , bu objemizi hafızadan silmeden evvel ekrana çizdiği çizgileri silmesini yine finalize() metodunda belirtebiliriz.

Akıllarda tutulması gereken diğer bir konu ise eğer uygulamanız çok fazla sayıda çöp objesi(kullanılmayan obje) üretmiyorsa , çöp toplayıcısı (garbage collector) devreye girmeyebilir.Bir başka  nokta ise eğer System.gc() ile çöp toplayıcısını tetiklemezsek , çöp toplayıcısının ne zaman devreye girip  çöp haline dönüşmüş olan objeleri hafızadan temizleneceği bilinemez.

 ör-garbage-collector-1

class Elma {	

	int i = 0 ;

	Elma(int y) {
		this.i = y ;
		System.out.println("Elma Objesi Olusturuluyor = " + i );		
	}	

	public void finalize() {
		System.out.println("Elma Objesi Yok Ediliyor = "+ i );
	} 
}

public class Temizle {

	public static void main(String args[]) {
		for (int y=0 ; y<10 ;y++) {
			Elma e = new Elma(y);
		}		

		for (int y=10 ; y<21 ;y++) {
			Elma e = new Elma(y);
		}
	}

}

ör-garbage-collector-1 , örneğimizde iki döngü içersinde toplam 21 adet elma objesi oluşturulmaktadır. Kendi bilgisayarımda 256 MB RAM bulunmaktadır, böyle bir koşulda çöp toplayıcısı(garbage collector) devreye girmeyecektir ,sebebi ise elma objelerinin java için ayrılan  hafıza alanında yeterince yer kaplamamasıdır.Değişik bilgisayar konfigurasyonlarında örneğin 16 MB RAM bulunan bir makinada , çöp toplayıcısı devreye girebilir.Uygulamanın çıktısı aşağıdaki gibidir. 

Elma Objesi Olusturuluyor = 0
Elma Objesi Olusturuluyor = 1
Elma Objesi Olusturuluyor = 2
Elma Objesi Olusturuluyor = 3
Elma Objesi Olusturuluyor = 4
Elma Objesi Olusturuluyor = 5
Elma Objesi Olusturuluyor = 6
Elma Objesi Olusturuluyor = 7
Elma Objesi Olusturuluyor = 8
Elma Objesi Olusturuluyor = 9
Elma Objesi Olusturuluyor = 10
Elma Objesi Olusturuluyor = 11
Elma Objesi Olusturuluyor = 12
Elma Objesi Olusturuluyor = 13
Elma Objesi Olusturuluyor = 14
Elma Objesi Olusturuluyor = 15
Elma Objesi Olusturuluyor = 16
Elma Objesi Olusturuluyor = 17
Elma Objesi Olusturuluyor = 18
Elma Objesi Olusturuluyor = 19
Elma Objesi Olusturuluyor = 20

Uygulamanın çıktısından anlaşılacağı üzere , Elma objesinin finalize() metodu hiç çağrılmadı , sebebi ise çöp toplayıcısının (garbage collector) hiç tetiklenmemiş olmasıdır.

Aynı örneğimizi biraz değiştirelim . 

 ör-garbage-collector-2

class Elma2 {	

	int i = 0 ;

	Elma2(int y) {
		this.i = y ;
		System.out.println("Elma2 Objesi Olusturuluyor = " + i );		
	}	

	public void finalize() {
		System.out.println("Elma Objesi Yok Ediliyor = "+ i );
	} 
}

public class Temizle2 {

	public static void main(String args[]) {
		for (int y=0 ; y<10 ;y++) {
			Elma2 e = new Elma2(y);
		}

		System.gc() ; // çöp toplayıcısi tetiklendi

		for (int y=10 ; y<21 ;y++) {
			Elma2 e = new Elma2(y);
		}
	}
}

 

System sınıfının statik bir metodu olan gc() , çöp toplayıcısının , kodu yazan kişi tarafından tetiklenmesi sağlar. Böylece çöp toplayıcısı çöp haline gelmiş olan objeleri bularak hafızadan siler.

 

İlk for döngüsünde oluşturulan Elma objeleri ,  yinr bu for döngüsü bitince çöp  halini alacaklardır. Bu for döngüsünün içersinde Elma tipinde olan e değişkeni her bir tur yapışında başka bir Elma objesine bağlanmaktadır.Böylece bağlanmış olduğu diğer objeler çöp halini alacaktır. En son olarak for döngüsünün bitimiyle Elma tipinde olan e değişkeni kapsama alanına çıkacağından , bu değişkene bağlı olan en son Elma objeside çöp halini alacaktır. Böylece heap bölgesi duran gereksiz Elma objeleri çöp toplayıcısı tarafından hedef durumuna gelmiştir. System.gc() komutu ile çöp toplayıcısını (garbage collector) tetiklediğimizde , gereksiz olan bu on adet Elma objesi hafızadan silinecektir.Bu objeler hafızadan silinirken , finalize() metodunun nasıl çağrıldığını görebiliyoruz.

Hafızadan hangi objeler silinir

Çöp toplayıcısı(Garbage Collector) hafızadan , bir değişkene bağlı olmayan objeleri siler. Eğer -ki obje bir değişkene bağlı ise , o obje uygulama tarafından kullanılıyordur anlamını çıkartabiliriz. Bir değişkene bağlı olan objelere çöp toplayıcısı tarafından dokunulmaz.

örnek-cop-obje

public class CopObje {

	public static void main(String args[]) {
		Elma e = new Elma(1);
		new Elma(2);
		System.gc() ; // cop toplayıcisini cagirdik 
	}

}

Yukarıdaki örneğimizde , 2 adet Elma objesinden biri çöp toplayıcısı(garbage collector) tarafından hafızadan silinirken , diğer obje hafızada yaşamanı sürdürmeye devam eder . Bunun sebebi yapılandırıcısına bir sayısını gönderdiğimiz objenin , Elma tipindeki e değişkenine bağlı olmasıdır.Uygulamanın çıktısı  aşağıdaki gibidir.

Elma Objesi Olusturuluyor = 1
Elma Objesi Olusturuluyor = 2
Elma Objesi Yok Ediliyor = 2

Uygulamamızı şekil üzerinde gösterirsek :

 Şekil-2

finalize() metodununa güvenirsek neler olur ?

Konu başlığını bir örnek ile açıklıyalım ;

class Ucak {	

	String ucak_isim  ;
	boolean benzin_deposu_dolu = false ;
	boolean benzin_deposu_kapagi_acik_mi = false ; 	

	Ucak(boolean depoyu_doldur ,String ucak_isim) {
		benzin_deposu_kapagi_acik_mi = true ; // kapagi aciyoruz
		benzin_deposu_dolu = depoyu_doldur ;//depo full
		this.ucak_isim =ucak_isim ;
	}	

	public void finalize() {

		if (benzin_deposu_kapagi_acik_mi) {  // kapak aciksa
			benzin_deposu_kapagi_acik_mi = false ;// kapagi kapa
			System.out.println(ucak_isim + " - kapaklari kapatildi ");
		}
	} 
}

public class BenzinDepo {

	public static void main(String args[]) {			

	    Ucak ucak_1 = new Ucak(true,"F-16"); // benzin doldur
	    new Ucak(true,"F-14");  //benzin doldur
	    System.gc();//kapaklari kapat
	    System.out.println("Ucaklara benzin dolduruldu,kapaklari kapatildi");
	}
}

Bu örneğimizde iki adet Ucak objeyi oluşturuyoruz , objelerimizi oluşturur oluşturmaz , kapaklarını açıp depolara benzinleri doldurmaktayız . Kapakları kapatma işlemi için finalize() metodunu kullanıyoruz. Sonuçta System.gc() çağrılınca çöp toplayıcısının (garbage collector) tetiklendiğini bilmekteyiz .Uygulamanın çıktısı : 

F-14 - kapaklari kapatildi
Ucaklara benzin dolduruldu,kapaklari kapatildi

Uygulama çıktısının gösterdiği gibi , sadece F-14 Ucak objesinin benzin deposunun kapağı kapatılmış durumda , F-16 Ucak objesinin  ise kapağı hala açık durmaktadır. Bu yanlışlığın sebebi , F-16 Ucak objesine Ucak tipinde olan  ucak_1 değişkenin bağlanmış olmasıdır . Çöp toplayıcısı (garbage collector) boşta olan objeleri hafızadan temizler , yani herhangi bir objeye bir değişken bağlanmamış ise o obje boşta demektir. Bu örneğimizde boşta olan obje F-14 Ucak objedir ve çöp toplayıcısı tarafından temizlenmiştir.

Özet olarak , System.gc() ile çöp toplayıcısını(garbage collector) tetikliyebiliriz ama değişkenler  objelere bağlandığı sürece , bu objelerin hafızadan atılması söz konusu değildir.Bu sebepten dolayı finalize() metodunu kullanırken dikkatli olmalıyız. 

Çöp toplayıcısı (Garbage Collector) nasıl çalışır ?

Çöp toplayıcısının temel görevi , kullanılmayan objeleri bulurak bunları hafızadan silmektir.Sun Microsystems tarafından tanıtılan Java  HotSpot VM (Virtual Machine)sayesinde heap bölgesindeki objeler nesillerine göre ayrılmaktadır. Bu nesiller eski nesil ve yeni nesil objeler olmak üzere iki çeşittir.Belli başlı parametreler kullanarak Java HotSpot VM mekanizmasını kontrol etmeniz mümkündür . Java HotSpot VM bizlere sağlandığı hazır parametreler ile normal bir uygulama gayet performanslı çalışabilir.Eğer sunucu(server-side) üzerinde uygulamalar geliştiriyorsanız , Java HotSpot VM parametrelerini doğru kullanarak uygulamanızın performasını bulunduğu yerden daha yükseklere çıkartmanız mümkündür.

Daha evvelden söz ettiğimiz gibi , objelerin hafızadan silinmesi görevi kodu yazan kişiye ait değildir.Bu işlem tamamen çöp toplayıcısının sorumluluğundadır.Java HotSpot VM 1.3.1 ait çöp toplayıcısı iki konuyu kullanıcılara garanti etmektedir.

  • Kullanılmayan objelerin kesinlikle hafızadan silinmesi sağlamak.

  • Obje hafıza alanının parçalanmasını engellemek , ve hafızanın sıkıştırılması sağlamak.

Bu bölümde dört adet çöp toplama  algoritmasından bahsedeceğim , bunlardan ilki  ve ilkel olanı  referans sayma yöntemidir , bu yöntem modern JVM (Java Virtual Machine) ler  tarafından kullanılmaz .

Eski yöntem

Referans Sayma Yöntemi

Bu yöntemde , bir obje oluşturulur oluşturulmaz kendisine ait bir sayaç çalışmaya başlar ve bu sayacın ilk değeri birdir , bu objemizin ismi X olsun.Bu sayaç'ın saydığı şey,oluşturduğumuz objenin  kaç adet  değişkene bağlı olduğudur.Ne zaman yeni bir değişken bu X objeyesine bağlansa, bu sayac bir artar , aynı şekilde ne zaman bu X objesine bağlı olan bir değişken geçerlilik alanı dışına çıksa veya değişkenin içinde bulunduğu başka bir obje çöp toplayıcısı tarafından hafızadan silinde , X objesine ait bu sayac bir eksilir.Eğer sayaç sıfır değerini gösterirse  X objemiz çöp toplayıcısı tarafından hafızadan silinir. 

Bu yöntem , kısa zaman aralıkları ile çalıştırıldağında iyi sonuçlar vermektedir ve gerçek zamanlı uygulamalar için uygun olduğu söylenebilir. Fakat bu yöntemin kötü yanı döngüsel ilişkilerde referans sayacının doğru değerler göstermemesidir.Örneğin iki objemiz olsun , bunlardan biri ana obje diğeri ise çocuk objesi  olsun(ana obje - cocuk obje terimlerini kalıtım konusunda inceliyeceğiz). Eğer ana obje , çocuk objeye , çocuk objede ana objeyi döngüsel bir biçimde bağlanmış ise , bu objeler artık kullanılmıyor olsa dahi  , sayaçları hiç bir zaman sıfır olmaz ve bu yüzden çöp toplayıcısı tarafından hafızadan silinmezler.

Yeni Yöntemler 

Toplam üç adet yeni çöp toplayıcısı yönetimi mevcuttur.Bu üç yönteminde ,yaşayan objeleri bulma stratejisi aynıdır. Bu strateji  , hafıza içinde yer alan , statik ve stack alanlarındaki değişkenlerin bağlı bulunduğu objeler aranılarak bulunur.Eğer bir değişken (referans) bir objeye bağlıysa , bu obje uygulama tarafından kullanılıyor demektir yani canlı objedir.

Kopyalama yöntemi

Oluşturulan bir obje  heap bölgesindeki  yeni nesil alanında yerini alır , eğer bu obje zaman içinde çöp toplayıcısı tarafından silinmemiş ise belli bir olgunluğa ulaşmış demektir ve heap bölgesindeki eski nesil alanına geçmeye hak kazanır.Yeni nesil bölgeleri  arasında kopyalanma işlemi ve bu alandan , eski nesil alana  kopyalanma işlemi , kopyalama yöntemi sayesinde gerçekleşir.

İşaretle ve süpür yöntemi

Zaman içinde objeler belli bir olgunluğa erişince heap bölgesindeki eski nesil alanına taşındıklarını belirtmiştik. Eski nesil alanındaki objeleri hafızadan silmek ve bu  alanındaki paçalanmaları engellemek için işaretle ve süpür yöntemi kullanılır. İşaretle ve süpür yöntemi , kopyalama yöntemine göre daha yavaş çalışmaktadır.

Artan (sıra) yöntem

Kopyalama yöntemi veya  işaretle ve süpür yöntemi ,uygulamanın üretmiş olduğu  büyük objeleri hafızadan silerken kullanıcı tarafından farkedilebilir bir duraksama oluştururlar. Bu farkedilir duraksamaları ortadan kaldırmak için Java HotSpot Vm  artan yönetimini geliştirmiştir.

Artan yöntem , büyük objelerin hafızadan silinmesi için  orta nesil alanı oluşturur , bu alan  içersinde küçük küçük bir çok bölüm vardır , bu  sayede büyük objeleri hafızadan silerken  oluşan  farkedilir duraksamalar , küçük ve farkedilmez duraksamalara dönüştürülmektedir.

Artan yönetemi devreye sokmak için -Xincgc  , devreden çıkartmak için ise -Xnoincgc parametreleri kullanılır. Java HotSpot Vm  normal şartlarda bu yöntemi kullanmaz eğer kullanılmasını istiyosak  bu işlemi kendimiz yapmak zorundayız. 

gösterim-5

java -Xincgc BenzinDepo

Heap bölgesi

Java HotSpot VM (Virtual Machine), heap bölgesini nesillere göre yönetir. Hafıza alanında değişik nesillerdeki objeler bulunur. Aşağıdaki şeklimizde heap bölgesinin nesillere göre nasıl ayrıldığını görebiliriz.

 

Şekil-3

Kalıcı alan özel bir bölgedir (32 MB dan 64 MB kadar yer kaplayabilir). Bu bölgede JVM'e ait bilgiler bulunur. -XX:MaxPermSize=64m komutu ile , bu alanın boyutlarını kontrol edebiliriz.

Yeni Nesil

Yeni Nesil toplam 3 alandan oluşur , başlangıç alanı , ve iki adet boş alan (ba#1 ve ba#2) . Bu iki boş alandan bir tanesi bir sonraki kopyalama (kopyalama yöntemi çalışır)için her zaman için boş tutulur. Başlangıç alanındaki objeler belli bir olgunluğa ulaştıkları zaman boş olan alanlara kopyalanırlar.

Eski Nesil

Eski nesil objeler heap alanındaki eski alanında bulunurlar. Uygulama tarafından kullanılan uzun ömürlü objeler yeni nesil alanından , eski nesil alanına taşınırlar.Bu objeler eski nesil alanında biriktikçe hafıza alanında yetersizlik ortaya çıkabilir , bu esnada  işaretle ve süpüryöntemi devreye girer.

Heap bölgesinin boyutlarını nasıl kontrol edilir.

Heap bölgesine minimum  veya maksimum değerleri vermek için -Xms veya -Xmx parametlerini kullanırız. Performansı arttırmak amacı ile  geniş kapsamlı sunucu (server-side) uygulamalarında minimum ve maksimum değerler birbirlerine eşitlenerek sabit boyutlu heap bölgesi elde edilir.

JVM herhangi bir çöp toplama yöntemini çağırdıktan sonra , heap bölgesini, boş alanlar ile yaşayan objeler arasındaki farkı ayarlamak için büyütür veya azaltır.Bu oranı yüzdesel olarak minimum veya maksimum değerler atamak istiyorsak -Xminf veya -Xmaxf parametreleri kullanılabilir.Bu değer (SPARC Platform versiyonu) için  hali hazırda  minimum %40 , maksimum %70 dır.

gösterim-6  

java -Xms32mb Temizle 

gösterim-7

java -Xminf%30 Temizle

Heap bölgesinin JVM tarafından büyütülüp  küçültüldükçe , eski nesil ve yeni nesil alanlarıda NewRatio parametresine göre yineden hesaplanır. NewRatio parametresi eski nesil alan ile yeni nesil alan arasındaki oranı belirlemeye yarar. Örneğin -XX:NewRatio=3 parametresinin anlamı eski nin yeniye oranının 3:1 olması anlamına gelir, yani eski nesil alanı heap bölgesinin 3/4 , yeni nesil ise 1/3 yer  kaplayacakdır. bu şartlarda kopyalama yöntemi daha sık çalışması beklenir. Eğer ki eski nesil alanı daha küçük yaparsak o zaman işaretle ve süpür yöntemi daha sık çalışacakdır. Daha evvelden belirtildiği gibi işaretle ve süpür yöntemi  , kopyalama yöntemine göre daha yavaş çalışmaktadır.

gösterim-8

java -XX:NewRatio=8 Temizle

NewSize ve MaxNewSize parametreleri , yeni nesil alanının minimum ve maksimum değerlerini belirmek için kullanılır.Eğer bu parametleri birbirlerine eşitlersek , sabit uzunlukta yeni nesil alanı elde etmiş oluruz.Aşağıdaki gösterimde yeni nesil alanlanın 32MB olucağı belirtilmiştir.

gösterim-9

java -XX:MaxNewSize=32m Temizle

 

Kopyalama yönteminin gösterimi

Aşağıdaki şeklimizde , yaşayan objeler kırmızı renk ile ifade edilmişlerdir.

Şekil-4

Objelerimiz belli bir olgunluğa ulaşıncaya kadar yeni nesil alanının bölümleri arasında pas edililer.Bu esnada uygulanan yöntem kopyalama  yöntemidir. Belli bir olgunluğa ulaşan objelerimiz en son aşamada eski alana gönderilirler. 

İşaretle ve süpür yönteminin gösterimi

iyi tasarlanmış bir sistemde , çöp toplayıcısının bir kaç defa devreye girmesiyle ile birlikte çoğu objenin ölmesi gerekir , geriye kalan yaşayan objeler ise eski nesil alanına geçmeye hak kazanırlar.Eski nesil alanında işaretle ve süpür yöntemi kullanılır , bu yöntem kopyalama yöntemine göre daha yavaş fakat daha etkilidir.

Şekil-5

Aşağıdaki örneğimizde , ne zaman kopyala yönteminin çalıştığını , ne zaman işaretle ve süpür yönteminin çalıştığını gösterilmektedir.

ör-heap-gosterim

public class HeapGosterim {

	public static void main(String args[]) {
		for (int y=0 ; y<100000 ;y++) {
			new String("Yer Kaplamak icin");
			new String("Yer Kaplamak icin");
			new String("Yer Kaplamak icin");
			if ( (y%10000) == 0 ) { 
			    System.gc();
			}
		}
	}
}

Bu örneğimizde 3 adet String objesi her döngüde oluşturularak hafızadan yer alınmaktadır.Bu objelerimize herhangi bir değişken bağlanmadığından , çöp toplayıcısının herhangi bir zamanda devreye girmesiyle hafızdan silineceklerdir.Eğer uygulamamızı aşağıdaki formatta çalıştırsak gerçekleşen olayları daha iyi anlayabiliriz.

gösterim-10

java -verbosegc HeapGosterim

Uygulamanın çıktısı aşağıdaki gibidir.

 

Değişkenlere ilk değerlerini atama

Java uygulamalarında üç tür değişken çeşiti bulunur , yerel (local) değişkenler , obje'ye ait global değişkenler  ve son olarak sınıfa ait global değişkenler(statik değişkenler). Bu değişkenlerin tipleri ilkel (primitive) veya herhangi bir obje tipi olabilir. 

ör-javada-degiskenler

public class DegiskenGosterim {

    int x ; //objeye ait global degisken
    static int y ; // sınıfa ait global degisken
    public void metod () {
	int i ; //yerel degisken
	//static int y = 5 ;  // yanlis
    }
}

Yerel(local) değişkenler

Yerel değişkenlere kesin olarak ilk değerleri kodu yazan kişi tarafından verilmelidir. İlk değeri verilmeden kullanılan yerel değişkenlere uyarı , derleme(compile-time) anında verilir.

gösterim-11

public int hesapla () { // yerel değişkenlere ilk değerleri her zaman verilmelidir.

    int i ; 

    i++; // ! Hata ! ilk deger verilmeden üzerinde işlem yapılamaz

    return i ;

}

Objeye ait global değişkenler

Objelere ait global değişkenler'e ilk değerlerini kodu kişi vermek zorunda değildir. Java bizim yerimize bu değişkenlere ilk değerlerini verir. 

Objeye ait global ilkel tiplerde ilk değeri atama

Global olan ilkel tipdeki değişkenlere Javanın hangi değerleri atadığını görmek için aşağıdaki örneğimizi inceliyelim : 

ör-objeye-global-ilkel-tipler

public class  IlkelTipler {

  boolean mantiksal_deger;
  char krakter_deger;
  byte byter_deger;
  short short_deger;
  int int_deger;
  long long_deger;
  float float_deger;
  double double_deger;  

  public void ekranaBas() {
     System.out.println("Veri Tipleri   Ilk Degerleri");
     System.out.println("boolean        " + mantiksal_deger );
     System.out.println("char           [" + krakter_deger + "] "+ (int)krakter_deger ); 
     System.out.println("byte           " + byter_deger );
     System.out.println("short          " + short_deger );
     System.out.println("int            " + int_deger );
     System.out.println("long           " + long_deger );
     System.out.println("float          " + float_deger );
     System.out.println("double         " + double_deger);

  }

  public static void main(String args[]) {
	new IlkelTipler().ekranaBas();
	/* 
	       // yukaridaki ifade yerine 
	       // asagidaki ifadeyi de kullanabilirsiniz.
	       IlkelTipler it = new IlkelTipler();
	       it.ekranaBas();
	*/
  }
}

Uygulamamızın çıktısı aşağıdaki gibidir :

Veri Tipleri Ilk Degerleri
boolean false
char [ ] 0
byte 0
short 0
int 0
long 0
float 0.0
double 0.0
Objeye ait global obje tiplerinde ilk değeri atama

Global olan herhangi bir obje tipindeki değişkene Java tarafından ilk değer olarak "null" atanır . Eğer bu değişken vasıtası ile işlem yapmak istersek hata ile karşılaşırız , sebebi ise bu obje tipindeki değişkenin henüz kendi tipindeki bir objeye bağlanmamasıdır

ör-objeye-ait-global-obje-tipi

public class  ObjeTipleri {

   String s ;    

  public static void main(String args[]) {
	ObjeTipleri ot = new ObjeTipleri();
	System.out.println("s = " + ot.s );
	// ot.s = ot.s.trim(); // ! hata ! 
  }
}

Uygulamamızın çıktısı aşağıdaki gibidir 

s = null

Sınıflara ait global değişkenler

Sınıflara ait global değişkenler'e değer atamak ile  objelere ait global değişkenlere değer atamak arasında bir fark yoktur. Buradaki önemli nokta statik değişkenelere sadece bir kere değer atandığıdır.Örnekler ile açıklaya çalışırsak : 

Sınıflara ait global ilkel tiplerde ilk değeri atama

ör-sınıfa-ait-global-ilkel-tipler

public class  IlkelTiplerStatik {

  static boolean mantiksal_deger;
  static char krakter_deger;
  static byte byter_deger;
  static short short_deger;
  static int int_deger;
  static long long_deger;
  static float float_deger;
  static double double_deger;
  

  public void ekranaBas() {
     System.out.println("Veri Tipleri    Ilk Degerleri");
     System.out.println("static boolean  " + mantiksal_deger );
     System.out.println("static char     [" + krakter_deger + "] "+ (int)krakter_deger ); 
     System.out.println("static byte     " + byter_deger );
     System.out.println("static short    " + short_deger );
     System.out.println("static int      " + int_deger );
     System.out.println("static long     " + long_deger );
     System.out.println("static float    " + float_deger );
     System.out.println("static double   " + double_deger);
  }

  public static void main(String args[]) {
	new IlkelTiplerStatik().ekranaBas();
	/*     
	       // yukaridaki ifade yerine 
	       // asagidaki ifadeyi de kullanabilirsiniz.
	       IlkelTiplerStatik its = new IlkelTiplerStatik();
	       its.ekranaBas();
	*/
  }
}

Uygulamamızın çıktısı aşağıdaki gibidir

Veri Tipleri Ilk Degerleri
static boolean false
static char [ ] 0
static byte 0
static short 0
static int 0
static long 0
static float 0.0
static double 0.0
Sınıflara ait global obje tiplerinde ilk değeri atama

Sınıflara ait global olan  herhangi bir obje tipindeki değişkene Java tarafından ilk değer olarak "null" atanır.

ör-sınıfa-ait-global-obje-tipi

public class  StatikObjeTipleri {

   static String s ;    

   public static void main(String args[]) {
	StatikObjeTipleri sot = new StatikObjeTipleri();
	System.out.println("s = " + sot.s );
	// sot.s = sot.s.trim(); //hata
  }

}

Uygulamamızın çıktısı aşağıdaki gibidir . 

s = null

İlk değerleri atarken metod kullanımı

Global olan objeye ait veya statik değişkenlere ilk değerleri bir metod aracılığı ile de atanabilir. 

ör-objeye-ait-global-deg-deger-atamak

public class  KarisikTipler {

  boolean mantiksal_deger = mantiksalDegerAta() ;//true degerini alir
  static int int_deger = intDegerAta(); // 10 degerini alir
  String s  ; 
  double d = 4.17 ;  

  public boolean mantiksalDegerAta() {
      return true ;
  }

  public static int intDegerAta() {
      return 5*2 ;
  }

  public static void main(String args[]) {
	new KarisikTipler();
  }
}

Dikkat edecek olursanız , statik bir değişken olan int_deger  değişkenine ait ilk değer statik bir metod tarafında verildi.

İlk değer verme sıralaması

Objeye ait global değişkenlerde ilk değer hemen verilir , hatta yapılandırıcıdan bile önce.Değişkenlerin konumu hangi sırada ise , ilk değer verme sıralamasıda aynı sırada olur. 

ör-ilk-deger-verme-sirasi

class Kagit {

    public Kagit(int i) {
	System.out.println("Kagit ("+i+") ");
    }
}


public class Defter {    

    Kagit k1 = new Kagit(1);   

    public Defter() {
	System.out.println("Defter() yapilandirici metod");
	k2 = new Kagit(33); // artik baska bir Kagit objesini isaret etmektedir
    }

    Kagit k2 = new Kagit(2);
    public void islemTamam() {
	System.out.println("Islem tamam");	
    }
    Kagit k3 = new Kagit(3);

    public static void main (String args[]  ) throws Exception {
     Defter d = new Defter();
     d.islemTamam();
    }
}

Uygulamamızın çıktısı aşağıdaki gibidir.

Kagit (1)
Kagit (2)
Kagit (3)
Defter() yapilandirici metod
Kagit (33)
Islem tamam

Uygulamamızın  çıktısından da anlaşıldığı üzere , ilk olarak k1 daha sonra  k2 ve en son olarakta  k3 değişkenine değer atandı.Değişkenlerimize değer atandıktan sonra yapılandırıcı  çağrıldı. Ayrıca k3 değişkeni yapılandırıcının içersinde , oluşturulan başka bir Kagit objesine bağlanmıştır , peki k3 değişkeninin daha evvelden bağlanmış Kagit objeye ne olacaktır ? .Cevap çöp toplayıcısı (garbage collector) tarafından hafızdan silinecektir. Uygulamamız en son olarak islemTamam() metodunu çağırarak çalışmasını sonra erdirmektedir.

Statik ve statik olmayan değişkenlerin değer alma sıralaması

Statik değişkenler , sınıflara ait olan değişkenlerdir ve statik olmayan (obje değişkenleri) değişkenlere göre ilk değerlerini daha önce alırlar. 

ör-deger-alma-sıralaması

class Peynir {

    public Peynir(int i, String tur) {
      System.out.println("Peynir ("+i+") -->" + tur);
    }
}

class Tabak {
    public Tabak(int i, String tur) {
      System.out.println("Tabak ("+i+") -->" + tur);
    }

    static Peynir p1 = new Peynir(1,"statik degisken");
    Peynir p2 = new Peynir(2,"statik-olmayan degisken");
} 

class Catal {
    public Catal(int i , String tur) {
	System.out.println("Catal ("+i+") --> " + tur);
    }
}

public class Kahvalti {    

    static Catal  c1 = new Catal(1,"statik degisken");    

    public Kahvalti() {
	System.out.println("Kahvalti() yapilandirici metod");
    }

    Tabak t1 = new Tabak(1,"statik-olmayan degisken");
    public void islemTamam() {
	System.out.println("Islem tamam");	
    }    

    static Catal  c2 = new Catal(2,"statik degisken");

    public static void main (String args[]  ) throws Exception {
     Kahvalti d = new Kahvalti();
     d.islemTamam();
    }

    static Tabak t4 = new Tabak(4,"statik degisken");
    static Tabak t5 = new Tabak(5,"statik degisken"); 
}

Sırasıyla gidersek , ilk öncelikle c1 , c2 , t4 , t5 statik değişkenlerine ilk değerleri verilecektir.Catal objesini oluştururken ,yapılandırıcısına iki adet parametre gönderiyoruz , bunlardan biri int ilkel tipi(primitive) diğeri ise String tipindeki parametredir.Böylece hangi Catal tipindeki değişkenlerin , hangi Catal objelerine bağlandığını anlayabiliriz.Catal objesini oluştururken olaylar gayet açıktır, yapılandırıcı metod çağrılır ve ekrana gerekli bilgiler bastırılır.

Catal (1) --> statik degisken

Catal (2) --> statik degisken

Tabak objesini t4 değişkenine  bağlarken yine iki adet parametre kullanıyoruz .Biri int tipi diğeri ise String .Fakat Tabak sınıfını incelersek , yapılandırıcıyı çağırmadan  evvel bazı işlemlerin  yapılması gerektiği anlarız. Biri statik diğeri ise obje değişkeni olan bu iki global değişkene , Tabak sınıfına ait yapılandırıcıyı çağırmadan evvel ilk değerleri verilir. Statik olan değişkenin her zaman daha önce ve yanlız bir kere ilk değerini aldığı kuralını hatırlarsak , ekrana yansıyan sonuçun kafamızdaki ile aynı olduğunu görürüz 

Peynir (1) -->statik degisken

Daha sonradan da statik olmayan  Peynir tipindeki değişkenimize ilk değer verilir. 

Peynir (2) -->statik-olmayan degisken

Ve nihayet Tabak sınıfının yapılandırıcısı çalıştırılarak , Tabak objesi oluşturulur.

Tabak (4) -->statik degisken

Bu işlemlerde sonra sıra  t5 değişekine Tabak objesini bağlamaya geldi , bu işlemde sadece statik olmayan Peynir tipindeki p2 değişkenine değer atanır.Statik olan değişken tipi bu esnada herhangi bir veri atanmaz sebebi ise statik değişken tiplerine sadece bir kere değer atanabilmesidir. Bu işlemden sonra Tabak objesinin yapılandırıcısana girilir.

Tabak (5) -->statik degisken

Peynir (2) -->statik-olmayan degisken

Sonra obje değişkeni olan t1 değişkenini Tabak objesini bağladık.

Tabak (1) -->statik-olmayan degisken

Son işemden bir önceki işlem olarak, Kahvalti sınıfının yapılandırıcısı çağırdaktan Kahvalti objesini oluşturuyor. 

Kahvalti() yapilandirici metod

En son işlem olarak, Kahvalti objesinin islemTamam() metodunu çağırarak, ekrana aşağıdaki mesajı bastırırız.

Islem tamam

Ekran çıktısını toplu halde gösterirsek :

Catal (1) --> statik degisken
Catal (2) --> statik degisken
Peynir (1) -->statik degisken
Peynir (2) -->statik-olmayan degisken
Tabak (4) -->statik degisken
Peynir (2) -->statik-olmayan degisken
Tabak (5) -->statik degisken
Peynir (2) -->statik-olmayan degisken
Tabak (1) -->statik-olmayan degisken
Kahvalti() yapilandirici metod
Islem tamam

Statik değişkenlere toplu değer atama

Statik değişkenlerimize toplu olarakta değer atıyabiliriz 

ör-statik-toplu-deger-atama

class Kopek {

    public Kopek() {
	System.out.println("Hav Hav");
    }
}



public class StatikTopluDegerAtama {

    static int x  ;
    static double y ;
    static Kopek kp ;
    {
	x = 5 ;
	y = 6.89 ;
	kp = new Kopek();
    }

    public static void main(String args[]) {
	new StatikTopluDegerAtama();
   }   
}

Statik değişkenlerimize toplu değer vermek için kullandığımız bu blok sadece , StatikTopluDegerAtama sınıfınından obje oluşturduğumuz zaman veya bu sınıfa ait herhangi bir statik değişkene erişmeye çalıştığımız zaman (statik alanlara erişmek için obje oluşturmak zorunda değilsiniz), bir kere çağrılır.

Statik olmayan değişkenlerine toplu değer atama

Statik olmayan değişkenlerine toplu değer verme , şekilen statik değişkenlere toplu değer vermek gibidir.

ör-non-static-toplu_deger_atama

class Dinozor {
    public Dinozor() {
	System.out.println("Ben Denver");
    }
}

public class NonStatikTopluDegerAtama {

    int x  ;
    double y ;
    Dinozor dz ;
    {
	x = 5 ;
	y = 6.89 ;
	dz= new Dinozor();
    }

    public static void main(String args[]) {
	new NonStatikTopluDegerAtama();
   }   
}

 

Diziler (Arrays)

Diziler objedir , dizi objesi içinde belli sayıda değişken bulundurur .Eğer bu sayı sıfır ise dizimiz boş demektir.Dizinin içersindeki  değişkenlere isimleri ile değil , negatif olmayan tam sayı ile ifade edilen  dizi erişim indeks(gösterge) ile erişilir.  Dizi içersindeki değişkenler dizinin elemanlarıdır.Eğer bir dizide n tane eleman varsa dizinin uzunluğuda  n kadardır diyebiliriz . Dizi içersindeki ilk elemanın konumu  0 'dan başlar , son elemanın yeri ise  n-1 'dir.

Dizi içersindeki her elemanın tipi aynı olmak zorundadır. Eğer dizi içersindeki bir elemanın tipi double ise , bu dizinin tipi için double diyebiliriz.Bu double tipinde olan diziye String tipinde bir obje atmayı denersek , hata ile karşılaşırız. Dizilerimiz ilkel tipdeki veya herhangi bir obje tipindeki elemanlardan oluşabilirler.

Dizi tipindeki değişkenler

Dizi tipindeki değişkenler objeye bağlanmaktadır.Dizi tipinde değişken tanımlamak , dizimizi hemen kullanacağımız anlamına gelmez.

gösterim-12

double[] dd  ; // double tipindeki dizi
double dd[] ;  // double tipindeki dizi
float [] fd ;  // float tipindeki dizi
Object[] ao ;  // Object tipindeki dizi

 

     

Gösterim-12 de sadece dizi objelerine bağlanacak olan değişkenleri tanımladık. Bu dizi objelerimiz  hafıza alanında henüz yer kaplamamışlardır. Dizi objelerini oluşturmak için new anahtar kelimesini kullanmamız gereklidir.

Dizileri oluşturmak

Dizilerimize , herhangi bir obje oluşturuyormuş gibi oluşturabiliriz.

gösterim-13

double[] d = new double[20] ; // 20 elemanlı double tipindeki dizi
double dd[] = new double[20]; // 20 elemanlı double tipindeki dizi
float [] fd = new float [14]; // 14 elemanlı float tipindeki dizi
Object[] ao  = new Object[17]; // 17 elemanlı Object tipindeki dizi
String[] s = new String[25] ;  // 25 elemanlı String tipindeki dizi  

örneğin new double[20] ifadesi ile 20 elemandan oluşan double dizisi elde etmiş olduk.

Dizilerin tekrardan boyutlandırılması

Dizilere bir kere boyutları verildi mi , artık değiştirilemezler.

gösterim-14

int  liste[] = new int[5] ;
liste = new int[15] ; // yeni bir dizi objesine baglandi

Gösterim-14' de dizimizin boyutlarını büyütüğümüzü sanmayın , sadece yeni bir dizi objesi daha  oluşturduk. liste dizi değişkeninin daha evvelden bağlanmış olduğu  dizi objesi ( new int[5]  )  , çöp toplama işlemi sırasında çöp toplayıcısı (garbage collector ) tarafından hafızadan silinecektir.

Dizi içersindeki elemalara ulaşım

Java dilinde  dizi kullanımının diğer dillere nazaran daha az risklidir. Bunun anlamı ,eğer tanımladığımız dizinin sınırlarını aşarsak, çalışma-anı (runtime) hatasını java bize çekinmeden vereceğidir.

.Örneğin 20 lik bir double dizisi tanımladık eğer bu dizinin 78. elemana ulaşmak istersek(- ki böyle bir indeks de böyle bir eleman yok) , olanlar olur ve çalışma-anı hatasını alırız . Bu çalışma-anı hatasının verilmesi aslında güzel bir olaydır, böylece Java dizi için ayrlmış hafıza alanından dışarı çıkıp , başka verilere mudahale etmemize engeller.

ör-dizi-1 

public class DiziElemanlariGosterimBir {
    

    public static void main(String args[]) {
	double[] d  = { 2.1 , 3.4 , 4.6 ,1.1 ,0.11 } ; 
	String[]  s  = { "defter" ,"kalem" , "sarman" ,"tekir" ,"boncuk" }; ;	

	// double tipindeki dizimizi ekrana bastiriyoruz 
	for (int i = 0 ; i < d.length ; i ++) {
	    System.out.println("d["+i+"] = " + d[i] );
	    // System.out.println("d["+78+"] = " + d[78] ); // ! Hata !
	}
	System.out.println("---------------");
	// String tipindeki dizimizi ekrana bastiriyoruz 
	for (int x = 0 ; x < s.length ; x ++) {
	    System.out.println("s["+x+"] = " + s[x] );
	    // System.out.println("s["+78+"] = " + s[78] );  // ! Hata ! 
	}
    } 
}

length ifadesi ile bir dizinin içersindeki eleman sayısını öğreniriz.Bu örneğimizde iki adet dizi tanımladık , double ve String tipindeki dizilerimiz içersine 5'er adet eleman yerleştirdik ve sonradan bunları for döngüsü ile ekrana bastırdık. i < d.length  ifadesine dikkat ederseniz , döngü sayacımız 4'e kadar işlemektedir ama döngü sayacamız 0'dan başladığını unutmayalım.Bunun sebebi dizilerin 0 dan başlamalarıdır , unutmaması gereken bir ayrıntı.

Eğer 5 eleman'a sahip olan dizilerimizin 78. elemanına erişmeye kalkışırsak , derleme anında (compile-time) bir hata ile karşılaşmayız ama uygulamamızı çalıştırdığımız zaman yani çalışma-anında (runtime) hata ile karşılaşırız.

Uygulamamızın çıktısı aşağıdaki gibidir. 

d[0] = 2.1
d[1] = 3.4
d[2] = 4.6
d[3] = 1.1
d[4] = 0.11
---------------
s[0] = defter
s[1] = kalem
s[2] = sarman
s[3] = tekir
s[4] = boncuk

Aynı örneğimizi daha değişik sekilde ifade edebiliriz.

ör-dizi-2

public class DiziElemanlariGosterimIki {

    double[] d ; 
    String[]  s  ;        	     

    public  DiziElemanlariGosterimIki() {
	// double tipindeki dizimize eleman atiyoruz
	d = new double[5];
	d[0] = 2.1 ;
	d[1] = 3.4  ;
	d[2] = 4.6 ;
	d[3] = 1.1 ;
      d[4] = 0.11 ;
	// d[5] = 0.56 ; //  ! Hata ! 

	// String  tipindeki dizimize eleman atiyoruz
	s = new String[5] ;
	s[0] = new String("defter");
	s[1] = new String("kalem");
	s[2] = new String("sarman");
	s[3] = new String("tekir");
	s[4] = new String("boncuk");
	// s[5] = new String("duman"); // ! Hata ! 
    }

    public void ekranaBas() {
	// double tipindeki dizimize ekrana bastiriyoruz 
	for (int i = 0 ; i < d.length ; i ++) {
	    System.out.println("d["+i+"] = " + d[i] );
	}
	System.out.println("------------------------");
	// String tipindeki dizimize ekrana bastiriyoruz 
	for (int x = 0 ; x < s.length ; x ++) {
	    System.out.println("s["+x+"] = " + s[x] );
	}
    }

    public static void main(String args[]) {
	   DiziElemanlariGosterimIki deg = new DiziElemanlariGosterimIki();
	   deg.ekranaBas();
    }
}

Bu örneğimizde , 5 eleman kapasitesine sahip olan dizilerimize , 6. elemanı ekleye çalıştığımız zaman , derleme esnasında (compile-time) herhangi bir hata ile kaşılaşmayız. Ne zaman ki uygulamamızı çalıştırdığımızda , Java taradından çalışma-anı (runtme) hatasını alırız.Fakat hatali satirlar kapali olduğu için uygulamamız sorunsuz bir şekilde çalışmaktadır.

Uygulamamızın çıktısı aşağıdaki gibidir.

d[0] = 2.1
d[1] = 3.4
d[2] = 4.6
d[3] = 1.1
d[4] = 0.11
------------------------
s[0] = defter
s[1] = kalem
s[2] = sarman
s[3] = tekir
s[4] = boncuk

Diziler içersinde elemanların sıralanması

Diziler içersindeki elemanlarımızı büyükten küçüğe doğru sıralatmak için , java.util paketinin alındaki Arrays sınıfını kullanabiliriz. Bu sınıfın sort() metodu sayesinde dizilerin içersindeki elemanlar kolaylıkla sıralanırlar.

ör-eleman-sıralama

import java.util.*; // java.util kutuphanelerini kullanmak icin

public class DiziSiralama {

    public static void ekranaBas(double[] d) {
	for (int i = 0 ; i < d.length ; i++) {
	    System.out.println("d["+i+"] = " + d[i]);
	}
    }

    public static void main(String args[]) {
	double d[] = new double[10] ;
	d[0] = 2.45 ;
	d[1] = 3.45 ;
	d[2] = 4.78 ;
	d[3] = 1.45 ;
	d[4] = 15.12 ;
	d[5] = 1 ;
	d[6] = 9 ;
	d[7] = 15.32 ;
	d[8] = 78.17 ;
	d[9] = 4.58 ;
	System.out.println("Karisik sirada");
	ekranaBas(d); 
	Arrays.sort( d ) ;
	System.out.println("Siralanmis Sirada");
	ekranaBas(d);
    }
}

Uygulamamızın çıktısı aşağıdaki gibidir.

Karisik sirada
d[0] = 2.45
d[1] = 3.45
d[2] = 4.78
d[3] = 1.45
d[4] = 15.12
d[5] = 1.0
d[6] = 9.0
d[7] = 15.32
d[8] = 78.17
d[9] = 4.58
Siralanmis Sirada
d[0] = 1.0
d[1] = 1.45
d[2] = 2.45
d[3] = 3.45
d[4] = 4.58
d[5] = 4.78
d[6] = 9.0
d[7] = 15.12
d[8] = 15.32
d[9] = 78.17

 

Dizilerin dizilere kopyalanması

Eğer elimizde uygun iki dizi var ise bunları birbirlerine kopyalıyabiliriz.

ör-dizilerin kopyalanması

public class DizilerinKopyalnmasi {

    public static void main(String args[]) {
	// ilk dizimiz
	int[] dizi1 = { 1,2,3,4 } ;
	// daha genis olan 2. dizimiz
	int[] dizi2 = { 100 , 90 , 78 , 45 ,40 , 30 , 20 ,10} ;
	// kopyalama islemini baslatiyoruz
	// 0. indeksten dizi1 uzunlugu kadar kopyalama yap
	System.arraycopy(dizi1,0,dizi2,0,dizi1.length);
	for (int i = 0 ; i < dizi2.length ; i++) {
	    System.out.println("dizi2["+i+"] = "+ dizi2[i] );
	} 
    }
}

System sınıfının statik bir metodu olan arraycopy() sayesinde dizi1'i dizi2'ye kopyaladık.Burada dikkat edilmesi gereken konu ,arraycopy() metodu tarafından kopyalanan dizi objelerinin kendisi değil,dizi objelerin referanslarıdır.Uygulamamızın çıktısı aşağıdaki gibidir .

dizi2[0] = 1
dizi2[1] = 2
dizi2[2] = 3
dizi2[3] = 4
dizi2[4] = 40
dizi2[5] = 30
dizi2[6] = 20
dizi2[7] = 10

Çok Boyutlu Diziler

Java programlama dilindeki çok boyutlu diziler , diğer programlama dillerindekilerden farklıdır.Sonuçta dizinin tek tipte olma şartı vardır , yani dizi içersinde diziler ( dizilerin içersinde dizilerin içersindeki diziler .......) tanımlıyabilirsiniz.

gösterim-15

  int[][] t1 = {

     { 1, 2, 3, },

     { 4, 5, 6, },

  };

Gösterim-15 'de ifade edildiği gibi , iki vektörlü (bukleli parantezler ile belirtilmiş), ilkel tipten oluşmuş çok boyutlu dizi oluşturabilirsiniz.

Çok boyutlu dizileri oluşturmanın diğer bir yolu .

gösterim-16

int [] [] t1 = new int [3][4] ;
int [] [] t1 = new int [][4]  ;  // ! Hata ! 

ör-çok-boyutlu-1

public class CokBoyutluDizilerOrnekBir {     

    public static void main(String args[]) {
	 int ikiboyutlu[][] = new int[3][4] ;
	 ikiboyutlu[0][0] = 45 ;
	 ikiboyutlu[0][1] = 83 ;
	 ikiboyutlu[0][2] = 11 ;
	 ikiboyutlu[0][3] = 18 ;

	 ikiboyutlu[1][0] = 17 ;
	 ikiboyutlu[1][1] = 56 ;
	 ikiboyutlu[1][2] = 26 ;
	 ikiboyutlu[1][3] = 79 ;

	 ikiboyutlu[2][0] = 3 ;
	 ikiboyutlu[2][1] = 93 ;
	 ikiboyutlu[2][2] = 43 ;
	 ikiboyutlu[2][3] = 12 ;

	 // ekrana bastiriyoruz
	 for (int i = 0 ; i<ikiboyutlu.length ; i++  ) {
	     for (int j = 0 ;  j < ikiboyutlu[i].length ; j++ ) {
		 System.out.println(" ikiboyutlu["+i+"]["+j+"] =" + ikiboyutlu[i][j] );
	     }
	 } 
     }
}

Yukarıdaki örneğimizde int ilkel tipinde 3'e 4 lük  çok boyutlu dizi oluşturduk veya 3'e 4'lük bir matris gibi de düşüne biilirsiniz. Uygulanın çıktısı aşağıdaki gibidir.

ikiboyutlu[0][0] =45
ikiboyutlu[0][1] =83
ikiboyutlu[0][2] =11
ikiboyutlu[0][3] =18
ikiboyutlu[1][0] =17
ikiboyutlu[1][1] =56
ikiboyutlu[1][2] =26
ikiboyutlu[1][3] =79
ikiboyutlu[2][0] =3
ikiboyutlu[2][1] =93
ikiboyutlu[2][2] =43
ikiboyutlu[2][3] =12

Uygulamamızın çıktısını matris gibi düşünürsek.

45 83 11 18
17 56 26 79
 3 93 43 12

Uygulamamızı şekil olarak gösterirsek 

Şekil 6

 

Çok boyutlu dizilerde , dizilere bağlı dizilerin aynı boyutta olma zorunluluğu yoktur.

ör-çok-boyutlu-2

public class CokBoyutluDiziler {

     public static void main(String args[]) {
	int ikiboy[][] = new int[3][];	

	ikiboy[0] = new int[2] ; 
	ikiboy[1] = new int[1] ; 
	ikiboy[2] = new int[3] ; 	

	ikiboy[0][0] = 89 ;
	ikiboy[0][1] = 32 ;	

	ikiboy[1][0] = 5 ;
	// ikiboy[1][1] = 3 ;  // ! Hata ! 	

	ikiboy[2][0] = 55 ;
	ikiboy[2][1] = 78 ;
	ikiboy[2][2] = 49 ; 
     } 
}

Yukarıdaki örneğimizde görüldüğü üzere , dizilere bağlı her dizinin boyutları birbirinden farklı olmuştur.Şekil üzerinde ifade etmeye çalışırsak.

 

Şekil-7

Çok boyutlu dizilerin içersine Obje tipileride yerleştirmemiz mümkündür.Örneğin String tipinden oluşmuş çok boyutlu dizi oluşturulmuş olsun : 

ör-çok-boyutlu-3

public class HayvanlarAlemi {

    String isimler[][][]  ;  

    public HayvanlarAlemi() {
	   isimler = new String[2][2][3] ;
	   veriAta();
    }

    public void veriAta() {
	 isimler[0][0][0] = "aslan" ;
	 isimler[0][0][1] = "boz AyI" ;
	 isimler[0][0][2] = "ceylan";

	 isimler[0][1][0] = "deniz AnasI" ;
	 isimler[0][1][1] = "essek" ;
	 isimler[0][1][2] = "fare"  ;


	 isimler[1][0][0] = "geyik" ;
	 isimler[1][0][1] = "hamsi" ;
	 isimler[1][0][2] = "inek" ;



	 isimler[1][1][0] = "japon baligi" ;
	 isimler[1][1][1] = "kedi" ;
	 isimler[1][1][2] = "lama" ;

       ekranaBas() ;

    }



    public void ekranaBas() {
	for (int x = 0 ; x < isimler.length ; x++) {
	    for (int y = 0 ; y < isimler[x].length ; y++) {
		for (int z = 0 ; z < isimler[x][y].length ; z ++) {
		    System.out.println("isimler["+x+"]["+y+"]["+z+"]  =" + isimler[x][y][z]);
		}
	    }
	}
    }

    public static void main(String args[]) {
	HayvanlarAlemi ha = new HayvanlarAlemi();
    } 
}

 

Bu örneğimizde HayvalarAlemi objesinin  new oluşturulması ile olaylar tetiklenmektedir.İster tek boyutlu olsun ister çok boyutlu olsun , diziler üzerinde işlem yapmak istiyorsak onları oluşturmamız gerektiğini daha evvelden belirtmiştik. Bu örneğimizde olduğu gibi dizimizi ilk oluşturduğumuzda dizi içersindeki  String objelerinin ilk değeri null dır.Uygulamamızın çıktısı aşağıdaki gibidir.

isimler[0][0][0] =aslan
isimler[0][0][1] =boz AyI
isimler[0][0][2] =ceylan
isimler[0][1][0] =deniz AnasI
isimler[0][1][1] =essek
isimler[0][1][2] =fare
isimler[1][0][0] =geyik
isimler[1][0][1] =hamsi
isimler[1][0][2] =inek
isimler[1][1][0] =japon baligi
isimler[1][1][1] =kedi
isimler[1][1][2] =lama

Oluşan olayları şekil üzerinde göstermeye çalışırsak 

Şekil-7

Bölüm Sonu

Bu bölümde , objeleri oluşturulmadan evvel yapılandırıcı metodların çağrıldığını ve bu yapılandırıcı metodlar sayesinde , objelerimiz ilk posizyonlarını aldıklarını öğrendik.Uygulamalarda objelerin ilk posizyonlarına getirilmesi büyük bir önem taşımaktadır.Objeleri oluşturmak kadar , gereksiz oldukları zaman da hafızadan silmenin önemli olduğu bir gerçektir.Java kullanılmayan objelerin silinmesini kodu yazan kişiye bırakmamaktadır.Javanın aktif olarak kullandığı 3 adet çöp toplama yöneteminin olduğunu bu bölümde inceledik.

Ayrıca değişkenlerin çeşitlerine deyindik , global obje değişkeni , global statik değişken,ve yerel değişken.Bunların arasındaki farkları iyi bilmek çok önemlidir.Statik ve statik olmayan metodların ne zaman tanımlanması gerektiği uygulamalarda büyük bir önem teşkil etmektedir.

Sorular

  1. Varsayılan yapılandırıcı olan bir sınıf yazınız .Bu sınıf'ı daha sonradan bir uygulama içinde new ile oluşturunuz ? Uygulama main() metodu sayesinde test edilebilsin.

  2. Parametre alan yapılandırıcıya sahip bir sınıf yazınız.Bu sınıf'ı daha sonradan bir uygulama içinde new ile oluşturunuz ? Uygulama main() metodu sayesinde test edilebilsin.

  3. Yapılandırıcısın bir sınıf'a Javanın nasıl varsayılan yapılandırıcı koyduğunu bir uygulama üzerinde gösteriz ?

  4. String obje tipinde bir dizi oluşturup , bu dizi elemanlarına String objeleri atayın,daha sonra for döngüsüyle , dizi içersindeki elemanları ekrana basan bir uygulama yazınız.Uygulama main() metodu sayesinde test edilebilsin.

  5. Bir Araba sınıfı oluşturun , bu Araba sınıfının içersinde sur() adında 3 adet adaş metod içeren uygulama yazınız. Uygulama main() metodu sayesinde test edilebilsin.

  6. İki yapılandırcıya sahip bir sınıf yazınız , yapılandırıcıların birinden , diğerini this kelimesini kullanarak çağırınız.Uygulama main() metodu sayesinde test edilebilsin.

  7. finalize() metodunun çağrılacağı bir uygulama yazınız.Uygulama main() metodu sayesinde test edilebilsin.

  8. Statik global değişkenlerin , sınıfa ilk ulaşıldığında ilk değerlerinin hemen verildiğini ispatlayan bir uygulama yazınız ?Uygulama main() metodu sayesinde test edilebilsin.

  9. Statik metodlar(sınıf metodları) ile statik olmayan (obje metodları) metodlar arasında farkı bir uygulama üzerinde gösteriniz.Uygulama main() metodu sayesinde test edilebilsin.

  10. System.gc() komutunun nasıl çalıştığını bir uygulama üzerinde açıklayınız.Uygulama main() metodu sayesinde test edilebilsin.

  11. Bir metodun içersinde iki boyutlu float dizisi oluşturun , dizinin boyutları metod'a gelen parametreler ile belirlensin. Başka bir metod içersinde de bu dizi içersindeki elemanları ekrana basınız.Uygulama main() metodu sayesinde test edilebilsin.

  12. Bir metodun içersinde dört boyutlu double dizisi oluşturun , dizinin boyutları metod'a gelen parametreler ile belirlensin. Başka bir metod içersinde de bu dizi içersindeki elemanları ekrana basınız.Uygulama main() metodu sayesinde test edilebilsin.

 

Mail Grubu

Java Kitap Projesinin mail grubuna üye olmak için = java_kitap_projesi-subscribe@yahoogroups.com 

Üyelikten çıkmak için =  java_kitap_projesi-unsubscribe@yahoogroups.com

 

Sorulanız ve Yorumlarınız için : upux@yahoo.com

 

Bu dökümanın her hakkı saklıdır

1