---
title: 'Program Yaşam Döngüsü'
source: 'https://academia.sh/tr/kurslar/java-temelleri/program-yasam-dongusu'
course: 'Java Temelleri'
language: tr
updated: '2026-08-17T18:09:39+00:00'
license: 'CC BY-SA 4.0'
---

# Program Yaşam Döngüsü

Sanal makine dört koşulu birden sağlayan tek bir imzayı giriş noktası sayar; sınıf başlatma bloğu programın başında değil sınıfın ilk kullanıldığı anda ve yalnız bir kez çalışır; kaynakta iki ad yazılan bir numaralandırma ise sınıf dosyasında üç alan ve beş yöntem taşır.

Önceki ders sınıf dosyasının **ne olduğunu** ölçtü: kaynakta yazılan on iki yöntemin sekizi
orada kaynakta bulunmayan bir adım taşıyordu, ve kaynakta hiç yazılmamış üç komutluk bir
yapıcı da tabloda duruyordu. O yapıcının ne zaman çağrıldığı söylenmedi; sınıfın kendi
başlatma işinin nerede durduğu da sorulmadı.

Bu ders o boşluğu ölçer. Bir sınıf dosyası diskte durduğu sürece hiçbir şey yapmaz; sanal
makine onu yükler, hazırlar ve içindeki komutları bir sıraya göre çalıştırır. Sorular üç
tanedir: sanal makine hangi imzayı **giriş noktası (entry point)** sayıyor, bir sınıfın
başlatma işi ile bir örneğin başlatma işi hangi sırayla çalışıyor, ve kaynakta yazılmamış
hangi üyeler bu sıraya katılıyor?

## Ölçüm Çekirdeği

Önceki dersin `Olcek` çekirdeği komut sayıyordu. Bu derste sayılan şey komut değil **üyedir**:
sınıf dosyasındaki alanlar ve yöntemler, her yöntemin imzası, genel ve statik olup olmadığı,
dönüş tipi. Çekirdek aynı işi yapar — derler ve sınıf dosyasını okur — ama bu derste
kullanılmayan komut çözümlemesini taşımaz.

- **CZ6** — Giriş noktası ölçümü sınıf dosyasının kendisinden okunur: yöntemin adı,
  belirteçleri, dönüş tipi ve parametre listesi. Ölçüm çalıştırıcıyı çağırmaz; dilin
  belirttiği dört koşulu gerçek sınıf dosyalarına karşı sınar.
- **CZ7** — Sıra ölçümünde kâhin nesnenin kendi kaydıdır: her başlatma adımı çalıştığında
  adını ortak bir listeye yazar, dolayısıyla sıra bir çıkarım değil bir dökümdür.
- **CZ8** — Kayıt listesi program sonunda tek seferde basılır; basma işi ölçülen sıraya
  karışmaz.
- **CZ9** — Üye dökümü sınıf dosyasındaki **bütün** üyeleri sayar; kaynakta yazılanlar da
  listededir. Eklenen üye, listeyle kaynağın karşılaştırılmasından okunur.
- **CZ10** — Ölçüm ortama bağlı hiçbir veri okumaz: süre, bellek adresi, kimlik ve yol
  yazılmaz; sayılan tek şey üye sayısı ile çalışma sırasıdır.

```java
// Olcek.java — sınıf dosyasının üye dökümü: alanlar, yöntem imzaları ve belirteçleri
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.classfile.*;
import java.lang.constant.MethodTypeDesc;
import java.lang.reflect.AccessFlag;
import java.nio.file.*;
import java.util.*;
import java.util.spi.ToolProvider;

class Olcek {
    record Uye(String ad, String imza, boolean genel, boolean statik, String donus) {}
    record Sinif(List<String> alan, List<Uye> yontem) {}

    static Sinif oku(String kaynak, String sinif) throws Exception {
        Path d = Files.createTempDirectory("olcek");
        Path k = d.resolve(sinif + ".java");
        Files.writeString(k, kaynak);
        PrintWriter bos = new PrintWriter(Writer.nullWriter());
        if (ToolProvider.findFirst("javac").orElseThrow()
                .run(bos, bos, "-d", d.toString(), k.toString()) != 0)
            throw new IllegalStateException("derlenmedi: " + sinif);
        ClassModel cm = ClassFile.of().parse(d.resolve(sinif + ".class"));
        List<String> alan = new ArrayList<>();
        for (FieldModel f : cm.fields()) alan.add(f.fieldName().stringValue());
        List<Uye> yontem = new ArrayList<>();
        for (MethodModel m : cm.methods()) {
            MethodTypeDesc t = m.methodTypeSymbol();
            List<String> p = new ArrayList<>();
            for (var a : t.parameterList()) p.add(a.displayName());
            yontem.add(new Uye(m.methodName().stringValue(), "(" + String.join(", ", p) + ")",
                    m.flags().has(AccessFlag.PUBLIC), m.flags().has(AccessFlag.STATIC),
                    t.returnType().displayName()));
        }
        return new Sinif(alan, yontem);
    }

    static List<String> adlar(Sinif s) {
        List<String> a = new ArrayList<>();
        for (Uye u : s.yontem()) a.add(u.ad() + u.imza());
        return a;
    }
}
```

## Giriş Noktası: Dört Koşulu Birden Sağlayan İmza

Bir programın nereden başladığı Java'da dosyanın ilk satırı değildir. Çalıştırıcıya bir
**sınıf** adı verilir; o sınıf yüklenir ve içinde belirli bir yöntem aranır. Aranan şey bir ad
değil, bir **imzadır**: yöntemin adı `main` olacak, genel olacak, statik olacak, hiçbir değer
döndürmeyecek ve tek parametresi bir dizgi dizisi olacaktır. Dört koşul birden sağlanmazsa o
yöntem giriş noktası değildir.

Aşağıdaki ölçüm beş ayrı kaynağı derler ve her birinin sınıf dosyasındaki `main` yöntemini bu
koşullara karşı sınar. Beşinin de kaynağı kusursuzdur ve beşi de sorunsuz derlenir — ayrım
derleme aşamasında değil, yükleme sonrasında ortaya çıkar.

```java
// Ana.java — sanal makinenin giriş noktası saydığı imza
import java.util.*;

public class Ana {
    public static void main(String[] args) throws Exception {
        Map<String, String> aday = new LinkedHashMap<>();
        aday.put("Dogru",     "class Dogru { public static void main(String[] a) { } }");
        aday.put("Statiksiz", "class Statiksiz { public void main(String[] a) { } }");
        aday.put("Genelsiz",  "class Genelsiz { static void main(String[] a) { } }");
        aday.put("Dizisiz",   "class Dizisiz { public static void main(String a) { } }");
        aday.put("Donduren",  "class Donduren { public static int main(String[] a) { return 0; } }");
        System.out.printf("%-10s %-11s %-6s %-6s %-6s %s%n",
                "sinif", "imza", "genel", "statik", "donus", "giris noktasi");
        for (var g : aday.entrySet())
            for (Olcek.Uye u : Olcek.oku(g.getValue(), g.getKey()).yontem())
                if (u.ad().equals("main")) {
                    boolean giris = u.genel() && u.statik()
                            && u.donus().equals("void") && u.imza().equals("(String[])");
                    System.out.printf("%-10s %-11s %-6s %-6s %-6s %s%n", g.getKey(), u.imza(),
                            u.genel(), u.statik(), u.donus(), giris);
                }
    }
}
```

```
sinif      imza        genel  statik donus  giris noktasi
Dogru      (String[])  true   true   void   true
Statiksiz  (String[])  true   false  void   false
Genelsiz   (String[])  false  true   void   false
Dizisiz    (String)    true   true   void   false
Donduren   (String[])  true   true   int    false
```

Beş sınıfın **biri** giriş noktası taşıyor. Kalan dördü tek bir koşulda düşüyor ve her düşüş
ayrı bir şey söylüyor. `Statiksiz` statik değildir: statik olmayan bir yöntemi çağırmak için
önce bir örnek gerekir, oysa programın başında henüz hiçbir örnek yoktur — sanal makine
yaratmadan çağıramaz. `Genelsiz` sınıfın dışından erişilebilir değildir; çağıran taraf sınıfın
kendisi olmadığına göre erişim kapalıdır. `Dizisiz` bir dizgi alır, dizgi dizisi değil:
komut satırı argümanları bir dizidir ve sayısı önceden bilinmez. `Donduren` bir değer
döndürür; programın sonlanma bilgisi dönüş değeriyle değil, ayrı bir çağrıyla verilir.

Parametrenin dizgi dizisi olması bir biçim ayrıntısı değildir: programa dışarıdan verilen
argümanlar oraya, sırasıyla ve sayısı önceden bilinmeden gelir. Giriş noktası, dış dünyadan
gelen tek veriyi bu tek parametrede karşılar.

Buradaki asıl gözlem, kusurun **nerede yakalandığıdır**. Beş kaynağın hepsi derlenmiştir;
sınıf dosyaları oluşmuştur. Bir yöntemin giriş noktası olup olmadığı derleyicinin sorusu
değildir, sanal makinenin sorusudur. Bu, önceki dersin kurduğu ayrımın öbür yüzüdür: derleyici
kaynağa adım ekler, sanal makine ise sınıf dosyasında ne bulacağına dair kendi koşullarını
taşır.

## Sınıf Bir Kez, Örnek Her Seferinde

Giriş noktası bulunduktan sonra sıra çalışmaya gelir. Bir sınıfın hazırlanmasında iki ayrı
düzey vardır: **sınıf düzeyi** iş bir kez yapılır, **örnek düzeyi** iş her yeni nesnede
yeniden yapılır. Her düzeyin de iki adımı vardır — alan başlatıcılar ve **başlatma blokları
(static initializer)**. Bunların üstüne bir de **yapıcı (constructor)** gelir.

Ölçüm için kursun somut ekseni kullanılır: bir depo kaydını tutan `Kayit`. Üç alanı vardır —
`ad`, `adet` ve gram cinsinden `agirlik`. Her başlatma adımı çalıştığında adını ortak bir
listeye yazar; liste sonda basılır.

```java
// Sira.java — sınıf başlatma bloğu, alan başlatıcı ve yapıcı hangi sırayla çalışır
import java.util.*;

public class Sira {
    static final List<String> IZ = new ArrayList<>();
    static void iz(String s) { IZ.add(s); }

    static class Kayit {
        static { iz("sinif: baslatma blogu"); }
        static final long BIRIM_AGIRLIK = agirlikBirimi();
        String ad;
        int adet = adetBasla();
        long agirlik;
        { iz("ornek: baslatma blogu"); }

        Kayit(String ad, long agirlik) {
            iz("yapici: " + ad);
            this.ad = ad;
            this.agirlik = agirlik;
        }

        static long agirlikBirimi() { iz("sinif: alan baslatici"); return 1L; }
        static int adetBasla() { iz("ornek: alan baslatici"); return 0; }
    }

    public static void main(String[] args) {
        iz("-- main basladi");
        new Kayit("civata", 12L);
        iz("-- ilk ornek bitti");
        new Kayit("somun", 7L);
        IZ.forEach(System.out::println);
    }
}
```

```
-- main basladi
sinif: baslatma blogu
sinif: alan baslatici
ornek: alan baslatici
ornek: baslatma blogu
yapici: civata
-- ilk ornek bitti
ornek: alan baslatici
ornek: baslatma blogu
yapici: somun
```

Dökümün ilk satırı beklenmedik olanıdır: `main` **sınıf başlatma bloğundan önce** başlıyor.
Bir sınıfın hazırlanması program açılışında değil, o sınıfa ilk kez gerçekten ihtiyaç
duyulduğunda yapılır — burada ilk `new` çağrısında. Bir sınıf hiç kullanılmazsa başlatma
bloğu hiç çalışmaz.

İkinci gözlem tekrarın yokluğudur. İkinci `new` çağrısında `sinif:` ile başlayan iki satır
**yeniden görünmüyor**; yalnız örnek düzeyi adımlar tekrarlanıyor. Sınıf düzeyi iş, kaç örnek
üretilirse üretilsin bir kez yapılır. Sayı olarak: iki örnek için sınıf düzeyi **2** adım,
örnek düzeyi **6** adım çalıştı.

Dökümdeki `sinif: alan baslatici` satırı ayrıca bir ayrımı görünür kılıyor. `BIRIM_AGIRLIK`
alanı `static final` bildirilmiştir, yani bir daha değişmeyecektir — buna karşın değeri sınıf
başlatma adımında **çalışarak** üretiliyor, çünkü bir yöntem çağrısından geliyor. Değeri
derleme zamanında hesaplanabilen bir sabit olsaydı önceki dersin sabit katlama ölçümündeki
yola girer ve burada hiçbir adım görünmezdi. Bir alanın değişmez olması, değerinin çalışmadan
elde edildiği anlamına gelmez.

Üçüncüsü sıranın kendisidir. Her iki düzeyde de önce **alan başlatıcı**, sonra **başlatma
bloğu** çalışıyor; ikisi de kaynaktaki yazılma sırasını izliyor. Yapıcının gövdesi bunların
**sonuncusudur** — yani bir yapıcı, gövdesi çalışmaya başladığında alanların çoktan
başlatılmış olduğunu varsayabilir. Bir alanı yapıcıda atamak ile bildiriminde atamak aynı işi
yapmaz: ikincisi önce çalışır, birincisi onun üzerine yazar.

## Kaynakta Yazılmayan Üyeler

Önceki dersin tablosunda kaynakta hiç yazılmamış bir `<init>` vardı. Şimdi ona doğrudan
bakılabilir — ve yanına, çok daha fazlasını ekleyen bir biçim konabilir. Kaynakta iki ad
yazılan bir **numaralandırma (enum)**, sınıf dosyasında kaç üye taşır?

```java
// Uye.java — kaynakta yazılmayan üyeler ve yapıcı yazıldığında olan
public class Uye {
    public static void main(String[] args) throws Exception {
        Olcek.Sinif bos = Olcek.oku("class Depo { }", "Depo");
        System.out.println("class Depo { }            -> alan " + bos.alan()
                + ", yontem " + Olcek.adlar(bos));
        Olcek.Sinif kendi = Olcek.oku("class Depo { Depo(int adet) { } }", "Depo");
        System.out.println("class Depo { Depo(int) }  -> alan " + kendi.alan()
                + ", yontem " + Olcek.adlar(kendi));

        Olcek.Sinif y = Olcek.oku("enum Yon { KUZEY, GUNEY }", "Yon");
        System.out.println();
        System.out.println("kaynakta yazilan ad sayisi: 2");
        System.out.println("sinif dosyasindaki alanlar (" + y.alan().size() + "): " + y.alan());
        System.out.println("sinif dosyasindaki yontemler (" + y.yontem().size() + "): "
                + Olcek.adlar(y));
    }
}
```

```
class Depo { }            -> alan [], yontem [<init>()]
class Depo { Depo(int) }  -> alan [], yontem [<init>(int)]

kaynakta yazilan ad sayisi: 2
sinif dosyasindaki alanlar (3): [KUZEY, GUNEY, $VALUES]
sinif dosyasindaki yontemler (5): [values(), valueOf(String), <init>(String, int), $values(), <clinit>()]
```

Gövdesi tümüyle boş bir sınıf, sınıf dosyasında **bir yöntem** taşıyor: parametresiz bir
yapıcı. Kaynakta bir tek karakter yazılmadan bir üye eklenmiştir; bunun adı **varsayılan
yapıcıdır** ve tek işi taban sınıfın yapıcısını çağırmaktır. Önceki dersin tablosundaki üç
komutluk `<init>` satırı buydu.

Numaralandırma çok daha ileri gider. Kaynakta yazılan iki ada karşılık sınıf dosyasında **üç
alan** ve **beş yöntem** var. `KUZEY` ile `GUNEY` yazılmıştır; `$VALUES` yazılmamıştır.
Yöntemlerin hiçbiri yazılmamıştır: `values` ile `valueOf` numaralandırmanın belirtimde
tanımlanan yüzeyidir, `$values` onlara hizmet eder, `<init>` iki parametre alır — oysa
kaynakta hiçbir yapıcı çağrısı yazılmamıştır — ve `<clinit>` bir önceki bölümde ölçtüğümüz
sınıf başlatma bloğunun sınıf dosyasındaki adıdır. Numaralandırmanın sabitleri işte orada
kurulur.

Böylece kursun üçüncü iddiası doğuyor: **eklenen şey yalnız adım değil, üyedir.** Bir sınıfın
yüzeyi, kaynağında yazılan üyelerden ibaret değildir.

## Sınırlayıcı Ölçüm: Yazınca Kaybolan Üye

Çıktının ikinci satırı tezin sınırını çiziyor. `Depo` sınıfına tek parametreli bir yapıcı
yazıldığında sınıf dosyasında yine **bir** yöntem var, ama bu sefer o yöntem `<init>(int)` —
yani yazdığımızın kendisi. Parametresiz yapıcı **kaybolmuştur**.

Bunun görünen sonucu vardır ve sessiz değildir. Aynı çağrıyı iki `Depo` biçiminin yanına
koyup derlemeyi deneyelim.

```java
// Etki.java — eklenen yapıcı kaybolunca çağıran tarafta ne oluyor
public class Etki {
    static String dene(String kaynak) {
        try { Olcek.oku(kaynak, "Depo"); return "derlendi"; }
        catch (Exception e) { return "derlenmedi"; }
    }

    public static void main(String[] args) {
        String kul = " class Kul { static Depo yap() { return new Depo(); } }";
        System.out.println("yapici yazilmamis + new Depo() -> " + dene("class Depo { }" + kul));
        System.out.println("yapici yazilmis   + new Depo() -> "
                + dene("class Depo { Depo(int adet) { } }" + kul));
    }
}
```

```
yapici yazilmamis + new Depo() -> derlendi
yapici yazilmis   + new Depo() -> derlenmedi
```

Çağıran taraf iki durumda da **aynı** satırı yazıyor, ve satırın kendisi değişmediği hâlde
biri derleniyor öbürü derlenmiyor. `Depo` sınıfına eklenen bir yetenek kaldırılmadı; kaynağa
bir yapıcı **eklendiği için** eklenen yapıcı ortadan kalktı. Derleyici bir üyeyi yalnızca
**hiçbiri yazılmamışken** koyar; yazılan tek bir yapıcı, eklenen yapıcıyı yerinden eder.

Eklenen adımın koşulu buradan da okunur. Derleyici bir boşluğu doldurur; boşluk
doldurulduğunda ekleme yapılmaz. Bir sınıfın dosyasında ne bulacağımızı bilmek için kaynakta
ne yazdığımıza değil, **ne yazmadığımıza** bakmak gerekir.

## Özet

- Bir sınıf dosyası ancak yüklendiğinde iş yapar; giriş noktası dört koşulu birden sağlayan
  tek bir imzadır — genel, statik, değer döndürmeyen, tek parametresi dizgi dizisi olan `main`.
- Beş aday sınıfın hepsi sorunsuz derlenir, yalnız **biri** giriş noktası taşır: bu koşullar
  derleyicinin değil, sanal makinenin sorusudur.
- Sınıf düzeyi başlatma programın açılışında değil, sınıfa ilk kez ihtiyaç duyulduğunda ve
  yalnız **bir kez** çalışır; iki örnek için sınıf düzeyi 2, örnek düzeyi 6 adım sayıldı.
- Her iki düzeyde sıra aynıdır: önce alan başlatıcı, sonra başlatma bloğu, en sonda yapıcının
  gövdesi.
- Gövdesi boş bir sınıf sınıf dosyasında bir varsayılan yapıcı taşır; kaynakta iki ad yazılan
  bir numaralandırma **üç alan** ve **beş yöntem** taşır — eklenen şey yalnız adım değil, üyedir.
- Tek parametreli bir yapıcı yazıldığında eklenen parametresiz yapıcı kaybolur: derleyici
  yalnız hiçbiri yazılmamış olan üyeyi ekler.

## Sonraki Adım

Bu derste bir sınıfın ne zaman ve hangi sırayla çalıştığı ölçüldü, ama sanal makinenin o
sınıfı **nasıl bulduğu** hiç sorulmadı. Ölçümlerde geçen `Depo` ve `Kayit` adları tek
başlarına yazıldı; oysa bir sınıf dosyasındaki her tip adı, önceki derste gördüğümüz gibi,
tam biçimiyle taşınıyordu. Adın kısası ile tamı arasındaki fark nerede kapanıyor — derleme
zamanında mı, yükleme sırasında mı? Sonraki ders bunu ölçer: aynı program içe aktarımla ve
tam nitelikli adla yazıldığında üretilen sınıf dosyaları birbirinden ayırt edilebiliyor mu,
ve paket adı bu dosyada ne değiştiriyor?
