---
title: 'Denetim Akışı'
source: 'https://academia.sh/tr/kurslar/java-temelleri/denetim-akisi'
course: 'Java Temelleri'
language: tr
updated: '2026-08-17T18:09:40+00:00'
license: 'CC BY-SA 4.0'
---

# Denetim Akışı

Aynı davranış sınıf dosyasında iki ayrı arama komutuna derlenir: yoğun etiketli seçim doğrudan indeksleyen bir çizelge, seyrek etiketli seçim etiketler arasında arayan bir komut kurar; etiketli atlama düz `break`'e karşı hem adım hem doğru satır kazandırır, bayraklı biçime karşı ise sıfır adım.

Önceki derste dal komutu bir işlecin yan ürünü olarak ortaya çıkmıştı: kısa devre
değerlendirme, ikinci işlenene hiç girmeyen bir atlamaydı. Bu ders dalı asıl konusu yapar.
Kaynakta `if`, `switch`, `for`, `while`, `break` ve `continue` diye ayrı ayrı yazılan
biçimler, sınıf dosyasında bu çeşitliliği bulmaz. Orada yalnız birkaç tür komut vardır:
koşullu dal, koşulsuz atlama ve iki tane **seçim** komutu.

Programlama Temelleri kursu koşullu dallanmayı, döngüyü ve döngü denetimini kavram olarak
kurmuştu — **bunlar tekrarlanmaz**. Orada bir akış biçiminin ne yaptığı anlatıldı. Burada
ölçülen şey, aynı davranışın hangi komuta derlendiği ve iki farklı komutun **ayrı arama
maliyeti** taşıdığıdır.

## Akış Biçimi ile Akış Komutu

Sınıf dosyasında bir seçimi karşılayan iki ayrı komut vardır ve derleyici hangisini
kullanacağına **etiketlerin dağılımına** bakarak karar verir.

- **Yoğun seçim komutu** bir çizelge tutar: en küçük etiketten en büyüğüne kadar her değer
  için bir giriş. Verilen değer çizelgeye doğrudan indekslenir; arama yoktur, tek bir
  konum hesabı vardır. Karşılığında çizelge, aradaki boşluklar için de yer tutar.
- **Seyrek seçim komutu** yalnız gerçek etiketleri sıralı olarak tutar ve verilen değeri
  bu sıralı liste içinde **arar**. Yer tutmaz, ama her seçimde bir arama ödenir.

Üçüncü bir yol da vardır: aynı davranışı `if` zinciriyle yazmak. O zaman ortada hiç seçim
komutu bulunmaz; etiket sayısı kadar koşullu dal art arda dizilir ve son etiket için
hepsi denenir. Bu ders üç yazımı da kurs boyunca sürdürülen deponun **raf numarası**
üzerinde ölçer, sonra döngü biçimlerine geçer.

## Ölçüm Çekirdeği

Çekirdek önceki derslerinkidir; bu derste iki ölçüm okur. Birincisi **dal komutu
sayısıdır**. İkincisi, yöntemde bir seçim komutu varsa **hangisi** olduğudur: yoğun
seçimde çizelgenin kapsadığı aralık, seyrek seçimde tutulan etiket sayısı basılır.

- **SD16** — Dal sayısı sınıf dosyasındaki koşullu ve koşulsuz atlama komutlarının
  sayısıdır; kaç kez çalıştırıldıkları değil, kaç tane yazıldıkları sayılır.
- **SD17** — Seçim komutu tek bir yöntemde bir kez bulunur; sütun boş kaldığında o
  yöntemde hiç seçim komutu yok demektir.

```java
// Olcek.java — akış biçiminin sınıf dosyasında hangi komuta derlendiğini okur
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.classfile.*;
import java.lang.classfile.instruction.*;
import java.nio.file.*;
import java.util.*;
import java.util.spi.ToolProvider;

class Olcek {
    record Yontem(String ad, int komut, int dal, String secim, List<String> eklenen) {}

    static List<Yontem> oku(String kaynak, String sinif) throws Exception {
        Path d = Files.createTempDirectory("olcek");
        Files.writeString(d.resolve(sinif + ".java"), kaynak);
        PrintWriter bos = new PrintWriter(Writer.nullWriter());
        if (ToolProvider.findFirst("javac").orElseThrow().run(bos, bos, "-d", d.toString(),
                d.resolve(sinif + ".java").toString()) != 0)
            throw new IllegalStateException("derlenmedi: " + sinif);
        List<Yontem> yontemler = new ArrayList<>();
        for (MethodModel m : ClassFile.of().parse(d.resolve(sinif + ".class")).methods()) {
            if (m.code().isEmpty()) continue;
            List<String> eklenen = new ArrayList<>();
            String secim = "-";
            int komut = 0, dal = 0;
            for (CodeElement e : m.code().get()) {
                if (e instanceof Instruction) komut++;
                if (e instanceof BranchInstruction) dal++;
                else if (e instanceof TableSwitchInstruction t)
                    secim = "yogun " + t.lowValue() + ".." + t.highValue();
                else if (e instanceof LookupSwitchInstruction l)
                    secim = "seyrek " + l.cases().size() + " etiket";
                else if (e instanceof InvokeInstruction iv)
                    eklenen.add(iv.name().stringValue());
                else if (e instanceof TypeCheckInstruction tc && tc.opcode() == Opcode.CHECKCAST)
                    eklenen.add("tip denetimi");
            }
            yontemler.add(new Yontem(m.methodName().stringValue(), komut, dal, secim, eklenen));
        }
        return yontemler;
    }
}
```

## On Akış Biçimi

Ölçülen sınıfın ilk üç yöntemi **aynı yazımı** üç ayrı etiket dağılımıyla verir; dördüncüsü
üçüncüsünün eşlemesini `if` zinciriyle yazar. Sonraki dördü aynı toplamı dizide ve listede,
gelişmiş `for` ile ve elle açılmış biçimle hesaplar. Son ikisi iç içe iki döngüden çıkmanın
iki yolunu gösterir.

```java
// Akis.java — on akış biçimi, sınıf dosyasında hangi komut
public class Akis {
    static final String KAYNAK = """
        import java.util.Iterator;
        import java.util.List;
        class Depo {
            static int yogunSecim(int raf) {
                switch (raf) { case 1: return 10; case 2: return 20;
                               case 3: return 30; case 4: return 40; default: return 0; }
            }
            static int bosluklu(int raf) {
                switch (raf) { case 1: return 10; case 2: return 20;
                               case 3: return 30; case 10: return 40; default: return 0; }
            }
            static int seyrekSecim(int raf) {
                switch (raf) { case 1: return 10; case 100: return 20;
                               case 10000: return 30; case 1000000: return 40; default: return 0; }
            }
            static int ifZinciri(int raf) {
                if (raf == 1) return 10;
                if (raf == 100) return 20;
                if (raf == 10000) return 30;
                if (raf == 1000000) return 40;
                return 0;
            }
            static int diziToplami(int[] adetler) { int t = 0; for (int x : adetler) t += x; return t; }
            static int diziIndeksli(int[] adetler) {
                int t = 0;
                for (int i = 0; i < adetler.length; i++) t += adetler[i];
                return t;
            }
            static int listeToplami(List<Integer> adetler) { int t = 0; for (int x : adetler) t += x; return t; }
            static int elleYineleyici(List<Integer> adetler) {
                int t = 0;
                for (Iterator<Integer> y = adetler.iterator(); y.hasNext(); ) { int x = y.next(); t += x; }
                return t;
            }
            static int etiketli(int[][] depo, int aranan) {
                int satir = -1;
                dis:
                for (int i = 0; i < depo.length; i++)
                    for (int j = 0; j < depo[i].length; j++)
                        if (depo[i][j] == aranan) { satir = i; break dis; }
                return satir;
            }
            static int bayrakli(int[][] depo, int aranan) {
                int satir = -1;
                boolean bulundu = false;
                for (int i = 0; i < depo.length && bulundu == false; i++)
                    for (int j = 0; j < depo[i].length; j++)
                        if (depo[i][j] == aranan) { satir = i; bulundu = true; break; }
                return satir;
            }
        }
        """;

    public static void main(String[] args) throws Exception {
        System.out.printf("%-16s %6s %4s %-16s %s%n",
                "yontem", "komut", "dal", "secim komutu", "kaynakta yazilmayan adim");
        for (Olcek.Yontem y : Olcek.oku(KAYNAK, "Depo")) {
            if (y.ad().startsWith("<")) continue;
            System.out.printf("%-16s %6d %4d %-16s %s%n", y.ad(), y.komut(), y.dal(), y.secim(),
                    y.eklenen().isEmpty() ? "-" : String.join(", ", y.eklenen()));
        }
    }
}
```

```
yontem            komut  dal secim komutu     kaynakta yazilmayan adim
yogunSecim           12    0 yogun 1..4       -
bosluklu             12    0 yogun 1..10      -
seyrekSecim          12    0 seyrek 4 etiket  -
ifZinciri            22    4 -                -
diziToplami          24    2 -                -
diziIndeksli         18    2 -                -
listeToplami         20    2 -                iterator, hasNext, next, tip denetimi, intValue
elleYineleyici       20    2 -                iterator, hasNext, next, tip denetimi, intValue
etiketli             32    6 -                -
bayrakli             38    7 -                -
```

İlk üç satır dersin ana ölçümüdür. Üç yöntem de dört etiket ve bir varsayılan dal taşıyor,
aynı yazımla yazılmış ve sınıf dosyasında **aynı 12 komuta** dönüşüyor. Ama seçim komutu
aynı değil:
etiketler 1'den 4'e yayıldığında **yoğun** komut, milyonlara yayıldığında **seyrek** komut
üretiliyor. Kaynakta iki `switch` arasında yapısal hiçbir fark yoktur; fark yalnız
sayıların değerindedir ve derleyici o değerlere bakarak ayrı bir komut seçmiştir. Komut
sayısı bunu görmez — burada sayılacak şey komut sayısı değil, **hangi komut** olduğudur.

İkinci satır kararın nasıl verildiğini gösteriyor. `bosluklu` yalnız dört etiket taşıyor,
ama en küçüğü 1, en büyüğü 10; derleyici yine **yoğun** komutu seçiyor ve çizelge
**1'den 10'a** kadar uzanıyor. Yani sınıf dosyası kullanılmayan altı giriş için de yer
tutuyor. Derleyicinin verdiği karar bir takastır: dolduran boşluklar dosyada yer kaplar,
karşılığında her seçimde arama yerine tek bir indeksleme yapılır. Etiketler yeterince
dağıldığında takas tersine döner.

Dördüncü satır seçimin alternatifidir. Aynı eşleme `if` zinciriyle yazıldığında ortada
hiç seçim komutu yoktur: **22** komut ve **4** dal. Dört dalın anlamı, son etiketin
bulunabilmesi için önceki üçünün de denenmesi gerektiğidir. Seçim komutları bu denemeyi
tek adıma indirir; `if` zinciri etiket sayısıyla birlikte büyür. Aynı davranış, iki ayrı
maliyet.

## Aynı Döngü, İki Ayrı Adım

Ortadaki dört satır gelişmiş `for` döngüsünün tek bir yazım olmadığını gösteriyor.

`diziToplami` **24** komut ve kaynakta yazılmayan **hiçbir** adım üretiyor: derleyici
gelişmiş `for` yazımını sayaçlı bir döngüye açıyor. `listeToplami` aynı gövdeyi **20**
komutta bitiriyor ama **beş** eklenen adım taşıyor — yineleyici alınıyor, iki kez
sorgulanıyor, dönen nesne denetleniyor ve sayıya açılıyor. Bu sayılar ilk derste
kutulamanın bedeli olarak okunmuştu; burada okunuşu başkadır. Aynı **yazım**, gezilen şeyin
tipine göre iki ayrı döngüye derleniyor. Sözdizim tek, komut iki.

Yanlarındaki iki satır bunu kanıtlıyor. `diziIndeksli` elle yazılmış sayaçlı döngüdür ve
**18** komut üretiyor — gelişmiş biçimden **altı** komut az, çünkü gelişmiş biçim dizi
referansını ve uzunluğu ayrı yuvalara kopyalar. `elleYineleyici` ise elle yazılmış
yineleyici döngüsüdür ve `listeToplami` ile **birebir aynı** çıkıyor: aynı 20 komut, aynı
iki dal, aynı beş adım. Liste üzerindeki gelişmiş `for`, tam olarak bu döngünün
kısaltmasıdır; dizi üzerindeki ise sayaçlı döngünün biraz daha bol yazılmış halidir.

Son iki satır bir sonraki bölümün konusudur: etiketli atlama **32** komut ve **6** dal,
bayraklı biçim **38** komut ve **7** dal.

## Etiketli Atlamanın Ölçülen Kazancı

İç içe iki döngüden birden çıkmak için Java üç yol sunar: dış döngüye bir etiket koyup
`break dis` yazmak, bir bayrak değişkeni tutmak, ya da yalnız `break` yazıp iç döngüden
çıkmak. Üçü aynı programı **vermez**.

- **SD18** — Depo üç satır ve satır başına dört kayıt taşır; aranan ad **iki** satırda
  bulunur, ilki ikinci satırın üçüncü kaydıdır. Doğru yanıt **1**'dir.
- **SD19** — Adım sayacı yalnız iç döngünün gövdesinde artırılır, yani karşılaştırılan
  **kayıt sayısını** sayar; süre ölçülmez.

```java
// Atlama.java — üç çıkış biçimi, kaç adım ve hangi satır
public class Atlama {
    record Kayit(String ad, int adet, long agirlik) {}

    static int adim = 0;

    static Kayit[][] depo() {
        return new Kayit[][] {
            { new Kayit("vida", 4, 40L), new Kayit("pul", 9, 5L),
              new Kayit("somun", 2, 12L), new Kayit("mil", 1, 300L) },
            { new Kayit("conta", 7, 8L), new Kayit("yay", 3, 15L),
              new Kayit("civata", 5, 90L), new Kayit("bilye", 6, 20L) },
            { new Kayit("civata", 2, 90L), new Kayit("kama", 8, 30L),
              new Kayit("burc", 4, 60L), new Kayit("rulman", 1, 250L) },
        };
    }

    static int etiketli(Kayit[][] d, String aranan) {
        int satir = -1;
        dis:
        for (int i = 0; i < d.length; i++)
            for (int j = 0; j < d[i].length; j++) {
                adim++;
                if (d[i][j].ad().equals(aranan)) { satir = i; break dis; }
            }
        return satir;
    }

    static int bayrakli(Kayit[][] d, String aranan) {
        int satir = -1;
        boolean bulundu = false;
        for (int i = 0; i < d.length && bulundu == false; i++)
            for (int j = 0; j < d[i].length; j++) {
                adim++;
                if (d[i][j].ad().equals(aranan)) { satir = i; bulundu = true; break; }
            }
        return satir;
    }

    static int duzBreak(Kayit[][] d, String aranan) {
        int satir = -1;
        for (int i = 0; i < d.length; i++)
            for (int j = 0; j < d[i].length; j++) {
                adim++;
                if (d[i][j].ad().equals(aranan)) { satir = i; break; }
            }
        return satir;
    }

    public static void main(String[] args) {
        Kayit[][] d = depo();
        System.out.printf("%-12s %6s %8s%n", "bicim", "satir", "adim");
        adim = 0;
        System.out.printf("%-12s %6d %8d%n", "etiketli", etiketli(d, "civata"), adim);
        adim = 0;
        System.out.printf("%-12s %6d %8d%n", "bayrakli", bayrakli(d, "civata"), adim);
        adim = 0;
        System.out.printf("%-12s %6d %8d%n", "duz break", duzBreak(d, "civata"), adim);
    }
}
```

```
bicim         satir     adim
etiketli          1        7
bayrakli          1        7
duz break         2        8
```

Üçüncü satır ölçümün en önemli kısmıdır ve bir başarım farkı değildir. Düz `break` yalnız
**iç** döngüden çıkar; dış döngü kaldığı yerden sürer, ikinci satırdaki eşleşme
bulunduktan sonra üçüncü satır da taranır ve `satir` değişkeni **yeniden yazılır**. Sonuç
**2**, yani yanlış. Bir adım fazla ödenmiştir ve o adım yanlış yanıtı üretmiştir. İç içe
döngülerde `break`'in hangi döngüye ait olduğu bir üslup sorusu değildir.

İlk iki satır ise etiketli atlamanın gerçek kazancını sınırlıyor. Etiketli biçim ile
bayraklı biçim **aynı satırı** buluyor ve **aynı 7 adımı** ödüyor. Etiketli atlamanın
bayraklı biçime karşı çalışma zamanı kazancı **sıfırdır**.

## Sınırlayıcı Ölçüm: Kazanç Nerede, Bedel Nerede

Etiketli atlamanın ölçülebilir kazancı iki yerdedir ve ikisi de küçüktür.

- **SD20** — Karşılaştırma sınıf dosyası ölçümündeki `etiketli` ve `bayrakli`
  yöntemleridir; ikisi de aynı imzayı taşır ve aynı sonucu döndürür.

Birincisi sınıf dosyasındadır: etiketli biçim **32** komut ve **6** dal, bayraklı biçim
**38** komut ve **7** dal üretiyor — altı komut ve bir dal az. Bayrak bir yuva tutar, her
dış tur başında okunur ve eşleşmede yazılır; bunların hepsi komuttur. İkincisi
kaynaktadır: bayraklı biçimde çıkış koşulu **iki yere** dağılır — dış döngünün başlığına
ve iç döngünün gövdesine. Etiketli biçimde çıkış tek satırdadır.

Bedel de yazılmalıdır. Etiket, akışı okuyan kişinin gözünü kaynakta **geriye** gönderir:
`break dis` satırını okuyan biri, `dis` etiketinin hangi döngüye konduğunu bulmak için
yukarı bakmak zorundadır. İki döngüde bu bakış kısadır; üç ya da dört düzeyde, ve
aralarında `continue` de varsa, etiketler akışı izlenmesi güç bir hale getirir. Ölçüm
kararı vermez, yalnız neyin ne kadar olduğunu söyler: kazanç **altı komut ve bir
yuvadır**, çalışma zamanı adımı olarak **sıfırdır**, ve bedel okuyanın yukarı bakmasıdır.

Bu sınır dersin tezini de tamamlıyor. Akış biçimleri arasındaki seçim, çoğu yerde komut
sayısını değiştirir ama ödenen adımı değiştirmez. Gerçekten değişen tek yer, biçimin
**yanlış davranışı** mümkün kıldığı yerdir — düz `break` satırındaki gibi.

## Özet

- Aynı seçim, etiketlerin dağılımına göre iki ayrı komuta derlenir: yoğun dağılımda
  doğrudan indeksleyen bir çizelge, seyrek dağılımda etiketler arasında arayan bir komut.
  Üçünde de komut sayısı **12**'dir; değişen şey hangi komut olduğudur.
- Yoğun komutun çizelgesi en küçük etiketten en büyüğüne uzanır: dört etiketli bir seçim
  **1'den 10'a** kadar yer tutabilir. Karar bir takastır, dosya boyutuna karşılık arama.
- Aynı eşleme `if` zinciriyle yazıldığında hiç seçim komutu bulunmaz: **22** komut ve
  **4** dal, ve son etiket için hepsi denenir.
- Gelişmiş `for` tek bir yazım değildir: dizide **24** komut ve **0** eklenen adım,
  listede **20** komut ve **5** eklenen adım üretir; liste biçimi elle yazılmış yineleyici
  döngüsüyle birebir aynıdır.
- Etiketli atlama düz `break`'e karşı hem bir adım hem de **doğru satırı** kazandırır;
  bayraklı biçime karşı kazancı **altı komut**, çalışma zamanı adımı olarak **sıfırdır**
  ve bedeli okuyanın etiketi yukarıda aramasıdır.

## Sonraki Adım

Bu ders akış biçimlerinin hangi komuta derlendiğini ölçtü: aynı seçim iki ayrı arama
komutuna, aynı gelişmiş `for` döngüsü dizide ve listede ayrı adıma dönüştü. Ölçümlerin
hepsi bir şeyin **üzerinde** gezindi ve o şey hiç sorgulanmadı. Geriye üzerinde gezinilen
şeyin kendisi kaldı. Sıradaki ders diziyi ölçer: `new int[3]` satırı sınıf dosyasında kaç
komut bırakıyor, `d.length` yazımı bir alan okuması mı yoksa kendi başına bir komut mu, ve
bir dizinin sınırını denetleyen adım kaynağın neresinde duruyor.
