BÖLÜM 5
|
|
Altuğ
B. Altıntaş
|
Belli bir amaç için yazılmış ve doğruluğu kanıtlanmış olan
sınıfları, yeni uygulamaların içerisinde kullanmak hem iş süresini kısaltacaktır
hem de yeni yazılan uygulamalarda hata çıkma riskini en aza indirgeyecektir.
Uygulamalarımızda daha evvelden yazılmış ve doğruluğu kanıtlanmış olan
sınıfları tekrardan kullanmanın iki yöntemi bulunur. (yorum ekle)
Birinci yöntem komposizyon’dur. Bu yöntem sayesinde daha önceden
yazılmış ve doğruluğu kanıtlanmış olan sınıf/sınıfları, yeni yazılan sınıfın
içerisinde doğrudan kullanabilme şansına sahip oluruz. Daha önceki bölümlerde
komposizyon yöntemini çokça kullandık. (yorum ekle)
İkinci yöntem ise kalıtımdır (inheritance). Bu yöntemde yeni
oluşturacağımız sınıfı, daha evvelden yazılmış ve doğruluğu kanıtlanmış olan
sınıftan türetilir; böylece yeni oluşan sınıf, türetildiği sınıfın
özelliklerine sahip olur; Ayrıca oluşan bu yeni sınıfın kendisine ait yeni
özellikleri de olabilir. (yorum ekle)
Komposizyon yönetimini daha önceki örneklerde kullanıldı. Şimdi bu
yöntemin detaylarını hep beraber inceleyelim. (yorum ekle)
Gösterim-5.1:
class Meyva {//... } |
Gösterim-5.2:
class Elma { private Meyva m = new Meyva();//... } |
Elma sınıfı, Meyva sınıfını doğrudan kendi içerisinde
tanımlayarak, Meyva sınıfının içerisindeki
erişilebilir olan özellikleri kullanabilir. Buradaki yapılan iş Elma sınıfını Meyva sınıfına bağlamaktır. Sınıfların arasındaki ilişki UML
diyagramında gösterilirse; (yorum ekle)
Şekil-5.1. Komposizyon-I
Başka bir örnek verilirse,
Örnek-5.1: Motor.java (yorum ekle)
public class Motor { private static int motor_gucu = 3600; public void calis() { System.out.println("Motor Calisiyor") ; } public void dur() { System.out.println("Motor Durdu") ; } } |
Şimdi bu Motor sınıfını,
arabamızın içerisine yerleştirelim;
Örnek-5.2: AileArabasi.java (yorum ekle)
public class AileArabasi { private Motor m = new Motor(); public void hareketEt() { m.calis(); System.out.println("Aile Arabasi Calisti"); } public void dur() { m.dur(); System.out.println("Aile Arabasi Durdu"); } public static void main(String args[]) { AileArabasi aa = new AileArabasi() ; Aa.hareketEt(); Aa.dur(); }} |
AileArabası sınıfının içerisine, Motor tipinde global bir alan yerleştirilerek,
bu iki sınıf birbirine bağlanmış oldu. AileArabası
sınıfının hereketEt() ve
dur() metotlarında, önce Motor sınıfına ait yordamlar (methods) direk olarak çağrıldı. Bu
ilişki UML diyagramında incelenirse: (yorum ekle)
Şekil-5.2.
Komposizyon-II
Motor sınıfının private erişim belirleyicisine sahip olan motor_gucu alanına, AileArabasi sınıfının içerisinde
ulaşamayız. Bunun nedenlerini bir önceki bölümlerde incelemiştik. AileArabasi sınıfı Motor sınıfının sadece iki adet public yordamına (method) erişebilir: calis() ve dur(). Olaylara kuş bakışı bakarsak, karşımızdaki
manzara aşağıdaki gibidir. (yorum ekle)
Şekil-5.3. Kuş Bakışı Görünüş
AileArabasi sınıfı çalıştırılırsa,
ekrana görülen sonucun aşağıdaki gibi olması gerekir: (yorum ekle)
Motor CalisiyorAile Arabasi CalistiMotor DurduAile Arabasi Durdu
Komposizyon yöntemine en iyi örnek bir zamanların
ünlü çizgi filmi Voltran'dır. Bu çizgi
filmi hatırlayanlar bileceklerdir ki, büyük ve yenilmez olan robotu (Voltran) oluşturmak için değişik ufak robotlar bir araya gelmekteydi. Kollar,
bacaklar, gövde ve kafa bölümü... Bizde kendi Voltran robotumuzu oluşturmak istersek, (yorum ekle)
Örnek-5.3: Voltran.java (yorum ekle)
class Govde { void benzinTankKontrolEt() {}}class SolBacak { void maviLazerSilahiAtesle() {} } class SagBacak { void kirmiziLazerSilahiAtesle() {}}class SagKol { void hedeHodoKalkaniCalistir() {}}class SolKol { void gucKaynagiKontrolEt() {}}class Kafa { void tumBirimlereUyariGonder() {} void dusmanTanimlamaSistemiDevreyeSok() {}}public class Voltran { Govde gv = new Govde(); SolBacak slb = new SolBacak(); SagBacak sgb = new SagBacak(); SagKol sgk = new SagKol() ; SolKol slk = new SolKol() ; Kafa kf = new Kafa() ; public static void main(String args[]) { Voltran vr = new Voltran() ; vr.kf.dusmanTanimlamaSistemiDevreyeSok(); vr.kf.tumBirimlereUyariGonder(); vr.sgb.kirmiziLazerSilahiAtesle(); } } |
Voltran sınıfı 6 değişik sınıf tarafından
oluşturulmaktadır; bu sınıflara ait özellikler daha sonradan Voltran sınıfının içerisinde ihtiyaçlara
göre kullanılıyor. Oluşan olaylar UML diyagramında tanımlanırsa: (yorum ekle)
Şekil-5.4. Komposizyon - III
Kalıtım konusu nesneye yönelik programlamanın (object oriented programming) en önemli kavramlarından bir tanesidir. Kalıtım kavramı,
kısaca bir sınıftan diğer bir sınıfın türemesidir. Yeni türeyen sınıf,
türetilen sınıfın global alanlarına ve yordamlarına (statik veya değil) otomatik olarak sahip olur (private olanlar hariç). (yorum ekle)
Unutulmaması gereken unsur, yeni türeyen sınıf,
türetilen sınıfın private global alanlarına ve
yordamlarına (statik
veya değil) otomatik
olarak sahip olamaz. Ayrıca yeni türeyen sınıf eğer türetilen sınıf ile ayrı
paketlerde ise yeni türeyen sınıf, türetilen sınıfın sadece public ve protected erişim belirleyicisine
sahip olan global alanlarına (statik veya değil) ve yordamlarına (statik veya değil) otomatik olarak sahip olur. (yorum ekle)
Gösterim-5.3:
class Kedi {//.. }class Kaplan extends Kedi { //..} |
Kedi sınıfından türeyen Kaplan sınıfı… İki sınıf arasındaki
ilişkiyi şöyle tarif edebiliriz, her Kaplan
bir Kedi dir. Yani her kaplan kedisel özellikler taşıyacaktır ama bu
özelliklerin üzerine kendisine bir şeyler eklemiştir. (yorum ekle)
Yazılış ifadesi olarak, türeyen sınıf isminin yanına
extends ifadesini koyarak, hemen
sonrasında kendisinden türetilme yapılan sınıfın kendisini yerleştiririz (bkz: gösterim-5.3). Yukarıdaki örneğimizi UML
diyagramında göstermeye çalışırsak; (yorum ekle)
Şekil-5.5. Kalıtım İlişkisi-I
Kedi ve Kaplan sınıflarımızı biraz daha geliştirelim,
Örnek-5.4: KediKaplan.java
(yorum ekle)
class Kedi { protected int ayakSayisi = 4 ; public void yakalaAv() { System.out.println("Kedi sinifi Av yakaladi"); } public static void main(String args[]) { Kedi kd= new Kedi() ; kd.yakalaAv() ; } }class Kaplan extends Kedi { public static void main(String args[] ) { Kaplan kp = new Kaplan(); kp.yakalaAv(); System.out.println("Ayak Sayisi = " + kp.ayakSayisi); } } |
Kaplan sınıfı Kedi sınıfından türemiştir. Görüldüğü üzere Kaplan sınıfının içerisinde ne yakalaAv() yordamı ne de ayaksayisi alanı tanımlanmıştır. Kaplan sınıfı bu özelliklerini
kendisinin ana sınıfı olan Kedi
sınıfından miras almıştır. (yorum ekle)
Kedi sınıfının içerisinde
tanımlanmış ayaksayisi alanı, protected erişim belirleyicisine sahiptir. Bunun
anlamı, bu alana aynı paket içerisinde olan sınıflar ve ayrı
paket içerisinde olup bu sınıftan türetilmiş olan sınıfların erişebileceğidir.
Böylece Kaplan sınıfı ister Kedi sınıfı ile aynı pakette olsun veya
olmasın, Kedi sınıfına ait global int ilkel (primitive) tipindeki alanına (ayaksayisi) erişebilir. (yorum ekle)
Her sınıfın içerisine main yordamı yazarak onları tek başlarına çalışabilir bir hale sokabiliriz (standalone application); bu yöntem sınıfları test etmek açısından iyidir.
Örneğin Kedi sınıfını çalıştırmak
için komut satırından java
Kedi veya Kaplan sınıfını çalıştırmak için java Kaplan yazılması yeterli
olacaktır. (yorum ekle)
Oluşturduğumuz her yeni sınıf otomatik ve gizli
olarak Object sınıfından türer. Object sınıfı Java programlama dili
içerisinde kullanılan tüm sınıfların tepesinde bulunur. (yorum ekle)
Örnek-5.5: YeniBirSinif.java (yorum ekle)
public class YeniBirSinif { public static void main(String[] args) { YeniBirSinif ybs1 = new YeniBirSinif(); YeniBirSinif ybs2 = new YeniBirSinif(); System.out.println("YeniBirSinif.toString()" + ybs1 ) ; System.out.println("YeniBirSinif.toString()" + ybs2 ) ; System.out.println("ybs1.equals(ybs2)"+ybs1.equals(ybs2)) ;// .... }} |
Uygulamamızın çıktısı aşağıdaki gibi olur:
YeniBirSinif.toString() YeniBirSinif@82f0dbYeniBirSinif.toString() YeniBirSinif@92d342ybs1.equals(ybs2) false
YeniBirSinif sınıfımızda, toString() ve equals() yordamları tanımlanmamasına rağmen bu
yordamları kullandık, ama nasıl ? Biz yeni bir sınıf tanımladığımızda, Java
gizli ve otomatik olarak extends
Object,
ibaresini yerleştirir. (yorum ekle)
Gösterim-5.4:
public class YeniBirSinif extends Object { |
Bu sayede Object
nesnesine ait erişebilir yordamları kullanabiliriz. Object nesnesine ait yordamlar aşağıdaki gibidir: (yorum ekle)
· clone(): Bu nesnenin (this) aynısını klonlar ve yeni nesneyi döndürür. (yorum ekle)
· equals(Object
obj):
obj referansına bağlı olan nesnenin, kendisine (this) eşit olup olmadığı kontrolü yapan yordam. (yorum ekle)
· finalize(): Çöp toplayıcısı tarafından
silinmeden önce çalıştırılan yordam. (yorum ekle)
· getClass(): Bu nesnenin (this) çalışma anındaki sınıf bilgilerini Class
nesnesi şeklinde geri döner. (yorum ekle)
· hashCode(): Bu nesnenin (this) hash kodunu geri döner.
· notify(): Bu nesnenin (this), monitöründe olan tek bir iş parçacığını (thread) uyandırır. (ilerleyen bölümlerde
inceleyeceğiz)
(yorum ekle)
· notifyAll(): Bu nesnenin (this), monitöründe olan tüm iş parçacıklarını (thread) uyandırır. (ilerleyen bölümlerde incelenecektir) (yorum ekle)
· toString(): Bu nesnenin (this), String tipindeki ifadesini
geri döner. (yorum ekle)
· wait(): O andaki iş parçacığının (thread) beklemesini sağlar; Bu bekleme notify()
veya notifyAll() yordamları sayesinde sona
erer. (yorum ekle)
· wait
(long zamanAsimi): O andaki iş parçacığının (thread), belirtilen süre kadar beklemesini sağlar (zamanAsimi); bu bekleme notify() veya notifyAll() yordamları sayesinde de sona
erdirilebilir. (yorum ekle)
·
wait (long zamanAsimi, int nanos): O
andaki iş parçacığının (thread), belirtilen gerçek süre
kadar (zamanAsimi+ nanos) beklemesini sağlar; bu
bekleme notify() veya notifyAll() yordamları sayesinde de
sona erdirilebilir. nanos parametresi 0-999999
arasında olmalıdır. (yorum ekle)
Kısacası, oluşturulan her yeni sınıf, yukarıdaki,
yordamlara otomatik olarak sahip olur. Bu yordamları yeni oluşan sınıfların
içerisinde tekrardan istediğimiz gibi yazabiliriz (uygun olan yordamları iptal
edebiliriz-override). Örneğin finalize() yordamı kendi sınıfımızın
içerisinde farklı sorumluluklar verebiliriz (çizgi çizen bir nesnenin, bellekten silinirken
çizdiği çizgileri temizlemesi gibi). Bu olaya, ana sınıfın yordamlarını iptal etmek (override) denir. Biraz sonra iptal etmek (override) konusunu daha detaylı bir şekilde incelenecektir. (yorum ekle)
Akıllara şöyle bir soru gelebilir, Kaplan sınıfı hem Kedi sınıfından hem de Object
sınıfından mı türemiştir? Cevap hayır. Java programlama dilinde çoklu kalıtım (multiple inheritance) yoktur. Aşağıdan yukarıya doğru gidersek, Kaplan sınıfı Kedi sınıfından türemiştir, Kedi
sınıfa da Object sınıfından (gizli ve otomatik olarak) türemiştir. Sonuçta Kaplan sınıfı hem Kedi sınıfının hem de Object
sınıfına ait özellikler taşıyacaktır. Aşağıdaki şeklimizde görüldüğü üzere her
sınıf sadece tek bir sınıftan türetilmiştir. Object sınıfı, Java programlama dilinde, sınıf hiyerarşinin en
tepesinde bulunur. (yorum ekle)
Şekil-5.6. Gizli Kalıtım
Çoklu kalıtım (multiple
inheritance),
bazı konularda faydalı olmasının yanında birçok sorun
oluşturmaktadır. Örneğin iki ana sınıf düşünün, bunların aynı isimde değişik
işlemler yapan yordamları bulunsun. Bu olay türetilen sınıfın içerisinde birçok
probleme yol açacaktır. Bu ve bunun gibi sebeplerden dolayı Java programlama
dilinde çoklu kalıtım yoktur. Bu sebeplerin detaylarını ilerleyen bölümlerde
inceleyeceğiz. (yorum ekle)
Java programlama dilinde çoklu kalıtımın
faydalarından yararlanmak için Arayüzler (Interface) ve dahili sınıflar (inner class) kullanılır. Bu konular yine ilerleyen bölümlerde inceleyeceğiz. (yorum ekle)
Tek bir sınıf içerisinde ilk
değerlerin nasıl alındığı 3. bölümde incelenmişti İşin içerisine birde kalıtım
kavramı girince olaylar biraz karışabilir. Kalıtım (inheritance) kavramı bir sınıftan, başka bir sınıf
kopyalamak değildir. Kalıtım kavramı, türeyen bir sınıfın, türetildiği sınıfa
ait erişilebilir olan özellikleri alması ve ayrıca kendisine ait özellikleri
tanımlayabilmesi anlamına gelir. Bir sınıfa ait nesne oluşurken, ilk önce bu
sınıfa ait yapılandırıcının (constructor) çağrıldığını önceki
bölümlerimizden biliyoruz. (yorum ekle)
Verilen örnekte, UcanYarasa nesnesi oluşmadan evvel, UcanYarasa sınıfının ana sınıfı olan Yarasa nesnesi oluşturulmaya çalışılacaktır.
Fakat Yarasa sınıfıda Hayvan sınıfından türetildiği için daha
öncesinde Hayvan sınıfına ait olan
yapılandırıcı çalıştırılacaktır. Bu zincirleme giden olayın en başında ise Object sınıfı vardır. (yorum ekle)
Örnek-5.6: IlkDegerVermeSirasi.java
(yorum ekle)
class Hayvan { public Hayvan() { System.out.println("Hayvan Yapilandiricisi"); }}class Yarasa extends Hayvan { public Yarasa() { System.out.println("Yarasa Yapilandiricisi"); }}class UcanYarasa extends Yarasa{ public UcanYarasa() { System.out.println("UcanYarasa Yapilandiricisi"); } public static void main(String args[]) { UcanYarasa uy = new UcanYarasa(); }} |

Şekil-5.7. Kalıtım ve ilk değer alma sırası
Object sınıfını bir kenara
koyarsak, ilk olarak Hayvan sınıfının
yapılandırıcısı çalışacaktır, daha sonra Yarasa
sınıfının yapılandırıcısı çalışacaktır ve en son olarak UcanYarasa sınıfının yapılandırıcısı çalışacaktır. Bu
yapılandırıcıların hepsi, fark edildiği üzere varsayılan yapılandırıcıdır (default
constructor). Uygulamanın çıktısı
aşağıdaki gibi olacaktır; (yorum ekle)
Hayvan YapilandiricisiYarasa YapilandiricisiUcanYarasa Yapilandiricisi
Ana sınıfa ait yapılandırıcı
çağırma işlemi, varsayılan yapılandırıcılar için otomatik olarak yürürken,
parametre alan yapılandırıcılar için olaylar biraz daha değişiktir. Kısacası,
ana sınıfın parametre alan yapılandırıcısını açık olarak super anahtar kelimesi ile
çağırmak gereklidir. Şöyle ki; (yorum ekle)
Örnek-5.7: IlkDegerVermeSirasiParametreli.java
(yorum ekle)
class Insan { public Insan(int par) { System.out.println("Insan Yapilandiricisi " + par); }}class ZekiInsan extends Insan { public ZekiInsan(int par) { super(par+1); //dikkat System.out.println("ZekiInsan Yapilandiricisi " + par); }}class Hacker extends ZekiInsan{ public Hacker(int par) { super(par+1); //dikkat System.out.println("Hacker Yapilandiricisi " + par); } public static void main(String args[]) { Hacker hck = new Hacker(5); }} |
Yukarıdaki örneğimizde, her
sınıf, yapılandırıcısına gelen değeri bir arttırıp ana sınıfının
yapılandırıcısına göndermektedir. Fark edildiği üzere ana sınıfın parametre
alan yapılandırıcısını çağırırken super anahtar kelimesini kullandık. Uygulamanın
çıktısı aşağıdaki gibidir. (yorum ekle)
Insan Yapilandiricisi 7ZekiInsan Yapilandiricisi 6Hacker Yapilandiricisi 5
Dikkat edilmesi gereken bir
başka husus, aynı this anahtar kelimesinin
kullanılışı gibi super anahtar kelimesi de içinde bulunduğu
yapılandırıcının ilk satırında yer almalıdır. (yorum ekle)
Örnek-5.8: IlkDegerVermeSirasiParametreliAmaHatali.java
(yorum ekle)