---
title: 'İlkel Tipler ve Sarmalayıcılar'
source: 'https://academia.sh/tr/kurslar/java-temelleri/ilkel-tipler-ve-sarmalayicilar'
course: 'Java Temelleri'
language: tr
updated: '2026-08-17T18:09:40+00:00'
license: 'CC BY-SA 4.0'
---

# İlkel Tipler ve Sarmalayıcılar

Bir harflik kaynak farkı sınıf dosyasında bir yöntem çağrısıdır: `Integer adet = 5` satırı `Integer.valueOf`, ters yönü `Integer.intValue` ekler; aynı 200 kutulama depoya göre 100 ile 200 arasında nesne üretir ve önbelleğin bittiği yerde kimlik ile değer ayrılır.

Önceki ders ad çözümlemesinin derleme zamanında bittiğini ölçtü: içe aktarım satırı sınıf
dosyasında hiçbir iz bırakmıyordu, çünkü derleyici adı çözüp yerine tam nitelikli karşılığını
koyuyordu. Geriye bir soru kaldı. Ad çözümlemesi derleme zamanında bitiyorsa, adın taşıdığı
**değerin kendisi** ne zaman ve nerede belirleniyor.

Java bu soruya tek yanıt vermez. `int adet = 5` yazıldığında değer yöntemin kendi yerel
alanında, sayının kendisi olarak durur. `Integer adet = 5` yazıldığında öbekte bir nesne
bulunur ve yerel alanda yalnız o nesnenin referansı durur. Kaynaktaki fark tek harftir;
sınıf dosyasındaki fark bir **yöntem çağrısıdır**. Programlama Temelleri kursu temel veri
tiplerini ve örtük dönüşümü kavram olarak kurmuştu — **bu tekrarlanmaz**. Orada bir değerin
tipinin ne belirlediği anlatıldı; burada ölçülen şey, derleyicinin bu geçiş için sınıf
dosyasına **kaç adım** koyduğudur.

## İki Tip Ailesi

Java'nın tipleri ikiye ayrılır. **İlkel tipler** sekiz tanedir ve sayıdan başka bir şey
taşımazlar; **referans tipleri** ise nesnelerin adresini taşır. Her ilkel tipin bir
**sarmalayıcı** sınıfı vardır: aynı değeri bir nesnenin içinde tutan referans tipi.

| İlkel tip | Genişlik (bit) | Sarmalayıcı |
|---|---|---|
| `boolean` | belirtilmemiş | `Boolean` |
| `byte` | 8 | `Byte` |
| `short` | 16 | `Short` |
| `char` | 16 | `Character` |
| `int` | 32 | `Integer` |
| `long` | 64 | `Long` |
| `float` | 32 | `Float` |
| `double` | 64 | `Double` |

Ayrımın kaynaktaki görünürlüğü düşüktür, çünkü iki aile arasındaki geçiş yazılmaz. Bir
`int` değeri bir `Integer` bekleyen yere kendiliğinden girer (**otomatik kutulama**), bir
`Integer` de `int` bekleyen yere kendiliğinden girer (**kutudan çıkarma**). İki yönde de
kaynakta yalnız bir eşittir işareti görünür.

Ayrımın var olma nedeni de buradadır. Bir ilkel tip nesne değildir: yöntemi yoktur, `null`
olamaz ve **referans bekleyen hiçbir yere giremez**. Bir liste, bir eşleme ya da tip
değişkeni alan herhangi bir yapı yalnız referans saklar; bir `int` değerini listeye koymak
için önce onu saracak bir nesne gerekir. Sarmalayıcı bu boşluğu doldurur ve otomatik
kutulama, o dolduruşu kaynaktan **silen** bir kısaltmadır. Sonuç şudur: `List<Integer>`
yazan bir kaynak, `int` yazan bir kaynağın yaptığı işi yapmaz — arada bir nesne katmanı
vardır ve o katmanın bedeli sınıf dosyasında durur.

Kurs boyunca tek bir veri taşıyıcı kullanılır: bir depo kaydı olan `Kayit`, üç alanlı —
`String ad`, `int adet`, `long agirlik` (gram). Bu ders kutulamayı **`adet` alanı**
üzerinde ölçer.

## Ölçüm Çekirdeği

Ölçüm, verilen bir kaynak metnini derleyiciyi program içinden çağırarak derler, ürettiği
sınıf dosyasını okur ve her yöntem için iki şey döndürür: kaç komut üretildiği ve o
yöntemde hangi çağrıların bulunduğu. Derleyici ve sınıf dosyası okuyucusu standart
kitaplığın parçasıdır.

- **SD1** — Kâhin kurgunun kendisidir: ölçülen kaynağı biz yazdığımız için ne yazdığımızı
  biliriz. Sağ sütun sınıf dosyasındaki **bütün** çağrıları listeler; kaynakta yazılmış
  olanı okuyucu düşer, kalan **derleyicinin eklediğidir**.
- **SD2** — Ölçülen kaynak metni sabittir ve her koşumda aynı derleyiciye verilir; sayılar
  tek bir derleyici koşumundan gelir ve o koşumda belirlenimcidir.
- **SD3** — Yapıcı (`<init>`) tabloya alınmaz; kaynakta yazılmayan üyeler ayrı bir konudur.

```java
// Olcek.java — verilen kaynağı derler, sınıf dosyasındaki komutları ve çağrıları 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, 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<>();
            int komut = 0;
            for (CodeElement e : m.code().get()) {
                if (e instanceof Instruction) komut++;
                if (e instanceof InvokeInstruction iv)
                    eklenen.add(kisa(iv.owner().asInternalName()) + "." + iv.name().stringValue());
                else if (e instanceof TypeCheckInstruction t && t.opcode() == Opcode.CHECKCAST)
                    eklenen.add("tip denetimi " + kisa(t.type().asInternalName()));
            }
            yontemler.add(new Yontem(m.methodName().stringValue(), komut, eklenen));
        }
        return yontemler;
    }

    static String kisa(String ic) { return ic.substring(ic.lastIndexOf('/') + 1); }
}
```

## Atamanın Eklediği Çağrı

Ölçülen kaynak beş yöntem taşır. `kutula` bir `int` değerini `Integer` adına bağlar,
`kutuAc` tersini yapar. `diziToplami` ile `listeToplami` **aynı döngüyü** yazar; tek fark
gezilen şeyin `int[]` mi yoksa `List<Integer>` mi olduğudur. `say` bir sayacı `adet`
değeriyle günceller.

```java
// Kutulama.java — ilkel tip ile sarmalayıcı arasındaki geçişin sınıf dosyasındaki izi
public class Kutulama {
    static final String KAYNAK = """
        import java.util.List;
        import java.util.Map;
        class Depo {
            static Object kutula(int adet) { Integer k = adet; return k; }
            static int kutuAc(Integer k) { int adet = k; return adet; }
            static int diziToplami(int[] adetler) { int t = 0; for (int x : adetler) t += x; return t; }
            static int listeToplami(List<Integer> adetler) { int t = 0; for (int x : adetler) t += x; return t; }
            static void say(Map<String, Integer> sayac, String ad, int adet) { sayac.put(ad, adet); }
        }
        """;

    public static void main(String[] args) throws Exception {
        System.out.printf("%-14s %6s  %s%n", "yontem", "komut", "sinif dosyasindaki cagri");
        int toplam = 0;
        for (Olcek.Yontem y : Olcek.oku(KAYNAK, "Depo")) {
            if (y.ad().equals("<init>")) continue;
            System.out.printf("%-14s %6d  %s%n", y.ad(), y.komut(),
                    y.eklenen().isEmpty() ? "-" : String.join(", ", y.eklenen()));
            toplam += y.eklenen().size();
        }
        System.out.println("toplam: " + toplam);
    }
}
```

```
yontem          komut  sinif dosyasindaki cagri
kutula              5  Integer.valueOf
kutuAc              5  Integer.intValue
diziToplami        24  -
listeToplami       20  List.iterator, Iterator.hasNext, Iterator.next, tip denetimi Integer, Integer.intValue
say                 7  Integer.valueOf, Map.put
toplam: 9
```

`kutula` kaynağında hiçbir çağrı yazmaz; sınıf dosyasında **`Integer.valueOf`** vardır.
`kutuAc` de hiçbir çağrı yazmaz; sınıf dosyasında **`Integer.intValue`** vardır. Otomatik
kutulama bir dil kolaylığı değil, kaynakta görünmeyen bir **yöntem çağrısıdır** ve tersi de
öyledir. `say` satırı SD1'in nasıl okunacağını gösterir: `Map.put` kaynakta yazılıdır ve
düşülür, `Integer.valueOf` yazılı değildir ve kalır.

Alt iki satır kursun ikinci iddiasını ödüyor. `diziToplami` **24** komut üretiyor ve
kaynakta yazılmayan **hiçbir** adım taşımıyor: gelişmiş `for` bir dizide sayaçlı bir
döngüye çevriliyor ve toplama doğrudan sayılar üzerinde yapılıyor. `listeToplami`
**20** komut üretiyor — daha az — ama kaynakta yazılmayan **beş** adım taşıyor: yineleyici
alınıyor, iki kez sorgulanıyor, dönen nesne `Integer`'a denetleniyor ve `intValue` ile
sayıya açılıyor. **Daha az komut, daha çok eklenen adım.** Komut sayısı ile eklenen adım
sayısı aynı yöne gitmez; hangisinin sayıldığı söylenmeden bir ölçüm karşılaştırılamaz.

Bu beş adımın dördü döngü gövdesinin içindedir, yani öğe başına ödenir. Sınıf dosyası bunu
göstermez; gösterdiği şey adımın **varlığıdır**. Kaç kez ödendiğini görmek için koşmak
gerekir.

## Aynı Sayıda Kutulama, İki Ayrı Nesne Sayısı

`Integer.valueOf` her çağrıldığında yeni bir nesne üretmez. Belirtim, `int` değerleri için
**-128 ile 127 arasındaki** her değerin kutulamasında **aynı nesnenin** döndürülmesini
zorunlu kılar; bu aralığın dışı gerçekleştirime bırakılmıştır. Bunun ölçülebilir sonucu
şudur: aynı sayıda kutulama, değerlerin nereye düştüğüne göre ayrı sayıda nesne üretir.

- **SD4** — Üç depo da **100** kayıt taşır ve her kaydın `adet` değeri ayrıdır; tek fark
  değerlerin başladığı tabandır. Her depoda her `adet` **iki kez** kutulanır, yani üç ölçüm
  de **200** kutulama çağrısı yapar.
- **SD5** — Ayrı nesne sayısı, üretilen sarmalayıcılar kimliklerine göre bir eşleme içinde
  toplanarak bulunur; hiçbir nesnenin kimlik değeri basılmaz, yalnız **kaç ayrı nesne**
  olduğu sayılır.

```java
// Uretim.java — aynı sayıda kutulama, iki ayrı nesne sayısı
import java.util.IdentityHashMap;
import java.util.Map;

public class Uretim {
    record Kayit(String ad, int adet, long agirlik) {}

    static Kayit[] depo(int n, int taban) {
        Kayit[] d = new Kayit[n];
        for (int i = 0; i < n; i++) d[i] = new Kayit("K" + i, taban + i, 1000L + i);
        return d;
    }

    static int ayriNesne(Kayit[] depo) {
        Map<Integer, Boolean> kimlik = new IdentityHashMap<>();
        for (int tur = 0; tur < 2; tur++)
            for (Kayit k : depo) kimlik.put(k.adet(), Boolean.TRUE);   // her put bir valueOf
        return kimlik.size();
    }

    public static void main(String[] args) {
        System.out.printf("%-22s %8s %8s %10s%n", "depo", "kutulama", "deger", "ayri nesne");
        for (int taban : new int[] {1, 100, 1000}) {
            Kayit[] d = depo(100, taban);
            System.out.printf("adet %4d..%-4d %8d %8d %10d%n",
                    taban, taban + 99, 2 * d.length, d.length, ayriNesne(d));
        }
        Kayit[] buyuk = depo(100, 1000);
        long t = 0;
        for (Kayit k : buyuk) t += k.adet();
        System.out.println("ilkel toplama: " + t + " | uretilen sarmalayici: 0");
    }
}
```

```
depo                   kutulama    deger ayri nesne
adet    1..100       200      100        100
adet  100..199       200      100        172
adet 1000..1099      200      100        200
ilkel toplama: 104950 | uretilen sarmalayici: 0
```

Üç satırda da aynı kod çalışıyor, aynı sayıda kutulama yapılıyor ve aynı sayıda ayrı değer
kutulanıyor. Üretilen nesne sayısı **100**, **172** ve **200**. Kaynakta bu farkı üreten
hiçbir şey yoktur; fark tümüyle eklenen adımın, yani `Integer.valueOf`'un kararıdır.

Orta satır sınırı doğrudan gösteriyor. `adet` değerleri 100 ile 199 arasında dağıldığında
128'in altındaki 28 değer önbellekten dönüyor ve her biri için **tek** nesne oluşuyor;
kalan 72 değer iki turda **iki ayrı** nesne üretiyor. Toplam 28 artı 144, yani **172**.
Sayının kendisi bir eşiği işaretliyor.

Son satır ölçünün diğer ucudur. Aynı depo `int` üzerinden toplandığında hiç sarmalayıcı
üretilmez: `t += k.adet()` satırında kutulama yoktur, çünkü `long` da `int` de ilkel
tiptir. Bir döngüde sarmalayıcı kullanmanın bedeli, o döngünün her adımında bir nesne
üretilmesi olabilir — ve bu, kaynakta hiçbir yerde yazmaz.

## Kimlik ile Değerin Ayrıldığı Yer

Nesne üretimindeki bu fark yalnız sayıda kalmaz; **görünen bir sonucu** vardır. İki
referans tipi `==` ile karşılaştırıldığında değerleri değil, **aynı nesne olup olmadıkları**
sınanır. Önbellek aralığında iki kutulama aynı nesneyi verdiği için `==` doğru sonuç verir
gibi görünür; aralığın dışında aynı kod yanlış sonuç verir.

```java
// Onbellek.java — sarmalayıcı kimliğinin ve değerinin ayrıldığı yer
public class Onbellek {
    record Kayit(String ad, int adet, long agirlik) {}

    static Kayit kimlikleAra(Kayit[] depo, Integer aranan) {
        for (Kayit k : depo) {
            Integer adet = k.adet();            // kutulama: Integer.valueOf
            if (adet == aranan) return k;       // iki sarmalayici: kimlik karsilastirmasi
        }
        return null;
    }

    static Kayit degerleAra(Kayit[] depo, Integer aranan) {
        for (Kayit k : depo) {
            Integer adet = k.adet();
            if (adet.equals(aranan)) return k;  // deger karsilastirmasi
        }
        return null;
    }

    public static void main(String[] args) {
        Kayit[] depo = { new Kayit("vida", 127, 40L), new Kayit("civata", 128, 90L) };
        System.out.printf("%-10s %-12s %-12s%n", "aranan", "kimlikle", "degerle");
        for (Integer aranan : new Integer[] {127, 128}) {
            Kayit a = kimlikleAra(depo, aranan), b = degerleAra(depo, aranan);
            System.out.printf("adet %-5d %-12s %-12s%n", aranan,
                    a == null ? "bulunamadi" : a.ad(), b == null ? "bulunamadi" : b.ad());
        }
        System.out.println();
        Integer a = 127, b = 127, c = 128, d = 128;
        System.out.println("Integer 127 == 127 : " + (a == b) + " | equals: " + a.equals(b));
        System.out.println("Integer 128 == 128 : " + (c == d) + " | equals: " + c.equals(d));
        Long e = 127L, f = 127L;
        Double g = 1.0, h = 1.0;
        System.out.println("Long    127 == 127 : " + (e == f) + " | equals: " + e.equals(f));
        System.out.println("Double  1.0 == 1.0 : " + (g == h) + " | equals: " + g.equals(h));
    }
}
```

```
aranan     kimlikle     degerle
adet 127   vida         vida
adet 128   bulunamadi   civata

Integer 127 == 127 : true | equals: true
Integer 128 == 128 : false | equals: true
Long    127 == 127 : true | equals: true
Double  1.0 == 1.0 : false | equals: true
```

`kimlikleAra` 127 adetli kaydı buluyor, 128 adetli kaydı **bulamıyor**. Kaynakta iki arama
arasında hiçbir fark yoktur; aranan değer bir arttığı için sonuç değişmiştir. Bu, kursun
dördüncü okumasının en açık örneğidir: **kararı programcı değil, eklenen adım vermiştir.**
Aynı kod küçük depolarda doğru, büyük depolarda yanlış çalışır ve derleyici uyarı üretmez.

Alt tablo sınırı çiziyor. `Integer` ve `Long` için 127 kimlik olarak eşit, 128 değil;
`Double` için 1.0 bile eşit değil, çünkü kesirli sarmalayıcılarda önbellek yoktur. `equals`
sütunu **dört satırda da doğru**: değer karşılaştırması kutulamanın hangi nesneyi
döndürdüğünden etkilenmez. Kural buradan çıkar — sarmalayıcılar `==` ile değil, `equals`
ile karşılaştırılır; `==` yalnız ilkel tiplerde değer karşılaştırmasıdır.

Aynı gözlem sınırlayıcı ölçümü de verir: bu dersin ölçtüğü eklenen adımın, kimlik üzerinde
hiçbir etkisinin **olmadığı** bir aralık vardır. -128 ile 127 arasında kutulama yeni nesne
üretmez, `==` beklendiği gibi davranır ve nesne sayısı değer sayısına eşittir. Eklenen
adımın görünürlüğü, değerin hangi aralığa düştüğüne bağlıdır.

## Özet

- İlkel tip ile sarmalayıcı arasındaki geçiş kaynakta bir eşittir işaretidir, sınıf
  dosyasında bir çağrıdır: kutulama **`Integer.valueOf`**, kutudan çıkarma
  **`Integer.intValue`** ekler.
- Aynı döngü dizide **24** komut ve **0** eklenen adım, `List<Integer>` üzerinde **20**
  komut ve **5** eklenen adım üretir; daha az komut, daha çok eklenen adım.
- `Integer.valueOf` her çağrıda yeni nesne üretmez: aynı 200 kutulama, değerlerin
  aralığına göre **100**, **172** ya da **200** ayrı nesne bırakır.
- Belirtim -128 ile 127 arasında aynı nesnenin döndürülmesini zorunlu kılar; bu aralıkta
  `==` doğru görünür, dışında aynı kod yanlış sonuç verir ve derleyici uyarı üretmez.
- Sarmalayıcılar `equals` ile karşılaştırılır; `equals` dört ölçümün dördünde de doğru
  sonuç verdi, `==` yalnız ilkel tiplerde değer karşılaştırmasıdır.

## Sonraki Adım

Bu ders bir değerin hangi biçimde tutulduğunu ölçtü: sayının kendisi mi, yoksa sayıyı
saran bir nesne mi. Geriye adın kendisi kaldı. Sıradaki ders aynı sayıda adı üç ayrı yere
yazar — bir yöntemin içine, bir örneğe ve sınıfın kendisine — ve sınıf dosyasında bu üç
adın nereye düştüğünü sayar: hangisi bir **yuvaya**, hangisi bir **alana** yazılıyor,
bir bloğun içinde açılan ad neden sınıf dosyasında hiç ad olarak görünmüyor ve iç blokta
aynı adı yeniden bildirmek neden derleme hatası veriyor.
