Java – Object Oriented Kavramı II

Object Oriented Programlama mantığından bahsetmeye başlamıştım, fakat bunun bir yazıda halledilemeyeceğini anlayınca yarıda bırakmıştım. Bu yazıda devam ediyorum. Bir önceki yazıya Java – Object Oriented Kavramı I linkinden ulaşabilirsiniz. Object, class, inheritance, interface kavramlarını kısaca anlattıktan sonra, bir class’dan object oluşturma sırasında gerçekleşen işlemleri yani decleration, instantiation ve initialization işlemlerini anlatmıştım. Bu yazımda kaldığımız yerden devam ederek, static keyword’ünden, final keyword’ünden, methodlardan, overloading kavramından, access modifier‘lardan bahsetmeyi düşünüyorum. Çok uzatmadan başlayalım ve mümkün olduğunca kısa yazarak sıkmamaya çalışalım 🙂

Static:

Burada static‘den bahsetmeden önce bir önceki yazıda anlattığım instantiation işlemi yani class’dan object üretilmesi işlemini hatırlayalım. Bunun için referans tutan bir değişken ile hafıza alanı açıyor ve o alana constructor ile değerleri atıyorduk.

Yukarıdaki örnekte Car class‘ından yukarıdaki gibi üretilen her car object‘i için bir ID tutmak istediğinizi düşünelim. Bu ID’lerin çakışmaması için de auto_increment kullanacağınızı ve mevcut durumda kaç tane Car Object‘i olduğunu bilmeniz gerektiğini düşünün. Bu durumda yukarıdaki gibi memory’de bulunan Car Object’lerinin sayısını tutan değişken herhangi bir Car Object‘ine ait olamaz. Olsa olsa Car class‘ına ait olabilir. Fakat class’ların sadece bir tasarım olduğunu ve new keyword’ü ile object türetilmediği sürece değişkenlerinin veya methodlarının bir karşılığı olmadığını söylemiştik. İşte bunun gibi bir durumda class değişkeni oluşturabilmenizi sağlamak amacıyla static keyword’ü kullanılır.

Static keyword’ünün kullanımına birçok örnek verilebilir. Tüm Car Object‘leri için aynı olacak bir değişken varsa, bunu bir field yapıp her Car Object‘inde tekrar tekrar oluşturmak, hafızada fazladan birçok alanın kullanılmasına neden olacaktır. Ayrıca bu değişkende bir değişiklik olması gerektiğinde ise tüm Object‘lere erişip o değişkeni değiştirmek de çok maliyetli olacaktır. İlk paragrafta verdiğim örneği uygulayalım;

class Car {

    private String color;
    private int speed;
    private int gear;

    //* her car object'inin kendi id değişkeni olacak.
    private int id;

    //* object'lerden bağımsız tek countCars değişkeni olacak.
    private static int countCars = 0;

    public Car () {
         this("black", 0, 1);
    }

    public Car (String newColor, int newSpeed, int newGear) {
         color = newColor;
         speed = newSpeed;
         gear  = newGear;
         id = countCars++;
    }

    public int getID() {
         return id;
    }

    //* genelde static değeri yönetmek için static method kullanılır.
    public static int getNumberOfCars() {
         return countCars;
    }

    public void changeColor(String newValue) {
         color = newValue;
    }

    public void changeGear(int newValue) {
         gear = newValue;
    }

    public void speedUp(int increment) {
         speed = speed + increment;
    }

    public void applyBrakes(int decrement) {
         speed = speed - decrement;
    }
}

Yukarıdaki gibi bir class’dan c1, c2 ve c3 şeklinde 3 adet Car Object‘i oluştursak, memory’de şöyle bir durum ortaya çıkacaktır;

Gördüğünüz gibi static değişkenler her nesne için tek tek oluşturulmazlar, bu yüzden bunlara Object değişkenleri yerine Class değişkenleri denir. Bu değişkenlere herhangi bir object üzerinden ulaşılabilirken, hiçbir object üretilmese bile Class üzerinden de ulaşılabilir, ki bu yöntem en doğrusudur.

Buraya kadar static değişkenler ile ilgili bahsettiğim şeyler, static methodlar içinde geçerlidir. Aynı şekilde bunlara da class methodları denir ve object üretilmese dahi Car.getNumberOfCars() şeklinde ulaşılabilir.

Final:

Bir değişkenin önüne final keywordü konduğunda bu değişkenin değerinin değiştirilmeyeceğini belirtmiş olursunuz. Bundan sonra eğer değeri değiştirmeye kalkarsanız compiler hata verecektir. Bir class’da bir değişken final tanımlanırsa o class’dan türeyen her object için ayrı bir son değeri olacaktır. Eğer tam olarak sabit bir değer tanımlamak istersek final keywordü tek başına yeterli olmayacaktır (Bknz. constants).

Final keywordü bir methodun önüne yazılırsa yine aynı şekilde bu methodün son hali olduğunu ve artık değiştirilmeyeceğini belirtmektedir. Eğer burda ne kastettiğimi anlamadıysanız overriding kavramınından bahsetmem gerekebilir (Bknz. overriding).

Constants:

Bir değişkenin önüne hem static hemde final keywordleri konulursa o değişken constant olarak tanımlanmış olur. Static ile o değişkenin tek olmasını ve final ile de değiştirilemez olmasını sağlarsınız. Örneğin;


//* adettendir, constantlar büyük harf yazılır,
//* kelimeler arasına "_" (alt tire) konur.

public static final double PI = 3.1415926;

Constant değişkenler tanımlandıktan ve değer atandıktan sonra projeyi compile ederseniz, compiler değişkenin geçtiği her yere değişkenin değerini yazacak ve o şekilde compile edecektir. Eğer değeri değiştirmek isterseniz projeyi yeniden compile etmeniz gerekir.

Methods:

Methodların ne olduğundan ve neden method yazdığımızdan bahsettim. Burada kısaca işin “nasıl”ından bahsedeceğim. Bir method tanımlamak için minimum gereksinimler bir dönüş tipi, bir method ismi, açılıp kapanan normal parantez ( ), ve açılıp kapanan süslü parantez { } ‘dir. Ama genelde methodlar şöyle tanımlanır;

  • Bir access modifier: methoda erişimi kontrol etmek amacıyla kullanılır (Bknz. access modifiers).
  • Bir return tipi: methodun döndüreceği data tipidir, yoksa void yazılır.
  • Bir method ismi: method ismi.. burada kabul görmüş standartlara uyulmalı.
  • Parantez içinde parametreler: virgülle ayrılmış, “parametre tipi ve adı” çiftleri.
  • Exception listesi: method içerisinde oluşabilecek hataların listesi (Bknz. exceptions).
  • Bir method gövdesi: Kıvrık parantezler içerisinde methodu oluşturan kodlar.

Yukarıda belirtilen bileşenler içerisinde sadece iki tanesi method imzası denen ve methodları birbirinden ayıran yapıyı oluşturur. Method ismi ve parametre tipleri. Örneğin topla(int, int);

Aşağıdaki 2 methodun imzası aynı olduğu için compiler ayırt edemeyecek ve hata verecektir;


public void topla (int sayi1, int sayi2) {
     int sonuc = sayi1 + sayi2;
     System.out.println(sonuc);
}

public int topla (int x, int y) {
     int z = x + y;
     return z;
}

Overloading:

Hemen yukardaki örnekten devam edelim. Java dili aynı isimde iki method tanımlamanıza izin verir. Buna overloading denir. Tek şart bu 2 methodun imzalarının farklı olmasıdır. İsimleri aynı olduğuna göre imzaları farklı yapmanın tek yolu farklı sayıda ve/veya tipte parametre kullanmaktır. Burada dönüş tipinin bir önemi olmadığına dikkat ediniz. Yukarıda örnekte verilen topla methodunu overload edelim;


public int topla (int sayi1, int sayi2) {
     int sonuc = sayi1 + sayi2;
     return sonuc;
}

public int topla (int sayi1, int sayi2, int sayi3) {
     int sonuc = sayi1 + sayi2 + sayi3;
     return sonuc;
}

public double topla (double sayi1, double sayi2){
     double sonuc = sayi1 + sayi2;
     return sonuc;
}

Yukarıdaki örnekte compiler hepsini ayırt edebileceği için hata vermeyecek ve çağırdığınızda verilen parametrelere göre uygun methodu çalıştıracaktır. Constructor‘larında aynı yöntemle yazılabildiğini hatırlayın.

Access Modifiers:

Şimdiye kadar isimleri çokça geçmiş olsada burada kısaca değinmek istiyorum. public, private ve protected olarak 3 adet access modifier‘den bahsedeceğim. Bunlara ek olarak hiçbir access modifier yazmadığınız durumlarda ne olduğuna da değineceğim. Aşağıdaki örnek tüm olayı özetleyecektir. Öncelikle 4 adet class’ımız olsun;

MyClass:

package firstPackage;

class MyClass {
     //* my fields here
     public int x;
     private string y;
     protected double z;
     int t;
}

YourClass:

package firstPackage;

class YourClass {
     //* your class body goes here
}

MySubClass:

package secondPackage;

class MySubClass extends MyClass {
     //* my sub class body goes here
}

TheirClass:

package secondPackage;

class TheirClass {
     //* their class body goes here
}

Yukarıdaki 4 class’ın MyClass class’ındaki x, y, z ve t değişkenlerine erişimleri şu şekildedir;

Modifier MyClass YourClass MySubClass TheirClass
X + + + +
Y +
Z + + +
T + +

Örneği incelediğinizde public alanlara heryerden erişilebildiğini görürsünüz. Buna karşın private alanlara sadece bulunduğu class içerisinden erişilebilir. Herhangi bir modifier kullanılmadan tanımlanan değişkenlere sadece tanımlandığı class‘dan ve aynı package içerisindeki diğer class‘lardan erişilebilir. Buna package-private denir. protected ise package-private gibi class’ın kendisi ve aynı package’da bulunan diğer class’lar ve ek olarak başka bir package’da bulunsa bile sub-class’lar erişebilir.

Bu yazımda değinmeyi düşündüğüm bazı konuları ayrı bir yazı şeklinde ele almaya karar verdim. Sadece interface ile ilgili; “neden interface oluşturulur?”, “nasıl yazılır?”, “nasıl type olarak kullanılır?”, “api nedir?”, “interface-multi inheritance ilişkisi nedir?” gibi soruları ve cevaplarını içeren bir yazı olabilir.

Daha sonra sadece inheritance ile ilgili, “overriding nedir?”, “hiding nedir?”, “polymorphism nedir?”, “Object class’ı ve methodları nelerdir?”, “abstract class nedir?” gibi soruları ve cevaplarını içeren bir yazı olabilir. Bunlarla birlikte 4 yazı olacak, bence yeterli. Biz durmazsak anlatacak çok şey var çünkü 🙂

~ Şarkı ~