JSM etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
JSM etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

17 Ağustos 2011 Çarşamba

Java ve J'li Harf Çorbası

Java programlamaya başlayanların aklını karıştıran konuların başında, ekseriye J ile başlayan teknoloji, standart ve ürün adları gelir. Çoğu zaman arka planda ne olup bittiğinin bilinmemesinden kaynaklanan bu durum, pek çok zamansa Sun şirketinin seçtiği (ve değiştirdiği) adlandırma biçiminden kaynaklanır.

İnternet olgusunun ayırdındaki herkesin bildiği ve yazıya giriş tümcesinden de anlaşılabileceği gibi, Java gözde bir programlama dilinin adıdır. Java kullanılarak yazılan programlar, genelde derlemeli-yorumlama yöntemi ile çalıştırılır. Yani; Java kaynak kodu önce derlenerek [Bytecode adındaki] bir ara dilin komutlarını içeren sınıf dosyalarına çevrilir, sonra Java Sanal Makinesi (JSM; İng., Java Virtual Machine [JVM]) adı verilen bir diğer sistem yazılımı tarafından çalıştırılır. JSM, Bytecode komutlarından oluşan sınıf dosyalarını yorumlamanın yanısıra, sınıf yükleme ve çalıştırma sırasında güvenlik denetimleri de yapar. Örneğin, derleme ile yükleme arasında geçen zaman zarfında sınıf dosyasında oluşabilecek değişiklikler, sınıfın yüklenmesi sırasında yapılan sağlamalar ile saptanabilir. Böylece, kimi kötü niyetle yapılan değişikliklerin kullanıcıya zarar vermesinin  önüne geçilmiş olur. Benzer şekilde, programı çalıştıran kullanıcının yetkisiz olduğu güvenlik zaafı yaratabilecek işlemler programın çalıştırılması sırasında yapılan denetimler ile engellenir. JSM'nin bir diğer önemli görevi, kullanılmaz hale gelmiş yığın bellek bölgelerini toplayan çöp toplayıcı bileşeni yoluyla kaynak yönetimini kolaylaştırmaktır. Bu sayede, Java programcıları kaynak yönetimine dönük daha az kod yazacaklar ve bunun sonucunda üretilen kod daha güvenilir olacaktır.

Rekabete açık bir sektör olan yazılım geliştirme işinde başarıya giden yol, olabildiğince az kaynak (geliştirici zamanı, bilgisayar zamanı ve belleği, vb.) kullanarak piyasanın rağbet edeceği nitelikli ürünleri en kısa sürede geliştirip piyasaya sürmekten geçer. Bu bağlamda uygulanan evrensel kural, aynı ürünün tekrar tekrar geliştirilmesinden kaçınarak daha önceden yapılmış eş işlevli bir ürünün kullanılmasıdır. Yeniden geliştirme için harcanacak zamanın ürünün eniyileştirilmesi ve son ürüne tümleştirilmesine ayrılması, daha güvenilir ve daha yüksek performanslı yazılım üretilmesini olanaklı kılacaktır. İşte bu noktada, Java geliştiricisinin hizmetine Java ortamı (platform) adı verilen standart kitaplıklar sunulur. Söz konusu ortam, hedeflenen kitleye bağlı olarak farklı işlevsellikler sunabilir ve buna bağlı olarak değişik şekillerde adlandırılabilir. Java SE, standart (dizüstülü/masaüstülü) geliştiriciye hitap ederken, Java EE başta İnternet olmak üzere dağıtık ortamlarda çalışacak sunucu tarafı programların geliştirilmesini kolaylaştıran yazılım çerçeveleri içerir; Java ME ise mobil aygıtlar başta olmak üzere gömük sistemler düşünülerek hazırlanmıştır.

Java platformlarının oluşturulması, Java Topluluğu'nun ortaklaşa çabasıyla ortaya çıkarılan çeşitli standartların ürün haline getirilmesiyle mümkün olur. Örneğin, programcıların sıklıkla ihtiyaç duydukları veri yapılarına dair arayüz/sınıf tanımlarının bulunduğu Java Kapları (İng., Java Collections) Java SE'nin bir parçası iken, bileşen temelli dağıtık sunucu programları geliştirmekte kullanılan Java Şirket Taneleri (İng., Enterprise Java Beans [EJB]) Java EE'nin bir parçasıdır.

Java geliştiricileri çalıştıkları platformdaki sınıf dosyası kitaplıklarına ek olarak,  yazılım geliştirmenin değişik aşamalarında kullanılmak üzere, derleyici, JSM, arşivci ve dokümantasyon aracı gibi bir çok sistem programına ihtiyaç duyar. Bu araçlar ve platformun bileşkesi, Yazılım Geliştirme Kutusu (YGK; İng., Software Development Kit [SDK]) olarak adlandırılır. Söz konusu platformun Java SE olması durumunda, adlandırma Java Geliştirme Kutusu (JGK; İng., Java Development Kit [JDK]) şeklinde yapılır.

Java kaynak kodu yazmaktansa Java platformunda hazırlanmış programları kullananların geliştirme araçlarının tümüne ihtiyacı olmaz. Örneğin, bu kişiler programları çalıştırmak için JSM'ne ihtiyaç duyarken, derleyici ve arşivci gibi sistem programlarına ihtiyaç duymayacaklardır. Sadece kullanıcı olmak özelliği taşıyan bu kişiler için Java Çalıştırma Ortamı (JÇO; İng., Java Runtime Environment [JRE]) yeterli olacaktır.

Pek çok zaman, kullanılan platform hangisi olursa olsun, Java geliştiricileri ilişkin kısaltmayı da kısaltarak, Java SE/Java EE/JavaME yerine Java demek yoluna giderler. Ancak bu kullanımın programlama dili adı kullanımından ayırt edilmesi gerekir. Çünkü; en yaygın dilin Java olmasına karşın Java platformu, kaynak kodları sınıf dosyasına derlenebilen tüm dillere açıktır. Ne de olsa, programlar sınıf dosyalarının JSM üzerinde çalıştırılması ile işini görmektedir. Dolayısıyla, Python'un Java platformu uyarlaması olan Jython veya Groovy'nin platform indinde Java programlama dilinden hiçbir farkı yoktur. Ne demek istediğime aşağıdaki örnek üzerinden açıklık getireyim.


Sırasıyla, A ve B şirketleri tarafından yazılan a.groovy ve b.py dosyalarının derlenmesiyle elde edilen a.class ve b.class, iki şirketin ortak müşterilerinden birinin yazdığı Prog.java içinden kullanılmaktadır. Sınıf yolu (İng., classpath) ayarlarına uygun bir şekilde müşteri bilgisayarının erişimindeki disk konumlarına yerleştirilen bu dosyalar, Prog.java dosyasındaki ilişkin atıfların doğruluk denetimlerinin yapılması noktasında derleyici tarafından kullanılır. Derleyicinin kaynak kod yerine sınıf dosyasından yararlanabilmesini olanaklı kılan özellik, sınıf dosyası formatı ve dosyanın içeriğini oluşturan Bytecode komutlarının standardize edilmiş olmasıdır. Bir diğer deyişle, Java kaynak kodlarının taşınabilirliğine ek olarak, sınıf dosyaları da nesne kodu düzeyinde taşınabilirliğe sahiptir. Daha sonra, derleme sonucunun olumlu olması durumunda üretilen Prog.class, JSM'ne geçirilerek çalıştırılacak ve Java, Python ve Groovy dillerinin işlevsellik kattıkları program işini görecektir.

6 Mayıs 2011 Cuma

Java Platformunun Dikkate Değer Bir Dili: Scala-1

Başlık yazının konusunu ele veriyor: sınıf dosyasına derlenmek suretiyle JSM tarafından çalıştırılabilir programlar üretmekte kullanılabilecek Scala dili. .NET'in CIL komutları içeren DLL formatına da derlenebilen, fonksiyonel programlama kavramlarını destekleyen bu nesne yönelimli dil, Java'ya eklenmesi düşünülen pek çok kavram için bir laboratuar ve esin kaynağı olma özelliği taşıyor. Scala programlama diline dair dizimizin ilk bölümü olan bu yazıda da bu dili Java bilenlere tanıtmaya çalışacağız.


Basit Bir Scala Programı


Java'da olduğu gibi, Scala'da da her şey bir sınıf içine yazılmak zorundadır. Aslına bakılırsa, ilkel türleri de sınıflarla temsil eden Scala bu konuda Java'dan daha katıdır diyebiliriz. Bir diğer farklılık, ';' karakterinin pek çok C-temelli dil tarafından kabul edilen komut sonlandırıcılık görevindeki değişikliktir. Satır sonlarının komut sonlandırma noktası olarak algılanması nedeniyle, birden fazla komutun aynı satıra konulmaları durumunda kullanılması dışında, Scala'da komut sonlarına ';' yerleştirilmesine gerek yoktur.

SelamMillet.scala
object SelamMillet {
  def main(args: Array[String]) =
    println("Selam millet!")
} // SelamMillet sınıfının sonu
Yukarıda verilen kaynak kodda main metodunun dönüş türünün eksik olduğunu düşünüyorsanız, Scala'nın bir diğer özelliğini söyleyerek yanıt verelim: Scala, ML ve Haskell programlama dillerinde olduğu gibi, program metninin sağladığı bilgilerden yararlanarak programlama öğelerinin türlerini çıkarsamaya çalışır. Bundan dolayı, Scala programcılarının derleyicinin çıkarsayamadığı yerler dışında hiçbir yere tür bilgisi koymasına gerek yoktur. Derleyici tarafından kabul görmekle birlikte, main metodunun imzasının aşağıdaki şekilde yazılması gerekmez.
def main(args: Array[String]): Unit = 
Örneğimizde sınıf kavramının izlerini arayanlar, kendilerini ikna etmekte zorlanabilirler. Zira, sınıf sözcüğü (class) yerini nesne sözcüğüne (object) bırakmıştır. Sınıfın bir şablon, nesnenin ise söz konusu sınıfın bir örneği olduğunu bilenleriniz bilgilerinden kuşku duymaya başladıysalar üzülmesinler, her şey eskiden olduğu gibi. Beklenen class sözcüğü yerine object sözcüğünün olması, tanımlanmakta olan sınıfın bir özelliğini yansıtmaktadır: Java terminolojisi ile açıklayacak olursak, SelamMillet sınıfının yegâne metodu olan main static'tir. Bir diğer deyişle, main metodu, SelamMillet sınıfının nesnesi yaratılmadan kullanılabilir. Nesne paradigması ile bağdaşmayan bu durum—problem çözümünde merkezi rol oynayan nesnenin bırakın kullanılmasını, yaratılması bile söz konusu değil—kimi zaman çalıştırılabilir sınıfın tek bir nesnesinin yaratılıp bu nesneye ileti gönderilmesi yoluyla aşılır. İşte, tasarım desenleri dünyasında tek örnek[li sınıf] (İng., singleton [class]) deseni olarak adlandırılan bu kullanım, Scala'da object anahtar sözcüğünün kullanımı ile belirtilir. Bu şekilde tanımlanmış sınıfların, tüm öğelerinin static olduğu varsayılır. Bundan dolayıdır ki, Scala'da static anahtar sözcüğüne yer yoktur, programcı bu tür özellikleri tek örnekli sınıflar içinde toplayarak bir object olarak tanımlamalıdır.

Kaynak kodun yazılması sonrasında yapılacaklar Java'daki ile aynıdır: ilk adımda sınıf dosyasına çevrilerek derlenen kod, JSM tarafından çalıştırılmalıdır.
# Derleme aşaması
$ scalac -encoding utf-8 SelamMillet.scala
$ ls SelamMillet*
SelamMillet.class   SelamMillet$.class   SelamMillet.scala
# Yorumlama aşaması
$ scala SelamMillet
Selam millet!
Yukarıdaki listelemenin iki noktası dikkatinizi çekmiştir. Öncelikle, Scala programlama dilinin derleyicisi olan scalac komutunun ürettiği iki tane sınıf dosyası vardır. Bunlardan, SelamMillet.class uygulama dosyası iken, SelamMillet$.class tek örnekli sınıf deseninin gerçekleştirildiği kodu içerir. İkinci nokta ise, JSM'nin java yerine scala komutuyla çağrılmasıdır. Bu, her programlama dili için JSM'nin ayrı ayrı gerçekleştirilmesi gerektiği yanılgısına neden olmamalıdır. Çünkü, scala komutu java komutunun Scala'ya has sınıf dosyalarını yükleyerek işini gören uyarlaması olarak düşünülebilir. Dolayısıyla, yorumlama aşması aşağıdaki şekilde de yapılabilir.
# Yorumlama aşaması
$ java -cp .:/usr/share/java/scala-library.jar SelamMillet
Selam millet!
Bunun güzel bir sonucu, çöp toplama, güvenlik denetimleri, anında derleme gibi JSM tarafından sağlanan pek çok hizmetin Scala programları için de otomatikman sağlanmasıdır; Scala dilinin (ve diğer JSM üzerinde çalışan dillerin) geliştiricilerinin bu hizmetleri ayrıca gerçekleştirmelerine gerek yoktur.

Java Programlama Dili Sınıflarının Kullanımı


JSM dili olmaları nedeniyle, gerek Java gerekse Scala, kurulumunuzun sınıf yolu üzerindeki tüm sınıf dosyalarını, kaynak kodları hangi dilde yazılmış olursa olsun, kullanabilir. Scala programcısı açısından bakıldığında bu, Scala kaynak kodu içinden Java platformunun bildik tüm işlevselliğinden sanki Scala'da yazılmış gibi yararlanılabileceği anlamına gelir. Bu, aşağıdaki örnekte verildiği gibi işlevselliği kullanmak şeklinde olabileceği gibi, platformdaki bir sınıftan kalıtlama veya bir arayüzü gerçekleştirme şeklinde de olabilir. Ancak, Java'dan gelip Scala kaynak kodu üretenlerin yararlanılacak işlevi Scala sözdizimi ile kullanmaları gerektiğini akılda tutmaları gerekir. Aşağıdaki örnek üzerinden görelim.1

Açıklamalara Scala'nın Java ile farklılık gösteren bir özelliğine dikkat çekerek başlayalım: dosya içinde dosya ile aynı ada sahip bir sınıfın bulunması gerekmez; Scala'da böyle bir zorunluluk yok. Ancak, daha sonraki derleme/çalıştırma komutlarında örneği verildiği gibi, derleyiciye geçirilenin dosya adı iken yorumlayıcıya geçirilenin sınıf dosyasının adı olduğu unutulmamalıdır.

Örneğimizin Scala'ya yeni gelenler için dikkat çeken bir yönü, import bildiriminin ilk satırdaki seçimli kullanımı.2 Tekli ve jokerli3 kullanımlara sahip bu bildirim, örneğimizde de olduğu gibi, bir veya daha fazla sayıda türün görünür kılınmasında da kullanılabilir. Dikkat çekecek bir diğer anahtar sözcük ise, simgesel sabit tanımlamak için kullanılan val.4

Örnek2.scala
import java.util.{Scanner, Vector}

object ScaladanJava {
  val grdKnl = new Scanner(System.in);
  println("İkinci Scala örneğine hoş geldiniz...")

  def main(args: Array[String]) = {
    val tekSayılar = new Vector[Int]()
    print("Artı bir tamsayı giriniz: ")
    val j = grdKnl.nextInt()
    for(i <- 1 to j by 2) tekSayılar.add(i)
    println("1 ile" + j + " arasındaki tek sayılar: " + tekSayılar)
  }

  println("Bu örnek Scala kodundan Java'da yazılmış işlevselliği kullanmaya dönük")
} // ScaladanJava sınıfının sonu
Metot gövdeleri dışındaki komutların varlığı da sizi telaşa sürüklemesin; bu komutların sınıf içindeki sıraları korunacak şekilde sınıf ve/veya nesne ilkleme bloğu içine konulduğunu düşünmeniz işinizi kolaylaştıracaktır. Tek örnekli bir sınıf olması nedeniyle tüm öğeleri static varsayılan sınıfımızda, metot tanımları dışındaki her şey sınıf ilkleme bloğuna konulup uygulamanın çalıştırılması sonrasında ilk iş olarak işlenirken, new işleci ile nesnesi yaratılabilen çok örnekli sınıflarda bu tür öğeler nesne ilkleme bloğuna konulacak ve sentezlenen bu nesne ilkleme bloğu her nesne yaratılışı noktasında yapıcı çağrısı öncesinde işlenecektir.
$ scalac -encoding utf-8 Örnek2.scala
$ scala ScaladanJava
İkinci Scala örneğine hoşgeldiniz...
Bu örnek Scala kodundan Java'da yazılmış işlevselliği kullanmaya dönük
Artı bir tamsayı giriniz: 16
1 ile 16 arasındaki tek sayılar: [1, 3, 5, 7, 9, 11, 13, 15]

Diziler Soysal Bir Türün Örneğidir


Verdiğimiz örneklerde komut satırından geçirilen argümanları tutmak için kullanılan dizi ve ikinci örneğimizdeki Vector nesnesinden de görülebileceği gibi, Scala'da soysallık tür parametrelerine karşılık gelen tür argümanlarının köşeli ayraç çifti arasında geçirilmesiyle sağlanır. Buna göre Vector[Int], Int türlü elemanlara sahip bir kabın kullanılacağını gösterir. Bu tanım sonrasında, söz konusu kap parametrik türün (java.util.Vector) sağladığı tüm işlevsellikten yararlanarak manipüle edilebilir. Bu, soysal Array türünün örneği olan diziler için de geçerlidir. Tanımlanan bir dizi, Array sınıfınca sunulan tüm işlevsellikten yararlanabilecektir.


  1. Gözünüzden kaçmış olabilir, işaret etmekte yarar var: ilk örneğimizin aksine ikinci örneğimizin metot gövdesi kıvrımlı ayraç çifti arasına yazılmıştır. Bunun sebebi, tek komuttan oluşması durumunda Scala'daki metot gövdelerinin, tıpkı Java'daki if, while yapılarının gövdeleri gibi, kıvrımlı ayraç çifti arasına yazılmalarının zorunlu olmamasıdır.
  2. java.lang paketindeki türler, herhangi bir bildirime gerek olmadan Scala derleyicisi tarafından görünür hale getirilirler.
  3. Söz konusu paketin tümünü görünür kılan jokerli kullanım, Java'da "*" ile belirtilirken Scala'da '_' ile belirtilir.
  4. Tanımlayıcının güncellenebilir bir içerik tutabilmesi için val yerine var ile nitelenmesi gerekir.

18 Şubat 2011 Cuma

İlk Java Programı

Adet olduğu üzre, benim de Java günlüğümün ilk yazısı Java programlarının çalıştırılma biçimiyle ilgili. Kaynak kodun makine koduna derlendikten sonra—olası bir bağlama aşamasının ardından—fiziksel makine üzerinde doğrudan işlenerek çalıştırıldığı C/C++, Pascal gibi derlemeli dillerin aksine, Java kaynak kodu derlemeli yorumlama (İng., interpretive compilation) ile çalıştırılır. Buna göre, Java dilinde yazılmış olan bir program önce Bytecode adındaki bir ara dilin komutlarını içeren ve formatı standardize edilmiş sınıf dosyasına çevrilerek sanal bir makine üzerinde yorumlanarak çalıştırılır. Her iki yöntemin de artıları ve eksileri vardır. Ancak, bu konuya girmeden önce derlemeli yorumlamanın nasıl işlediğini aşağıdaki minik örnek üzerinden görelim.

SelamMillet.java
public class SelamMillet {
  public static void main(String[] ksa) {
    System.out.println("Selam millet!");
  } // void main(String[]) sonu
} // SelamMillet sınıfının sonu

Aşağıda olduğu gibi derleyiciye kaynak dosya adının geçirilmesi ile icra edilen derleme aşaması, Java dili kurallarına uygun kodu class uzantılı Bytecode dili komutları içeren sınıf dosyası haline çevirir. [Kaynak kod içinde birden çok sınıfın tanımının yapılması durumunda, her sınıfa karşılık bir sınıf dosyası üretilecektir.]

# Derleme aşaması
$ javac SelamMillet.java
$ ls SelamMillet.*
SelamMillet.class   SelamMillet.java

Sınıf dosyasının oluşturulmasıyla birlikte Java Sanal Makinesi'ne (JSM) geçirilip çalıştırılmasının bir edimli kütüğün çalıştırılmasından aslında çok farkı yoktur. Her iki durumda da derleme sonucunda oluşturulan çalıştırılabilir dosya bir makine üzerinde yorumlanır. Ancak, edimli kütük için yorumlama bir fiziksel makine üzerinde yapılırken, sınıf dosyası için soyut bir makine üzerinde yapılır.

# Yorumlama aşaması
$ java SelamMillet
$ Selam millet!

Çalıştırılabilir sınıf dosyalarının soyut makine tarafından yorumlanması, dosya içeriğini oluşturan Bytecode komutlarının fiziksel makine komutları karşılıklarına dönüştürülmesini takiben bu makine komutlarının fiziksel makine üzerinde işlenmesiyle tamamlanır. Derlemeli dillerde programın çalışması öncesinde yapılan bu dönüşümün varlığı, Java programlarının daha yavaş çalışacağı anlamına gelir. Ancak, bu yavaşlama yorumlanan dilin (Bytecode) bir ara dil olması ve makine diline yüksek düzey dillerden daha yakın olması nedeniyle safkan yorumlamadaki kadar fazla olmayacaktır. Ayrıca, sanal makinenin yazılımda gerçekleştirilmesi ve Bytecode dili ile sınıf dosyası formatının standardize edilmiş olması, sınıf dosyalarının JSM'ne sahip tüm donanımlarda çalıştırılabileceği anlamını taşır. Yani, program çalışma hızındaki yavaşlamaya karşılık, nesne kodu düzeyinde taşınabilirlik elde edilmiştir.

Çalışma hızındaki yavaşlama, Java dilinde yazılmış programları doğrudan makine diline çeviren bir derleyici—mesela, gcj—ile aşılabilir. Platforma özel yazılımların üretiminde kabul edilebilirse de, Java'nın taşınılmazlığın vazgeçilmez olduğu İnternet ortamının dili olarak lanse edildiği düşünüldüğünde, çoğu kullanım senaryosu için taşınabilirliği olanaksız kılan bu seçenek uygulanabilir olmayacaktır. Dolayısıyla, taşınabilirlikten vazgeçmeyen bir çözümün tercih edilmesi daha yerinde olacaktır. Aranan çözüm, hemen hemen tüm JSM'lerinin desteklediği anında derlemeden geçer. Programcı/kullanıcı açısından  derlemeli yorumlamadan farkı bulunmayan bu çalıştırma yönteminde  JSM, Bytecode komutlarını yorumlarken bazı eniyilemeler yapar. Örneğin, ilk kez çağrılan bir metodun ilk çağrısı sırasında sonraki çağrıların daha hızlı olması için söz konusu metot veya metodun bulunduğu sınıf o noktada makine koduna çevrilerek bir önbelleğe yerleştirilir. Sonraki çağrılar, silinmediyse  önbellekteki derlenmiş kodu kullanacak ve programın çalışma hızı makine koduna derlenen programlarınkine benzer  bir düzeye çıkacaktır.

Özetlemek gerekirse; Bytecode komutları içeren sınıf dosyalarına derlenen Java kaynak kodu, bu sınıf dosyalarının anında derleme desteğiyle yorumlanması sayesinde nesne kodu düzeyindeki taşınabilirlik özelliğini yitirmeden hızlı bir şekilde çalıştırılır.