|
~BöLÜM-3~ |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Başlangıç Durumuna Getirme ve Temizlik İşlemleriUygulamalardaki 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
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.
Yapılandırıcılar , normal metodlar gibi parametre alabilirler. ör-yapılandırcı-2
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 .
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
Uygulamanın çıktısı
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
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.
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
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
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
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
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
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
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 kelimesithis 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
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.
Peki , gunEkle() metodunun içersinde this.gun ifadesi yerine sadece gun ifadesi kullansaydık sonuç nasıl değişirdi ? ör-this-2
Uygulamamızın çıktısı aşağıdaki gibidir.
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
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
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.
Bir yapılandırıcıdan diğer bir yapılandırıcıyı çağırmakBir 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ı
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 .
Uygulamamızın çıktısı aşağıdaki gibidir
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
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 metodlarStatik 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
Bir metodun statik mi yoksa obje metodu mu olucağını neye göre karar vereceğizBir 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
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
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() metodufinalize() 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
ö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.
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
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
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.
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 ;
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ı :
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.
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öntemReferans Sayma YöntemiBu 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öntemlerToplam üç 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öntemiOluş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öntemiZaman 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öntemKopyalama 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
Heap bölgesiJava 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 NesilYeni 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 NesilEski 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
gösterim-7
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
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
Kopyalama yönteminin gösterimiAş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österimiiyi 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
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
Uygulamanın çıktısı aşağıdaki gibidir.
Değişkenlere ilk değerlerini atamaJava 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
Yerel(local) değişkenlerYerel 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
Objeye ait global değişkenlerObjelere 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 atamaGlobal 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
Uygulamamızın çıktısı aşağıdaki gibidir :
Objeye ait global obje tiplerinde ilk değeri atamaGlobal 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
Uygulamamızın çıktısı aşağıdaki gibidir
Sınıflara ait global değişkenlerSı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
Uygulamamızın çıktısı aşağıdaki gibidir
Sınıflara ait global obje tiplerinde ilk değeri atamaSı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
Uygulamamızın çıktısı aşağıdaki gibidir .
İ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
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
Uygulamamızın çıktısı aşağıdaki gibidir.
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ı
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.
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
Daha sonradan da statik olmayan Peynir tipindeki değişkenimize ilk değer verilir.
Ve nihayet Tabak sınıfının yapılandırıcısı çalıştırılarak , Tabak objesi oluşturulur.
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.
Sonra obje değişkeni olan t1 değişkenini Tabak objesini bağladık.
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.
En son işlem olarak, Kahvalti objesinin islemTamam() metodunu çağırarak, ekrana aşağıdaki mesajı bastırırız.
Ekran çıktısını toplu halde gösterirsek :
Statik değişkenlere toplu değer atamaStatik değişkenlerimize toplu olarakta değer atıyabiliriz ör-statik-toplu-deger-atama
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 atamaStatik olmayan değişkenlerine toplu değer verme , şekilen statik değişkenlere toplu değer vermek gibidir. ör-non-static-toplu_deger_atama
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şkenlerDizi 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
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şturmakDizilerimize , herhangi bir obje oluşturuyormuş gibi oluşturabiliriz. gösterim-13
ö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
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şımJava 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
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.
Aynı örneğimizi daha değişik sekilde ifade edebiliriz. ör-dizi-2
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.
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
Uygulamamızın çıktısı aşağıdaki gibidir.
Dizilerin dizilere kopyalanmasıEğer elimizde uygun iki dizi var ise bunları birbirlerine kopyalıyabiliriz. ör-dizilerin kopyalanması
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 .
Çok Boyutlu DizilerJava 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
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
ör-çok-boyutlu-1
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.
Uygulamamızın çıktısını matris gibi düşünürsek.
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
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
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.
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
Bu dökümanın her hakkı saklıdır |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||