23 Şubat 2011 Çarşamba

Bir Java Programının Anatomisi

Önceki yazımda Java programlarının nasıl çalıştırıldığına değinmiştim. Bu yazımda ise, Java programlarını oluşturan dosyaların nasıl yapılandırılabileceğini anlatmaya çalışacağım. Bunu yaparken de aşağıdaki küçük programdan yararlanacağım.

BasitProgram.java
import java.util.Date;
import java.util.Scanner;
import static java.lang.System.*;

public class BasitProgram {
  public static void main(String[] ksa) {
    out.println("Tarih: " + new Date());
    Scanner tarayici = new Scanner(in);
    double x = tarayici.nextDouble();
    out.println("e^" + x + " = " + Math.exp(x));

    return;
  } // void main(String[]) sonu
} // BasitProgram sınıfının sonu

C++ dilindeki küresel fonksiyonlara alışmış olanları uyararak ilk kuralımızı söyleyelim: Java'da her şey bir sınıf içinde bulunmak zorundadır. Dosya içinde tek bir sınıfın var olması durumunda bu sınıf ile sınıf tanımını içeren dosyanın aynı ada sahip olması gerekir.

Sınıf tanımı, veriler ve bu verileri işleyecek metotlardan oluşur. Sınıfı oluşturan tüm öğeler, tıpkı sınıfın kendisi gibi, bir erişim niteleyicisine sahiptir. Bu niteleyici, söz konusu öğenin Java platformundaki hangi sınıflar içinden kullanılabileceğini belirler. Örneğin, public olarak nitelenmiş BasitProgram sınıfındaki main metodu public—yani, kamuya açık—tanımlandığı için platformdaki tüm sınıflar tarafından kullanılabilecektir.

Çalıştırılabilir sınıfların—yani, JSM'ne geçirilerek doğrudan işletilen sınıfların—tanımında yer alan main metodu özel bir şekilde ele alınır. JSM kendisine geçirilen bir sınıfı belleğe yükledikten sonra main metodu içindeki ilk edimli satırı işleyerek programı çalıştırmaya başlar. Bir diğer deyişle, main metodu programların giriş noktasıdır.

main metodunun gövdesi dışındaki özellikleri değişmez ve şu şekilde yorumlanabilir.
  • public: main metodunun JSM tarafından çağrılabilmesi için public erişime sahip olması zorunludur.
  • static: static öğeler içinde bulundukları sınıfın nesnesi yaratılmadan  da kullanılabilir. Nesne yaratmanın programın çalışması sırasında olduğu ve programın main metodunun ilk edimli komutunun işlenmesi ile çalışmaya başladığı düşünülürse, main metodunun nesne yaratılması sonrasında çağrılmasının olanaksızlığı görülecektir.
  • void: main metodu çağırıcısı olan JSM'ne değer döndürmez. İşlerin yolunda gitmeyip bunun döndürülecek bir değer ile bildirilmesinin gerektiği durumlarda, System.exit metodunun kullanılması yerinde olacaktır.
  • String[] ksa: main metodu String'lerden oluşan bir dizi değer—0 veya daha fazla sayıda—bekler. main metodunun giriş noktası olduğu ve dolayısıyla değerleri içeren argümanın program içindeki bir diğer metottan geçirilmesinin söz konusu olmadığı düşünüldüğünde, argüman geçirmenin değer sağlamanın özel bir yöntemi olan komut satırı argümanları ile yapılması gerektiği görülecektir. [Aslında, main metodu program içindeki, kendisi de dahil, herhangi bir metottan çağrılabilir. Ancak, ilk çağrı her zaman program dışından olacaktır.]
İzin verildiği durumlarda, sınıf içindeki programlama öğeleri değişik şekillerde kullanılabilir. İlk yöntem, kullanılmak istenen öğeye dair tanımı içeren sınıfı programımıza görünür hale getirmek ve bunun ardından bu sınıfı kısa adından yararlanarak kullanmaktan geçer. Yukarıdaki programın ilk satırındaki import bildirimi ve ilişkin sınıfın 8. satırdaki kullanımı buna örnektir; ilk satırda programımıza görünür hale getirilen Scanner sınıfı, 8.satırda nesne yaratmak amacıyla kısa adıyla kullanılabilmaktedir. import bildiriminin bulunmaması durumunda, ilişkin sınıfların uzun adlarıyla kullanılmaları gerekir. Dolayısıyla, ilk satırdaki import bildiriminin iptal edilmesi sonrasında, 8.satırın aşağıdaki biçimde yazılması gerekecektir.

java.util.Scanner tarayici = new java.utility.Scanner(in);

Bu noktada, iki özel durumu vurgulamakta yarar var. Öncelikle, içinde bulunulan sınıfın öğelerinin kısa adlarıyla kullanılması için herhangi bir şeyin yapılması gerekmez. Ayrıca, java.lang paketinde bulunan sınıflar, kullanıcının import bildirimini kullanmasına gerek bırakmadan otomatikman ithal edilmiş varsayılır.

import bildiriminin diğer bir kullanımı, aynı paket içinde bulunan sınıfların tümünü görünür hale getirir. Örneğin, aynı paket içinde bulunan Date ve Scanner sınıflarını ayrı ayrı ithal etmektense, aşağıdaki jokerli import bildiriminden yararlanarak iki satırda yaratılan etkiyi tek bir satırda yaratmak mümkündür.

import java.util.*;

import bildiriminin programımızda da geçen son kullanımı, herhangi bir sınıf—paket değil!—içindeki static öğelerin sınıf adı olmaksızın kullanılmasını olanaklı kılar. Buna göre, System sınıfı içinde tanımlanmış ve değiştirilmedikçe ekran ile klavyeye bağlanmış olan standart çıktı ve girdi dosyalarını temsil eden kamuya açık static out ve in, System.out ve System.in yerine, sırasıyla, out ve in şeklinde kullanılabilmektedir.

Son olarak; bir programın nasıl sona ereceğine değinerek yazımızı bitirelim. Yaptığı iş gereği sonsuz bir döngü içinde göçene kadar çalışması beklenen programlar dışında tüm programlar eninde sonunda bitecektir. Bu üç şekilde olabilir.
  • return: Bu komut işlemekte olan metottan çağırıcısına dönülmesini sağlar. main metodundaki gibi bir dönüş değerinin beklenmediği durumlarda, metot gövdesinin son komutu sonrasında bir return komutu olduğu varsayılır. Buna göre, örneğimizin 12.satırındaki komut gereksizdir. Çalışması sonucunda değer döndüreceği ilan edilen metotlarda ise, return komutu mecburidir ve dönüş türüne tür uyumlu bir değer ile birlikte kullanılmalıdır.
  • System.exit: Bu komut hangi metot içinde olursa olsun, çağrı zincirindeki metotların tümünü atlayarak, işlemekte olan metottan JSM'ne döner. Dönüş değeri olarak System.exit'e geçirilen argümanın değeri kullanılır.
  • Ayrıksı durum🔎: Oluşması düşük olasılıklı bir durumun ortaya çıkması sonrasında, metottan durumu özetleyen bilgiyi içeren bir ayrıksı durum nesnesi ile de dönülebilir.