Karar Verme – II

Bir önceki yazımızla birlikte javada karar verme konusuna giriş yapmıştık. Öncelikle if ifadesinin tek başına kullanılmasına değinmiştik. Örneğimizde kartta para varsa kulaklık satın alma işlemini gerçekleştirmiştik. Bu örneğin ardından kartta para varsa kulaklığı al ve kartta para yoksa ekrana “Bakiye yetersiz” yazdıran bir if-else örneği yapmıştık.

Ancak bazen öyle durumlar olur ki, karar verme anında ikiden fazla seçeneğimiz olur. Üzerinde uğraştığımız kredi kartları örneğine odaklanarak şöyle bir durum düşünebiliriz; Bir kartın tipi gold ise o karta 4 taksit uygulanır, kartın tipi normal ise o karta 2 taksit uygulanır ve kart tipi bunlardan farklı ise o karta taksit uygulanmaz. Bu daha uzun bir zincir de olabilirdi. Örneğin platium karta n taksit, silver karta t taksit olabilirdi. Bütün bu durumları çok sayıda if-else bloğu ile halletmek makul olmayacaktır.

Javada bu konu da çözüme kavuşturulmuştur. Bu yazıda ilk olarak bunu inceleyeceğiz. Öncelikle mevcut uygulamamızda bir takım değişiklikler yaparak main methodumuzu daha temiz bir hale getirelim. Yazdığımız kodu kaybetmemek için, main methodumuzun içerisindeki kodu, yeni, ayrı bir kartOrnegi() methodumuza alıyoruz. Bu methodu main içerisine commentli bir şekilde ekleyeceğiz. Methodu çalıştırmak istediğimizde, method çağrısını un-comment etmek yeterli olacaktır.

public class TestClass {

	public static void main(String[] args) {
		
		// İlk örneğin çalışması için method çağrısının 
		// önündeki comment işaretini (//) kaldırınız.
		
		//kartOrnegi();

	}

	private static void kulaklikSatinAl(KrediKarti kart) {
		
		if(kart.getBakiye() >= 750) {
			System.out.println("Kulaklık satıldı");
			int yeniBakiye = kart.getBakiye() - 750;
			kart.setBakiye(yeniBakiye);
		} else {
			System.out.println("Bakiye yetersiz");
		}
		
	}
	
	private static void kartOrnegi() {
		
		KrediKarti normalKart = new KrediKarti();
		normalKart.setKartTipi("normal");
		normalKart.setBakiye(1000);
		
		KrediKarti goldKart = new KrediKarti();
		goldKart.setKartTipi("gold");
		goldKart.setBakiye(10000);

		System.out.println("Normal kart önceki bakiye:" + normalKart.getBakiye());
		kulaklikSatinAl(normalKart);
		System.out.println("Normal kart sonraki bakiye:" + normalKart.getBakiye());
		
		System.out.println("Gold kart önceki bakiye:" + goldKart.getBakiye());
		kulaklikSatinAl(goldKart);
		System.out.println("Gold kart sonraki bakiye:" + goldKart.getBakiye());
		
		System.out.println("Normal kart önceki bakiye:" + normalKart.getBakiye());
		kulaklikSatinAl(normalKart);
		System.out.println("Normal kart sonraki bakiye:" + normalKart.getBakiye());
		
	}
	
}

Yukarıda yaptığımız işlem tamamen çalışma alanımızı temiz tutmak içindi. Şimdi de ikinci örneğimiz için yeni bir method kodlayacağız. Bu methodumuzun ismini kartOrnegiCokluSecenek() olarak belirliyoruz. Bu method içerisinde KrediKarti sınıfından 4 tane instance oluşturacağız. İsimlerini ise sırasıyla platiniumKart, silverKart, bronzeKart ve normalKart koyuyoruz. Bu nesnelerin kartTipi fieldlarını da kart ismine uygun olacak şekilde setleyiniz.

Farklı kartlar için bankanın bir kampanya yürüttüğünü ve kart tipine göre taksit yapıldığını düşünelim. Örneğin platinium için 8 taksit, silver için 6 taksit, bronze için 4 taksit uygulansın. Normal kartlarda ise taksit uygulaması olmasın. Banka geriye kalan tüm kart tipleri için ise 2 taksit uygulasın.

Bir başka methodumuz ise kendisine gelen kartın tipine bakıp, kaç taksit yapıldığını geriye dönsün. Bu methodumuzun ismi ise getTaksitSayisi() olsun. TestClass içerisine bu methodları kodlayacağız. İlk adım olarak getTaksitSayisi() methodumuzun boş olacağını belirteyim. Bu method tahmin edeceğiniz üzere parametre olarak bir KrediKarti nesnesi alacaktır.

	private static void kartOrnegiCokluSecenek() {
		
		KrediKarti platiniumKart = new KrediKarti();
		platiniumKart.setKartTipi("platinium");
		
		KrediKarti silverKart = new KrediKarti();
		silverKart.setKartTipi("silver");
		
		KrediKarti bronzeKart = new KrediKarti();
		bronzeKart.setKartTipi("bronze");
		
		KrediKarti normalKart = new KrediKarti();
		normalKart.setKartTipi("normal");
		
	}
	
	private static int getTaksitSayisi(KrediKarti kart) {
		//Derleme hatasını engellemek için 
		// geçici bir return değeri kullanıyoruz
		return -1;
	}

Bu noktada karar verme aşamasında kullandığımız if ve if-else bloklarının dışında yeni bir yapı kullanacağız. Bu yapımıza else-if yapısı ismini veriyoruz. Yukarıda bahsettiğimiz banka kampanyasının kodlamasını da bu yapıyı kullanarak yapacağız. Taksit sayısını get eden methodumuzu aşağıdaki gibi düzenliyoruz.

	private static int getTaksitSayisi(KrediKarti kart) {
		
		String kartTipi = kart.getKartTipi();
		
		if (kartTipi.equals("platinium")) {
			return 8;
		} else if(kartTipi.equals("silver")) {
			return 6;
		} else if(kartTipi.equals("bronze")) {
			return 4;
		} else if(kartTipi.equals("normal")) {
			return 1;
		} else {
			return 2;
		}
		
	}

Kodu biraz incelersek, yeni bir ifade ile karşılaşırız. Bu ifade else if ifadesidir. İfadeyi kullanabilmek için bir if ifadesi öncelikle bulunmalıdır. Bu ifadenin görevi, ikiden fazla seçenek olduğunda bunu işleme tek seferde alabilecek bir yapıyı oluşturmaktır.

Burada bir de minik bir detay daha eklemek istiyorum. Dikkat ettiyseniz String ifadeleri kıyaslarken == operatörünü kullanmadım. Bunu sonraki konularda ele alacağız. Ancak String ifadeleri kıyaslarken, geriye true ya da false değer dönen equals() methodunu kullanmanız gerektiğini, aklınızın bir köşesine yazınız.

Nihayetinde bankanın kampanyasının kodlamaya dökülmüş hali bu şekildedir. Şimdi bunu test etme zamanı. kartOrnegiCokluSecenek() methodumuzu uygun şekilde düzenliyoruz.

	private static void kartOrnegiCokluSecenek() {
		
		KrediKarti platiniumKart = new KrediKarti();
		platiniumKart.setKartTipi("platinium");
		
		KrediKarti silverKart = new KrediKarti();
		silverKart.setKartTipi("silver");
		
		KrediKarti bronzeKart = new KrediKarti();
		bronzeKart.setKartTipi("bronze");
		
		KrediKarti normalKart = new KrediKarti();
		normalKart.setKartTipi("normal");
		
		System.out.println("=== Taksit sayıları ===");
		System.out.println("platinium:" + getTaksitSayisi(platiniumKart));
		System.out.println("silver:" + getTaksitSayisi(silverKart));
		System.out.println("bronze:" + getTaksitSayisi(bronzeKart));
		System.out.println("normal:" + getTaksitSayisi(normalKart));
		
	}

Bununla birlikte bu methodu main methodumuzdan çağırılacak şekilde kodumuzu ayarlıyoruz. Ardından ise main methodumuzu çalıştıracağız.

	public static void main(String[] args) {
		
		// İlk örneğin çalışması için method çağrısının 
		// önündeki comment işaretini (//) kaldırınız.
		
		//kartOrnegi();

		kartOrnegiCokluSecenek();
	}
Çıktı:
=== Taksit sayıları ===
platinium:8
silver:6
bronze:4
normal:1

Bir ufak pratik olarak kartTipi gold olan bir nesne daha yaratınız ve yukarıda yaptığımız gibi ekrana taksit sayısını yazdırınız. Gold kart olarak ekranda 2 taksit olduğunu göreceksiniz.

Yukarıda yaptığımız örneği java alternatif bir yöntem ile daha yapmamıza izin verir. Bu yöntem switch-case yöntemidir. Örneği hiç değiştirmeden yeni bir method ile bunu da inceleyelim. Öncelikle sözdizime değinmek istiyorum. Bir switch case ifadesi kabaca şu şekilde oluşur:

switch (anahtar) {
	case alternatifDeğer:
		//Çalışacak kod
		break;
	case alternatifDeğer2:
		//Çalışacak kod
		break;
	//...
	//diğer case ifadeleri
	//...
	default:
		//Çalışacak kod
		break;
}

Dikkat ederseniz çok sayıda farklı durumu switch-case ile yönetebiliyoruz. Ancak sözdiziminde bir takım detaylar ilgi çekmekte. Bunun sebeplerine gelirsek;

  • Öncelikle switch ifadesi çalışır ve bir değer üretir.
  • Üretilen bu değer her case ifadesi ile karşılaştırılır.
  • Eğer bir eşleşme varsa o blok çalıştırılır.
  • Eğer bir eşleşme yoksa default içerisinde yazılmış kod çalışır.
  • Bir kod çalıştıktan sonra break ile switch akışı kırılır ve ifadenin çalışması sonlandırılır.

Şimdi de taksit sayılarını ekrana yazdıran bir method daha yazalım. Bu method içerisinde birlikte switch-case kullanalım. Methodumuzun ismi ise getTaksitSayisiSwitchCase() olsun. Öncelikle bu methodu tek başınıza kodlamaya çalışın. Sonra aşağıdaki kod ile karşılaştırın.

	private static void getTaksitSayisiSwitchCase(KrediKarti kart) {

		String kartTipi = kart.getKartTipi();

		switch (kartTipi) {
		case "platinium":
			System.out.println("Platinium kart için taksit sayısı:" + 8);
			break;
			
		case "silver":
			System.out.println("Silver kart için taksit sayısı:" + 6);
			break;
			
		case "bronze":
			System.out.println("Bronze kart için taksit sayısı:" + 4);
			break;
			
		case "normal":
			System.out.println("Normal kart için taksit sayısı:" + 1);
			break;

		default:
			System.out.println("Diğer kart için taksit sayısı:" + 2);
			break;
		}
	}

kartOrnegiCokluSecenek() methodumuzun içerisinde bulunan nesneler ile bu methodu test edelim ve çıktıyı else-if yapısındaki çıktıyla karşılaştıralım. Methodumuza şu satırları ekliyoruz ve ardından uygulamamızı çalıştırıyoruz.

		System.out.println("=== Taksit sayıları / switch-case ===");
		
		getTaksitSayisiSwitchCase(platiniumKart);
		getTaksitSayisiSwitchCase(silverKart);
		getTaksitSayisiSwitchCase(bronzeKart);
		getTaksitSayisiSwitchCase(normalKart);
Çıktı:
=== Taksit sayıları ===
platinium:8
silver:6
bronze:4
normal:1
=== Taksit sayıları / switch-case ===
platinium:8
silver:6
bronze:4
normal:1

Javanın temel karar verme ifadelerini son iki yazımız ile inceledik. Umarım faydalı olmuştur. Yazı içerisinde bazı ufak detaylara da dikkat çekmek istedim. Örneğin bir method geriye değer dönerken diğeri dönmüyor. Bu durumda ekran yazdırmak için farklı yaklaşımlar sergiledik. Siz iki methodu da değer dönen veya dönmeyen hale getirip pratik yapabilirsiniz. Ayrıca String ifadelerin nasıl kıyaslanacağına değindik. Üzerinde çalıştığımız örnekleri sınırlı sayıda tuttuk, ancak, bu örnekleri daha da uzatabileceğimizi gözlemledik. Editörümüzde şu ana kadar 4 ayrı proje oluşturduk. Bunlar zamanla daha da artacaktır. Siz bütün bu projeler dışında yeni bir proje oluşturup, farklı örnekler yapıp, tüm öğrendiklerinizi bir arada kullanmaya çalışın. Unutmayın, pratik mükemmelleştirir. Sonraki yazıda görüşmek üzere.

Karar Verme

Karar verme, belirli bir olgunun gerçekleşmesi veya gerçekleşmemesi durumunda, elimizdeki seçeneklerden hangisinin kullanılabilir olduğunun tayin edilmesidir. Karar verme eylemi, akış şemalarımızda sıklık ile karşımıza çıkmaktadır. Programlamanın temellerinden olan bu durumu, javada da karşılayabilmekteyiz.

Bu yazımızda kullanmak üzere, içerisinde TestClass’ı bulunan bir proje oluşturuyoruz ve ismini 04 – DecisionMaking olarak belirliyoruz. Bu konuyu işlerken yine gerçek dünyadan bir örnekle ilerleyeceğiz. Uygulamamızın içerisinde KrediKarti isminde yeni bir sınıf yaratıyoruz. Bu sınıfımız iki field barındırmalıdır. Fieldlardan bir tanesi String değer taşıyan kartTipi diğeri ise sayısal bir değer tutan bakiyedir. Bu alanların erişim düzenleyicileri ise private olmalı ve alanlara getter/setter vasıtasıyla ulaşılmalıdır. Sınıfı öncelikle kendiniz yazıp, sonrasında aşağıdaki kod ile karşılaştırınız.

public class KrediKarti {
	
	private String kartTipi;
	
	private int bakiye;

	public String getKartTipi() {
		return kartTipi;
	}

	public void setKartTipi(String kartTipi) {
		this.kartTipi = kartTipi;
	}

	public int getBakiye() {
		return bakiye;
	}

	public void setBakiye(int bakiye) {
		this.bakiye = bakiye;
	}

}

Şimdi main methodumuza gidiyoruz ve iki ayrı değişken yaratıyoruz. İki değişkenimizin de tipi KrediKarti olup; değişkenlerden bir tanesinin ismi normalKart ve diğeri ise goldKart olacaktır. KrediKarti sınıfına ait bu instanceların, kendi içlerindeki kartTipi alanları sırasıyla “normal” ve “gold” olarak setlenmeli, bakiyeleri ise 1000 ve 10000 olmalıdır. Kodunuzu kendiniz yazdıktan sonra yine aşağıdaki blokla karşılaştırınız.

public class TestClass {

	public static void main(String[] args) {

		KrediKarti normalKart = new KrediKarti();
		normalKart.setKartTipi("normal");
		normalKart.setBakiye(1000);
		
		KrediKarti goldKart = new KrediKarti();
		goldKart.setKartTipi("gold");
		goldKart.setBakiye(10000);
		
	}
	
}

Bu kısa kodlama seansından sonra bu iki obje ile neler yapabileceğimizi düşünelim. Öncelikle bunlar bakiyeleri olan kartlar olduğu için, bu bakiyeleri nasıl karar verme noktası olarak kullanacağımızı hayal edebiliriz. Bu kartlar elinizde olsaydı ve değeri 750 birim olan bir kulaklık almak isteseydiniz, her iki kartın da bunu sağlıyor olması gerekirdi. Ancak normalKart bu kulaklığı bir kere alabiliyorken goldKart burada size farklı bir sonuç sağlayacaktır. O halde kart bakiyesini karar verme noktası olarak kullanabiliriz. Gelin kulaklık satma işlevini gerçekleştiren ve parametre olarak KrediKarti tipinde bir input alan methodu, test sınıfımıza ekleyelim. Bu methoda gönderilen kart objesinin bakiyesi 750 birimi karşılayabiliyorsa, konsola çıktı olarak “Kulaklık satıldı” yazsın ve hemen ardından karttan 750 birim bakiye eksiltsin. Methodumuz şu şekilde olacaktır.

	private static void kulaklikSatinAl(KrediKarti kart) {
		
		if(kart.getBakiye() >= 750) {
			System.out.println("Kulaklık satıldı");
			int yeniBakiye = kart.getBakiye() - 750;
			kart.setBakiye(yeniBakiye);
		}
		
	}

Öncelikle şu ana kadar yaptığımız örnekler arasında, programlamaya gerçekten en yakın durduğumuz örneğin bu olduğunu belirtmek isterim. Yukarıdaki method her ne kadar böyle bir özellik taşısa da, normal şartlarda tasarımını doğru bulacağım bir kodu temsil etmiyor. Unutmayın ki, bu yazıların amacı belli başlı kalıpları öğretmektir. Dolayısı ile örneklerdeki hedefimiz doğru tasarım yapmak değil, konuyu benimsetmektir. Eğer bir miktar programlama bilginizle bu yazıları okuyorsanız ve size yanlış gelen şeyler varsa, lütfen bu bahsettiğim önceliği gözden kaçırmayınız.

Bu kısa bilgiden sonra methodu incelemeye alalım. Method henüz değinmediğimiz bir sebeple static anahtar sözcüğünü imzasında taşımaktadır. Bu noktaya takılmadan devam edelim. Methodumuz TestClass sınıfımızda bulunan, main methodundan sonraki ikinci methoddur. Methodların sınıf içerisinde satır numarası olarak yerleri önemli değildir. Dolayısı ile buradaki ikinci ifadesi sizi yanıltmasın. Methodumuzun dönüş tipi voiddir. Yani geriye bir bilgi dönmez. Methodumuz içerisine KrediKarti tipinde bir parametre alır. Bu durumu kartı karşınızdaki kişiye vermek gibi düşünebilirsiniz.

Methodun içerisine baktığımızda ise bir if bloğu içerisinde kulaklığın satıldığını konsola yazdıran ve hemen ardından kart bakiyesinden 750 birim düşmekte olan bir kod görürüz. Buradaki yöntemde, mevcut bakiye get edilmiş, get edilen değerden 750 birim çıkarılmış ve yeniBakiye ismindeki bir int değişkene atanmıştır. Sonrasında bu int değişken ile güncel bakiye setlenmiştir.

Gelelim bu kodun karar verdiğimiz kısmına. buradaki if ile başlayıp hemen ardından parantezle devam eden kısım bizim karar noktamızdır. Burada öncelikle parantez içerisindeki ifadeye odaklanalım.

if(kart.getBakiye() >= 750)
Karar noktamız

Operatörler yazımızdan da hatırlayacağınız üzere >= ifadesi bir operatördür. Bu operatör sağında ve solunda bulunan ifadeleri kıyaslar. Eğer soldaki ifade sağdakinden büyükse veya eşit ise geriye true döner. Bu durumda ifademizi şu şekilde okuyabiliriz : if(condition), yani konuşma diline yaklaşmak istersek “Eğer şartımız doğru ise” şeklinde belirtebiliriz. Bu durumun bir sonucu olarak, eğer şart doğru ise if bloğundaki kod çalışır. Yani bu methoda gönderilen kartın bakiyesi 750’den büyük veya eşit ise blok çalışır. Gelin bunu birlikte deneyimleyelim. TestClass’ın durumu test etmek üzere düzenlenmiş son halini aşağıya yapıştırıyorum ve ardından çalıştırıp aldığım çıktısını ekliyorum.

public class TestClass {

	public static void main(String[] args) {

		KrediKarti normalKart = new KrediKarti();
		normalKart.setKartTipi("normal");
		normalKart.setBakiye(1000);
		
		KrediKarti goldKart = new KrediKarti();
		goldKart.setKartTipi("gold");
		goldKart.setBakiye(10000);

		System.out.println("Normal kart önceki bakiye:" + normalKart.getBakiye());
		kulaklikSatinAl(normalKart);
		System.out.println("Normal kart sonraki bakiye:" + normalKart.getBakiye());
		
		System.out.println("Gold kart önceki bakiye:" + goldKart.getBakiye());
		kulaklikSatinAl(goldKart);
		System.out.println("Gold kart sonraki bakiye:" + goldKart.getBakiye());
		
	}

	private static void kulaklikSatinAl(KrediKarti kart) {
		
		if(kart.getBakiye() >= 750) {
			System.out.println("Kulaklık satıldı");
			int yeniBakiye = kart.getBakiye() - 750;
			kart.setBakiye(yeniBakiye);
		}
		
	}
	
}
Çıktı:
Normal kart önceki bakiye:1000
Kulaklık satıldı
Normal kart sonraki bakiye:250
Gold kart önceki bakiye:10000
Kulaklık satıldı
Gold kart sonraki bakiye:9250

Gördüğünüz gibi bakiye başarılı şekilde düşüyor ve kulaklığın alındığına dair ibareleri görüyoruz. Peki normalKart ile ikinci bir satın alma işlemi daha yapmak isteseydik, yani bakiyemiz olmasaydı, neler olurdu? Tahmin edebildiniz mi? main methoduna aşağıdaki kod parçasını ekleyip kodu tekrar çalıştırdığımızda çıktı şu şekilde olacaktır.

		System.out.println("Normal kart önceki bakiye:" + normalKart.getBakiye());
		kulaklikSatinAl(normalKart);
		System.out.println("Normal kart sonraki bakiye:" + normalKart.getBakiye());
Çıktı:
Normal kart önceki bakiye:1000
Kulaklık satıldı
Normal kart sonraki bakiye:250
Gold kart önceki bakiye:10000
Kulaklık satıldı
Gold kart sonraki bakiye:9250
Normal kart önceki bakiye:250
Normal kart sonraki bakiye:250

Görüleceği üzere kulaklığın satıldığına dair bir ifade olmadan önceki ve sonraki bakiye ekrana basılmış. Bu da if şartının çalıştığını, bunun sonucunda false değer geldiği için bloğun kendisinin çalıştırılmadığını göstermektedir. Peki biz bunu da belirtmek istesek, yani eğer bakiye yoksa “Bakiye yetersiz” yazdırmak istesek ne yapardık? Bir if bloğu daha eklemek iş görecektir. Ancak bu durumda mantıksal bir problem oluşur. Bu iki şartın bir arada bulunma olasılığı yok. Yani hem bakiye olmaması hem de kulaklığı almanız olası değil. Dolayısı ile iki işi ayrı ayrı yapmak biraz verimsiz gözüküyor. Yani şartlardan biri çalışırsa, diğerinin çalışmaması makul gözüküyor. İşte bu durumu javada çözebiliyoruz. if ifadesinin kardeşi olan ve birlikte kullanılan else ifadesi bunu bize sağlamaktadır. if-else kullanımını şu şekilde temsil edebiliriz:

if(şart) { 
	//şart doğru ise çalışır
} else { 
	//şart doğru değil ise çalışır
}

Dikkat ederseniz else ifadesinin yanında bir parantez bulunmuyor. Dolayısı ile if koşulunun başarısız olmasının, else bloğundaki kodun çalışması için yeterli olduğunu söyleyebiliriz. O halde bunu deneyip görelim. kulaklikSatinAl methodumuzun içeriğini şu şekilde düzenleyelim.

	if(kart.getBakiye() >= 750) {
		System.out.println("Kulaklık satıldı");
		int yeniBakiye = kart.getBakiye() - 750;
		kart.setBakiye(yeniBakiye);
	} else {
		System.out.println("Bakiye yetersiz");
	}

Hemen ardında programı tekrar çalıştırıp çıktısına bakalım.

Çıktı:
Normal kart önceki bakiye:1000
Kulaklık satıldı
Normal kart sonraki bakiye:250
Gold kart önceki bakiye:10000
Kulaklık satıldı
Gold kart sonraki bakiye:9250
Normal kart önceki bakiye:250
Bakiye yetersiz
Normal kart sonraki bakiye:250

Az öncekinden farklı olarak “Bakiye yetersiz” ifadesini konsolda göreceğiz. Böylece iki ayrı if yazmadan bu durumu nasıl yöneteceğimizi görmüş olduk.

Bir sonraki yazımızda da karar verme konusuna devam edeceğiz. Bu noktadan sonra işler tamamen ciddileşmiş gözüküyor. Daha evvel önerdiğim gibi, geçmiş konuları zaman zaman tekrar etmeniz size faydalı olacaktır. Yazılarda mümkün olduğunca önceki konulara referans vermeye çalışıyorum. Eğer bu referansları çok sık kontrol etme ihtiyacı duyuyorsanız tekrar şart demektir. Bir sonraki yazıda görüşmek üzere!

Erişim Düzenleyiciler

Getter ve setter konumuzdan sonra, javadaki başka bir önemli kavrama da değinmek istiyorum. Kod bloklarımızda sıklıkla gördüğümüz public anahtar sözcüğünün ne işe yaradığını, bu sözcüğün alternatiflerini ve bütün bu alternatiflerin ne anlama geldiğini bu yazıda ele alacağız.

Öncelikle encapsulation kavramına değinmek gerekiyor. Zira bir önceki konumuzda bazı eksiklerimiz olduğunu söylemiştik. Encapsulation, yani sarmalama, nesneye yönelik programlamanın en temel kavramlarındandır. Bu kavramı, temel olarak bilginin uygun şekilde saklanması olarak düşünebiliriz. Peki ama neden bir bilgiyi saklamak isteyelim ki? Bunun sebebine aslında getter ve setter methodlarımızda değinmiştik. Ancak bu yazıda farklı bir örnekle temsil etmek istiyorum.

Bir markete gittiğimizi düşünelim. Aldıklarımızı kasaya getirdiğimizde kasiyer bunların barkodunu okutur ve sistem otomatik olarak fiyatları size gösterir. Ancak müşteri olarak bizim fiyatlara müdahale yetkimiz yoktur. Yani 100 TL’lik ürünün fiyatını müşteri olarak 50 TL olarak değiştiremeyiz. Ancak bunu marketin personeli değiştirebilir. Belki her personel bu yetkiye sahip olmayabilir. Görüldüğü üzere bazen bir değeri setlemek çeşitli ayrıcalıklara sahip olmayı gerektiriyor. Aynı durum 100 TL’lik ürünün markete geliş fiyatını görmek istediğimizde de geçerli olacaktır. Yani bazı değerleri getlemek için ayrıcalık sahibi olmalısınız.

Bu durumu önceki yazımızdaki örnek üzerinden tekrar değerlendirirsek; bir ApartmanDairesi nesnesinin fieldlarına getter veya setter kullanmadan ulaşılamaz olmalıdır. Ancak önceki örneğimizin main methodunu hatırlarsak, hem getter/setter kullanılan insaat2() hem de kullanılmayan insaat() methodları çalışmaktaydı. Oysa biz insaat2() methodunu fieldların denetimini sağlamak için yazmıştık. İşte bu denetimi zorunlu kılmanın bir yolu var: Doğru erişim düzenliyici kullanmak!

Bu yazımız için 03 – AccessModifiers isminde bir proje oluşturalım. Bu defa öncelikle TestClass sınıfımızı oluşturacağım. Bu bilgi bundan sonraki tüm projelerimizde de geçerli olacaktır. Bununla birlikte, market örneğinden yola çıkarak bir sınıf kodlayalım. Bu sınıf üzerinde fiyat ve isim tutan bir ürünü temsil etsin ve ismi de Urun olsun.

public class Urun {

	public int fiyat;
	
	public String isim;
	
}

Main methodumuzun içerisinde bu sınıfı kullanarak bir nesne yaratalım. Temsili olarak bir sakızı düşünebiliriz. İsmi sakiz ve tipi/sınıfı Urun olan bir değişken yaratalım. sakiz’in fiyat değeri 1 ve isim değeri de çiklet olsun. Nihayetinde bu objenin fiyat ve ismini konsola yazdıralım.

public class TestClass {

	public static void main(String[] args) {

		Urun sakiz = new Urun();
		sakiz.fiyat = 1;
		sakiz.isim = "çiklet";
		
		System.out.println(sakiz.fiyat);
		System.out.println(sakiz.isim);
	}
	
}

Yukarıdaki örnekte görüldüğü üzere, sakiz nesnesinin fiyatını direkt olarak değiştirebiliyoruz. Aynı şekilde fieldların değerini hiçbir denetime tabi olmadan okuyabiliyoruz. Şimdi bir düzenleme daha yapıyoruz ve Urun sınıfımızdaki getter ve setterları ekliyoruz.

public class Urun {

	private int fiyat;
	
	public String isim;

	public int getFiyat() {
		return fiyat;
	}

	public void setFiyat(int fiyat) {
		this.fiyat = fiyat;
	}

	public String getIsim() {
		return isim;
	}

	public void setIsim(String isim) {
		this.isim = isim;
	}
	
}

Yukarıdaki kodu dikkatli incelerseniz bir durumun farkına varacaksınız. Getter ver setterları ürettikten sonra bir düzenleme daha yaptım ve fiyat fieldının önündeki public anahtar sözcüğünü private ile değiştirdim. Bu değişikliği siz de sağladığınızda editörünüzde bir hata aldığınızı göreceksiniz.

Buradaki hatayı incelerseniz (imleci kırmızı çizili yere getirir veya ampül işaretine tıklarsanız) fiyat alanının görünü olmadığına dair bir uyarı göreceksiniz. Bu durumu aşmak için fiyatı set ettiğim yerde setter ve get ettiğim yerde getter kullanmalıyız. Kodumuzu aşağıdaki gibi düzenliyoruz.

public class TestClass {

	public static void main(String[] args) {

		Urun sakiz = new Urun();
		sakiz.setFiyat(1);
		sakiz.isim = "çiklet";
		
		System.out.println(sakiz.getFiyat());
		System.out.println(sakiz.isim);
	}
	
}

Bu değişikliği yaptığımızda getter ve setter kullanımını zorunlu hale getirmiş olduk. Peki tam olarak neler oldu? Bizim burada yaptığımız şey bir fieldın erişim seviyesini değiştirmek oldu. Javada bunu yaparken fieldların, methodların ve hatta sınıfların tanımlarının önünde kullanabileceğimiz, 4 adet erişim düzenleyicisi bulunmaktadır.

public : Erişimin her yerden sağlanabildiğini gösterir. Diğer sınıflar, diğer paketler bu erişim düzenliyicisi ile tanımlanmış methodlarınıza, fieldlarınıza ve sınıflarınıza ulaşabilir. Güvende kalmak için uygulama düzeyinde gerekmedikçe public sözcüğünü kullanmamalıyız.

protected : Önüne geldiği varlığı, bulunduğu paket, alt sınıflar ve sınıfın kendi içinden çağrılabilir halde tanımlamak için kullanılır. Buradaki alt sınıf ve paket kavramı kafanızı kurcalamasın. Bunların bir kısmına ilerleyen dönemde değineceğiz.

boş erişim düzenleyici : Diğer 3 erişim düzenleyiciyi kullanmadan tanım yapmak anlamını taşır. Mesela Urun sınıfımızdaki isim fieldının önündeki public sözcüğünü silersek boş erişim düzenliyicisi ile bu alanı tanımlamış oluruz. Boş erişim düzenleyicisi kullandığınız varlıklar alt sınıflardan ve diğer sınıflardan direkt olarak çağırılamaz. Ancak paket içerisinden erişilebilir durumdadır.

private : En sıkı erişim düzenleyicisidir. Önüne konulduğu java elemanını sadece o sınıf içerisinden erişilebilir hale getirir. Bu erişim düzenleyicisi parmağa bağlanan bir ip gibi aklınızda olmalı, bütün varlıklarınızı öncelikle private tanımlamayı düşünmelisiniz.

sınıfpaketalt sınıfdiğer sınıflar
publicVarVarVarVar
protectedVarVarVarYok
boşVarVarYokYok
privateVarYokYokYok
Erişim Tablosu

Örneğimize dönecek olursak; fiyat alanını private yapmamız sebebiyle alana direkt erişim yetkisi sadece sınıfın kendisinde kalmış durumda. Dolayısı ile farklı bir sınıftan (TestClass) buraya erişim sağlanamıyor. Ancak bu alanın setter ve getterları public olduğundan ve biz bunlara ulaşabildiğimizden, dolaylı olarak fiyat bilgisine erişebilmekteyiz. Yani fiyat bilgisine erişim denetim altına alınmış durumda. Ancak isim bilgisi için aynı şey geçerli değil. İsim public olarak tanımlanmış bir field olduğundan direkt erişim mümkün ve değeri değiştirilebilir ve bir denetime tabi olmadan okunabilir durumda.

Bu noktada artık farklı önlemler alabilir durumdayız. Diyelim ki fiyat girişi yapan personel kazara fiyatı 0 girdi. Setter methodumuz içerisinde gelen bu fiyatın kontrolünü yapabilir durumda olduğumuzdan; “Eğer fiyat sıfır girildiyse hata ver” ya da “İsmi olmayan ürünün adını ‘Tanımsız’ olarak listele” gibi daha programatik kontrolleri sağlayabilir halde oluruz.

Son iki yazımızla birlikte encapsulation konusuna değinmiş olduk. Özetle encapsulation sayesinde alanlarımızı sadece-okuma (read-only), sadece yazma (write-only) gibi biçimlerde kullanmamız mümkündür. Ayrıca veri okumayı ve yazmayı daha kontrollü bir hale getirir. Uygulamanızı daha güvenli kılar ve bakım süreçlerini kolaylaştırır. Hata ayıklama süreçlerinde işimizi kolaylaştırır. Kodumuzun yeniden kullanılabilir olmasını sağlar. İlerleyen yazılarla birlikte bunların hepsini yavaş yavaş bir arada kullanabilmeye başlayacağız. Kendinize bir ödev olarak önceki projeleri getter ve setterlı halde yazmayı alabilirsiniz. Pratik sizi daha iyi bir noktaya taşıyacaktır. Bir sonraki yazıda görüşmek üzere.

Getter ve Setter

Son yazımızda KaraAraclari sınıfımızda bulunan bazı methodların kullanımını ve bunların birbiri ile ilişki içerisinde nasıl hareket edebileceğini görmüştük. Sınıf yapılarına dair yazılarımızın genelinde ayrıca constructor kullanımına da odaklanmıştık.

Bu yazımızda yine sınıf yapılarının bir parçası olarak kabul edilen, getter ve setter ismi verilen methodlardan bahsedeceğiz. İsimlerinden yola çıkarak bu methodların bazı değerleri getirdiğini (getter) ve bazı değerleri güncellediğini (setter) söyleyebiliriz. Yazılım dünyasında bu methodların yaptığı işlere “get etmek” ve “set etmek” ismi verilmektedir.

Bildiğiniz gibi sınıfların ana yapılarından bir tanesi, o sınıfta bulunan fieldlardır. Daha evvel yazdığımız sınıfımızda iki adet fieldımız vardı. Kısaca bunları hatırlamak gerekirse aşağıdaki kod parçasına göz atabilirsiniz.

	public int koltukSayisi;
	
	public String renk;

Sürekli aynı proje üzerinden devam etmemek için, gelin bu yazımız için yeni bir proje oluşturalım. Bu yazımızda ele alacağımız projemizin ismini 02 – GettersAndSetters olarak belirleyelim. Editörümüzün sol tarafındaki kısma baktığımızda şöyle bir görüntüyle karşılaşacağız.

Getter ve setter örneğimiz için, yeni bir sınıf daha yaratmamız gerekiyor. Bu tarz durumlarda, çok klişe olarak 4 işlemden sorumlu bir sınıf yazılır. Ben mümkün olduğunca bundan uzak durmak istiyorum. Java sınıflarını gerçek hayattaki objelere benzetmek, OOP mantığını güçlendirecek bir hareket olacaktır. Eğer OOP konusunda kendinizi yeterli hissetmiyorsanız önce elle tutulabilir örnekleri düşünmeniz yararlı olacaktır. Matematik işlemleri, soyut kalmaları sebebiyle, bu anlattığım duruma uymamakta. Dolayısı ile biz farklı bir örnek ile ilerleyeceğiz.

Gelin bugün bir inşaat işi üzerine düşünelim. Diyelim ki bir apartman inşa edilecek. Bu apartmandan daire almak isteyen farklı profilde insanlar var. Dolayısı ile farklı dairelere ihtiyacımız var. Her bir daireyi bu işin içerisindeki bir nesne olarak düşünebiliriz. Bir apartman dairesi denildiğinde aklımıza gelen şeyleri düşünelim. Bir kapıdan içeri gireriz, duvarları boyalı, odaları sayılı olan, belki iki katlı olan, bazen açık mutfaklı olan bir yapıdır. Buradaki özellikleri düşündüğümüzde çok fazla madde çıkabilir durumdadır. Gelin bunların kısıtlı bir kısmına odaklanalım. Projemizde ApartmanDairesi isminde bir sınıf yaratıp bu örnek için kullanmaya başlayalım. Bizim apartman dairemizi bir duvar rengi, oda sayısı ve çelik kapılı olup olmaması temsilediyor olsun.

public class ApartmanDairesi {

	public String duvarRengi;
	
	public int odaSayisi;
	
	public boolean celikKapiliMi;
	
}

Uygulamamız, inşaata söz konusu olan her bir apartmanın java nesnesini yaratacak olsun. Yani apartmanımızdaki farklı farklı daireleri yukarıdaki sınıf ile oluşturacağız. Bu işi de önceki örneğimizde olduğu gibi TestClass isminde, main methodunu barındıran bir sınıf ile yapacağız. 4 adet daire oluşturuyor ve bunların fieldlarını dolduruyoruz.

public class TestClass {

	public static void main(String[] args) {
		
		ApartmanDairesi daire1 = new ApartmanDairesi();
		daire1.celikKapiliMi = true;
		daire1.duvarRengi = "Mavi";
		daire1.odaSayisi = 3;
		
		ApartmanDairesi daire2 = new ApartmanDairesi();
		daire2.celikKapiliMi = false;
		daire2.duvarRengi = "Gri";
		daire2.odaSayisi = 3;
		
		ApartmanDairesi daire3 = new ApartmanDairesi();
		daire3.celikKapiliMi = true;
		daire3.duvarRengi = "Mavi";
		daire3.odaSayisi = 2;
		
		ApartmanDairesi daire4 = new ApartmanDairesi();
		daire4.celikKapiliMi = true;
		daire4.duvarRengi = "Yeşil";
		daire4.odaSayisi = 1;
		
	}
}

Main methodumuzu çalıştırdığımızda 4 adet nesne oluşturmuş olacağız. Bunlar gerçek dünya gözüyle bakıldığında birbirinden farklı özelliklere sahip 4 adet apartman dairesidir. Java diliyle konuşursak; ApartmanDairesi sınıfına ait 4 farklı instance/nesne yaratmış durumdayız.

Varsayalım ki inşaat bitti ve dairelere taşınmalar başladı. Ancak daire2‘nin sahibi eski kararından pişman oldu ve çelik kapı taktırmak istedi. Bu durumda programatik olarak yapmamız gereken daire2 nesnesine ait celikKapiliMi fieldını true olarak güncellemektir. Gerçek hayatta yapılacak şey ise kapının sökülüp yenisinin takılmasıdır.

daire2.celikKapiliMi = true;

Bu defa tersten düşünelim. 3 odalı bir eviniz var ve bunu 4 odalı yapmak istiyorsunuz. Bir odanızı ikiye bölmediğinizi düşünürsek 4. bir odayı inşa etmeniz söz konusu olamamaktadır. Ancak programatik olarak 3 odalı olan daire1 nesnemize bunu yapabilmemizin önünde bir engel yoktur. Kısacası aşağıdaki kodu main methodumuza yazdığımızda çalıştığını görürüz.

daire1.odaSayisi = 4;

Demek ki java dünyasında, tıpkı gerçek hayatta olduğu gibi, bazı aksiyonları denetleme ihtiyacı duymaktayız. Yani duvarları boyamak bir ustanın varlığına, eve oda eklemek bazı fiziksel engellere tabidir. Şu ana kadar bir değeri setlemek üzerine konuştuk. Bazı durumlarda ise getlerken denetime ihtiyaç duyabiliriz. Örneğin daire sahipleri evinin rengini paylaşmak istemiyor olabilir. Bu durumda aşağıdaki kodun çıktı vermemesini sağlamak isteriz.

System.out.println(daire1.duvarRengi);

Bütün bu durumları kontrol altında tutmak için field değerlerinin okunması ve yazılması esnasında yardımcı methodlar kullanabiliriz. Bunlara getter ve setter methodlar ismi verilmektedir. ApartmanDairesi sınıfımızın fieldları için getter ve setter methodları oluşturalım. Sınıfımızı aşağıdaki gibi düzenliyoruz.

public class ApartmanDairesi {

	public String duvarRengi;
	
	public int odaSayisi;
	
	public boolean celikKapiliMi;

	public String getDuvarRengi() {
		return duvarRengi;
	}

	public void setDuvarRengi(String duvarRengi) {
		this.duvarRengi = duvarRengi;
	}

	public int getOdaSayisi() {
		return odaSayisi;
	}

	public void setOdaSayisi(int odaSayisi) {
		this.odaSayisi = odaSayisi;
	}

	public boolean isCelikKapiliMi() {
		return celikKapiliMi;
	}

	public void setCelikKapiliMi(boolean celikKapiliMi) {
		this.celikKapiliMi = celikKapiliMi;
	}
	
}

Dikkat edilirse getter methodlar (ismi get ile başlayan) geriye o fieldın tipinde dönüş sağlarken, setter methodlar geriye değer dönmüyor. Getter ve setter methodların olağan davranışı bu şekildedir. Tekrar koda dikkat ederseniz, setter methodlar parametre alırken, getter methodlar almamaktadır. Gerçek hayatta da, evinizin duvar rengini görmek için ek bir parametreye ihtiyaç duymazsınız. Ancak evinizin duvarını boyamak için hangi renk boyayı istediğinizi belirtmek durumundasınızdır. Yani bir değeri setliyorsam yeni değeri belirtirim. Ancak bir değeri getliyorsam değer zaten oradadır ve parametre vermem.

Bu noktada bir de kısayoldan bahsetmek istiyorum. Çoğu editör, getter ve setter üretimi için kısayollar barındırmaktadır. Eclipse ile getter ve setter üretmek için sınıfınızın içerisinde boş bir yerde sağ tıklayın. Açılan menüde source kısmına gelip Generate Getters and Setters menüsüne tıklayınız.

Açılan pencerede getter ve setter üretmek istediğiniz tüm fieldları işaretleyip, Generate butonuna tıklarsanız, editör tüm getter ve setterları sizin için sınıfınızın içerisine ekleyecektir.

Getter ve setterları ekledikten sonra test sınıfımızı da değiştiriyoruz. Field değerlerine direkt ulaşım yerine getter ve setterları kullanacağız. Programımızı uygun şekilde değiştireceğiz. Ancak karşılaştırma yapmayı kolaylaştırmak için Programın ilk halinde bulunan akışı insaat() isminde bir methoda alacağım. insaat2() ismindeki methodda ise getter setter kullanımını göstereceğim. Test sınıfımızı aşağıdaki gibi değiştiriyoruz.

public class TestClass {

	public static void main(String[] args) {
		
		insaat();
		insaat2();
		
	}

	private static void insaat2() {
		
		ApartmanDairesi daire1 = new ApartmanDairesi();
		daire1.setCelikKapiliMi(true);
		daire1.setDuvarRengi("Mavi");
		daire1.setOdaSayisi(3);
		
		ApartmanDairesi daire2 = new ApartmanDairesi();
		daire2.setCelikKapiliMi(false);
		daire2.setDuvarRengi("Gri");
		daire2.setOdaSayisi(3);
		
		ApartmanDairesi daire3 = new ApartmanDairesi();
		daire3.setCelikKapiliMi(true);
		daire3.setDuvarRengi("Mavi");
		daire3.setOdaSayisi(2);
		
		ApartmanDairesi daire4 = new ApartmanDairesi();
		daire4.setCelikKapiliMi(true);
		daire4.setDuvarRengi("Yeşil");
		daire4.setOdaSayisi(1);
		
	}

	private static void insaat() {
		
		ApartmanDairesi daire1 = new ApartmanDairesi();
		daire1.celikKapiliMi = true;
		daire1.duvarRengi = "Mavi";
		daire1.odaSayisi = 3;
		
		ApartmanDairesi daire2 = new ApartmanDairesi();
		daire2.celikKapiliMi = false;
		daire2.duvarRengi = "Gri";
		daire2.odaSayisi = 3;
		
		ApartmanDairesi daire3 = new ApartmanDairesi();
		daire3.celikKapiliMi = true;
		daire3.duvarRengi = "Mavi";
		daire3.odaSayisi = 2;
		
		ApartmanDairesi daire4 = new ApartmanDairesi();
		daire4.celikKapiliMi = true;
		daire4.duvarRengi = "Yeşil";
		daire4.odaSayisi = 1;
		
	}
}

Burada peşpeşe iki method çağrısı yapmış bulunmaktayız. Çıktı olarak bu methodların aynı işi yaptığını söyleyebiliriz. Ancak görüldüğü üzere alanların değer değişiklikleri, insaat2() methodunda setterlar vasıtasıyla yapılmaktadır. Peki ilk haline göre neler değişti?

  • Bir değerin get edilmesini veya set edilmesini bir methoda bıraktık. Yani okunmasını ve yazmasını kontrol altına aldık. Buna encapsulation veya sarmalama/kapsülleme/paketleme ismi verilmektedir.
  • Bir değerin sınıf içerisinde farklı okunurken sınıf dışında farklı okunmasına imkan verdik. Örneğin inşaat sınıfı duvar rengi olarak tüm yeşil tonlarını dış sınıflara yeşil dönerken, sınıf içerisinde farklı tonlarla işlem yapabilir. Bir eve perde alırken duvar rengi ton olarak çok büyük mesele teşkil etmeyebilir. Ancak duvarın bir parçasını daha boyayacaksanız önemli olabilir. Bu yüzden bazı noktalarda ayrım, bazı noktalarda genel geçer cevaplar gereklidir.
  • Kodun debug edilmesi sırasında değer değişikliklerinin takibi için güvenilir bir nokta oluşturduk. Böylece değişikliğin tek sınıfın tek noktasında gerçekleştiğini garanti altına almış olduk.

Getter ve setter kullanımının yararları aslında burada görüldüğünden daha fazladır. Ancak bunu daha iyi yorumlamak için farklı bir konuyu da anlamaya ihtiyaç vardır. Bu konu ise access modifiers yani erişim düzenleyicilerdir. Her ne kadar yukarıdaki örnekte getter ve setterlar oluşturmuş olsak da, örneğimiz bu haliyle eksik kalmakta ve amaca tam olarak hizmet etmemektedir.

Bir sonraki yazımızda erişim düzenleyicileri konusuna değineceğiz ve yukarıdaki kodun neden eksik kaldığına dair örnekler yapacağız.

Sınıf Yapıları – III

Bundan önceki iki yazımızda sınıf yapılarına değinmiştik. Bir sınıfa dair tanımlamanın ne olduğu, sınıfların hangi parçalardan oluştuğunu, bir sınıftan nesne yaratmayı, this anahtar sözcüğünü ve nesne yaratırken izleyebileceğimiz alternatif yöntemleri işlemiştik.

Methodlar

Sınıflarla ilgili şimdi de sınıf içerisindeki methodların kullanımına değineceğiz. Öncelikle KaraAraclari sınıfımızın son halini buraya tekrar ekleyelim.

public class KaraAraclari {

	public int koltukSayisi;
	
	public String renk;
	
	public KaraAraclari() {
		System.out.println("Yeni bir araç yaratıldı (Parametresiz)");
	}
	
	public KaraAraclari(int kacKoltuk) {
		System.out.println("Yeni bir araç yaratıldı (1 parametreli)");
		this.koltukSayisi = kacKoltuk;
	}
	
	public KaraAraclari(int koltukSayisi, String renk) {
		System.out.println("Yeni bir araç yaratıldı (2 parametreli)");
		this.koltukSayisi = koltukSayisi;
		this.renk = renk;
	}

	public void hareketEt() {
		System.out.println("Hareket başladı");
	}

	public void dur() {
		System.out.println("Hareket bitirildi");
	}

	public void yonVer() {
		System.out.println("Yön verildi");
	}

}

Tekrar belirtmek gerekirse sınıfımız field, constructor ve method gruplarından oluşmaktadır. Bunları yukarıdaki koda bakarsanız yukarıdan aşağıya sıralı olarak göreceksiniz. Görüleceği üzere methodlarımız şu imzalar ile sınıfımızda bulunmaktadır.

  • hareketEt()
  • dur()
  • yonVer()

Hatırlarsanız bu sınıftan nesne(ler) yaratırken constructor methodlarımıza parametre geçtiğimiz örneklerimiz olmuştu. Bu parametreler sayesinde sınıf içerisindeki koltukSayisi ve renk fieldlarına değer atamış yani onları initialize etmiştik. Birbirinden farklı nesneler yaratıp, onlara farklı farklı isimler vermiştik. Bu yarattığımız nesneleri gerçek hayatta düşünürsek; tamamı hereket eden, durabilen ve yön değiştirebilen nesneler olurdu. Peki javada bu nesneleri nasıl hareket ettirir, nasıl durdurur ve nasıl yönlendirirdik? Bu sorunun kısa cevabı “Method çağrısı yaparak” olurdu. Yani methodlar belirli bir işlevi yerine getirmek üzere yazılmış kod bloklarıdır. TestClass sınıfı içerisinde bunu uygulamalı olarak görelim.

İsmi arac olan KaraAraclari tipinde bir nesne yaratacağız. Bunu yaparken parametresiz constructor kullanıyoruz. Bu tercihimizin tek sebebi kullanım kolaylığı. Nesneyi yarattıktan sonra arac. yazıp beklerseniz veya arac. yazıp ctrl+boşluk tuş kombinasyonunu kullanırsanız aşağıdaki gibi bir menünün açıldığını göreceksiniz. Bu özelliğin ismi auto-complete yani otomatik tamamlamadır.

Bu menü, sınıf ile ilgili olarak erişebileceğimiz field ve methodları listelemektedir. Bunlardan bazıları java tarafından bize verilmiştir. Bunların detayına değinmeyeceğim. Ancak listeye baktığınızda tanıdık isimler de göreceksiniz. Burada az evvel nasıl kullanacağımıza dair cevap aradığımız methodların da listelendiğini görüyoruz. O halde bunlardan bir tanesini seçelim ve TestClass sınıfımızı şu hale getirelim.

public class TestClass {

	public static void main(String[] args) {
		
		KaraAraclari arac = new KaraAraclari();
		
		arac.hareketEt();

	}
}

Yukarıdaki kodu çalıştırdığımızda şu şekilde bir çıktı elde ederiz.

Pekala, başka bir methodu daha çağırabiliriz. Hareket ettirdiğimiz aracımıza yön verelim ve arkasından durduralım.

public class TestClass {

	public static void main(String[] args) {
		
		KaraAraclari arac = new KaraAraclari();
		
		arac.hareketEt();
		arac.yonVer();
		arac.dur();

	}
}

Peki bu defa çıktımız nasıl olacaktır? Çalıştırmadan önce tahmin etmenizi öneririm. Böylece aktif olarak kodu takip etmiş olacaksınız. Bu programın çıktısı aşağıdaki gibi olacaktır.

Özet olarak bir method çağrısı yapmak için, önce değişkenimizin (nesne, obje, object, instance) ismini yazıyoruz ve arkasından çalıştırmak istediğimiz methodun ismini yazıyoruz.

Şimdi sınıfımıza iki ayrı method daha ekleyeceğim. Bunların içerikleri aşağıdaki gibidir.

	public void gaz() {
		System.out.println("Gaz verildi");
	}
	
	public void fren() {
		System.out.println("Fren yapıldı");
	}

Bu iki methodu ekleme sebebim hareketEt() ve dur() methodlarını biraz daha zenginleştirmektir. Bu sayede hem method içerisinden method çağrımı yapmayı, hem de this anahtar sözcüğü ile methodları bir arada kullanmayı gözlemleyeceğiz.

Hatırlarsanız this sözcüğünü daha evvel  “Bu sınıfın içerisindeki” anlamıyla bağdaştırmıştık. Hareket ettireceğimiz kara aracında gaza basmamız, durduracağımız kara aracında ise frene basmamız gerekir. O halde bu methodları birbirleriyle ilişkilendirmemiz gerekiyor. Şu şekilde bir yaklaşım sergileyebiliriz; hareketten (Hareket başladı) evvel gaza basılmalı ve durma eyleminden (Hareket bitirildi) önce fren yapılmalıdır. O halde ben hareketEt() methodunu tetiklediğimde ekrana önce “Gaz verildi” yazmalı ve sonrasında “Hareket başladı” yazmalıdır. dur() methodu için de benzer bir yaklaşımı kullanabiliriz. Bu iki methodumu da aşağıdaki gibi düzenliyoruz.

	public void hareketEt() {
		this.gaz();
		System.out.println("Hareket başladı");
	}

	public void dur() {
		this.fren();
		System.out.println("Hareket bitirildi");
	}

Bütün methodlarımız KaraAraclari sınıfımızn içerisinde olduğundan, diğer methodları çağırırken yine this anahtar sözcüğüne başvururuz. Burada da bunun bir örneğini görmüş olduk. TestClass sınıfımızı bu şekilde çalıştırdığımızda oluşacak çıktıya gelin birlikte bakalım.

Sınıflar ve methodlar ile ilgili olarak şimdilik bu noktada bitirebiliriz. Bir sonraki yazımız sınıf yapılarının nispeten devamı olacak nitelikte bir yazı olacak. Ancak javadaki bir diğer temel kavram ile ilgili olacağından ayrı bir yazı olarak yayınlamaya karar verdim. Bir sonraki yazımızda görüşmek üzere.

Sınıf Yapıları – II

Bir önceki yazımızda sınıf yapılarına giriş yapmıştık. Sınıfların genel özelliklerinden bahsetmiştik ve sınıfların fieldlar ve methodlardan oluştuğunu söylemiştik. Bununla birlikte constructor (Yapıcı fonksiyonlar) ile nesne yaratmaya değinmiştik.

Bir Sınıfa Ait Bir Nesne Yaratmak

Constructor yani yapıcı fonksiyon ile ilgili bazı eklemeler ile konuya devam edelim. Söz dizimi ile ilgili yazımızda methodların yanında bulunan parantezlerin, methoda yollanmak istenen parametrelerin eklendiği/sıralandığı yer olduğunu söylemiştik. Constructor çağrısını yaptığımız satırı tekrar hatırlayalım.

KaraAraclari otomobil = new KaraAraclari();

Eşittir operatörünün sağ tarafında gördüğünüz ifade, new sözcüğü sebebiyle, özel bir method çağrısıdır. Ancak KaraAraclari sınıfımızda yine ismi/imzası KaraAraclari() olan bir method tanımlamadık. Peki bu olmayan methodu nasıl çağırdık.

Constructor kavramı javada özelleşmiş kavramlardandır. Constructor barındırmayan bir sınıf olmaz. Eğer bir constructor tanımlamıyorsanız java sizin yerinize bir tane tanımlayacaktır. Buna default constructor ismi verilir. Peki java bize bir default constructor veriyorsa neden farklı bir tane tanımlayalım ki?

Bu sorunun birden fazla cevabı bulunmakta. Örneğin size şöyle bir istek geldi diyelim:

  • Yeni bir kara aracı yaratıldığında bununla ilgili bir bildirimi ekrana yazdırmalıyız.

Bu sorunu yeni bir kara aracı yarattığımız her yerde bir ekrana bildirim yazdıran bir satırla yapmak mümkün. Ancak bu durumun 1 milyon defa tekrar ettiğini ve bir gün bu bildirimin artık kapatılması gerektiğini düşünün. Böyle bir senaryoda 1 milyon ayrı kod satırının silinmesi büyük bir iş olacaktır. Peki bu durumu nasıl çözebilirdik?

Her nesne yarattığımız yerde bildirim üreten kodu yazmak yerine, nesnenin yaratılma anında bunu yaparsak daha doğru olacaktır. Yani constructor içerisinde! Böylece, bir gün bu bildirim kapatılacaksa bu durumda 1 milyon satır kod değiştireceğimize, 1 satırı değiştirmemiz yeterli olacaktır. Peki ama default constructor bizim yerimize java tarafından tanımlanıyordu. Bunu nasıl düzenleyebiliriz? Bunun cevabı da basit: Default constructor tanımını kodunuza ekleyerek. Gelin bunu kodda görelim.

public class KaraAraclari {

	public int koltukSayisi;
	
	public KaraAraclari() {
		
	}

	public void hareketEt() {
		System.out.println("Hareket başladı");
	}

	public void dur() {
		System.out.println("Hareket bitirildi");
	}

	public void yonVer() {
		System.out.println("Yön verildi");
	}

}

Burada “public KaraAraclari()” ile verilmiş blok java tarafından tanımlanan default constructor fonksiyonunun kendisidir. Bu fonksiyonu sınıfın içerisine aldığımız için artık üzerinde değişiklikler yapabiliriz. Örneğin ekrana yeni bir araç yaratıldığını yazdıralım.

	public KaraAraclari() {
		System.out.println("Yeni bir araç yaratıldı");
	}

Eğer TestClass sınıfınızdaki main methodunu çalıştırırsanız aşağıdaki gibi bir çıktı elde edersiniz.

Şimdi de farklı bir ihtiyaçtan bahsedelim. Yeni bir kara aracı yaratıldığında, bu aracın koltuk sayısı “4” olarak ayarlansın. Bunun için constructor üzerinde minik bir değişiklik yapmamız gerekecek. Aşağıdaki gibi kodumuzu düzenliyorum.

	public KaraAraclari() {
		//System.out.println("Yeni bir araç yaratıldı");
		this.koltukSayisi = 4;
	}

Burada yeni bir ifade ile karşılaşıyoruz: “this“. Bu yeni ifadenin anlamı “Bu sınıfın içerisindeki” olarak ifade edilebilir. Dolayısıyla this.koltukSayisi, bu sınıftaki koltuk sayısı anlamına gelmektedir. TestClass ile bu durumu test etmek istersek aşağıdaki gibi bir kod parçası ile bunu gerçekleştirebiliriz.

Buradaki ifadeye dikkat ederseniz, ekrana değeri yazdıran fonksiyonda this.koltukSayisi değil otomobil.koltukSayisi ifadesi kullanılmıştır. Çünkü TestClass sınıfı içerisinde kullanılacak olan this ifadesi yine TestClass sınıfına ait fieldları/methodları refere edecektir. Bir sınıfa ait fielda, o sınıfın dışından erişirken değişken adını kullanırız. Buradaki örnekte değişkenimizin adı otomobil olduğundan ifade de buna göre yazılmıştır.

Alternatif Nesne Yaratma Yöntemi

Bildiğiniz gibi bisiklet de otomobil de kara aracıdır. Ancak bunların birisi tek, diğeri ise dört koltukludur. Bu durumda az önce yaptığımız örnek yanlış bir şekilde çalışmaktadır. Bu durumu düzeltmek için yapıcı fonksiyonumuzu parametrik bir hale getirmeliyiz. Bunun için constructor üzerinde bir değişiklik yapacağız. Aşağıdaki düzenlemeyi gerçekleştiriyoruz:

	public KaraAraclari(int kacKoltuk) {
		this.koltukSayisi = kacKoltuk;
	}

Bu değişikliği yaptığınızda TestClass sınıfınız hata verecektir. Ancak telaşa mahal yok, bunu düzelteceğiz. Öncelikle burada ne yaptığımızı anlatalım. Kavramsal olarak bazı ifadelerin sizde de oluşması için, bu durumları jargona uygun şekilde ifade etmek istiyorum.

  • Method imzasını değiştirdik: Method imzası, bir methodun içeriği olmadan onun erişim düzenleyicisini, geriye döndürdüğü değerin tipini, methodun adını ve methoda gönderilen parametreleri, bu parametrelerin tipini gösteren ifadedir. Methoddaki ana/dış bloğu kaldırdığımızda geriye imzası kalmaktadır. Bu değişiklikle daha evvel parametre almayan bir methoda (constructor) parametre (int kacKoltuk) göndermeye başladık. Dolayısı ile method imzasını değiştirdik ifadesini kullanabiliriz.
  • Methoda parametre geçtik: Bir üst maddede belirttiğim gibi daha evvel parametresiz çalışan bir methoda sayısal bir değeri parametre olarak gönderdik. Parametreler parantez içindeki ifadelerdir. İmzaya parametre eklenirken yani methoda parametre geçilirken, önce parametrenin tipi sonra da method bloğu (scope) boyunca geçerli olacak olan parametre ismi eklenir.

Peki neden TestClass sınıfımız hata verdi? Yeri gelmişken editörümüzün kullanımına da bakalım. TestClass sınıfımıza gidelim. Altı kırmızı ile çizili ifadeye imlecimizi getirdiğimizde aşağıdaki gibi bir görüntüyle karşılaşacaksınız.

Burada editörün bize şunu söylediğini görüyoruz: “Parametre almayan, yani KaraAraclari() imzasına sahip, bir constructor bulunmamaktadır.”

İyi, ancak bu imza default constructora ait ve bunu java bize vermeliydi. Bu durum ancak başka bir constructor tanımı yapılmadıysa geçerlidir. Biz constructorımızın imzasını değiştirip ona parametre eklemiştik. Bu durumda java default constructor için sergilediği davranışını sergilemez ve onu yok eder. Peki bu kötü bir şey değil midir? Cevap ise hayır olacaktır. Bu iyi bir şeydir. Java sizin kurallara bağlı kalmanızı ister. Kural “1 parametre ile nesne yaratabilirsin” ise iki parametre ile ya da hiç parametresiz obje yaratma işlemi gerçekleşmez. Ancak size güzel bir haberim var. Java aynı zamanda yeni bir kural eklemenize izin verir. Gelin şimdi bu durumu yakından inceleyelim.

Kara araçlarını temsil ettiğimiz sınıfımıza yeni bir field daha ekleyerek bu işe başlayalım. Yine etrafımızdaki araçları düşünürsek hepsinin özelliği olarak bir renge sahip olmaları aklımıza gelecektir. O halde sınıfımıza bu özelliği ekleyelim. Tahmin edeceğiniz üzere renk özelliği sayısal bir değerle ifade edilmeyip; metin değer taşıyan bir özelliktir. O halde tipi String olacaktır. Sınıfımızı aşağıdaki gibi güncelliyoruz.

public class KaraAraclari {

	public int koltukSayisi;
	
	public String renk;
	
	public KaraAraclari(int kacKoltuk) {
		this.koltukSayisi = kacKoltuk;
	}

	public void hareketEt() {
		System.out.println("Hareket başladı");
	}

	public void dur() {
		System.out.println("Hareket bitirildi");
	}

	public void yonVer() {
		System.out.println("Yön verildi");
	}

}

Bu durumda ise mevcut constructor ile rengi olan bir araç yaratamıyoruz. Oysa ki constructor fonksiyonumuz iki parametre alsaydı bu duruma düşmeyecektik. Bu şekilde tasarlanmış bir constructor için kod şuna benzerdi:

public KaraAraclari(int koltukSayisi, String renk) {
    this.koltukSayisi = koltukSayisi;
    this.renk = renk;
}

Burada işleri biraz daha karıştırmak istedim. Öncelikle imzaya bakalım. Constructor imzasında, parametre sayısında bir değişiklik olmuş. Parametre kısmı bize şunu söylüyor: “Bu constructor 2 parametre ile çağırılabilir. Bunlardan ilk olan değer sayısaldır ve koltukSayisi ismindedir. İkinci sıradaki parametre ise metin bir değerdir ve ismi renk olacaktır.”

Buraya kadar her şey güzel. Ancak koltukSayisi ve renk isimleri sınıfımızın içerisinde ayrıca bir field idi. Bu ikisini (field ve parametre) birbirinden nasıl ayırt edeceğiz? Buradaki anahtar this sözcüğüdür. Constructor içerisine bakarsanız this.koltukSayisi = koltukSayisi ifadesini görürsünüz. Bunun anlamı “Sınıf içerisindeki koltukSayisi isimli fielda constructora parametre olarak gelen koltukSayisi değerini ata” olacaktır. Aynı durum renk için de geçerlidir.

Bu iki constructordan birini koda eklediğimizde parametresiz, yani default, constructor kaybolacak. Ancak bizim buradaki yaklaşımımız bu üçünden de aynı anda faydalanabilmekti. Güzel haber ise hiçbirinden vazgeçmeden üçüne birden sahip olabileceğimizdir. Hiç çekinmeden bu üç constructor fonksiyonumuzu kodumuza ekliyoruz ve kodumuzu finalize ediyoruz. Ben ayrıca her constructor içerisine bir de ekrana yazı yazan bir kod ekleyeceğim ki örnekler kafanızda daha net otursun.

public class KaraAraclari {

	public int koltukSayisi;
	
	public String renk;
	
	public KaraAraclari() {
		System.out.println("Yeni bir araç yaratıldı (Parametresiz)");
	}
	
	public KaraAraclari(int kacKoltuk) {
		System.out.println("Yeni bir araç yaratıldı (1 parametreli)");
		this.koltukSayisi = kacKoltuk;
	}
	
	public KaraAraclari(int koltukSayisi, String renk) {
		System.out.println("Yeni bir araç yaratıldı (2 parametreli)");
		this.koltukSayisi = koltukSayisi;
		this.renk = renk;
	}

	public void hareketEt() {
		System.out.println("Hareket başladı");
	}

	public void dur() {
		System.out.println("Hareket bitirildi");
	}

	public void yonVer() {
		System.out.println("Yön verildi");
	}

}

Parametresiz constructor da eklendiği için TestClass sınıfımızdaki hata da ortadan kaybolmuş olacak. Şimdi bunlarla bir test yapalım. TestClass sınıfımızı aşağıdaki gibi düzenliyoruz.

public class TestClass {

	public static void main(String[] args) {
		
		KaraAraclari birinciArac = new KaraAraclari();

		KaraAraclari ikinciArac = new KaraAraclari(1);
		
		KaraAraclari ucuncuArac = new KaraAraclari(2, "Kırmızı");
	}
}

Gördüğümüz üzere çıktıya bakarak 3 ayrı constructor fonksiyonunun da çalıştığını gözlemleyebiliyoruz. Ana konudan bağımsız olarak dikkatinizi bir noktaya daha çekmek istiyorum. Gönderdiğimiz parametrelerde sayısal değerleri doğrudan yazarken, metin ifadeleri çift tırnak içerisine aldık. Bu da bir söz dizim kuralı gereğidir. Eğer “Kırmızı” yerine Kırmızı yazsaydık editör bize kızacak ve programımız çalışmayacaktı.

Bu yazımızı da burada sonlandırıyoruz. Bir sonraki yazımız tekrar sınıflar üzerine olacak. Konuları giderek derinleştireceğiz ve javanın temellerinde yatan kavramları tek tek işleyeceğiz. Her zamanki gibi bol tekrar öneriyorum.

Sınıf Yapıları – I

Java öğrenmeye karar verdiyseniz karşınıza mutlaka sınıf, class, nesne gibi kavramlar çıkacaktır. Bu yazıda tam olarak bu kavramları ele alacağız.

Java nesneye yönelik bir programlama (OOP) dilidir. Bunu gerçek hayata bakarak yorumlayabiliriz. Gerçek hayatta motorlu taşıt kavramını düşünürsek;

  • Bu taşıtın bazı özellikleri (attribute) vardır. Bunlar ağırlık, yapabileceği en yüksek hız, renk gibi özellikler olabilir
  • Bu taşıtın ileri gitme, durma gibi bazı fonksiyonları (method) vardır
  • “Motorlu taşıt” çerçeve kavramdır (interface) ve buna uygun araçlar vardır. Otomobil, gemi, traktör gibi araçlar birer motorlu taşıttır
  • Volvo markası tüm araçlarına standart olarak bazı donanımları ekleyebilir. (Inheritance)
  • BMW 320 ve Massey Ferguson 240 birer motorlu taşıt olup birisi otomobil çerçevesinde(class-sınıf) diğeri ise traktör çerçevesinde özellikler taşıdığından hem benzerlerdir hem de ayrışırlar
  • Bazı motorlu taşıt markalarının parçalarını sadece yetkili servisler söküp takabilir (Encapsulation)
  • Bazı araçlar sadece insan taşımaya uygunken, bazı araçlar yükün ne olduğuna bakmaksızın (abstraction) istenileni taşır.

Yukarıda saydığım özellikleri javanın programlama pratiklerinde de görürüz. Özellikle parantez içerisinde verdiğim anahtar kelimeler size internette arama yapabilmek için yol gösterici olacaktır.

Bir Sınıfı Tanımlamak

Pekala, bir sınıf/class nasıl tanımlanır? Kod tekrarına düşmemek adına burada tekrar merhaba dünya örneğini kullanmayacağım. Bunun yerine gelin yukarıdaki motorlu taşıt örneğinden faydalanalım.

Bildiğiniz gibi bir sınıf tanımlamak için bir sınıf bloğu yaratmamız gerekiyor. Bloklar süslü parantezle başlayan ve biten yapılardır. Yani { ve } işaretlerinin arasında kalan bir yapı kuracağız.

Yine önceki yazılardan hatırlayacağınız üzere bir sınıfı tanımlarken ona bir isim vermemiz gerekiyor. Javada kültür olarak sınıf isimleri büyük harfle başlar ve isimlendirme için PascalCase yöntemi izlenir. Ben bu sınıfa KaraAraclari diyeceğim. Türkçe karakter kullanmamaya özen göstermeniz ve hatta isimlendirmeleri ingilizce yapmanız genel programlama kültürüne uygun olacaktır.

Bu sınıfı, sonraki yazılarda değineceğimiz public erişim düzenleyicisiyle yaratacağım. Bu herkes tarafından erişilebilir anlamına geliyor. Buraya çok takılmadan devam edebiliriz.

Son olarak KaraAraclari ismindeki sınıfımızın, nasıl sınıf olarak belirtileceğine değinelim. Yine sözdizim ile ilgili yazıda bahsettiğimiz gibi sınıflar class sözcüğü ile nitelendirilir. Şimdi elimizdeki bilgilerle KaraAraclari sınıfımızı yaratalım.

Öncelikle merhaba dünya yazısında anlattığım gibi yeni bir proje yaratalım. Projemizin ismini 01 – ClassExamples olarak belirleyelim. Proje isimlendirmesini bu şekilde yapmamın bir sebebi var. Editörü açtığınızda projelerinizin belirli bir sırada görünmesi öğrenme ve hatırlama açısından size yardımcı olacaktır.

Projemizi oluşturduktan sonra, proje içerisindeki src klasörüne sağ tıklıyoruz ve yeni bir sınıf oluşturuyoruz. Bunun da örneğini merhaba dünya projesinde yapmıştık. Yeni sınıfımıza
KaraAraclari ismini veriyoruz. Merhaba dünya sınıfından farklı olarak main methodu oluşturmayacağız. Dolayısıyla bu seçeneği seçmemeniz gerekiyor.

Bu noktada finish butonuna tıkladığınızda yeni sınıfı yaratmış olacaksınız. Karşınızda şöyle bir sınıf görmeniz gerekiyor.

public class KaraAraclari {

}

Şimdi yazının girişinde değindiğimiz maddeleri bu sınıfın içeriğine ekleyelim. Temel olarak sınıfları 2 ayrı parçadan oluşuyormuş gibi düşünebiliriz.

  • Özellikler (field, attribute)
  • Fonksiyonlar (method)

Öncelikle kara araçlarını düşünürsek bir çok araç aklımıza gelecektir. Bisiklet, buldozer, otomobil, traktör gibi örnekler bu tanıma uymakta. Bunları bir arada düşünürsek; hem özellik olarak, hem de fonksiyon olarak benzerlikler bulabiliriz. Bu araçların tamamında “hareket etme”, “yön verme”, “hareketi durdurma” gibi fonksiyonlar bulunur. Bunun dışında fonksiyon olarak sayamayacağımız, ancak özellik olarak niteleyebileceğimiz, ortak noktalar da bulunur. Bisiklet, buldozer ve traktör için tek kişilik oturma yeri bulunurken otomobil için 4 kişilik yer bulunur. Aralarında fark varmış gibi gözükse de, “oturma yeri” kavramı hepsinde ortaktır. O halde bu detayları, kodladığımız sınıfımıza ekleyelim.

public class KaraAraclari {

	public int koltukSayisi;

	public void hareketEt() {
		System.out.println("Hareket başladı");
	}

	public void dur() {
		System.out.println("Hareket bitirildi");
	}

	public void yonVer() {
		System.out.println("Yön verildi");
	}
}

Yukarıda paylaştığım sınıf 1 adet field ve 3 adet method ile oluşturulmuş bir sınıftır. Bunların detayına biraz değinirsek;

  • Tüm tanımlamalar public anahtar sözcüğü ile başlamaktadır. Bu da uygulamanın her yerinden bu sınıfa ve içeriğine ulaşılabileceği anlamına gelmektedir.
  • Methodlarımız public sözcüğünden sonra void ile devam etmektedir. Bu da geriye herhangi bir değer dönmedikleri anlamına gelir. Method bloklarının içlerine bakarsanız “return” ifadesini görmemekteyiz.
  • koltukSayisi isimli field, isminden de anlaşılacağı üzere sayısal bir değer taşımaktadır. buna uygun olarak “int” anahtar sözcüğü ile nitelendirilmiştir.

Bir Sınıfa Ait Bir Nesne Yaratmak

Bildiğiniz gibi java OOP (Object Oriented Programming) yapısı ile çalışacak şekilde tasarlanmış bir dildir. Bunu gerçek hayata benzetmek oldukça kolay. “Kara araçları” kavramını düşündüğümüzde günlük hayatta karşımıza bir çok örnek çıkar. İşte o aklınıza gelen her türlü araç, yazılım perspektifinden bir “nesne” olarak kabul edilir. Günlük hayatımızda karşımıza çıkan bu araçları javada siz yaratırsınız. Bu işi yapmanıza yarayan fonksiyonlara constructor ismi verilir.

Peki constructor nasıl kullanılır? Gelin bunu bir örnek ile inceleyelim. Az önce yarattığımız KaraAraclari sınıfına müdahale etmeden, farklı bir sınıf daha yaratalım. Bu sınıf test amaçları için yaratılmış olsun. src klasörüne sağ tıklayın ve yeni bir sınıf oluşturun. Sınıfımızın ismi TestClass olsun ve içeriğinde bir main barındırsın.

Şimdi bu sınıf içerisinde, KaraAraclari sınıfına ait bir nesne yaratalım. Bunun için “new” anahtar sözcüğünü, main methodu içerisinde kullanacağız.

public class TestClass {

	public static void main(String[] args) {
		
		KaraAraclari otomobil = new KaraAraclari();

	}
}

Buradaki main methodunun içerisinde gördüğünüz satırı yorumlayalım.

  • Javada bir nesne tanımı yapılırken öncelikle “tipi” belirtilir. Burada tanımlayacağımız nesnemizin tipi “KaraAraclari” olacağından ilk sözcük bu olmuştur.
  • Nesne tanımlarında tipten sonra, programın geri kalanında o nesnenin isminin ne olacağını belirtiriz. Buradaki “otomobil” sözcüğü bu nesnenin ismidir. İsimlendirme kuralları için değişkenler yazımıza göz atabilirsiniz.
  • Değişken isminden sonra eşittir, yani atama operatörü, yer alır. Bu operatör kendisinden sağda kalan değeri, kendisinin solundaki değere atar. Buraya kadar olan kısmı konuşma diliyle anlatırsak; “İsmi otomobil olan bir kara aracı yaratmak istiyorum ve değeri şudur: “
  • new anahtar sözcüğü constructor çağrısı yapacağımızın bir göstergesidir. Javada constructor çağrısı olmadan da yeni bir nesne yaratmak mümkündür. Bunu ilerleyen konularda ele alacağız. Ancak bizim buradaki amacımız constructor konusuna değinmek olduğundan bu detaya girmeyeceğiz.
  • new anahtar sözcüğünü tekrar KaraAraclari sözcüğünün takip ettiğini görüyoruz. Yine konuşma diline yakın bir ifadeyle düşünürsek, eşitliğin sağ tarafını “Yeni bir kara aracı yaratmak istiyorum” diye niteleyebiliriz.

Tüm bu noktaları birleştirdiğimizde karşımıza çıkan sonuç, ismi otomobil olan yeni bir değişken yaratmış olmamızdır ki bu değişkenin tipini de KaraAraclari olarak belirtmiş bulunuyoruz. İşte bu değişken, diğer isimleri ile nesne, object veya instance olarak da nitelenir.

Bu yazıyı burada noktalıyoruz. Sonraki yazımız yine sınıflar üzerine olacak.

Operatörler – II

Daha önceki yazıda aritmetik ve ilişkisel operatörleri incelemiştirk. Bu yazımızda da javadaki operatörleri incelemeye devam edeceğiz.

Önceki yazıdan kalan değişkenlerimizi tekrar hatırlayalım. Ancak bu defa değerleriyle biraz oynayalım. Pratiğimizi ilerletmek için değişkenleri javada tanımladığımız gibi yazacağım.

int a = 5;
int b = 15;
int c = 0;

Mantıksal operatörler genelde birden fazla durumun bir arada olumlu veya olumsuz olarak gerçekleşmesini denetlemek için kullanılır. Bunu günlük hayata benzetirsek; “Apartmana postacı dışında kimse girmiş mi?” sorusuna cevap ararken şu iki duruma bakarsınız:

  • Apartmana kimse girdi mi?
  • Önceki sorunun cevabı evet ise apartmana giren kişi(ler) postacı mıydı?

Görüldüğü üzere apartmana giren kişinin varlığı durumu anlamlandırmaya yetmiyor. Bir de o kişinin mesleğini denetlemek istiyoruz.

Mantıksal operatörleri kullanabilmemiz için değişkenlerimizin tipini değiştireceğim. Değişkenlerimizi doğru (true) veya yanlış (false) değerlerini taşıyabilen boolean tipinde yeniden tanımlıyoruz. Eğer int tipteki değişkenleri kullanmaya devam etseydiniz derleyiciniz size bunu yapamayacağınıza dair uyarı verirdi.

boolean a = true;
boolean b = true;
boolean c = false;

Şimdi de mantıksal operatörlerimizin neler olduğuna bakalım.

OperatörTanımİşlem -> Sonuç
&& (ve)Her iki tarafındaki ifade 0’dan(false) farklı ise true döner.a && b -> true
a && c ->false
|| (veya)Bir tarafındaki ifade 0’dan(false) farklı ise true döner.a || b -> true
a || c -> true
! (değilse)Mantıksal operatörün sonucunu terse çevirir!(a && b) -> false
!(a && c) -> true

Yine başka bir sınıf daha yazıp bu operatörleri burada deneyelim.

public class MantiksalOperator {

   public static void main(String args[]) {
      boolean a = true;
      boolean b = true;
      boolean c = false;

      System.out.println("a && b = " + (a&&b));
      System.out.println("a && c = " + (a&&c));
      System.out.println("a || b = " + (a||b) );
      System.out.println("a || c = " + (a||c) );
      System.out.println("!(a && b) = " + !(a && b));
      System.out.println("!(a && c) = " + !(a && c));
   }
}

Çıktı:

a && b = true
a && c = false
a || b = true
a || c = true
!(a && b) = false
!(a && c) = true

Elbette ki atama operatörleri de mevcuttur. Java her şeyi baştan tanımlayıp sonsuza kadar kullanmamız gereken bir dil değildir. Yani bir değişkenin değerini sonradan değiştirmemiz de mümkündür. Bir değişkenin değerinin nasıl değiştirilemez hale getirileceğini de ayrıca ilerideki yazılarda işleyeceğiz. Peki bir değişkene ne tarz atama işlemleri yapabiliriz? Aşağıdaki tabloda atama operatörlerini görebilirsiniz.

Önce değişkenlerimizi tekrar tanımlayalım.

int a = 5;
int b = 15;
int c = 10;
OperatörTanımİşlem -> Sonuç
=Basit atama operatörü. Sağdaki değeri soldakine atar.c=a+b -> c=20
+=Soldaki değere sağdakini ekler ve sonucu soldakine atar.c+=a -> c=15
-=Soldaki değerden sağdakini çıkarır ve sonucu soldakine atar.c -= a -> c=5
*=Soldaki değerle sağdakini çarpar ve sonucu soldakine atar.c *= a -> c=50
/=Soldaki değeri sağdakine böler ve sonucu soldakine atar.c /= a -> c=2

Her defasında olduğu gibi yeni bir sınıf yaratıyoruz. Ancak bu defa işlemlerde dikkatinizi çeken bir nokta olabilir. Koda bakarsanız her işlem öncesi c değişkenine tekrar tekrar atama yaptığımızı göreceksiniz. Bunun sebebi c değişkenin bir önceki işlemde manipüle edilmesidir. Kafa karışıklı yaratmaması adına her adımda tekrar c değişkeninin değerini 10’a eşitliyorum ki yukarıda verdiğimiz tabloyla uyumlu olsun. a ve b değişkenlerinin değerlerini manipüle etmediğimizden onlarla ilgili olarak sadece en başta yapılan atama işlemlerini göreceksiniz.

public class Op {

    public static void main(String args[]) {
        int a = 5;
        int b = 15;
        int c = 10;

        c = a + b;
        System.out.println("c = a + b = " + c );

        c = 10;
        c += a ;
        System.out.println("c += a  = " + c );

        c = 10;
        c -= a ;
        System.out.println("c -= a = " + c );

        c = 10;
        c *= a ;
        System.out.println("c *= a = " + c );

        c = 10;
        c /= a ;
        System.out.println("c /= a = " + c );

    }
}

Çıktı:

c = a + b = 20
c += a = 15
c -= a = 5
c *= a = 50
c /= a = 2

Javada en çok kullanılan operatörlere Operatörler – I ve Operatörler – II yazımızla değinmiş bulunuyoruz. Bitwise operatörler gibi daha farklı operatörlere ise değinmeyeceğiz. Temel java için şu ana kadar değindiğimiz operatörlerin yeni başlayanlar için yeterli olacağını düşünüyorum. Bir sonraki konu başlığında görüşmek üzere.

Operatörler – I

Operatörler, programlama dillerinde çeşitli işlemleri gerçekleştirmek üzere kullanılan işaretlerler veya ifadelerdir. Javada da değişkenleri ve değerleri denetlemek, kıyaslamak veya daha farklı amaçlara hizmet etmek üzere kullanılan çeşitli operatörler bulunmaktadır.

Aritmetik operatörleri konunun başlangıcı olarak alabiliriz. Java artırma, toplama, mod alma gibi işlemleri yapabilen operatörlere sahiptir. Aşağıdaki tabloda bu operatörleri bulabilirsiniz.

Bu tabloyu daha anlamlı hale getirmek için 2 adet int değişken düşünebiliriz. Değişken a ve değişken b sırasıyla 5 ve 15 değerlerini taşıyor olsun. Bu bilgiye istinaden aritmetik operatörlere göz atalım.

OperatörTanımİşlem -> Sonuç
+ (Toplama)Sağındaki ve solundaki sayıyı toplar.a+b -> 20
– (Çıkarma)Sağındaki sayıyı soldakinden çıkarıra-b -> -10
* (Çarpma)Sağındaki ve solundaki sayıyı çarpara*b -> 75
/ (Bölme)Solundaki sayıyı sağındaki sayıya bölerb/a -> 3
% (Mod alma)Verilen sayının modunu alır ve kalanı döner.b%a -> 0
++ (Artırma)Değişkenin değerini 1 artırır.a++ -> 6
— (Azaltma)Değişkenin değerini 1 azaltır.b– -> 14

Şimdi bu operatörleri bir de uygulamalı olarak görelim. Aşağıda bir java sınıfı görmektesiniz. Bu sınıfı isterseniz merhaba dünya örneğindeki gibi yeni bir proje oluşturarak, o projenin içerisinde oluşturabilir ve çalıştırabilirsiniz. İsterseniz merhaba dünya projenizde yeni bir sınıf oluşturup testinizi buradan yapabilirsiniz.

public class AritmetikOperator {

   public static void main(String args[]) {
      int a = 5;
      int b = 15;

      System.out.println("a + b = " + (a + b) );
      System.out.println("a - b = " + (a - b) );
      System.out.println("a * b = " + (a * b) );
      System.out.println("b / a = " + (b / a) );
      System.out.println("b % a = " + (b % a) );
      System.out.println("c % a = " + (c % a) );
      System.out.println("a++   = " +  (a++) );
      System.out.println("b--   = " +  (a--) );

   }
}

Çıktı:

a + b = 20 
a - b = -10
a * b = 75
b / a = 3
b % a = 0
a++ = 6
b-- = 14

Aritmetik operatörlerin yanısıra ilişkisel operatörler de javada mevcuttur. Bunlar daha çok iki değişken veya değer arasındaki kıyaslama için kullanılır. Aritmetik operatörlerdeki a ve b değişkenlerimizi elimizde tutarak ilişkisel operatörlere de göz atalım. Bir de c değişkenini ekleyelim ve bu değişkenin değeri 5 olsun.

OperatörTanımİşlem -> Sonuç
== (eşitlik)İki tarafındaki değeri karşılaştırır ve eşit ise true (doğru) döner.a==b -> false
!= (eşitsizlik)İki tarafındaki değeri karşılaştırır ve eşit ise false (yanlış) döner. Eşit değilse true döner.a!=b -> true
a!=c -> false
> (büyüktür)Solundaki sayı sağındakinden büyükse true döner.a>b -> true
a>c -> false
< (küçüktür)Solundaki sayı sağındakinden küçükse true döner.a<b -> true
a<c -> false
>= (büyük eşit)Solundaki sayı sağındakinden büyük veya eşitse true döner.a>=b -> false
a>=c ->true
<= (küçük eşit)Solundaki sayı sağındakinden küçük veya eşitse true döner.a<=b -> true
a<=c ->true

AritmetikOperator sınıfımızın yanına şimdi bir sınıf daha yaratalım ve iilişkisel operatörleri de test edelim.

public class Test {

   public static void main(String args[]) {
      int a = 5;
      int b = 15;
      int c = 5;

      System.out.println("a == b = " + (a == b) );
      System.out.println("a != b = " + (a != b) );
      System.out.println("a != c = " + (a != c) );
      System.out.println("a > b = " + (a > b) );
      System.out.println("a > c = " + (a > c) );
      System.out.println("a < b = " + (a < b) );
      System.out.println("a < c = " + (a < c) );
      System.out.println("a >= b = " + (a >= b) );
      System.out.println("a >= c = " + (a >= c) );
      System.out.println("a <= b = " + (a <= b) );
      System.out.println("a <= c = " + (a <= c) );
   }
}

Çıktı:

a == b = false 
a != b = true
a != c = false
a > b = false
a > c = false
a < b = true
a < c = true
a >= b = false
a >= c = true
a <= b = true
a <= c = true

Operatörlerin ilk kısmını bitirdik. Değneceğimiz diğer operatörlere Operatörler – II yazısından ulaşabilirsiniz.

Değişkenler

Değişkenler belirli bir değeri sizin için saklayan yapılar olarak düşünülebilir. Veri tipleri yazımızdaki bahsettiğimiz gibi java tip güvenli bir dildir. Dolayısı ile bir değişkenin nasıl bir değer taşıdığını biliriz. Bu durumu bir posta kutusu ile benzetebiliriz. Düşünün ki size gelen posta teslimatlarını sınıflandırmak istiyorsunuz. Mektuplar için bir posta kutunuz, büyük koli teslimatları için başka bir kutunuz var. Büyük koli kutusuna mektup atılması durumunda bir alarm çalıyor ve kutu mektubu kabul etmiyor. Bu durumda hangi posta kutusundan nasıl bir teslimat çıkacağını garantilemiş oluyorsunuz. Tip güvenliği de tıpkı bunun gibidir.

Değişken tanımı yapılırken belirli bir sırayı izlemeniz gerekmektedir. Önce değişkenin tipini yazar ve ardından değişkenin ismini yazarsınız. Şimdi bir tam sayı değer (int) taşıyan bir değişken tanımı yapalım. Değişkenimizin ismi ise count olsun.

int count;

Satır sonundaki noktalı virgül komutun sona erdiğini gösterir. Bunu ilerleyen yazılarda da göreceğiz.

Şimdi de birden fazla değişkeni bir arada tanımlamayı görelim.

String isim, soyisim, cinsiyet;

Yukarıdaki satır metin içerikli 3 adet değişkenin tanımlanmasını gösteriyor. Bunlar isim, soyisim ve cinsiyet ismindeki değişkenler.

Bu iki örnekte yaptığımız işlem değişken tanımlama (Variable Declaration) olarak isimlendirilmektedir. Yazının başında söylediğimiz gibi değişkenler belirli değerleri bizim için saklarlar. Bunun için bu değişkenlere nasıl değer ataması yapacağımızı da görelim. Öncelikle count değişkeni örneğine tekrar dönüyorum. Buradaki count değerini 5 yapmak için iki yaklaşım sergileyebiliriz.

int count;
count = 5;
int count = 5;

İlk örnekte değişken tanımlanmış ve sonraki satırda eşittir işareti, yani atama operatörü ile değeri 5 yapılmış. Eşittir işareti sağında bulunan değeri solunda bulunan değere atama yapmak için kullandığımız bir operatördür.

İkinci örnekte ise tanımlama anında değer yüklemesi yapılmış. Değişkenimize (count) değer yüklemesi yaptığımız adımlara İngilizce ismiyle Variable Initialization denilmektedir.

Birden fazla değişkeni aynı anda tanımlarken de değer yüklemesi yapmak mümkündür. Yukarıdaki örneği buna göre tekrar düzenleyelim.

String isim = "Uğur", soyisim = "Uçar", cinsiyet = "Erkek";

Bu örnek için bir de ayrı ayrı tanımlamanın nasıl yapıldığına bakalım.

String isim, soyisim, cinsiyet;

isim = "Uğur";
soyisim = "Uçar";
cinsiyet = "Erkek";

Eğer değer yüklemesini tanımdan önce yapmaya kalkarsanız derleyici sizi uyaracaktır ve programınız çalışmayacaktır. Ancak yukarıdaki atamaların yerini değiştirirseniz bu bir sorun yaratmayacaktır. Yani aşağıdaki ve yukarıdaki kod arasında fark yaratan bir çıktı bulunmamaktadır.

String isim, soyisim, cinsiyet;

soyisim = "Uçar";
cinsiyet = "Erkek";
isim = "Uğur";

Değişkenlerin İsimlendirilmesi

Java programlama dili sizi değişken isimlendirmelerinde belirli kuralların içerisinde tutmak ister. Bunun için isimlendirmede kullanabileceğiniz bir çerçeve çizmiştir. Değişken isimlendirme kurallarını aşağıda bulabilirsiniz.

  • Değişken isimleri bir harf başlar. Bununlar beraber başlangıç karakteri $ veya _ olabilir.
  • Değişken isimleri harfleri, sayıları, $ ve _ işaretlerini barındırabilir.
  • Değişken isimlerinde boşluk olamaz.
  • Değişken isimleri büyük-küçük harf duyarlıdır. ad, Ad, aD ve AD farklı değişkenlerdir.
  • Java için rezerve edilmiş anahtar kelimeleri değişken ismi olarak kullanamazsınız. (int, boolean vs.)

Değişkenlerle ilgili şimdilik aktaracaklarım bu kadar. Şu ana kadar temel java konu başlığının temellerini atmaya devam ettik. Sonraki yazıda görüşmek üzere.