Bilgisayar ve Programlamaya Giriş · Ders Notu
Java'ya Giriş ve İlk Program
Bir programı yazmak, onu çalıştırmanın yalnızca ilk adımıdır. Yazılan metin bir dosyaya kaydedilir, bir derleyiciden geçer, ara bir koda dönüşür ve sanal bir makinede çalışır. Bu yolda her adımın kendi kuralları ve kendi hata iletileri vardır. Bu not, program ve programlama dili kavramlarıyla başlar. Ardından Java'nın doğuşunu, JDK, JRE ve JVM ilişkisini ve ilk Java programını satır satır anlatır. Konsola yazdırma, kaçış dizileri ve dizgi birleştirme örneklerle işlenir. Derleme ve çalıştırma adımları, bytecode'un içi, dilin temel yapı taşları ve açıklamalar bunları izler. Son kesimler hata türlerini, hata iletilerini okumayı ve kodlamadan önce yapılacak hazırlığı ele alır. Notta gösterilen bütün programlar JDK 21 ile derlenip çalıştırılmıştır; derleyici iletileri ve konsol çıktıları bu çalıştırmalardan birebir alınmıştır.
Öğrenme çıktıları
- Program
- Programı girdi, işlem, durum ve çıktıdan oluşan bir bütün olarak açıklamak; programlama dilinin söz dizimi, anlam, tip sistemi ve çalışma modeli yönlerini ayırt etmek.
- Java
- Kaynak kod, bytecode ve JVM ilişkisini kurmak; JDK, JRE ve JVM'nin içeriğini ve görevini açıklamak.
- İlk program
- Bir Java dosyasını yazmak,
javacile derlemek vejavaile çalıştırmak;mainmetodunu, komut satırı argümanlarını,print/printlnfarkını, kaçış dizilerini ve dizgi birleştirmeyi doğru kullanmak. - Hatalar
- Söz dizimi, tip, çalışma zamanı ve mantık hatalarını ayırt etmek; derleyici ve JVM iletilerini okuyup hatanın yerini ve nedenini bulmak.
5.1Program ve programlama dili
Bilgisayar kendi başına hiçbir iş yapmaz. Hesap makinesi uygulaması da, bir oyun da, bir banka sistemi de bilgisayara ne yapacağını adım adım söyleyen komutlardan oluşur. Bu komutları yazmaya programlama, yazılan komutların bütününe program denir. Komutlar, bilgisayarın ve insanın anlaşabileceği yapay bir dille yazılır.
5.1.1Program nedir?
Program (program), bilgisayarın belirli bir görevi yerine getirmek için yürüttüğü komutlar bütünüdür. Bir program girdi alabilir, veriyi işler, ara değerleri bellekte saklar ve çıktı üretir.
Tanımdaki dört öğe her programda görülür. Girdi (input), programa dışarıdan verilen bilgidir: klavyeden yazılan bir sayı, bir dosyanın içeriği ya da bir algılayıcıdan gelen ölçüm. İşlem, girdiden yeni değerler üreten adımlardır: toplama, karşılaştırma, sıralama. Durum (state), programın çalışırken bellekte tuttuğu değerlerin o anki hâlidir. Çıktı (output) ise programın dışarıya verdiği sonuçtur: ekrana yazılan bir satır, kaydedilen bir dosya ya da yakılan bir lamba.
Şekil 5.1 bu öğeleri küçük bir örnek üzerinde gösterir. Program iki not alır: 70 ve 90. İlk adımda notlar okunur. İkinci adımda toplanır ve toplam, yani 160, bellekte saklanır. Üçüncü adımda toplam 2'ye bölünür ve 80 bulunur. Son adımda 80 ekrana yazılır. Aynı program 50 ve 60 notları için 55 yazar. Program değişmez, yalnızca girdi değişir. Programı değerli yapan da budur: bir kez yazılır, farklı girdilerle tekrar tekrar çalıştırılır.
Bir programın davranışını iki şey belirler. Birincisi, programın yazıldığı dilin kurallarıdır: hangi işaretin ne anlama geldiği, bir sayının nasıl saklandığı. İkincisi, programcının seçtiği yöntem, yani algoritmadır. Ortalama örneğinde “önce topla, sonra böl” bir algoritmadır. Aynı algoritma farklı dillerde yazılabilir. Bu notta dil olarak Java kullanılır.
5.1.2Programlama dilinin yönleri
Türkçe gibi doğal dillerde de bir cümlenin iki yönü vardır: doğru yazılıp yazılmadığı ve ne anlattığı. “Kitap masada okudu.” cümlesi dil bilgisi bakımından kusurludur. “Masa kitabı okudu.” ise doğru kurulmuştur ama anlamsızdır. Programlama dillerinde bu iki yöne iki yön daha eklenir. Bir programlama dili dört yönden incelenir.
int toplam = fiyat * adet; deyiminin dört yönü. Söz dizimi deyimin ; ile bitmesini ister. Tip sistemi iki int'in çarpımının da int olduğunu söyler. Anlam “çarp, sonucu kutuya yaz” demektir. Çalışma modeli ise sonucun bellekteki toplam kutusunda saklandığını anlatır.Söz dizimi (syntax), bir programın geçerli yazım biçimini belirleyen kurallardır. Anlam (semantics), geçerli bir program parçası çalıştırıldığında ne olacağını tanımlar. Tip sistemi (type system), her değerin bir tipi olmasını ve bir değerle yalnızca tipine uygun işlemlerin yapılmasını sağlar. Çalışma modeli (execution model), komutların hangi sırayla yürütüldüğünü ve değerlerin bellekte nasıl tutulduğunu anlatır.
Şekil 5.2 bu dört yönü tek bir deyim üzerinde gösterir: int toplam = fiyat * adet;. Söz dizimi bakımından deyim bir tip adıyla başlar, ardından bir ad, eşittir işareti, bir ifade ve noktalı virgül gelir. Noktalı virgül unutulursa deyim geçersizdir. Tip sistemi bakımından fiyat ve adet tam sayıdır (int), iki tam sayının çarpımı da tam sayıdır. Bu yüzden sonuç int tipindeki toplam'a konabilir. Anlam bakımından deyim “fiyat ile adedi çarp, sonucu toplam'a yaz” demektir. Çalışma modeli bakımından bellekte toplam adlı bir kutu ayrılır ve sonuç bu kutuda saklanır.
Java'da söz dizimi ve tip kuralları program çalıştırılmadan önce denetlenir. Bu denetimi derleyici (compiler) yapar. Noktalı virgülü unutulmuş ya da bir metni sayı kutusuna koymaya çalışan bir program hiç çalıştırılmaz; derleyici hatayı satır numarasıyla bildirir. Anlamla ilgili yanlışlar ise çoğu zaman ancak program çalışırken ortaya çıkar. Ortalamayı bulurken bölmeyi unutan bir program kusursuz derlenir ama yanlış sonuç üretir.
5.2Kaynak kod ve araçlar
Programcının yazdığı metne kaynak kod (source code) denir. Kaynak kod insan için yazılır: okunabilir sözcüklerden, sayılardan ve işaretlerden oluşur. İşlemci ise yalnızca kendi makine komutlarını anlar. Kaynak kodu çalışan bir programa dönüştürmek için birkaç araç birlikte kullanılır.
5.2.1Düz metin kaynak dosyası
Bir Java programı, uzantısı .java olan bir dosyaya yazılır. Bu dosyaya kaynak dosya (source file) denir. Kaynak dosya düz metindir (plain text). Düz metin dosyasında yalnızca karakterler bulunur; yazı tipi, renk, kalın yazı ya da sayfa düzeni gibi biçim bilgisi yoktur. Bu yüzden bir Java dosyası Not Defteri'yle, VS Code gibi bir kod düzenleyiciyle ya da bir tümleşik geliştirme ortamıyla açılabilir. Word belgesi ise düz metin değildir; içinde biçim bilgisi de saklanır. Kaynak kod Word'de yazılıp .docx olarak kaydedilirse derleyici dosyayı okuyamaz.
Merhaba.java düz metin dosyasıdır. Diskte her karakter bir ya da birkaç bayt olarak saklanır: “public” sözcüğünün harfleri onaltılık 70, 75, 62, 6C, 69, 63 baytlarıdır. ASCII karakterleri UTF-8'de 1 bayt, ç, ğ, ş gibi harfler 2 bayt tutar.Diskte her şey bayttır. Bir düz metin dosyasında her karakter, bir karakter kodlamasına (character encoding) göre bir ya da birkaç bayta çevrilir. Bugün en yaygın kodlama UTF-8'dir. UTF-8'de İngilizce alfabedeki harfler, rakamlar ve noktalama işaretleri ASCII'deki gibi tek bayt tutar. Şekil 5.3 “public” sözcüğünün baytlarını gösterir: p harfi onaltılık 70, yani onluk 112'dir. Türkçeye özgü ç, ğ, ı, ö, ş, ü harfleri UTF-8'de ikişer bayt tutar.
Kodlama, dosyayı yazan ve okuyan programların anlaşmasıdır. Dosya UTF-8 ile kaydedilip başka bir kodlamayla okunursa Türkçe harfler bozulur. “Dünya” sözcüğü “Dünya” gibi görünebilir. Java derleyicisi de kaynak dosyayı okurken bir kodlama varsayar. JDK 18'den beri varsayılan kodlama UTF-8'dir. Bu notun örnekleri JDK 21 ile hiçbir ek seçenek verilmeden derlenmiştir; “Dünya”, “Satır” ve “İndirim” gibi sözcükler doğru çıkmıştır. Daha eski bir JDK kullanılıyorsa derleyiciye kodlamayı -encoding UTF-8 seçeneğiyle açıkça söylemek gerekir.
Kodlama dışında iki ayrıntı daha tutarlı olmalıdır. Birincisi satır sonlarıdır: Windows satır sonunu iki karakterle (CR LF), Linux ve macOS tek karakterle (LF) yazar. Java derleyicisi ikisini de kabul eder. Ama aynı projede karışık satır sonları, değişiklikleri izleyen araçlarda gereksiz farklar doğurur. İkincisi dosya adıdır. Java'da dosya adı, içindeki public sınıfın adıyla aynı olmak zorundadır: Merhaba sınıfı Merhaba.java dosyasına yazılır.
Düz metnin bir yararı daha vardır. Sürüm kontrol sistemleri (version control system), bir dosyanın hangi satırını kimin, ne zaman değiştirdiğini kaydeder. Git en yaygın örnektir. Bu araçlar satır satır karşılaştırma yaptığı için düz metin dosyalarla en iyi sonucu verir.
5.2.2Programlama araçları
Bir Java programı yazılıp çalıştırılana kadar birkaç aracın elinden geçer. Her aracın tek ve belirli bir görevi vardır.
| Araç | Görevi | Örnek |
|---|---|---|
| Düzenleyici | Kaynak kodu yazmak ve kaydetmek | Not Defteri, VS Code |
| IDE | Proje yönetimi, kod tamamlama, derleme, çalıştırma ve hata ayıklama | IntelliJ IDEA, Eclipse, NetBeans |
javac | Kaynak kodu bytecode'a derlemek | javac Merhaba.java |
java | Bir sınıfı JVM üzerinde çalıştırmak | java Merhaba |
javadoc | Kaynak koddaki belge açıklamalarından HTML biçiminde API belgesi üretmek | javadoc Merhaba.java |
| Sürüm kontrolü | Değişiklik geçmişini tutmak | Git |
Kaynak kodu yazmak için bir düzenleyici (editor) yeterlidir. Kod düzenleyicileri sıradan metin düzenleyicilerden farklı olarak anahtar kelimeleri renklendirir, girintiyi kendiliğinden ayarlar ve satır numaralarını gösterir. Tümleşik geliştirme ortamı (integrated development environment, IDE) düzenleyiciyi derleyici, çalıştırıcı ve hata ayıklayıcıyla tek bir pencerede birleştirir.
javac Java derleyicisidir; kaynak kodu okur, denetler ve bytecode adı verilen ara koda çevirir. java komutu bir Java Sanal Makinesi başlatır ve bytecode'u çalıştırır. javadoc, kodun içine özel biçimde yazılmış açıklamalardan web sayfası biçiminde belge üretir. Java'nın kendi sınıf kütüphanesinin çevrim içi belgeleri de bu araçla üretilmiştir.
Merhaba.java, javac ile Merhaba.class dosyasına derlenir. java komutu bu dosyayı JVM'de çalıştırır ve konsola Merhaba yazılır. javadoc kaynak koddan belge üretir; sürüm kontrolü kaynak kodun geçmişini (v1 → v2 → v3) tutar.Şekil 5.4 araçların sırasını gösterir. Dikkat edilecek nokta şudur: javadoc ve sürüm kontrolü kaynak dosyayla çalışır, .class dosyasıyla değil. .class dosyası her derlemede yeniden üretilebildiği için sürüm kontrolüne genellikle eklenmez. Saklanması gereken asıl ürün kaynak koddur.
5.3Java
Java, bugün en çok kullanılan programlama dillerinden biridir. Bankacılık sistemlerinde, Android uygulamalarında, büyük veri araçlarında ve üniversitelerin programlamaya giriş derslerinde karşımıza çıkar. Bu yaygınlığın en önemli nedeni, Java programlarının işletim sisteminden ve işlemciden bağımsız çalışabilmesidir. Bu kesim Java'nın nasıl doğduğunu, nerelerde kullanıldığını ve bir Java programını çalıştırmak için hangi yazılımların gerektiğini anlatır.
5.3.1Java'nın doğuşu ve sürümleri
Java'nın hikâyesi 1991'de Sun Microsystems'te başlar. James Gosling ve ekibi “Green projesi” adlı bir çalışmada televizyon kutuları gibi akıllı ev cihazları için bir dil tasarlamaya girişti. Dilin ilk adı Oak'tı (meşe). Ev cihazları pazarı beklenen ilgiyi görmedi, ama aynı yıllarda Web hızla yayılıyordu. Farklı işletim sistemli bilgisayarlarda aynı biçimde çalışabilen bir dil tam da Web'in ihtiyacıydı. Dil 1995'te Java adıyla yayımlandı.
Java'nın tasarım hedefi tek bir cümleyle özetlenir: bir kez yaz, her yerde çalıştır (write once, run anywhere). Bir Java programı bir kez derlenir. Derleyicinin ürettiği bytecode, Windows'ta, Linux'ta ya da macOS'ta, o sistem için yazılmış Java Sanal Makinesi üzerinde değiştirilmeden çalışır. Programcının her işletim sistemi için ayrı derleme yapması gerekmez.
Java'nın öteki temel özellikleri şunlardır:
- Nesne yönelimli (object-oriented): Programlar sınıflar ve nesneler etrafında kurulur. En küçük Java programı bile bir sınıfın içine yazılır.
- Güçlü tip denetimi: Her değişkenin bir tipi vardır ve derleyici tip uyuşmazlıklarını program çalışmadan önce yakalar.
- Otomatik bellek yönetimi: Artık kullanılmayan nesnelerin belleği çöp toplama (garbage collection) ile kendiliğinden geri alınır. Programcı belleği elle serbest bırakmaz.
- Geniş sınıf kütüphanesi: Ekrana yazma, dosya okuma, ağ bağlantısı, tarih hesabı gibi işler için hazır sınıflar vardır.
Java'nın sahibi 2010'da değişti: Oracle, Sun Microsystems'i satın aldı ve dili o günden beri geliştiriyor. Java'nın kaynak kodu 2006'dan beri açıktır ve OpenJDK projesinde yayımlanır. Bugün Oracle'ın yanı sıra Eclipse Adoptium (Temurin), Amazon (Corretto) ve Microsoft gibi kuruluşlar da OpenJDK tabanlı JDK dağıtımları sunar. Bu notun örnekleri Eclipse Adoptium'un JDK 21 dağıtımıyla çalıştırılmıştır.
2018'den beri her altı ayda bir yeni Java sürümü çıkar: Mart ve Eylül aylarında. Her sürüm yalnızca birkaç ay güncelleme alır. Bazı sürümler ise uzun süreli destek (long-term support, LTS) sürümü olarak ilan edilir ve yıllarca güvenlik güncellemesi alır. Kurumlar ve dersler bu yüzden LTS sürümlerini tercih eder. Java 8, 11, 17, 21 ve 25 LTS sürümleridir. Bu derste Java 21 kullanılır.
5.3.2Java nerelerde kullanılır?
Java'nın kullanım alanları çok geniştir. Aşağıdaki tablo en bilinen alanları özetler.
| Alan | Açıklama |
|---|---|
| Kurumsal sunucular | Bankacılık, e-ticaret ve kamu sistemlerinin arka ucu; Spring ve Jakarta EE çatıları |
| Android | Uygulamalar Java ya da Kotlin ile yazılır; bytecode, telefondaki Android çalışma ortamına (ART) uygun biçime çevrilir |
| Büyük veri | Hadoop ve Kafka gibi veri işleme araçları JVM üzerinde çalışır |
| Geliştirme araçları | IntelliJ IDEA, Eclipse ve NetBeans gibi IDE'lerin kendisi de Java ile yazılmıştır |
| Akıllı kartlar | SIM kartlar ve banka kartlarındaki küçük programlar Java Card ile çalışır |
| Oyun | Minecraft'ın Java Sürümü; masaüstü oyunlar ve simülasyonlar |
Tablodaki örneklerin bir kısmı Java dilinin değil, Java Sanal Makinesinin başarısıdır. JVM yalnızca Java'yı çalıştırmaz. Kotlin, Scala ve Groovy gibi diller de aynı bytecode'a derlenir ve aynı sanal makinede çalışır. Android uygulamalarının bugün çoğu Kotlin ile yazılır, ama arkadaki çalışma modeli aynıdır.
5.3.3JDK, JRE ve JVM
Java ile ilgili belgelerde üç kısaltma sürekli geçer: JDK, JRE ve JVM. Üçü iç içe katmanlardır; dıştaki katman içtekini kapsar.
Java Sanal Makinesi (Java Virtual Machine, JVM), bytecode'u yürüten yazılımdır. JRE (Java Runtime Environment, Java çalışma ortamı), JVM ile Java sınıf kütüphanesinin birleşimidir: bir Java programını çalıştırmak için gereken her şeyi içerir. JDK (Java Development Kit, Java geliştirme kiti), JRE'ye geliştirme araçlarını (javac, jar, javadoc …) ekler.
Kısaca: JDK = JRE + geliştirme araçları, JRE = JVM + sınıf kütüphanesi. Şekil 5.6 bu ilişkiyi gösterir. Yalnızca hazır bir Java programını çalıştıracak kullanıcıya JRE yeterlidir. Program yazacak ve derleyecek geliştiricinin ise JDK kurması gerekir, çünkü javac yalnızca JDK'de bulunur.
Bu ayrım günümüzde biraz değişti. Oracle, Java 11'den beri ayrı bir JRE paketi sunmuyor; geliştirici de kullanıcı da JDK kurar. Bazı dağıtıcılar JRE'yi hâlâ ayrıca sunar. Kavram olarak üç katmanı bilmek yine de önemlidir, çünkü hata iletileri ve belgeler bu adları kullanır.
JVM'nin dört parçası programın yaşamında sırayla iş görür. Sınıf yükleyici (class loader), .class dosyasını diskten okuyup belleğe alır. Bytecode doğrulayıcı (bytecode verifier), yüklenen kodun kurallara uyduğunu denetler; örneğin bir komutun yığında olmayan bir değeri kullanmaya çalışmadığını. Yorumlayıcı (interpreter) bytecode komutlarını tek tek yürütür. JIT derleyicisi (just-in-time compiler, tam zamanında derleyici) sık çalışan kısımları işlemcinin makine koduna çevirerek hızlandırır. Çöp toplayıcı da program çalıştıkça kullanılmayan belleği geri alır.
Bilgisayarda birden çok Java kurulu olabilir ve komut satırı ilk bulduğunu kullanır. Bu notun hazırlandığı bilgisayarda javac JDK 21'den, PATH'teki java ise eski bir Java 8 kurulumundan geliyordu. JDK 21 ile derlenen Merhaba.class, Java 8 ile çalıştırılınca şu ileti alındı:
Exception in thread "main" java.lang.UnsupportedClassVersionError: Merhaba has been compiled by a more recent version of the Java Runtime (class file version 65.0), this version of the Java Runtime only recognizes class file versions up to 52.0İleti açıktır: sınıf dosyası sürüm 65 (Java 21) biçimindedir, çalıştıran JVM ise en çok sürüm 52'yi (Java 8) tanır. Eski bir JVM yeni bytecode'u çalıştıramaz. Çözüm, java -version ile javac -version çıktılarının aynı sürümü gösterdiğinden emin olmaktır.
5.4İlk Java programı
Programlama öğrenmenin geleneksel ilk adımı ekrana kısa bir selam yazdıran bir programdır. Bu program küçüktür, ama bir Java programının bütün zorunlu parçalarını içerir. Bu kesim programı önce parçalarına ayırır, sonra JVM'nin onu nasıl çalıştırdığını adım adım izler.
5.4.1Programın parçaları
Aşağıdaki program Merhaba.java adlı bir dosyaya kaydedilir:
public class Merhaba {
public static void main(String[] args) {
System.out.println("Merhaba");
}
}Program derlenip çalıştırıldığında konsola tek bir satır yazar:
> javac Merhaba.java
> java Merhaba
MerhabaKonsol oturumlarında > ile başlayan satırlar kullanıcının yazdığı komutlardır; altındaki satırlar komutun çıktısıdır. javac komutunun altında hiçbir satır yoktur: derleme başarılı olduğunda derleyici sessiz kalır.
main metodudur. En içteki satır konsola yazan deyimdir ve ; ile biter. Her } açtığı bloğu kapatır.Şekil 5.7 programı iç içe üç katman olarak gösterir. Satırları tek tek inceleyelim.
public class Merhaba {bir sınıf bildirimidir (class declaration). Java'da her kod bir sınıfın içinde bulunur.classanahtar kelimesinden sonra sınıfın adı gelir.public, sınıfın her yerden erişilebilir olduğunu söyler. Public bir sınıfın adı dosya adıyla birebir aynı olmalıdır:MerhabasınıfıMerhaba.javadosyasına yazılır. Satır sonundaki{sınıfın gövdesini açar.public static void main(String[] args) {main metodunu (main method) bildirir. Metot, bir ada sahip bir iş birimidir. JVM bir programı çalıştırırken işe her zamanmainmetodundan başlar; bu yüzdenmainprogramın giriş noktasıdır (entry point).System.out.println("Merhaba");programın asıl işini yapan deyimdir (statement).System.outstandart çıktı akışını, yani konsolu temsil eder.printlnmetodu kendisine verilen metni konsola yazar ve satırı bitirir. Çift tırnak içindeki"Merhaba"bir dizgidir (string): karakterlerden oluşan bir metin değeri. Deyim noktalı virgülle biter.- İlk
}mainmetodunun gövdesini, ikinci}sınıfın gövdesini kapatır.
main metodunun başındaki sözcüklerin her birinin bir görevi vardır:
| Sözcük | Anlamı |
|---|---|
public | Metot her yerden erişilebilir; JVM de onu dışarıdan çağırabilir |
static | Metot sınıfa aittir; çağırmak için önce bir nesne oluşturmak gerekmez |
void | Metot bir değer döndürmez |
main | Metodun adı; JVM giriş noktasını bu adla arar |
String[] args | Metodun parametresi: komut satırı argümanlarını taşıyan dizgi dizisi |
Bu sözcüklerin her biri (erişim belirleyiciler, static metotlar, dönüş tipleri, diziler) başlı başına bir konudur. İlk programlar için bu satırı olduğu gibi yazmak yeterlidir. Yine de her sözcüğün neden orada olduğunu bilmek, yanlış yazıldığında alınan iletiyi anlamayı kolaylaştırır.
5.4.2main metodunun imzası
Bir metodun adı, parametreleri ve dönüş tipi onun imzasını oluşturur. JVM bir sınıfı çalıştırırken tam olarak public static void main(String[] args) imzalı bir metot arar. İlginç olan şudur: imzası yanlış yazılmış bir main derleme hatası vermez. Derleyici için Main adlı ya da int döndüren bir metot sıradan, geçerli bir metottur. Hata ancak java komutu programı başlatmaya çalışırken ortaya çıkar.
Aşağıdaki tablo, A.java dosyasındaki A sınıfında main farklı biçimlerde yazıldığında ne olduğunu gösterir. Her sürüm javac A.java ile hatasız derlendi, sonra java A ile çalıştırıldı.
| Yazılan | java A ile çalıştırınca |
|---|---|
public static void main(String[] args) | çalışır |
public static void Main(String[] args) | Error: Main method not found in class A, … |
public void main(String[] args) | Error: Main method is not static in class A, … |
public static int main(String[] args) | Error: Main method must return a value of type void in class A, … |
static void main(String[] args) | Error: Main method not found in class A, … |
public static void main(String a[]) | çalışır |
JVM'nin iletisi aslında daha uzundur ve doğru imzayı da önerir. Örneğin Main büyük harfle yazıldığında tam ileti şöyledir:
Error: Main method not found in class A, please define the main method as:
public static void main(String[] args)
or a JavaFX application class must extend javafx.application.ApplicationSon satırdaki JavaFX cümlesi, grafik arayüzlü JavaFX uygulamalarının başka bir başlatma yolu olduğunu hatırlatır; bu derste kullanılmaz. Tablodan üç sonuç çıkar:
- Java büyük-küçük harfe duyarlıdır (case-sensitive):
Mainilemainfarklı adlardır. public,staticvevoidzorunludur. Biri eksik olduğunda JVM metodu ya hiç bulamaz ya da bulup reddeder.- Parametrenin adı serbesttir:
argsyerineayazılabilir.String a[]biçimindeki eski yazım da geçerlidir. Yine de Java geleneğiString[] argsyazımıdır.
Java'nın yeni sürümlerinde bu kurallar gevşetildi. Java 25'ten itibaren public ya da static olmayan, hatta parametresi olmayan bir main metodu da başlatılabilir. Bu ders Java 21 ve her sürümde çalışan klasik imzayı kullanır.
5.4.3Program adım adım
java Merhaba komutu yazıldıktan sonra ekranda Merhaba görünene kadar birkaç adım geçer. Bu adımları bilmek, çalışma zamanı hatalarını ve yığın izlerini anlamanın temelidir.
Merhaba.class dosyasındaki bytecode'u yükler ve main() metodunu çalıştırır; System.out çıktı akışı metni konsola taşır.| Adım | Satır | Olan | Çağrı yığını | Konsol |
|---|---|---|---|---|
| 1 | 1 | JVM başlar, Merhaba.class dosyasını belleğe yükler | boş | |
| 2 | 2 | JVM main metodunu çağırır; argüman verilmediği için args boş bir dizidir | main(args) | |
| 3 | 3 | "Merhaba" metni println metoduna gönderilir | main(args) → println | |
| 4 | 3 | println metni yazar ve satır sonu ekler; main'e dönülür | main(args) | Merhaba⏎ |
| 5 | 4 | main bloğunun } karakterine gelinir; main biter | boş | Merhaba⏎ |
| 6 | 5 | Çalışacak başka kod yoktur; JVM kapanır | — | Merhaba⏎ |
Tablodaki “çağrı yığını” sütunu, o anda hangi metotların çalıştığını gösterir. Bir metot başka bir metodu çağırdığında çağrılan metot yığının tepesine eklenir; bitince kaldırılır. 3. adımda main, println'i çağırmıştır ve ikisi birden yığındadır. println bitince yalnız main kalır. main de bitince yığın boşalır ve program sonlanır. Toplam 1 deyim çalışmış ve 1 satır çıktı üretilmiştir.
5.4.4Komut satırı argümanları
main metodunun parametresi olan args, programa komut satırından bilgi vermenin yoludur. java komutunda sınıf adından sonra yazılan sözcüklere komut satırı argümanları (command-line arguments) denir. JVM bu sözcükleri bir dizgi dizisine koyar ve main'e verir.
Dizi, aynı tipte birden çok değeri sıra numarasıyla tutan yapıdır. Dizideki her hücrenin bir indisi vardır ve indisler 0'dan başlar. args[0] ilk argüman, args[1] ikinci argümandır. args.length dizideki argüman sayısını verir. Aşağıdaki program argümanları kullanır:
public class Selamla {
public static void main(String[] args) {
System.out.println(args.length);
System.out.println("Selam " + args[0]);
System.out.println("Selam " + args[1]);
}
}Program önce iki argümanla, sonra tek argümanla çalıştırıldı:
> java Selamla Ali Veli
2
Selam Ali
Selam Veli
> java Selamla Ali
1
Selam Ali
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1
at Selamla.main(Selamla.java:5)args dizisinin iki çalıştırmadaki durumu. Üstte java Selamla Ali Veli: iki hücre dolu, args.length = 2. Altta java Selamla Ali: yalnızca args[0] var, args[1] yok; uzunluk 1'dir.İlk çalıştırmada args dizisi ["Ali", "Veli"] olur (Şekil 5.9, üstte). 3. satır argüman sayısını, yani 2'yi yazar. 4. satırda "Selam " + args[0] ifadesi iki dizgiyi yan yana ekler ve Selam Ali yazılır. 5. satır aynı biçimde Selam Veli yazar.
İkinci çalıştırmada kod da .class dosyası da aynıdır; yeniden derlemeye gerek yoktur. Değişen yalnızca girdidir. Dizide tek hücre vardır. 3. ve 4. satırlar 1 ve Selam Ali yazar. 5. satır var olmayan args[1]'e erişmeye çalışır. JVM bu durumda bir istisna (exception) fırlatır (throw): ArrayIndexOutOfBoundsException, yani “dizi indisi sınırların dışında”. İleti, 1 numaralı indisin uzunluğu 1 olan bir dizinin dışında kaldığını söyler. Alt satır hatanın Selamla.java dosyasının 5. satırında, main metodunda oluştuğunu gösterir. Program o noktada durur.
Bu hatayı derleyici bilemezdi. Derleme anında programa kaç argüman verileceği belli değildir. Hata, çalışma sırasında ve girdiye bağlı olarak ortaya çıkar. Argüman hiç verilmezse hata bir satır önce, 4. satırda oluşur:
> java Selamla
0
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
at Selamla.main(Selamla.java:4)Aynı Selamla programı java Selamla Ayşe "Ali Veli" komutuyla çalıştırılıyor. Çıktı ne olur?
Komut satırı argümanları boşluklarla ayrılır. Çift tırnak ise içindeki boşluğu argümanın parçası yapar. Bu yüzden "Ali Veli" iki değil, tek bir argümandır ve tırnaklar argümana dahil değildir. args dizisi ["Ayşe", "Ali Veli"] olur, uzunluğu 2'dir:
> java Selamla Ayşe "Ali Veli"
2
Selam Ayşe
Selam Ali VeliFazla argüman ise hata doğurmaz. java Selamla 1 2 3 komutunda dizinin uzunluğu 3'tür. Program yalnızca ilk iki hücreyi kullandığı için üçüncüsü görmezden gelinir:
> java Selamla 1 2 3
3
Selam 1
Selam 25.4.5print ve println
Konsola yazmanın iki temel yolu vardır: System.out.print ve System.out.println. İkisi de kendisine verilen değeri yazar. İkisinin farkı, yazma işi bittikten sonra ortaya çıkar. println (print line) yazdıktan sonra bir satır sonu (newline) ekler; sonraki çıktı alt satırdan başlar. print satır sonu eklemez; sonraki çıktı aynı satırda, kaldığı yerden sürer.
Konsolda yazının nereye geleceğini imleç (cursor) belirler. İmleci, bir sonraki karakterin yazılacağı yer olarak düşünmek yeterlidir. Aşağıdaki programda imlecin yolculuğunu izleyelim:
public class Selam {
public static void main(String[] args) {
System.out.print("Merhaba ");
System.out.println("Dünya");
System.out.println("Java");
}
}Merhaba Dünya
Java| Satır | Deyim | Konsol | İmleç |
|---|---|---|---|
| 2 | (main başlar) | ▌ | satır 1, sütun 1 |
| 3 | print("Merhaba ") | Merhaba ▌ | satır 1, sütun 9 |
| 4 | println("Dünya") | Merhaba Dünya▌ | satır 2, sütun 1 |
| 5 | println("Java") | Merhaba DünyaJava▌ | satır 3, sütun 1 |
print("Merhaba ") sonrası: imleç aynı satırda, boşluğun hemen arkasında (satır 1, sütun 9). Altta println("Dünya") sonrası: satır bitti, imleç alt satırın başında (satır 2, sütun 1).“Merhaba ” dizgisi sekiz karakterdir: yedi harf ve sondaki boşluk. print bu sekiz karakteri yazar ve imleç 9. sütunda kalır (Şekil 5.10). Sondaki boşluk önemlidir. O olmasaydı “Dünya” bitişik yazılır ve çıktı MerhabaDünya olurdu. println("Dünya") aynı satıra devam eder, sonra satırı bitirir. println("Java") yeni satıra yazılır ve o satırı da bitirir. Üç çağrı yapılmıştır, ama yalnızca iki satır oluşmuştur, çünkü satırı yalnızca println bitirir.
Parametresiz System.out.println() yalnızca bir satır sonu yazar. İmleç bir satırın ortasındaysa o satırı bitirir; satırın başındaysa boş bir satır oluşturur. print ise her zaman bir değer ister; parametresiz System.out.print() derleme hatasıdır.
Programın son çıktısını print ile yazmak. Çıktı ekranda görünür, ama satır bitmediği için konsolun komut istemi aynı satıra yapışır: Sonuç: 80C:\ders> gibi. Programın son satırında println kullanmak bu karışıklığı önler.
5.5Kaçış dizileri ve dizgi birleştirme
Bir dizgi çift tırnakla başlar ve çift tırnakla biter. Bu basit kural iki soru doğurur. Birincisi, metnin içinde çift tırnak geçecekse ne yapılır? İkincisi, klavyeden doğrudan yazılamayan karakterler, örneğin satır sonu ya da sekme, dizgiye nasıl konur? Java iki soruyu da aynı yolla çözer: kaçış dizileri.
5.5.1Kaçış dizileri
Kaçış dizisi (escape sequence), dizgi ya da karakter sabiti içinde ters eğik çizgiyle (\) başlayan ve tek bir özel karakteri temsil eden yazımdır. Ters eğik çizgi, ardından gelen karakterin olağan anlamından “kaçmasını” sağlar.
| Dizi | Anlamı | Örnek | Çıktı |
|---|---|---|---|
\n | yeni satır (satır sonu) | "A\nB" | A, alt satırda B |
\t | sekme (tab) | "Ad\tNot" | Ad Not |
\" | çift tırnak | "\"Java\"" | "Java" |
\\ | ters eğik çizgi | "C:\\ders" | C:\ders |
\' | tek tırnak (karakter sabiti içinde) | '\'' | ' |
Kaynak koddaki \n iki karakter gibi görünür: ters eğik çizgi ve n harfi. Derleyici ise onu tek bir karaktere, satır sonu karakterine çevirir. Ekranda ne ters eğik çizgi ne de n görünür; yalnızca satır biter. Aynı biçimde \" tek bir çift tırnak karakteridir ve dizgiyi bitirmez. \\ ise tek bir ters eğik çizgi verir. Ters eğik çizginin kendisi kaçış dizilerini başlattığı için onu yazmanın tek yolu iki kez yazmaktır.
Tek tırnak dizgi içinde sorun çıkarmaz: "Ali'nin notu" kaçışsız yazılır. Tek tırnak yalnızca tek bir karakteri tutan char sabitlerinde özel anlamlıdır. 'A' bir karakter sabitidir; tek tırnak karakterinin kendisi '\'' diye yazılır.
5.5.2Sekme ve satır sonu adım adım
Sekme karakteri \t imleci bir sonraki sekme durağına (tab stop) taşır. Konsollarda sekme durakları genellikle 8 sütunda bir bulunur: 1, 9, 17, 25 … sütunlarında. Sekme, imleç hangi sütunda olursa olsun onu bir sonraki durağa götürür. Bu yüzden boyları farklı sözcüklerden sonra yazılan değerler aynı sütunda hizalanır. Aşağıdaki program bunu kullanır:
public class Kacis {
public static void main(String[] args) {
System.out.println("Ad\tNot");
System.out.println("Ali\t85");
System.out.println("\"Java\" dili");
System.out.print("Satır 1\nSatır 2\n");
}
}Ad Not
Ali 85
"Java" dili
Satır 1
Satır 2| Satır | Yazılan | Ne oldu? |
|---|---|---|
| 3 | Ad, sekme, Not, satır sonu | “Ad” 1. ve 2. sütunları kaplar; sekme imleci 9. sütuna taşır. Araya 6 boşluk girer; “Not” 9. sütundan başlar. |
| 4 | Ali, sekme, 85, satır sonu | “Ali” 3 sütun kaplar; sekme imleci yine 9. sütuna taşır. Araya 5 boşluk girer; 85, “Not” başlığının tam altına gelir. |
| 5 | "Java" dili, satır sonu | \" dizgiyi bitirmeden çift tırnak yazar. |
| 6 | Satır 1, satır sonu, Satır 2, satır sonu | Tek bir print iki satır üretir: dizgideki iki \n iki satırı bitirir. |
Son satırdaki print çağrısı dikkat çekicidir. print kendisi satır sonu eklemez, ama dizginin içindeki \n karakterleri satırı bitirir. Yani System.out.print("Satır 1\nSatır 2\n") ile iki ayrı println aynı çıktıyı verir. Bir ayrıntı: println'in eklediği satır sonu işletim sistemine göre değişir. Windows'ta println iki karakter (\r\n) yazar, \n ise her yerde tek karakterdir. Konsolda ikisi aynı görünür.
Sekmeyle hizalamanın bir sınırı vardır. Bir sözcük 8 karakter ya da daha uzunsa sekme imleci bir sonraki durağa, yani 17. sütuna taşır ve hizalama bozulur. Bu yüzden sekme kısa başlıklı basit tablolar için uygundur.
Bir öğrencinin dönem derslerini kredi ve AKTS değerleriyle hizalı bir tablo olarak yazdıran, altına da toplamları ekleyen program nasıl yazılır?
Her satır bir println ile yazılır ve sütunlar \t ile ayrılır. Toplamlar tek bir print ve iki \n ile iki satıra yazılır:
public class Kart {
public static void main(String[] args) {
System.out.println("Ders\tKredi\tAKTS");
System.out.println("Fizik I\t4\t6");
System.out.println("Java\t3\t5");
System.out.print("Toplam kredi: 7\nToplam AKTS: 11\n");
}
}Ders Kredi AKTS
Fizik I 4 6
Java 3 5
Toplam kredi: 7
Toplam AKTS: 11“Ders” 4, “Fizik I” 7, “Java” 4 karakterdir. Üçü de 8'den kısa olduğu için ilk sekme her satırda imleci 9. sütuna taşır ve kredi değerleri alt alta gelir. İkinci sekme 17. sütuna taşır; AKTS değerleri de hizalanır. “Fizik I” 7 karakter olduğu için ardından yalnızca 1 boşluk görünür.
5.5.3Kaçış dizisi tuzakları
Ters eğik çizgi en çok Windows dosya yolları yazılırken sorun çıkarır. Windows klasörleri ters eğik çizgiyle ayırır: C:\yeni. Bu yol bir dizgiye olduğu gibi yazılırsa derleyici \y'yi bir kaçış dizisinin başlangıcı sayar:
public class Y {
public static void main(String[] args) {
System.out.println("C:\yeni");
}
}Y.java:3: error: illegal escape character
System.out.println("C:\yeni");
^
1 error\y diye bir kaçış dizisi yoktur. Derleyici “geçersiz kaçış karakteri” (illegal escape character) der ve ^ ile sorunlu yeri gösterir. Program derlenmez. Daha sinsi durum, ters eğik çizgiden sonra gelen harfin geçerli bir kaçış dizisi oluşturmasıdır:
"C:\new" dizgisi kaynakta 7 karakter gibi görünür, ama bellekte 5 karakterdir: C, :, yeni satır, e, w. Ekranda “C:” ve alt satırda “ew” görünür.| Deyim | Sonuç |
|---|---|
System.out.println("C:\yeni"); | derleme hatası: illegal escape character; \y diye bir kaçış dizisi yok |
System.out.println("C:\new"); | derlenir ama iki satır yazar: C: ve ew |
System.out.println("C:\\yeni"); | C:\yeni: ters eğik çizgi iki kez yazılır |
System.out.println("Ali'nin notu"); | Ali'nin notu: dizgi içinde tek tırnak kaçışsız yazılır |
System.out.println("\u00C7ay"); | Çay: \u ve dört onaltılık basamak bir Unicode karakteri verir |
System.out.println("C:/yeni"); | C:/yeni: Java'nın dosya işlemleri Windows'ta da eğik çizgiyi kabul eder |
System.out.println("A\\nB"); | A\nB (tek satır): \\ bir ters eğik çizgi verir, ardından gelen n sıradan bir harftir |
Derlenebilen bütün örnekler, iki tablodaki sırayla, tek bir programda çalıştırıldığında çıktı şöyledir:
public class K {
public static void main(String[] args) {
System.out.println("A\nB");
System.out.println("Ad\tNot");
System.out.println("\"Java\"");
System.out.println("C:\\ders");
System.out.println('\'');
System.out.println("C:\new");
System.out.println("C:\\yeni");
System.out.println("Ali'nin notu");
System.out.println("\u00C7ay");
System.out.println("C:/yeni");
System.out.println("A\\nB");
}
}A
B
Ad Not
"Java"
C:\ders
'
C:
ew
C:\yeni
Ali'nin notu
Çay
C:/yeni
A\nBÇıktının 7. ve 8. satırları "C:\new" deyiminden gelir (Şekil 5.12). Program hatasız çalıştığı için bu yanlış kolay fark edilmez: derleyici uyarmaz, yalnızca çıktı tuhaftır. Son satır ise tersini gösterir: "A\\nB" yeni satır üretmez, ekrana ters eğik çizgi ve n harfini yazar.
\u ile başlayan yazıma Unicode kaçışı (Unicode escape) denir. Ardından gelen dört onaltılık basamak, karakterin Unicode numarasıdır. Ç harfinin numarası onaltılık 00C7'dir. Unicode kaçışı, klavyede bulunmayan ya da dosya kodlaması yüzünden bozulma riski taşıyan karakterleri yazmak için kullanılır:
public class U {
public static void main(String[] args) {
System.out.println("Dağ");
System.out.println("İstanbul");
System.out.println("Çay");
}
}Dağ
İstanbul
ÇayWindows yolunu tek ters eğik çizgiyle yazmak. "C:\ders\notlar" derlenir mi derlenmez mi, harflere bağlıdır: \d geçersiz olduğu için derleme hatası alınır. "C:\temp\new" ise derlenir ama \t sekmeye, \n satır sonuna dönüşür. Kural basittir: dizgideki her ters eğik çizgi \\ diye yazılır ya da yol "C:/temp/new" biçiminde eğik çizgiyle yazılır.
5.5.4Dizgi birleştirme
+ işareti Java'da iki ayrı iş yapar. İki sayı arasında toplar: 40 + 45 sonucu 85'tir. İşlenenlerden en az biri dizgiyse birleştirir: öteki işlenen metne çevrilir ve sona eklenir. "Puan: " + 85 sonucu "Puan: 85" dizgisidir. Bu ikinci işe dizgi birleştirme (string concatenation) denir.
Bir ifadede birden çok + varsa iki kural sonucu belirler:
- Aynı öncelikteki işlemler soldan sağa yapılır.
a + b + cifadesi(a + b) + cgibi değerlendirilir. *ve/her zaman+'dan önce yapılır. Parantez ise her şeyden önce gelir.
Her + adımında “işlenenlerden biri dizgi mi?” sorusu yeniden sorulur. Cevap evetse birleştirme, hayırsa toplama yapılır. Dizgi bir kez ortaya çıktıktan sonra sağındaki bütün +'lar birleştirme olur, çünkü soldaki ara sonuç artık bir dizgidir.
"Puan: " + 40 + 45 soldan sağa iki birleştirmeyle değerlendirilir: önce "Puan: " + 40 → "Puan: 40", sonra "Puan: 40" + 45 → "Puan: 4045".public class B {
public static void main(String[] args) {
System.out.println(40 + 45 + " puan");
System.out.println("Puan: " + 40 + 45);
System.out.println("Puan: " + (40 + 45));
System.out.println("Ort: " + 85 / 2);
System.out.println(5 + 3);
System.out.println("5 + 3");
}
}85 puan
Puan: 4045
Puan: 85
Ort: 42
8
5 + 3| İfade | Adımlar | Çıktı |
|---|---|---|
40 + 45 + " puan" | 40 + 45 → 85 (iki int: toplama); 85 + " puan" → "85 puan" | 85 puan |
"Puan: " + 40 + 45 | "Puan: " + 40 → "Puan: 40"; "Puan: 40" + 45 → "Puan: 4045" | Puan: 4045 |
"Puan: " + (40 + 45) | parantez önce: 40 + 45 → 85; "Puan: " + 85 → "Puan: 85" | Puan: 85 |
"Ort: " + 85 / 2 | / önce: 85 / 2 → 42 (iki tam sayı bölününce kesir atılır); "Ort: " + 42 | Ort: 42 |
5 + 3 | iki int: toplama | 8 |
"5 + 3" | tırnak içindeki her şey metindir; hesap yapılmaz | 5 + 3 |
Tablonun dördüncü satırı ayrı bir dikkat ister. 85 / 2 işleminde iki işlenen de tam sayı olduğu için sonuç da tam sayıdır. Kesirli kısım yuvarlanmaz, atılır: 42,5 değil 42 çıkar. Burada /'nin +'dan önce yapıldığına ve sonucun dizgiye 42 olarak eklendiğine dikkat edelim.
Bir sonucu açıklamayla birlikte yazdırırken toplamayı parantezsiz yazmak. System.out.println("Toplam: " + a + b); deyimi a = 40 ve b = 45 için Toplam: 4045 yazar, çünkü en soldaki işlenen dizgidir ve her iki sayı da metne eklenir. Toplamın kendisi isteniyorsa "Toplam: " + (a + b) yazılmalıdır.
5.6Java dosyasının yapısı
Merhaba programı tek bir sınıftan oluşuyordu. Gerçek programlarda bir dosyanın başında sınıftan önce iki tür bildirim daha bulunabilir: package ve import. Bir Java dosyasının parçaları belirli bir sırayla yazılır.
package okul;
import java.util.Scanner;
public class Program {
public static void main(String[] args) {
System.out.println("Merhaba");
}
}okul/Program.java dosyasının iç içe yapısı: 1. package bildirimi, 2. import bildirimleri, 3. sınıf. Sınıfın içinde main metodu, metodun içinde deyimler bulunur.Şekil 5.14 yapıyı gösterir. Sıra şöyledir:
package okul;sınıfın hangi pakete (package) ait olduğunu söyler. Paket, birbiriyle ilgili sınıfları bir arada tutan ad alanıdır; bilgisayardaki klasörlere benzer. Paket bildirimi varsa dosyanın ilk deyimi olmak zorundadır ve dosya paket adıyla aynı adlı bir klasörde durur:okul/Program.java.import java.util.Scanner;başka bir paketteki bir sınıfı kısa adıyla kullanmayı sağlar. Bu satır olmasaydı sınıf her yerde tam adıyla,java.util.Scannerdiye yazılmalıydı.importbildirimleri paket bildiriminden sonra, sınıftan önce gelir. Bu örnekteScannerkullanılmamıştır; yalnızca yeri göstermek için yazılmıştır. Kullanılmayan birimporthata vermez.public class Program { … }sınıfın kendisidir. Public sınıfın adı dosya adıyla aynıdır.
System sınıfı için import gerekmediğine dikkat edelim. System, String gibi en temel sınıflar java.lang paketindedir ve bu paket her dosyaya kendiliğinden eklenir.
Paketli bir sınıf derlenirken ve çalıştırılırken paket klasörünün bir üstündeki klasörde durulur. Çalıştırırken sınıfın paket adıyla birlikte tam adı yazılır:
> javac okul/Program.java
> java okul.Program
Merhaba
> java Program
Error: Could not find or load main class Program
Caused by: java.lang.ClassNotFoundException: ProgramSon komutta yalnızca Program yazıldığında JVM, paketsiz bir Program sınıfı arar ve bulamaz. Sınıfın gerçek adı okul.Program'dır. İlk programlarda paket kullanılmaz; sınıflar varsayılan, adsız pakette durur. IDE'ler ise yeni bir proje oluştururken çoğu zaman bir paket bildirimi ekler. Bir IDE'de yazılmış dosya komut satırında derlenirken bu ayrıntı unutulmamalıdır.
5.7Derleme ve çalıştırma
Bir Java programı yazıldığı anda çalışmaz. Önce derlenir, sonra çalıştırılır. Bu iki adım iki ayrı komutla yapılır ve iki ayrı dosya türüyle ilgilidir: derleyici .java dosyasını okur, JVM ise .class dosyasını çalıştırır. Bu ayrımı iyi kavramak, yeni başlayanların en sık takıldığı sorunların çoğunu önler.
5.7.1Derleme hattı
Derleme (compilation), kaynak kodun bütünüyle denetlenip başka bir dile çevrilmesidir. Java'da derleyici javac, kaynak kodu bytecode adı verilen ve işlemciden bağımsız olan bir ara koda çevirir. Bytecode .class uzantılı dosyalarda saklanır ve Java Sanal Makinesinde çalışır.
Bir Java programının yazılmasından çıktısının görünmesine kadar beş adım vardır. Bu adımların hepsine birlikte derleme hattı diyelim:
- Yaz. Kaynak kod bir düzenleyicide yazılır ve
Merhaba.javaadıyla kaydedilir. Dosya adı public sınıfın adıyla aynı olmalıdır. - Derle.
javac Merhaba.javakomutu derleyiciyi çalıştırır. Derleyici dosyanın tamamını, program daha çalışmadan önce okur; söz dizimini ve tipleri denetler. - Bytecode. Derleme başarılıysa aynı klasörde
Merhaba.classdosyası oluşur. Bu dosya düz metin değil, ikili bir dosyadır. Windows'ta, Linux'ta ve macOS'ta aynıdır. - Çalıştır.
java Merhabakomutu JVM'yi başlatır. Bu komuta dosya adı değil, sınıf adı verilir;.classuzantısı yazılmaz. JVM'nin sınıf yükleyicisi.classdosyasını belleğe alır, doğrulayıcı bytecode'u denetler, sonramainçağrılır. - Çıktı. JVM bytecode'u yorumlar; sık çalışan kısımları JIT derleyicisiyle makine koduna çevirir.
printlnkonsolaMerhabayazar.
javac başarılı olunca klasörde Merhaba.class oluşur. Bu dosya işlemciden bağımsız bir ara koddur ve ilk dört baytı onaltılık CA FE BA BE'dir.Her .class dosyası aynı dört baytla başlar: onaltılık CA FE BA BE. Bu “sihirli sayı”, JVM'nin dosyanın gerçekten bir Java sınıf dosyası olduğunu anlamasını sağlar. Bu notta derlenen Merhaba.class dosyasının ilk sekiz baytı ca fe ba be 00 00 00 41'dir. Son iki bayt sınıf dosyasının sürüm numarasıdır: onaltılık 41, onluk 65'tir ve Java 21'i gösterir. Eski bir JVM'nin yeni bir sınıf dosyasını reddederken andığı “class file version 65.0” bu sayıdır.
Derleyici bir hata bulursa hat ikinci adımda durur:
javac hatayı satır numarasıyla bildirir (Merhaba.java:3: error: ';' expected) ve .class dosyası üretmez. Çalıştırılacak bir şey yoktur.Derleme hatası olan bir program hiç çalışmaz; bir kısmı da çalışmaz. Derleyici bütün dosyayı denetler ve tek bir hata bile bulsa .class üretmez. Bu, satır satır çalışan yorumlanan dillerden önemli bir farktır. Python gibi bir dilde hatalı satırdan önceki satırlar çalışabilir. Java'da hatanın 50. satırda olması, 1. satırın da çalışmayacağı anlamına gelir.
5.7.2JDK kurulumu ve komutlar
Java programı yazmak için önce bir JDK kurulur. Kurulumdan sonra komut satırında birkaç temel komut kullanılır:
| Komut | Ne yapar? |
|---|---|
java -version | kurulu Java sürümünü gösterir |
javac -version | derleyici sürümünü gösterir (javac yalnız JDK'de bulunur) |
javac Merhaba.java | derler: Merhaba.class üretir |
java Merhaba | çalıştırır (uzantı yazılmaz) |
java Merhaba.java | Java 11 ve sonrası: tek dosyalık programı derleyip çalıştırır, .class dosyası bırakmaz |
Kurulumdan sonra ilk iş, iki sürüm komutunu çalıştırıp aynı sürümü gösterdiklerini görmektir. Bu notun bilgisayarında JDK 21 komutlarının çıktısı şöyledir:
> java -version
openjdk version "21.0.3" 2024-04-16 LTS
OpenJDK Runtime Environment Temurin-21.0.3+9 (build 21.0.3+9-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.3+9 (build 21.0.3+9-LTS, mixed mode, sharing)
> javac -version
javac 21.0.3Tablonun son satırındaki kullanım kısa denemeler için pratiktir. java komutuna bir .java dosyası verilirse JVM dosyayı bellekte derler ve hemen çalıştırır:
> java Merhaba.java
MerhabaBu yol tek dosyalık programlar için çalışır ve diskte .class dosyası bırakmaz. Derleme ile çalıştırmanın iki ayrı adım olduğunu görmek için bu notta iki adımlı yol kullanılır.
PATH ve JAVA_HOME. Komut satırına javac yazıldığında işletim sistemi bu adlı programı PATH ortam değişkenindeki (environment variable) klasörlerde arar. JDK'nin bin klasörü PATH'te değilse “komut bulunamadı” hatası alınır. Çözüm, JDK'nin bin klasörünü, örneğin C:\Program Files\Eclipse Adoptium\jdk-21.0.3.9-hotspot\bin klasörünü, PATH'e eklemektir. JAVA_HOME ortam değişkeni ise JDK'nin ana klasörünü gösterir; derleme araçları ve IDE'ler Java'yı bulmak için bu değişkene bakar.
Windows konsolunda Türkçe. Türkçe harfler iki yerde bozulabilir. Birincisi derleme sırasıdır: derleyici dosyayı yanlış kodlamayla okursa harfler daha bytecode'a girerken bozulur. JDK 18 ve sonrasında derleyicinin varsayılan kodlaması UTF-8'dir; daha eski sürümlerde javac -encoding UTF-8 Merhaba.java yazmak gerekir. İkincisi konsolun kendisidir. Program doğru çalıştığı hâlde ekranda harfler bozuk görünüyorsa konsolun kod sayfası UTF-8'e çevrilebilir: önce chcp 65001 komutu çalıştırılır.
5.7.3Derle, değiştir, yeniden derle
Yeni başlayanları en çok şaşırtan durumlardan biri şudur: kaynak kod değiştirilir, program çalıştırılır, ama eski çıktı görünür. Nedeni, java komutunun kaynak koda hiç bakmamasıdır. JVM yalnızca .class dosyasındaki bytecode'u çalıştırır. Aşağıdaki oturumda klasörde başta yalnızca Merhaba.java vardır ve program Merhaba yazar.
> java Merhaba
Error: Could not find or load main class Merhaba
Caused by: java.lang.ClassNotFoundException: Merhaba
> javac Merhaba.java
> java Merhaba.class
Error: Could not find or load main class Merhaba.class
Caused by: java.lang.ClassNotFoundException: Merhaba.class
> java Merhaba
MerhabaBu kısımda üç ayrı durum görülür. İlk komutta henüz Merhaba.class yoktur; JVM Merhaba adlı sınıfı bulamaz. İkinci satırdaki Caused by, hatanın altta yatan nedenini verir: ClassNotFoundException, yani sınıf bulunamadı. Derleme sessizce başarılı olur. Üçüncü komutta java'ya dosya adı verilmiştir. JVM “Merhaba.class” adlı bir sınıf arar. Java'da sınıf adları nokta içeremediği için bunu class adlı bir sınıf gibi yorumlar ve bulamaz. Uzantısız doğru komut programı çalıştırır.
Şimdi düzenleyicide "Merhaba" metni "Selam" olarak değiştirilip dosya kaydedilir ve oturum sürer:
> java Merhaba
Merhaba
> javac Merhaba.java
> java Merhaba
Selam| Adım | Komut ya da işlem | Merhaba.java | Merhaba.class | Çıktı |
|---|---|---|---|---|
| 1 | javac Merhaba.java | println("Merhaba") | “Merhaba” yazar (yeni) | — |
| 2 | java Merhaba | println("Merhaba") | “Merhaba” yazar | Merhaba |
| 3 | düzenle ve kaydet | println("Selam") | “Merhaba” yazar (eski!) | — |
| 4 | java Merhaba | println("Selam") | “Merhaba” yazar (eski!) | Merhaba |
| 5 | javac Merhaba.java | println("Selam") | “Selam” yazar (yeni) | — |
| 6 | java Merhaba | println("Selam") | “Selam” yazar | Selam |
Merhaba.java artık println("Selam") içerir, ama Merhaba.class hâlâ “Merhaba” yazan eski bytecode'dur. Kaynak değişti, .class değişmedi.Tablo 5.12 ve Şekil 5.17 aynı sonucu gösterir: .class dosyası kendiliğinden güncellenmez. Dördüncü adımda çıktı hâlâ Merhaba'dır. Kaynak kodda yapılan her değişiklikten sonra yeniden derlemek gerekir.
Sınıf adındaki harf büyüklüğü de önemlidir. Windows dosya adlarında büyük-küçük harf ayırmaz, Java ise ayırır. java merhaba komutunda Windows Merhaba.class dosyasını bulur, ama JVM dosyanın içindeki sınıfın adının merhaba değil Merhaba olduğunu görür ve reddeder:
> java merhaba
Error: Could not find or load main class merhaba
Caused by: java.lang.NoClassDefFoundError: merhaba (wrong name: Merhaba)İletideki wrong name: Merhaba ifadesi “yanlış ad, doğrusu Merhaba” demektir.
5.7.4IDE ile çalışmak
Komut satırında derleme ve çalıştırma, arka planda olanı açıkça gösterir. Günlük çalışmada ise çoğu programcı bir IDE kullanır. IDE aynı adımları bir düğmenin arkasına gizler.
';' expected) gösterilmiştir. Hata düzeltilip ▶ Çalıştır düğmesine basılınca IDE önce derler, sonra çalıştırır.IDE'nin sağladığı başlıca kolaylıklar şunlardır:
- ▶ Çalıştır düğmesi derleme ve çalıştırma adımlarını arka arkaya yapar. Değiştirilen dosyalar önce yeniden derlenir; bu yüzden eski
.classsorunu yaşanmaz. - Anında hata gösterimi: IDE kodu yazılırken arka planda derler. Derleme hataları kırmızı alt çizgiyle işaretlenir; imleç üzerine gelince derleyicinin iletisi görünür.
- Kod tamamlama:
System.out.yazıldığında kullanılabilecek metotlar listelenir. Yanlış yazılmış metot adları böylece azalır. - Otomatik girinti ve yeniden adlandırma: bir değişkenin adı değiştirildiğinde geçtiği her yer birlikte değişir.
- Hata ayıklayıcı (debugger): programı bir kesme noktasında (breakpoint) durdurur, değişkenlerin o anki değerlerini gösterir ve programı satır satır ilerletir.
Yaygın Java IDE'leri IntelliJ IDEA, Eclipse ve NetBeans'tir. VS Code da Java eklentileriyle aynı işleri yapar. IDE büyük kolaylık sağlar, ama arka planda olanı bilmek yine gereklidir. Sunucularda ya da uzak bağlantılarda çoğu zaman IDE yoktur; yalnızca komut satırı vardır. IDE'nin gösterdiği iletiler de javac'ın iletileridir. Komut satırındaki derleme adımlarını bilen biri bu iletileri her ortamda okuyabilir.
5.8Bytecode ve JVM
Java'nın “bir kez derle, her yerde çalıştır” ilkesi bytecode sayesinde mümkündür. Bu kesim bytecode'un neden işlemciden bağımsız olduğunu, bir .class dosyasının içinde ne bulunduğunu ve JVM'nin bytecode'u nasıl yürüttüğünü anlatır.
5.8.1Bir kez derle, her yerde çalıştır
C gibi dillerde derleyici, kaynak kodu doğrudan belirli bir işlemcinin makine koduna çevirir. Intel işlemcili bir Windows bilgisayar için derlenen program, ARM işlemcili bir Mac'te çalışmaz; orası için ayrıca derlenmesi gerekir. Java derleyicisi ise hiçbir gerçek işlemcinin makine kodunu üretmez. Var olmayan, hayali bir işlemcinin, yani Java Sanal Makinesinin komutlarını üretir. Bu komutlara bytecode denir.
Merhaba.class dosyası Windows (x86-64), Linux (x86-64) ve macOS (ARM64) üzerindeki JVM'lerde çalışır. Her JVM bytecode'u kendi işlemcisinin makine koduna çevirir.Hayali işlemciyi gerçek kılan, her platform için ayrı yazılmış JVM'dir (Şekil 5.19). Windows'un JVM'si de, macOS'un JVM'si de aynı bytecode'u okur. Her biri onu kendi işlemcisinde çalıştırır. Bunun için iki yol kullanılır. Yorumlayıcı bytecode komutlarını tek tek okuyup yürütür. JIT derleyicisi ise sık çalışan kısımları, örneğin binlerce kez dönen bir döngüyü, o işlemcinin makine koduna çevirir ve sonraki turlarda doğrudan makine kodunu çalıştırır. Böylece Java programları hem taşınabilir hem de hızlı olur.
Sınıf yükleme, bytecode doğrulama ve çöp toplama da çalışma ortamının parçalarıdır. Doğrulayıcı özellikle önemlidir. Bir .class dosyası ağdan indirilmiş ya da bozulmuş olabilir. Doğrulayıcı, kodun JVM'nin kurallarına uyduğunu çalıştırmadan önce denetler.
5.8.2Bytecode'un içi: javap
.class dosyası ikili bir dosyadır; Not Defteri'yle açılırsa anlamsız karakterler görünür. İçindeki komutları okumak için JDK ile gelen javap aracı kullanılır. -c seçeneği her metodun bytecode komutlarını okunur biçimde listeler:
> javap -c Merhaba
Compiled from "Merhaba.java"
public class Merhaba {
public Merhaba();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #13 // String Merhaba
5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}Çıktıda iki metot vardır. İlki public Merhaba();, yani sınıfın kurucusudur (constructor). Kaynak kodda böyle bir metot yazılmamıştı. Bir sınıfa hiç kurucu yazılmazsa derleyici boş bir kurucuyu kendisi ekler. İkinci metot main'dir ve dört komuttan oluşur:
| Konum | Komut | Ne yapar? |
|---|---|---|
| 0 | getstatic | System.out nesnesinin başvurusunu işlenen yığınına koyar |
| 3 | ldc | "Merhaba" sabitini işlenen yığınına koyar |
| 5 | invokevirtual | yığından iki değeri alır ve println'i çağırır |
| 8 | return | main'i bitirir |
Komutların solundaki sayılar, komutun metot içindeki bayt konumudur. Her komutun işlem kodu (opcode) tek bir bayttır; “bytecode” adı da buradan gelir. Bazı komutların ardından işlenen baytları gelir. getstatic 3 bayt tutar (1 işlem kodu, 2 işlenen); bu yüzden sonraki komut 3. konumdadır. ldc 2 bayt tutar ve invokevirtual 5. konumdan başlar. O da 3 bayt tutar; return 8. konumdadır. #7, #13 gibi işaretler, sınıf dosyasındaki sabit havuzunun (constant pool) giriş numaralarıdır. javap her girişin ne olduğunu sağdaki açıklamada yazar: #13, “Merhaba” dizgisidir.
JVM bir yığın makinesidir (stack machine). Gerçek işlemciler hesap yaparken değerleri yazmaç adı verilen küçük bellek hücrelerinde tutar. JVM ise değerleri bir işlenen yığınına (operand stack) koyar ve işlemleri yığının tepesindeki değerlerle yapar. Yığın, üst üste konmuş tabaklara benzer: en son konan değer en önce alınır. Yazmaç kullanmamak bytecode'u işlemciden bağımsız tutar, çünkü her işlemcinin yazmaç sayısı farklıdır.
Kaynak koddaki açıklamalar ve boşluklar bytecode'a girmez. Bol açıklamalı bir Merhaba programı da (5.10) aynı dört komuta derlenir.
5.8.3JVM bytecode'u yürütüyor
Yığın makinesinin nasıl çalıştığını görmek için biraz daha uzun bir programı izleyelim. Program iki sayıyı toplar ve sonucu yazar:
public class Topla {
public static void main(String[] args) {
int a = 3;
int b = 4;
int c = a + b;
System.out.println(c);
}
}7javap -c Topla komutu main metodu için şu komutları verir:
0: iconst_3
1: istore_1
2: iconst_4
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: istore_3
8: getstatic // System.out
11: iload_3
12: invokevirtual // println(int)
15: returnKaynaktaki dört satır 12 bytecode komutuna dönüşmüştür. Komutların adları bir düzene uyar. Baştaki i harfi int anlamına gelir. iconst_3 yığına 3 sabitini koyar. istore_1 yığının tepesindeki değeri alıp 1 numaralı yerel değişkene yazar. iload_1 1 numaralı yerel değişkenin değerini yığına kopyalar. iadd yığından iki değer alır, toplar ve sonucu yığına koyar.
Metodun yerel değişkenleri (local variables) numaralı yuvalarda tutulur. 0 numaralı yuvada main'in parametresi args durur. Sonraki yuvalar kaynak koddaki sırayla verilir: 1 numara a, 2 numara b, 3 numara c'dir. Aşağıdaki tablo her komuttan sonra yuvaları ve işlenen yığınını gösterir.
| Konum | Komut | 1 (a) | 2 (b) | 3 (c) | İşlenen yığını | Konsol |
|---|---|---|---|---|---|---|
| 0 | iconst_3 | — | — | — | [3] | |
| 1 | istore_1 | 3 | — | — | [ ] | |
| 2 | iconst_4 | 3 | — | — | [4] | |
| 3 | istore_2 | 3 | 4 | — | [ ] | |
| 4 | iload_1 | 3 | 4 | — | [3] | |
| 5 | iload_2 | 3 | 4 | — | [3, 4] | |
| 6 | iadd | 3 | 4 | — | [7] | |
| 7 | istore_3 | 3 | 4 | 7 | [ ] | |
| 8 | getstatic | 3 | 4 | 7 | [System.out] | |
| 11 | iload_3 | 3 | 4 | 7 | [System.out, 7] | |
| 12 | invokevirtual | 3 | 4 | 7 | [ ] | 7 |
| 15 | return | 3 | 4 | 7 | [ ] | 7 |
iadd komutundan önce ve sonra. Üstte iload_1 ve iload_2 sonrası: yığında 3 ve 4 var. Altta iadd sonrası: iki değer alındı, toplamları 7 yığına kondu. Yerel değişkenler bu sırada değişmez.İzlemede bir düzen göze çarpar. int c = a + b; satırı dört komuta dönüşür: iki yükleme, bir toplama, bir saklama. Toplama komutu iadd değişken adlarını hiç bilmez; yalnızca yığının tepesindeki iki değerle çalışır. Değerleri yığına getirmek iload, yığından değişkene götürmek istore komutlarının işidir (Şekil 5.20).
System.out.println(c); satırı da aynı düzeni izler. Önce println'in çağrılacağı nesne (System.out), sonra yazdırılacak değer (7) yığına konur. invokevirtual ikisini birden yığından alır ve println(int) metodunu çağırır. Konsola 7 yazılır. return ile main biter. Metodun çerçevesi kaldırılır ve yerel değişkenler ile işlenen yığını da çerçeveyle birlikte yok olur.
Komutların konumlarındaki boşluklar da açıklanabilir. 0'dan 7'ye kadar her komut tek bayttır, çünkü iconst_3, istore_1 gibi komutlar işlenenlerini adlarında taşır. getstatic 3 bayt tuttuğu için sonraki komut 11. konumdadır. iload_3 1 bayt, invokevirtual 3 bayttır ve return 15. konuma düşer.
Topla programında 3 yerine 5, 4 yerine 2 yazılır ve toplama yerine çarpma yapılırsa bytecode nasıl değişir?
public class Carp {
public static void main(String[] args) {
int a = 5;
int b = 2;
int c = a * b;
System.out.println(c);
}
}Komutların yapısı aynı kalır; yalnızca sabitler ve işlem değişir. iconst_3 yerine iconst_5, iconst_4 yerine iconst_2, iadd yerine çarpma komutu imul gelir. javap -c Carp bu tahmini doğrular:
public static void main(java.lang.String[]);
Code:
0: iconst_5
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: imul
7: istore_3
8: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_3
12: invokevirtual #13 // Method java/io/PrintStream.println:(I)V
15: returnProgram 10 yazar. Yığın, imul'dan hemen önce [5, 2], hemen sonra [10]'dur.
5.9Dilin yapı taşları
Derleyici bir Java dosyasını okurken onu önce küçük parçalara ayırır, sonra bu parçalardan daha büyük yapılar kurar. Harflerden sözcükler, sözcüklerden cümleler, cümlelerden paragraflar oluşması gibi. Bu kesim en küçük parçalardan, belirteçlerden başlar ve ifade, deyim ve blok kavramlarına ulaşır.
5.9.1Belirteçler
Belirteç (token), kaynak kodun derleyici için anlamlı olan en küçük parçasıdır. Java'da beş tür belirteç vardır: anahtar kelime (keyword), tanımlayıcı (identifier), sabit değer (literal), operatör (operator) ve ayırıcı (separator).
int toplam = fiyat * 3; deyiminin yedi belirteci: int anahtar kelime, toplam ve fiyat tanımlayıcı, = ve * operatör, 3 sabit değer, ; ayırıcı.Beş türü Şekil 5.21 üzerinden tanıyalım:
- Anahtar kelimeler dilin kendisine ayrılmış sözcüklerdir:
int,class,public,static,void,ifgibi. Anlamları sabittir ve başka bir amaçla kullanılamazlar.int class = 5;yazılamaz. - Tanımlayıcılar programcının verdiği adlardır: değişken, metot ve sınıf adları.
toplam,fiyat,Merhaba,mainbirer tanımlayıcıdır. Bir tanımlayıcı harfle,_ya da$ile başlar; sonra harf ve rakamlarla sürer. Boşluk içeremez ve rakamla başlayamaz. - Sabit değerler kodun içine doğrudan yazılmış değerlerdir:
3,120,2.5,"Merhaba",'A',true. - Operatörler işlem yapan işaretlerdir:
+,-,*,/,=,>gibi. - Ayırıcılar yapıyı belirleyen işaretlerdir:
;,.(){}[].
Java büyük-küçük harfe duyarlı bir dildir. sayi ile Sayi iki ayrı tanımlayıcıdır. System doğru, system yanlıştır. Int diye bir anahtar kelime yoktur; yalnızca int vardır. Deyimler çoğunlukla noktalı virgülle biter; bloklar süslü parantezle çevrilir.
5.9.2Boşluk, girinti ve süslü parantez
Aşağıdaki iki program aynıdır. İlki tek satıra yazılmıştır, ikincisi Java geleneğine göre girintilenmiştir:
public class Tek { public static void main(String[] args) { System.out.println("Merhaba"); } }public class Tek {
public static void main(String[] args) {
System.out.println("Merhaba");
}
}İki program da derlenir ve aynı bytecode'a dönüşür. Tek satırlık sürümün javap -c çıktısındaki main komutları, Merhaba programındaki dört komutun aynısıdır: getstatic, ldc, invokevirtual, return. Derleyici için boşluk, sekme ve satır sonu yalnızca belirteçleri birbirinden ayırır. x=1; ile x = 1; aynıdır. Ama publicclass tek bir sözcük sayılır ve iki belirteç olarak okunmaz. (İki sınıf dosyası bayt bayt aynı değildir, çünkü .class dosyası hata iletileri için satır numaralarını da saklar. Komutlar ise aynıdır.)
Boşluk derleyiciyi ilgilendirmiyorsa girinti neden önemlidir? Çünkü kod derleyici için olduğu kadar insan için de yazılır. Girinti (indentation), blokların iç içeliğini okuyana gösterir. Java geleneğinde kurallar şunlardır:
- Her yeni blok bir öncekinden 4 boşluk içeriden başlar.
- Açılış süslü parantezi
{satırın sonunda durur. - Kapanış süslü parantezi
}kendi satırındadır ve açtığı satırın hizasına döner.
Bu kurallara uyulduğunda bir }'nin hangi bloğu kapattığı bir bakışta görülür. Girintisi bozuk kodda bir süslü parantezin eksik olduğunu fark etmek çok zorlaşır.
Bir istisna vardır: dizgi içindeki boşluklar metnin parçasıdır. "Merhaba " ile "Merhaba" farklı dizgilerdir; ilki sekiz, ikincisi yedi karakterdir. print("Merhaba ") ile print("Merhaba") farklı çıktı verir.
5.9.3İfade, deyim ve blok
Belirteçler bir araya gelerek ifadeleri, ifadeler de deyimleri oluşturur.
İfade (expression), değerlendirildiğinde bir değer üreten kod parçasıdır: 2 + 3 → 5, sayi > 0 → true ya da false. Deyim (statement), bir işlemi tamamlayan kod birimidir ve çoğunlukla ; ile biter: toplam = 5;, System.out.println(toplam);. Blok (block), süslü parantez içindeki deyimleri tek bir yapı olarak gruplar.
Fark şu soruyla anlaşılır: “Bu parça bir değer mi, yoksa bir iş mi?” fiyat * adet bir değerdir (360 gibi); tek başına bir şey yapmaz. toplam = fiyat * adet; ise bir iştir: değeri hesaplar ve toplam'a yazar. Bir deyimin içinde bir ya da daha çok ifade bulunabilir.
fiyat * adet 360 değerini, ikincisinin ifadesi toplam > 300 true değerini üretir.Aşağıdaki kod parçası dört kavramı birlikte kullanır. Parça bir sınıfın main metoduna konup çalıştırıldığında konsola tek satır yazılır:
int fiyat = 120; // tanımlama deyimi
int adet = 3; // tanımlama deyimi
int toplam = fiyat * adet; // ifade + atama
if (toplam > 300) { // koşul + blok
System.out.println("İndirim");
}İndirim| Adım | Satır | Olan | fiyat | adet | toplam | Konsol |
|---|---|---|---|---|---|---|
| 1 | 1 | fiyat için bellekte int kutusu ayrılır, 120 yazılır | 120 | ? | ? | |
| 2 | 2 | adet kutusu oluşur | 120 | 3 | ? | |
| 3 | 3 | sağdaki ifade değerlendirilir: fiyat * adet → 120 * 3 → 360 | 120 | 3 | ? | |
| 4 | 3 | üretilen değer toplam'a atanır | 120 | 3 | 360 | |
| 5 | 4 | koşul ifadesi: toplam > 300 → 360 > 300 → true; bloğa girilir | 120 | 3 | 360 | |
| 6 | 5 | bloktaki deyim çalışır | 120 | 3 | 360 | İndirim |
| 7 | 6 | blok biter | 120 | 3 | 360 | İndirim |
Birinci ve ikinci satırlar bildirim deyimleridir (declaration statement): bir değişken tanımlar ve ona ilk değerini verirler. Üçüncü satırda önce ifade değerlendirilir, sonra atama yapılır (Tablo 5.15, 3. ve 4. adımlar). Eşittir işaretinin sağı her zaman önce hesaplanır. Dördüncü satırdaki if deyimi bir koşul ifadesi ve bir bloktan oluşur. Koşul true çıktığı için blok çalışır. adet 2 olsaydı toplam 240 olur, koşul false çıkar ve blok hiç çalışmazdı. Blok, birden çok deyimi tek bir birim hâline getirir: koşul doğruysa bloktaki deyimlerin hepsi, yanlışsa hiçbiri çalışmaz.
5.10Açıklamalar
Kod yalnızca derleyiciye değil, onu okuyacak insanlara da yazılır: aylar sonra kendi koduna dönen programcıya, takım arkadaşlarına, dersi değerlendiren öğretim elemanına. Açıklama (comment), kaynak koda okuyan kişi için eklenen ve derleyicinin görmezden geldiği metindir.
5.10.1Üç açıklama biçimi
Java'da üç açıklama biçimi vardır:
//ile başlayan açıklama satırın sonuna kadar sürer. Kısa notlar için kullanılır./*ile başlayıp*/ile biten açıklama birden çok satırı kapsayabilir./**ile başlayıp*/ile biten açıklama bir Javadoc açıklamasıdır.javadocaracı bu açıklamalardan API belgesi üretir. Genellikle sınıf ve metot bildirimlerinin hemen üstüne yazılır.
/** Merhaba programı (Javadoc). */
public class Merhaba {
/* Birden fazla satıra
yayılan açıklama */
public static void main(String[] args) {
// selamı yaz
System.out.println("Merhaba");
}
}> java Merhaba
Merhaba
> javap -c Merhaba
Compiled from "Merhaba.java"
public class Merhaba {
public Merhaba();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #13 // String Merhaba
5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}javac açıklamaları atlar. Kaynaktaki // selamı yaz ve /* açıklama */ satırları bytecode'a girmez; .class dosyasında yalnızca komutlar bulunur.Açıklamalı programın javap çıktısı, açıklamasız Merhaba programınınkiyle aynıdır (Şekil 5.23). Derleyici açıklamaları atlar; açıklamalar bytecode'a girmez ve programı yavaşlatmaz.
İyi bir açıklama, kodun ne yaptığını tekrarlamak yerine neden yaptığını anlatır. i = i + 1; // i'yi bir artır açıklaması işe yaramaz; koda bakan zaten bunu görür. toplam = toplam / 2; // iki sınavın ağırlığı eşit ise koddan çıkarılamayan bir bilgiyi verir.
5.10.2Açıklama tuzakları
Açıklamalar basit görünür, ama birkaç tuzak barındırır.
İç içe çok satırlı açıklama. /* … */ açıklamaları iç içe yazılamaz. Açıklama, ilk karşılaşılan */'da biter:
public class Ic {
public static void main(String[] args) {
/* dış /* iç */ kalan */
System.out.println("Merhaba");
}
}Ic.java:3: error: illegal start of expression
/* dış /* iç */ kalan */
^
1 errorDerleyici /* dış /* iç */ kısmını açıklama sayar; ortadaki ikinci /* açıklamanın içinde kaldığı için bir anlam taşımaz. Açıklama iç'ten sonraki */'da biter. Geriye kalan */ kalır ve derleyici bunu kod sanır. kalan bir tanımlayıcı, * çarpma operatörü sayılır. Çarpmanın sağında bir değer beklenirken / gelir ve hiçbir ifade / ile başlayamaz. Derleyici “ifadenin geçersiz başlangıcı” (illegal start of expression) der; ^ işareti bu son / karakterini gösterir. İçinde /* */ bulunan bir bölümü devre dışı bırakmak için satırları tek tek // ile açıklama yapmak daha güvenlidir.
Tırnak içindeki açıklama işaretleri. Dizgi içindeki // ya da /* */ açıklama değildir; metnin parçasıdır. Aşağıdaki programda hangi satırların çalıştığını izleyelim:
public class Y {
public static void main(String[] args) {
// System.out.println("1");
System.out.println("2"); // System.out.println("3");
/* System.out.println("4");
System.out.println("5"); */
System.out.println("6 // değil");
System.out.println("7 /* yorum değil */");
System.out.println("a // b");
}
}2
6 // değil
7 /* yorum değil */
a // bİlk satır baştan sona açıklamadır. İkinci satırda "2" yazılır; //'dan sonrası, yani 3'ü yazacak deyim, açıklamadır. /* … */ iki satırı (4 ve 5) birlikte açıklama yapar. Son üç satırdaki // ve /* */ işaretleri tırnak içinde olduğu için aynen yazılır.
Kodu geçici olarak devre dışı bırakmak. Bir satırın başına // koymak, o satırı silmeden çalışmaz hâle getirir. Hata ararken sık kullanılan bir yöntemdir. IDE'lerde Ctrl+/ kısayolu seçili satırları bu biçimde açıklamaya çevirir ve geri alır.
Eskimiş açıklama. En sinsi tuzak, kodla çelişen açıklamadır:
double fiyat = net * 1.20; // KDV %18Kod derlenir ve doğru çalışır: KDV oranı güncellenmiş, kod %20'ye göre yazılmıştır. Ama açıklama eski oranı söylüyor ve okuyanı yanıltır. Açıklama ile kod çeliştiğinde derleyici kodu çalıştırır; doğru olan her zaman koddur. Kod değiştirildiğinde ilgili açıklama da güncellenmelidir.
Aşağıdaki program ne yazar?
public class Yorum {
public static void main(String[] args) {
int x = 5; // x = 10;
/* x = 20; */ x = x + 1;
System.out.println("x = " + x); // x = 100
System.out.println("// x = " + (x * 2));
}
}3. satırda x 5 olur; // x = 10; açıklamadır. 4. satırda /* x = 20; */ açıklamadır, ama aynı satırdaki x = x + 1; açıklamanın dışında kalır ve çalışır: x 6 olur. 5. satır x = 6 yazar; satır sonundaki // x = 100 yalnızca yanlış bir nottur. 6. satırdaki // tırnak içinde olduğu için metnin parçasıdır; x * 2 parantez içinde önce hesaplanır ve 12 bulunur:
x = 6
// x = 125.11Hatalar
Her programcı hata yapar. İyi programcıyı ayıran, hatayı çabuk bulup düzeltebilmesidir. Bunun ilk adımı, hatanın türünü tanımaktır. Hatanın ne zaman ortaya çıktığı, onu kimin yakaladığını ve nasıl aranacağını belirler.
5.11.1Hata türleri
javac), çalışma zamanı hataları programı çalıştırırken (JVM), mantık hataları ancak sonuç kontrol edilirken bulunur.| Hata | Ne zaman? | Kim yakalar? | Örnek |
|---|---|---|---|
| Söz dizimi | derleme sırasında | javac | eksik noktalı virgül |
| Tip | derleme sırasında | javac | metni sayı değişkenine atama |
| Çalışma zamanı | program çalışırken | JVM | tam sayıyı sıfıra bölme |
| Mantık | program çalışır ama sonuç yanlıştır | yalnızca test | yanlış işlem sırası |
Söz dizimi hatası (syntax error), dilin yazım kurallarının bozulmasıdır: eksik noktalı virgül, kapanmamış parantez ya da tırnak. Tip hatası (type error), söz dizimi doğru olduğu hâlde bir değerin tipine uymayan bir yerde kullanılmasıdır. Bu ikisine birlikte derleme hatası (compile error) denir. Derleme hatası olan program .class dosyasına dönüşmez ve hiç çalışmaz.
Çalışma zamanı hatası (runtime error), derlenmiş bir programın çalışırken yapamayacağı bir işle karşılaşmasıdır. Java'da bu durumda JVM bir istisna fırlatır ve program, özel bir önlem alınmamışsa durur. Mantık hatası (logic error) ise en sinsi türdür: program derlenir, hatasız çalışır ve bir sonuç üretir, ama sonuç yanlıştır. Hiçbir araç hata iletisi vermez. Mantık hatasını yalnızca sonucu beklenen değerle karşılaştıran biri yakalayabilir.
Hatalar ne kadar erken yakalanırsa o kadar ucuza düzeltilir. Derleme hatası programcının bilgisayarında, saniyeler içinde bulunur. Mantık hatası ise program kullanıcılara ulaştıktan sonra fark edilebilir. Java'nın derleme sırasında tip denetimi yapması bu yüzden bir avantajdır: birçok yanlışı programcı daha programı çalıştırmadan görür.
5.11.2Dört hata, dört örnek
Aşağıdaki dört program her hata türünden birer örnek verir. Her birinin JDK 21'deki gerçek iletisi verilmiştir.
Söz dizimi hatası. 3. satırın sonunda noktalı virgül yoktur:
public class Hata1 {
public static void main(String[] args) {
System.out.println("Merhaba")
}
}Hata1.java:3: error: ';' expected
System.out.println("Merhaba")
^
1 errorDerleyici kuralın bozulduğu yeri satır numarasıyla gösterir: Hata1.java:3. ';' expected “noktalı virgül bekleniyordu” demektir. Son satırdaki 1 error toplam hata sayısıdır. .class üretilmez. Düzeltme: satır sonuna ; eklemek.
Tip hatası. Bir dizgi, int tipindeki bir değişkene konmaya çalışılır:
public class Hata2 {
public static void main(String[] args) {
int sayi = "42";
}
}Hata2.java:3: error: incompatible types: String cannot be converted to int
int sayi = "42";
^
1 errorSöz dizimi kusursuzdur: tip, ad, eşittir, değer, noktalı virgül. Ama "42" çift tırnak içinde olduğu için bir sayı değil, iki karakterlik bir metindir. İleti “uyumsuz tipler: String, int'e dönüştürülemez” der. ^ uyumsuz değeri gösterir. Düzeltme: tırnakları kaldırıp int sayi = 42; yazmak.
Çalışma zamanı hatası. Program derlenir, ama çalışırken bir tam sayı sıfıra bölünür:
public class Hata3 {
public static void main(String[] args) {
int a = 10, b = 0;
System.out.println(a / b);
}
}> javac Hata3.java
> java Hata3
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Hata3.main(Hata3.java:4)javac sessiz kalır: söz dizimi ve tipler doğrudur. b'nin değerinin 0 olacağını derleyici denetlemez. Program çalışırken 4. satırda tam sayı bölmesi sıfıra yapılır. Matematikte de tanımsız olan bu işlem karşısında JVM bir ArithmeticException (aritmetik istisna) fırlatır. İleti / by zero, yani “sıfıra bölme” der ve hatanın yerini verir: Hata3.java, 4. satır, main metodu. Program orada durur.
Mantık hatası. İki notun ortalaması bulunmak istenir:
public class Hata4 {
public static void main(String[] args) {
int a = 70, b = 90;
System.out.println(a + b / 2);
}
}> javac Hata4.java
> java Hata4
115Hata iletisi yoktur, ama sonuç yanlıştır. 70 ve 90'ın ortalaması 80'dir, program ise 115 yazar. Neden, işlem önceliğidir: /, +'dan önce yapılır. a + b / 2 ifadesi a + (b / 2) gibi değerlendirilir: 90 / 2 = 45, 70 + 45 = 115. Düzeltme: (a + b) / 2 yazmak; bu ifade (70 + 90) / 2 = 160 / 2 = 80 verir.
“Program derlendi ve çalıştı, öyleyse doğrudur.” sonucuna varmak. Derleme yalnızca söz dizimi ve tip hatalarının olmadığını gösterir. Mantık hatası olan bir program da sorunsuz derlenip çalışır. Programın doğru olduğuna ancak bilinen girdiler için çıktısını beklenen sonuçla karşılaştırarak güvenilebilir.
5.11.3Sık derleme ve başlatma hataları
Yeni başlayanların karşılaştığı derleme hatalarının büyük bölümü birkaç kalıba uyar. Aşağıdaki tablo bunları JDK 21'in gerçek iletileriyle verir.
| Hata | Gerçek ileti | Çözüm |
|---|---|---|
| Noktalı virgül yok | Merhaba.java:3: error: ';' expected | önceki deyimin sonuna ; eklemek |
Metot adı yanlış (System.out.printn) | error: cannot find symbolsymbol: method printn(String)location: variable out of type PrintStream | adı println yapmak |
Küçük harf (system.out) | error: package system does not exist | System'i büyük S ile yazmak |
| Sınıf adı ile dosya adı uyuşmuyor | error: class Selam is public, should be declared in a file named Selam.java | dosya adını public sınıf adıyla aynı yapmak |
Yanlış çalıştırma (java Selam) | Error: Could not find or load main class Selam | .class dosyası yok ya da ad/konum yanlış; önce derlemek, uzantı yazmamak |
İletilerin tam hâli, sorunun nerede olduğunu daha açık gösterir. Metot adı yanlış yazıldığında:
Merhaba.java:3: error: cannot find symbol
System.out.printn("Merhaba");
^
symbol: method printn(String)
location: variable out of type PrintStream
1 errorcannot find symbol, derleyicinin bir adı (değişken, metot ya da sınıf adı) tanımadığını söyler. Altındaki symbol: satırı hangi adın bulunamadığını, location: satırı ise nerede arandığını verir. Bu iletiye göre bir String alan printn metodu, PrintStream tipindeki out değişkeninde aranmış ve bulunamamıştır. ^ işareti out'tan sonraki noktayı gösterir; metot oraya bağlanmaya çalışılmıştır.
System küçük harfle yazıldığında ileti ilk bakışta şaşırtıcıdır:
Merhaba.java:3: error: package system does not exist
system.out.println("Merhaba");
^
1 errorDerleyici system adında bir sınıf tanımaz. system.out.println yazımındaki noktaları görünce system'in bir paket adı olabileceğini düşünür ve öyle bir paket de bulamaz. İletinin “paket” demesi bu yüzdendir; asıl sorun büyük-küçük harftir. Aynı durum adı yanlış yazılmış bir System için de görülür: Sytem.out.println için ileti package Sytem does not exist olur.
Public sınıfın adı dosya adından farklıysa derleyici hatayı sınıf bildirimi satırında verir:
public class Selam {
public static void main(String[] args) {
System.out.println("x");
}
}> javac Merhaba.java
Merhaba.java:1: error: class Selam is public, should be declared in a file named Selam.java
public class Selam {
^
1 error
> java Selam
Error: Could not find or load main class Selam
Caused by: java.lang.ClassNotFoundException: SelamDerleme başarısız olduğu için Selam.class da üretilmemiştir; ardından gelen java Selam komutu sınıfı bulamaz. Harf büyüklüğü de sayılır. Merhaba sınıfı merhaba.java dosyasına kaydedilirse Windows dosya adlarında harf büyüklüğünü ayırmasa da derleyici ayırır:
merhaba.java:1: error: class Merhaba is public, should be declared in a file named Merhaba.java
public class Merhaba {
^
1 errorİki derleme hatası daha sık görülür. Kapanmamış bir tırnak, satırın sonuna kadar her şeyi dizginin parçası yapar:
Tirnak.java:3: error: unclosed string literal
System.out.println("Merhaba);
^
1 error“Kapanmamış dizgi sabiti” iletisindeki ^, dizginin başladığı tırnağı gösterir. Eksik bir kapanış süslü parantezi ise dosyanın sonunda fark edilir. Aşağıdaki iletide sınıfı kapatan son } silinmiştir:
Merhaba.java:4: error: reached end of file while parsing
}
^
1 error“Ayrıştırma sırasında dosyanın sonuna ulaşıldı” iletisi, derleyicinin hâlâ bir bloğun kapanmasını beklerken dosyanın bittiğini söyler. Hatanın gerçek yeri dosyanın sonu değil, }'nin eksik olduğu yerdir. Doğru girintilenmiş kodda bu yer kolayca bulunur.
5.11.4Hata iletisini okumak
Hata iletileri ilk bakışta korkutucu görünür, ama hep aynı düzende yazılır. Düzeni bir kez öğrenmek, her iletiyi okumayı kolaylaştırır.
javac iletisi: dosya, satır, tür, açıklama; altında sorunlu kaynak satırı ve hatanın fark edildiği yeri gösteren ^. Altta bir çalışma zamanı iletisi: istisnanın türü ve ayrıntısı, ardından hangi metotta, hangi dosyanın hangi satırında oluştuğunu söyleyen yığın izi.Bir javac iletisinin ilk satırı dosya:satır: error: açıklama biçimindedir (Şekil 5.25). Not.java:3: error: ';' expected iletisinde dosya Not.java, satır 3, tür error (hata), açıklama ';' expected'dır. İkinci satır sorunlu kaynak satırını aynen gösterir. Üçüncü satırdaki ^ işareti, derleyicinin sorunu fark ettiği sütunu gösterir. En sonda toplam hata sayısı yazılır.
^ her zaman hatanın kendisini değil, derleyicinin onu fark ettiği yeri gösterir. Eksik noktalı virgül, deyimin bittiği yerde, yani satır sonunda fark edilir. Eksik bir } ise dosyanın sonuna kadar fark edilmez ve reached end of file while parsing olarak bildirilir. Bu yüzden hata gösterilen yerde bulunamazsa bir önceki satıra ya da yukarıdaki bloklara bakmak gerekir.
Çalışma zamanı iletileri farklı bir düzendedir. İlk satır Exception in thread "main" ile başlar ve istisnanın türünü ve ayrıntısını verir: java.lang.ArithmeticException: / by zero. Altındaki at ile başlayan satırlar yığın izidir (stack trace): hata anında çağrı yığınında hangi metotların bulunduğunu, en içteki metottan başlayarak listeler. at Hata3.main(Hata3.java:4), hatanın Hata3 sınıfının main metodunda, Hata3.java dosyasının 4. satırında oluştuğunu söyler. İlk programlarda yığın izi tek satırdır, çünkü yalnızca main çalışır. Metotlar birbirini çağırdıkça iz uzar.
5.11.5Hataları sırayla düzeltmek
Bir programda birden çok hata olabilir. Derleyici bunları her zaman bir anda bildirmez. Aşağıdaki programda iki hata vardır: 3. satırda noktalı virgül eksiktir, 4. satırda metot adı yanlıştır.
public class Not {
public static void main(String[] args) {
int puan = 85
System.out.printn("Puan: " + puan);
}
}İlk derleme yalnızca bir hata bildirir:
Not.java:3: error: ';' expected
int puan = 85
^
1 error4. satırdaki yanlış henüz görünmez. Bunun nedeni derleyicinin aşamalı çalışmasıdır. Derleyici önce söz dizimini çözümler. Söz dizimi bozuk olduğu sürece anlam denetimine, yani adların ve tiplerin denetimine geçmez. Hata düzeltilir, 3. satır int puan = 85; olur ve dosya kaydedilir. İkinci derleme bu kez başka bir hata bildirir:
Not.java:4: error: cannot find symbol
System.out.printn("Puan: " + puan);
^
symbol: method printn(String)
location: variable out of type PrintStream
1 errorSöz dizimi artık doğrudur; anlam denetimi çalışır ve printn adlı metodu bulamaz. symbol: satırı bulunamayan adı söyler. Metot adı println olarak düzeltilir. Üçüncü derleme sessizce başarılı olur, Not.class oluşur ve program çalışır:
> javac Not.java
> java Not
Puan: 85| Derleme | 3. satır | 4. satır | Bildirilen hata | Not.class |
|---|---|---|---|---|
| 1 | int puan = 85 ✗ | printn ✗ | Not.java:3: error: ';' expected | yok |
| 2 | int puan = 85; ✓ | printn ✗ | Not.java:4: error: cannot find symbol | yok |
| 3 | int puan = 85; ✓ | println ✓ | yok | oluştu |
Bu örnekten çıkan ders şudur: bir hatayı düzelttikten sonra hata sayısı azalmayabilir, hatta artabilir. Bu, düzeltmenin yanlış olduğu anlamına gelmez; derleyici yalnızca bir sonraki aşamaya geçmiştir. Tersine, aynı aşamadaki hatalar birlikte bildirilir. Aşağıdaki programdaki iki tip hatası tek derlemede görünür:
Iki.java:3: error: incompatible types: String cannot be converted to int
int a = "5";
^
Iki.java:4: error: incompatible types: String cannot be converted to int
int b = "6";
^
2 errorsBir söz dizimi hatası ise ardından gelen satırları da karıştırabilir ve gerçekte var olmayan hatalar bildirilebilir. Bu yüzden her zaman ilk hatadan başlanır. İlk hata düzeltilir, dosya yeniden derlenir ve yeni iletiler okunur.
5.12Kodlamadan önce
Hata iletilerini okumayı bilmek önemlidir, ama en ucuz hata hiç yapılmayandır. Mantık hatalarının çoğu kod yazılmadan önce, kâğıt üzerinde yapılan küçük bir hazırlıkla önlenebilir.
5.12.1Program bir tariftir
Bir yemek tarifi malzemeleri sayar, adımları sırayla verir ve sonunda ne elde edileceğini söyler. Bir program da aynı yapıdadır. Girdiler malzemelere, deyimler tarifin adımlarına, çıktı da ortaya çıkan yemeğe karşılık gelir. İyi bir tarifte her adımın ne gerektirdiği ve ne ürettiği bellidir: “Hamur mayalandıktan sonra (ön koşul) fırına verilir ve 30 dakika pişirilir (adım); ekmek kızarır (sonuç).”
Şekil 5.26 iki notun ortalamasını bulan programın tarifini dört adımda verir. Her adımın ön koşulu ve ürettiği değer bellidir:
- Girdiyi al. Ön koşul yoktur. Ürettiği değerler:
not1venot2. - Doğrula. Ön koşul: iki not okunmuştur. Her notun 0 ile 100 arasında olup olmadığı denetlenir. Geçersiz bir not varsa tarif burada durur ve bir hata iletisi verir.
- İşle. Ön koşul: iki not da geçerlidir. Ortalama
(not1 + not2) / 2ile hesaplanır. - Sonucu üret. Ön koşul: ortalama hesaplanmıştır. “Ortalama: 80” gibi bir satır yazılır.
Tarifi kodlamadan önce elle sınamak gerekir. 70 ve 90 için adımları izleyince 80 çıkıyor mu? 160 / 2 = 80; evet. 101 girilirse hangi adım durdurur? İkinci adım: 101 > 100 olduğu için doğrulama başarısız olur. Elle sınama, mantık hatalarını kod yazılmadan yakalar. İşlem sırası hatası örneğinde (a + b / 2) tarif açıkça “önce topla, sonra böl” dediği için hata daha tarif aşamasında görülebilirdi.
Büyük tarifler alt tariflere ayrılır. Bir pasta tarifinde “krema hazırla” ayrı bir tarif olabilir. Programlarda da büyük işler alt görevlere bölünür; Java'da her alt görev bir metot olarak yazılır. İlk programlarda tek bir main metodu yeterlidir, ama tarif düşüncesi baştan alışkanlık hâline getirilmelidir.
5.12.2Örnek girdi ve beklenen çıktı
Kodlamaya başlamadan önce yapılacak dört iş vardır:
- Örnek girdileri ve her biri için beklenen çıktıyı yazmak.
- Çözümü doğal dille ya da sözde kodla adımlara ayırmak.
- Sınır durumlarını (boundary case) ve hatalı girdide ne yapılacağını belirlemek.
- Ancak bundan sonra adımları Java yapılarına dönüştürmek.
Birinci adımın ürünü küçük bir tablodur. Ortalama programı için şöyle bir tablo hazırlanabilir:
| Örnek girdi | Beklenen çıktı | Neden? |
|---|---|---|
| 70, 90 | 80 | olağan durum |
| 0, 0 | 0 | alt sınır |
| 101, 50 | hata iletisi | geçersiz girdi |
Tablodaki her satır farklı bir şeyi sınar (Şekil 5.27). Olağan durum, programın gündelik kullanımda doğru çalışıp çalışmadığını gösterir. Sınır durumu, geçerli aralığın uçlarındaki değerlerdir; programlar en sık burada yanılır. < yerine <= yazmak gibi küçük bir yanlış yalnızca sınırda ortaya çıkar. Geçersiz girdi ise programın beklenmedik bir değer karşısında ne yaptığını gösterir: çökmemeli, anlaşılır bir ileti vermelidir.
Tablonun bir yararı daha vardır: program yazıldıktan sonra her satır bir sınamaya dönüşür. Program bu girdilerle çalıştırılır ve çıktısı beklenen sütunuyla karşılaştırılır. Farklı türde örnekler seçmek bu yüzden önemlidir. Yalnızca olağan girdilerle sınanan bir program, iki tam sayının bölümünde kesirli kısmın atılması gibi bir yanlışı gizleyebilir. Örneğin 75 ve 80 için doğru ortalama 77,5'tir, ama iki int'in bölümü 77 verir. Bu yanlış, toplamı tek sayı olan bir girdi denenmeden görünmez.
5.13Özet
- Program, bilgisayarın bir görevi yerine getirmek için yürüttüğü komutlar bütünüdür; girdi alır, işler, durum saklar ve çıktı üretir. Bir programlama dili söz dizimi, anlam, tip sistemi ve çalışma modeli yönlerinden incelenir.
- Kaynak kod düz metin bir
.javadosyasına yazılır. Karakter kodlaması (UTF-8), satır sonu ve dosya adı tutarlı olmalıdır. Düzenleyici, IDE,javac,java,javadocve sürüm kontrolü programın yolculuğundaki araçlardır. - Java 1995'te Sun Microsystems'te doğdu; 2010'dan beri Oracle geliştiriyor, kaynak kodu OpenJDK'dadır. Tasarım hedefi bir kez yaz, her yerde çalıştır ilkesidir. Altı ayda bir yeni sürüm çıkar; 8, 11, 17, 21 ve 25 LTS sürümleridir.
- JDK = JRE + geliştirme araçları; JRE = JVM + sınıf kütüphanesi. JVM bytecode'u yürütür; sınıf yükleyici, doğrulayıcı, yorumlayıcı, JIT derleyicisi ve çöp toplayıcı içerir.
- Her Java kodu bir sınıfın içindedir. Public sınıfın adı dosya adıyla aynıdır. JVM çalıştırmaya
public static void main(String[] args)imzalı metottan başlar. İmzası yanlış birmainderlenir ama başlatılamaz. Java büyük-küçük harfe duyarlıdır. - Komut satırı argümanları
argsdizisiylemain'e ulaşır; indisler 0'dan başlar,args.lengthargüman sayısıdır. Olmayan bir argümana erişmek çalışma sırasındaArrayIndexOutOfBoundsExceptionfırlatır. printlnyazdıktan sonra satır sonu ekler,printeklemez. Kaçış dizileri (\n,\t,\",\\,\',\u) dizgiye özel karakterler koyar. Windows yollarında tek ters eğik çizgi ya derleme hatası verir ya da sessizce yanlış çıktı üretir.+iki sayı arasında toplar, işlenenlerden biri dizgiyse birleştirir. İşlemler soldan sağa yapılır;*ve/önce gelir:"Puan: " + 40 + 45→Puan: 4045,"Puan: " + (40 + 45)→Puan: 85.javac Merhaba.javaderler veMerhaba.classüretir;java Merhabaçalıştırır. Kaynak değişince yeniden derlemek gerekir, çünkü JVM yalnızca.classdosyasını çalıştırır. Derleme hatası varsa.classüretilmez.- Bytecode işlemciden bağımsızdır ve
CA FE BA BEile başlayan dosyalarda saklanır. JVM bir yığın makinesidir:iloaddeğerleri işlenen yığınına koyar,iaddyığındaki değerleri toplar,istoresonucu yerel değişkene yazar.javap -cbytecode'u okunur biçimde gösterir. - Derleyici kodu belirteçlere ayırır: anahtar kelime, tanımlayıcı, sabit değer, operatör, ayırıcı. Boşluk yalnızca belirteçleri ayırır; girinti okuyan içindir. İfade değer üretir, deyim işlemi tamamlar, blok deyimleri gruplar.
- Açıklamalar
//,/* */ve/** */ile yazılır ve bytecode'a girmez./* */iç içe yazılamaz; tırnak içindeki açıklama işaretleri metnin parçasıdır. - Söz dizimi ve tip hataları derlemede, çalışma zamanı hataları çalışırken, mantık hataları ancak sonuç denetlenince bulunur.
javaciletisidosya:satır: error: açıklamabiçimindedir;^hatanın fark edildiği yeri gösterir. Çalışma zamanı iletisindeki yığın izi hatanın metodunu ve satırını verir. Hatalar her zaman ilk hatadan başlanarak düzeltilir. - Kodlamadan önce örnek girdiler, beklenen çıktılar, sınır durumları ve hata davranışı belirlenir; tarif elle sınanır.
5.14Kavram listesi
Terimlerin İngilizce karşılıkları ve öteki sunumlardaki kullanımları terim sözlüğünde bulunur.
- program program
- söz dizimi syntax
- anlam semantics
- tip sistemi type system
- çalışma modeli execution model
- kaynak kod source code
- kaynak dosya source file
- karakter kodlaması character encoding
- derleyici compiler
- derleme compilation
- bytecode (ara kod) bytecode
- Java Sanal Makinesi (JVM) Java Virtual Machine
- JRE (çalışma ortamı) Java Runtime Environment
- JDK (geliştirme kiti) Java Development Kit
- sınıf kütüphanesi class library
- sınıf yükleyici class loader
- bytecode doğrulayıcı bytecode verifier
- JIT just-in-time compilation
- çöp toplama garbage collection
- uzun süreli destek (LTS) long-term support
- IDE integrated development environment
- Javadoc (API belgesi) Javadoc
- sınıf bildirimi class declaration
- main metodu main method
- büyük-küçük harfe duyarlı case-sensitive
- komut satırı argümanı command-line argument
- konsol console
- satır sonu newline
- kaçış dizisi escape sequence
- sekme durağı tab stop
- Unicode kaçışı Unicode escape
- dizgi birleştirme string concatenation
- paket package
- ortam değişkeni environment variable
- yığın makinesi stack machine
- işlenen yığını operand stack
- belirteç token
- anahtar kelime keyword
- tanımlayıcı identifier
- sabit değer literal
- ayırıcı separator
- ifade expression
- deyim statement
- bildirim deyimi declaration statement
- açıklama comment
- söz dizimi hatası syntax error
- tip hatası type error
- derleme hatası compile error
- çalışma zamanı hatası runtime error
- mantık hatası logic error
- istisna exception
- yığın izi stack trace
- hata iletisi error message
- sınır durumu boundary case
5.15Kendini Dene
Bir bilgisayarda yalnızca JRE kurulu ve JDK yok. Bu bilgisayarda
Merhaba.javaderlenebilir mi? Başka bir bilgisayarda derlenmişMerhaba.classçalıştırılabilir mi?Cevap
Derlenemez:
javacbir geliştirme aracıdır ve yalnızca JDK'de bulunur. Çalıştırılabilir: JRE, JVM'yi ve sınıf kütüphanesini içerir, bu dajava Merhabaiçin yeterlidir. Bir koşulla:.classdosyası, JRE'nin sürümünden daha yeni bir JDK ile derlenmemiş olmalıdır. Aksi hâldeUnsupportedClassVersionErroralınır.Aşağıdaki programın çıktısı nedir, kaç satır oluşur?
public class KD2 { public static void main(String[] args) { System.out.print("1"); System.out.print("2\n3"); System.out.println("4"); System.out.print("\"5\""); System.out.println(); } }Cevap
12 34 "5"Üç satır oluşur. “1” ve “2” aynı satıra yazılır;
"2\n3"içindeki\nsatırı bitirir ve “3” alt satırdan başlar.println("4")“4”ü aynı satıra yazıp satırı bitirir.\"çift tırnak yazdığı için üçüncü satır"5"'tir; sonprintln()bu satırı bitirir.Aşağıdaki programın çıktısı nedir? Her satırdaki
+'ların hangisi toplama, hangisi birleştirmedir?public class KD3 { public static void main(String[] args) { System.out.println("Sonuç: " + 10 / 4 + 1); System.out.println(10 / 4 + 1 + " sonuç"); System.out.println("Sonuç: " + (10 / 4 + 1)); } }Cevap
Sonuç: 21 3 sonuç Sonuç: 31. satır:
/önce yapılır, 10 / 4 = 2 (tam sayı bölmesi). Ardından"Sonuç: " + 2birleştirmedir ve"Sonuç: 2"verir;+ 1de birleştirmedir:Sonuç: 21. 2. satır: 10 / 4 = 2,2 + 1iki sayı arasında olduğu için toplamadır ve 3 verir;3 + " sonuç"birleştirmedir. 3. satır: parantez önce hesaplanır, 2 + 1 = 3; sonra birleştirme yapılır.Bprogramı aşağıdadır.java Bvejava B Ecekomutlarıyla çalıştırılınca ne olur? Hata hangi türdendir?public class B { public static void main(String[] args) { System.out.println("İlk argüman: " + args[0]); } }Cevap
> java B Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0 at B.main(B.java:3) > java B Ece İlk argüman: EceArgüman verilmeyince
argsboş bir dizidir (uzunluğu 0) veargs[0]yoktur. JVM 3. satırdaArrayIndexOutOfBoundsExceptionfırlatır. Program derlendiği ve hata girdiye bağlı olarak çalışırken çıktığı için bu bir çalışma zamanı hatasıdır. Tek argümanla program sorunsuz çalışır.Aşağıdaki hataların her biri hangi türdendir: söz dizimi, tip, çalışma zamanı ya da mantık? (a)
Public class D {(b)int x = 3.5;(c) Bir dikdörtgenin çevresi için2 * a + byazmak (d)int a = 10, b = 0;sonraa / b.Cevap
(a) Söz dizimi hatası:
Publicbüyük harfle bir anahtar kelime değildir ve derleyici satırın başında bir sınıf bildirimi bekler. İleti:class, interface, enum, or record expected. (b) Tip hatası: 3,5 kesirli bir sayıdır veint'e sığmaz. İleti:incompatible types: possible lossy conversion from double to int. (c) Mantık hatası: kod derlenir ve çalışır, ama doğrusu2 * (a + b)'dir. (d) Çalışma zamanı hatası:ArithmeticException: / by zero.System.out.println("C:\temp\new");deyimi derlenir mi? Derlenirse ne yazar? Doğru yol nasıl yazılır?Cevap
Derlenir, çünkü
\tve\ngeçerli kaçış dizileridir. Ama çıktı yanlıştır:\tsekmeye,\nsatır sonuna dönüşür. Ekranda “C:”, bir sekme boşluğu ve “emp” görünür, alt satırda “ew” yazar:C: emp ewDoğru yazım
"C:\\temp\\new"ya da"C:/temp/new"'dir.Aşağıdaki programın
mainmetodu içinjavap -cçıktısında ilk sekiz komut ne olur?iloadkomutlarının sırası neden önemlidir?public class Cikar { public static void main(String[] args) { int a = 2; int b = 5; int c = b - a; System.out.println(c); } }Cevap
public static void main(java.lang.String[]); Code: 0: iconst_2 1: istore_1 2: iconst_5 3: istore_2 4: iload_2 5: iload_1 6: isub 7: istore_3 8: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 11: iload_3 12: invokevirtual #13 // Method java/io/PrintStream.println:(I)V 15: returna1,b2 numaralı yuvadadır.b - aiçin önceb(iload_2), sonraa(iload_1) yığına konur.isubalttaki değerden üstteki değeri çıkarır: 5 − 2 = 3. Toplamada sıra sonucu değiştirmez, çıkarmada değiştirir; yükleme sırası kaynak koddaki işlenen sırasını izler. Program 3 yazar.Merhaba.javaderlenmiş veMerhabayazan birMerhaba.classoluşmuştur. Sonra kaynakta"Merhaba","Selam"yapılıp kaydediliyor ve yeniden derlenmeden sıraylajava Merhaba,java Merhaba.java,java Merhabakomutları çalıştırılıyor. Her komut ne yazar?Cevap
> java Merhaba Merhaba > java Merhaba.java Selam > java Merhaba Merhabajava Merhaba,.classdosyasındaki eski bytecode'u çalıştırır veMerhabayazar.java Merhaba.javaise kaynak dosyayı o anda bellekte derleyip çalıştırır veSelamyazar; diskteki.classdosyasına dokunmaz. Bu yüzden son komut yineMerhabayazar..classancakjavacile yenilenir.Aşağıdaki program derleniyor. Programın hatasız derlenmesi için kaç derleme gerekir? Her derlemede hangi ileti alınır?
public class Iki { public static void main(String[] args) { System.out.println("A") system.out.println("B"); } }Cevap
Üç derleme gerekir. İlk derleme yalnızca söz dizimi hatasını bildirir; 4. satırdaki küçük harf hatası henüz görünmez:
Iki.java:3: error: ';' expected System.out.println("A") ^ 1 error3. satıra
;eklenince ikinci derleme anlam denetimine geçer ve 4. satırı bildirir:Iki.java:4: error: package system does not exist system.out.println("B"); ^ 1 errorsystem,Systemyapılınca üçüncü derleme başarılı olur. İki hata vardı, ama derleyici her seferinde yalnızca birini gösterdi.Bir dikdörtgenin kenar uzunluklarını alıp alanını yazan bir program yazılacak. Kodlamadan önce hangi örnek girdiler ve beklenen çıktılar tabloya yazılmalıdır?
Cevap
Örnek bir tablo: olağan durum, 3 ve 4 → 12. Kare, 5 ve 5 → 25 (iki kenar eşit olduğunda da çalışmalı). Sınır durumu, 0 ve 7 → 0 ya da “kenar 0 olamaz” iletisi; hangisinin isteneceği önceden kararlaştırılmalıdır. Geçersiz girdi, −2 ve 4 → hata iletisi (uzunluk negatif olamaz). Bu son iki satır, problemin tanımında eksik kalan kararları kodlamadan önce ortaya çıkarır.
5.16İleri okuma
- Oracle, The Java Tutorials, “Getting Started” ve “A Closer Look at the ‘Hello World!’ Application”: ilk programın her parçasını,
mainimzasını ve derleme–çalıştırma adımlarını resmî kaynaktan, kısa örneklerle anlatır. - R. Sedgewick, K. Wayne, Computer Science: An Interdisciplinary Approach (Addison-Wesley, 2016), 1.1 Your First Program: düzenleme, derleme ve çalıştırma döngüsünü, komut satırı argümanlarını ve sık hata iletilerini ele alır; kitabın sitesinde (introcs.cs.princeton.edu) bölüm özeti ve alıştırmalar vardır.
- A. B. Downey, C. Mayfield, Think Java (2. baskı, O'Reilly, 2019), Bölüm 1: program, derleme, hata türleri ve kaçış dizilerini programlamaya yeni başlayanlar için sade bir dille anlatır; kitabın ücretsiz çevrim içi sürümü vardır.
- Oracle, The Java Language Specification, Java SE 21 Edition, Bölüm 3 “Lexical Structure”: belirteçlerin, tanımlayıcıların, açıklamaların ve kaçış dizilerinin kesin tanımını verir. The Java Virtual Machine Specification'ın 2. ve 6. bölümleri ise işlenen yığınını ve
iload,iaddgibi komutları ayrıntılı açıklar.
5.17Kaynaklar
- P. Deitel, H. Deitel, Java: How to Program, Early Objects, 11. baskı, Pearson, 2018.
- Y. D. Liang, Introduction to Java Programming and Data Structures, 13. baskı, Pearson, 2024.
- Oracle, The Java Tutorials: A Closer Look at the "Hello World!" Application.
- Oracle, The Java Language Specification, Java SE 21 Edition: Chapter 3. Lexical Structure.
- Oracle, The Java Virtual Machine Specification, Java SE 21 Edition: çevrim içi sürüm.
- R. Sedgewick, K. Wayne, Computer Science: An Interdisciplinary Approach, Addison-Wesley, 2016: 1.1 Your First Program.
- A. B. Downey, C. Mayfield, Think Java, 2. baskı, O'Reilly, 2019 (ücretsiz çevrim içi sürüm).
- OpenJDK, JEP 330: Launch Single-File Source-Code Programs (Java 11); JEP 400: UTF-8 by Default (Java 18); JEP 512: Compact Source Files and Instance Main Methods (Java 25).
İlgili sayfalar: Java'ya Giriş ve İlk Program sunumu · Çalışma soruları · Java derleme ve çalıştırma simülatörü · Terim sözlüğü · © Sercan Külcü. Tüm hakları saklıdır.