← Kursa Dön
📄 Text · 15 min

Primitive (İlkel) Veri Tipleri

Java'da her şeyin bir tipi var. Bir değişken tanımlarken "bu değişken ne tür bir veri tutacak?" sorusuna cevap veriyorsun. İşte primitive tipler, Java'nın en temel yapı taşları — bellekte doğrudan değer saklayan, nesne olmayan tipler.

Bunu şöyle düşün: Bir depo kiralıyorsun. Küçük bir kutu mu istiyorsun, kocaman bir konteyner mı? Ne kadar yer ayırırsan o kadar ödüyorsun. Primitive tipler de tam olarak bu — her biri bellekte farklı büyüklükte yer kaplıyor.


Neden Primitive Tipler Var?

Java nesne yönelimli bir dil ama her şeyi nesne yapmak performans açısından maliyetli. Bir int değer bellekte sadece 4 byte yer kaplarken, onun nesne karşılığı Integer çok daha fazla yer kaplar ve ek yük getirir.

Bu yüzden Java, sık kullanılan temel veri türleri için "primitive" dediğimiz hafif tipler sunar. Toplam 8 tane var ve hepsini bilmen gerekiyor.


8 Primitive Tip — Büyük Tablo

Hepsini bir arada görelim, sonra tek tek inceleyeceğiz:

TipBoyutVarsayılanMin DeğerMax DeğerKullanım
byte1 byte0-128127Küçük sayılar, dosya verileri
short2 byte0-32,76832,767Orta küçük sayılar
int4 byte0-2,147,483,6482,147,483,647Genel amaçlı tam sayı
long8 byte0L-9,223,372,036,854,775,8089,223,372,036,854,775,807Çok büyük tam sayılar
float4 byte0.0f≈ ±1.4 × 10⁻⁴⁵≈ ±3.4 × 10³⁸Ondalıklı (düşük hassasiyet)
double8 byte0.0d≈ ±4.9 × 10⁻³²⁴≈ ±1.8 × 10³⁰⁸Ondalıklı (yüksek hassasiyet)
char2 byte'\u0000'065,535Tek karakter (Unicode)
boolean~1 bitfalsefalsetrueDoğru/yanlış

Tam Sayı Tipleri: byte, short, int, long

byte — Küçük Ama Öz

1 byte = 8 bit. -128 ile 127 arası değer tutar. Genellikle dosya okuma/yazma işlemlerinde veya ağ programlamada karşılaşırsın.

byte yas = 25;
byte sicaklik = -10;
byte maxByte = 127;
// byte hata = 128; // HATA! byte max 127

Günlük programlamada çok sık kullanmazsın ama dosya işlemlerinde (InputStream, OutputStream) sürekli karşına çıkar.

short — Nadiren Kullanılan Orta Boy

2 byte yer kaplar. -32,768 ile 32,767 arası. Dürüst olalım: pratikte neredeyse hiç kullanmayız. int zaten her yerde iş görüyor.

short population = 30000;
short altitude = -500;

Eğer bellekten çok tasarruf etmen gereken büyük diziler varsa ve değerlerin bu aralıkta olduğunu biliyorsan short mantıklı olabilir. Ama çoğu zaman int kullan, hayatını kolaylaştır.

int — Günlük Ekmek

4 byte. Yaklaşık ±2.1 milyar aralık. Java'da tam sayı deyince akla ilk gelen tip bu. Sayaçlar, indeksler, döngüler — hep int.

int nufus = 85000000;
int bakiye = -15000;
int max = Integer.MAX_VALUE; // 2,147,483,647
int min = Integer.MIN_VALUE; // -2,147,483,648

System.out.println("Max int: " + max);
System.out.println("Min int: " + min);

💡 Java'da bir sayıyı yazarken (literal) varsayılan tip int'tir. Yani 42 yazdığında Java bunu int olarak algılar.

long — Büyük İşler İçin

8 byte. Devasa sayılar için. Zaman damgaları (timestamp), dosya boyutları, büyük ID'ler — bunlar long ister.

long dunyaNufusu = 8000000000L; // Sonuna L koy!
long timestamp = System.currentTimeMillis();
long dosyaBoyutu = 5368709120L; // 5 GB in bytes

System.out.println("Şu anki zaman: " + timestamp);

⚠️ Dikkat: long literal yazarken sonuna L veya l eklemelisin. Küçük l sayı 1 ile karışabilir, bu yüzden büyük L kullan.

// long hata = 8000000000;  // HATA! int aralığını aşıyor
long dogru = 8000000000L;    // Sonuna L koyunca long olur

Ondalıklı Sayı Tipleri: float, double

float — Yeterli Hassasiyet

4 byte, yaklaşık 6-7 basamak hassasiyet. Grafik programlama, oyun geliştirme gibi alanlarda bellek önemliyse kullanılır.

float pi = 3.14f;          // Sonuna f koy!
float sicaklik = 36.6f;
float oran = 0.75f;

System.out.println("Pi: " + pi);

⚠️ Dikkat: Java'da ondalıklı sayılar varsayılan olarak double'dır. float kullanmak istiyorsan sonuna f eklemelisin.

double — Varsayılan Ondalıklı

8 byte, yaklaşık 15-16 basamak hassasiyet. Ondalıklı sayı gerektiğinde çoğu zaman double kullanırsın.

double pi = 3.141592653589793;
double maasBrut = 45750.50;
double avogadro = 6.022e23; // Bilimsel notasyon

System.out.println("Pi detaylı: " + pi);
System.out.println("Avogadro: " + avogadro);

float vs double — Hangisini Seçeyim?

Kısa cevap: double kullan. Daha hassas, modern donanımda performans farkı yok denecek kadar az.

float f = 0.1f + 0.2f;
double d = 0.1 + 0.2;

System.out.println("float:  " + f);  // 0.3
System.out.println("double: " + d);  // 0.30000000000000004

İkisi de tam doğru değil — bu ondalıklı sayıların doğasından kaynaklanan bir durum (IEEE 754). Ama double daha hassas olduğu için genellikle daha iyi sonuç verir.

💡 Para hesaplamalarında ne float ne double kullan! Kuruş kaybedersin. BigDecimal sınıfını kullan — buna ileride değineceğiz.


Karakter Tipi: char

2 byte. Tek bir Unicode karakteri tutar. Tek tırnak (') ile yazılır.

char harf = 'A';
char rakam = '7';
char turkce = 'Ş';
char emoji = '♥';
char unicode = '\u0041'; // 'A' nin Unicode karşılığı

System.out.println(harf);     // A
System.out.println(unicode);  // A

char aslında sayısal bir tiptir — 0 ile 65,535 arası bir tam sayı tutar. Bu sayı, Unicode tablosundaki karakterin numarasıdır.

char c = 'A';
int sayisal = c;
System.out.println(sayisal); // 65

char d = 66;
System.out.println(d); // B

Dikkat: char ile String farklı şeyler. char tek karakter, String karakter dizisi. char primitive, String nesne.

char c = 'A';       // Tek tırnak — char
String s = "A";     // Çift tırnak — String
// Bunlar aynı şey değil!

Mantıksal Tip: boolean

Sadece true veya false değeri alır. Koşullarda, kontrollerde, bayrak (flag) olarak kullanılır.

boolean aktif = true;
boolean ogrenci = false;
boolean yetiskin = (yas >= 18);

if (aktif) {
    System.out.println("Kullanıcı aktif");
}

Bellekte kaç byte kapladığı JVM implementasyonuna bağlı. Spesifikasyon "1 bit bilgi" diyor ama pratikte genellikle 1 byte kullanılır.

boolean sonuc = (10 > 5);      // true
boolean esit = (3 == 4);       // false
boolean degil = !true;         // false

System.out.println(sonuc);     // true
System.out.println(esit);      // false
System.out.println(degil);     // false

Varsayılan Değerler

Bir sınıfın alanı (field) olarak tanımlanan primitive değişkenlere otomatik varsayılan değer atanır:

public class VarsayilanDegerler {
    byte b;      // 0
    short s;     // 0
    int i;       // 0
    long l;      // 0L
    float f;     // 0.0f
    double d;    // 0.0d
    char c;      // '\u0000' (null karakter)
    boolean bo;  // false

    void yazdir() {
        System.out.println("byte: " + b);
        System.out.println("int: " + i);
        System.out.println("boolean: " + bo);
        System.out.println("char: [" + c + "]");
    }
}

⚠️ Ama lokal değişkenlere varsayılan değer atanmaz! Metot içinde tanımladığın bir değişkeni kullanmadan önce mutlaka değer atamalısın, yoksa derleme hatası alırsın.

void metot() {
    int x;
    // System.out.println(x); // DERLEME HATASI!
    
    int y = 0; // Bu doğru
    System.out.println(y); // OK
}

Ne Zaman Hangisini Kullanmalı?

İşte pratik bir rehber:

SenaryoKullan
Genel amaçlı tam sayıint
Döngü sayacıint
Dizi indeksiint
Zaman damgası, büyük IDlong
Dosya boyutulong
Ondalıklı sayı (genel)double
Koşul, bayrakboolean
Tek karakterchar
Dosya/ağ byte verileribyte
Bellek hassas, büyük dizilershort veya byte
Oyun/grafik, bellek kritikfloat

Şüpheye düştüğünde: tam sayı için int, ondalık için double, mantıksal için boolean. Bu üçlü seni çoğu durumda kurtarır.


Literal Gösterimler

Java'da sayıları farklı formatlarda yazabilirsin:

// Tam sayı literalleri
int onluk = 42;
int ikilik = 0b101010;      // Binary (0b prefix)
int sekizlik = 052;          // Octal (0 prefix)
int onaltilik = 0x2A;        // Hex (0x prefix)

System.out.println(onluk);     // 42
System.out.println(ikilik);    // 42
System.out.println(sekizlik);  // 42
System.out.println(onaltilik); // 42

Java 7'den itibaren okunabilirlik için alt çizgi kullanabilirsin:

int milyon = 1_000_000;
long krediKartiNo = 1234_5678_9012_3456L;
double pi = 3.14_15_92;

System.out.println(milyon);  // 1000000

Alt çizgiler derleme sırasında yok sayılır, sadece senin okumanı kolaylaştırır. Büyük sayılarda çok işe yarar.

Literal Kısıtlamaları

Alt çizginin nereye koyabileceğin konusunda kurallar var:

// Geçerli
int a = 1_000_000;
int b = 0xFF_EC_DE;
int c = 0b1010_1010;

// Geçersiz — derleme hatası
// int d = _1000;     // Başında
// int e = 1000_;     // Sonunda
// float f = 3._14f;  // Noktanın yanında
// int g = 0_x1A;     // Prefix'in içinde
// long h = 123_L;    // L suffix'in yanında

Bellekte Nasıl Saklanıyor?

Primitive tipler stack bellek alanında saklanır. Bu onları çok hızlı yapar.

int a = 5;
int b = a;  // a'nın DEĞERİ kopyalanır
b = 10;

System.out.println(a); // 5 — a değişmedi!
System.out.println(b); // 10

Primitive'lerde bir değişkeni diğerine atadığında değer kopyalanır. İki değişken birbirinden bağımsızdır. Bu, nesnelerden (referans tipleri) farklıdır — nesnelerde adres kopyalanır, bu yüzden biri değişince diğeri de etkilenebilir. Bu farkı ileride detaylı göreceğiz.


Pratik Örnek: Basit Hesap Makinesi Değişkenleri

public class HesapMakinesi {
    public static void main(String[] args) {
        int sayi1 = 100;
        int sayi2 = 45;
        double sonuc;

        sonuc = sayi1 + sayi2;
        System.out.println("Toplam: " + sonuc);  // 145.0

        sonuc = (double) sayi1 / sayi2;
        System.out.println("Bölüm: " + sonuc);   // 2.2222...

        boolean pozitif = (sonuc > 0);
        System.out.println("Pozitif mi? " + pozitif); // true

        char islem = '+';
        System.out.println("İşlem: " + islem);   // +
    }
}

Bu küçük örnekte int, double, boolean ve char — dört farklı primitive tipi bir arada kullandık. Gerçek projelerde de durum çoğunlukla böyle: birden fazla tip bir arada çalışır.


Primitive Tipler vs Referans Tipler

Java'da iki kategori veri tipi var: primitive ve referans. Bu farkı anlamak çok önemli çünkü davranışları tamamen farklı.

ÖzellikPrimitiveReferans (Nesne)
BellekteStack'te değerStack'te adres, heap'te nesne
Varsayılan0, false, '\u0000'null
null olabilir mi?HayırEvet
Metot çağrılabilir mi?HayırEvet
== ne yapar?Değer karşılaştırırAdres karşılaştırır
// Primitive — değer kopyalanır
int x = 10;
int y = x;
y = 20;
System.out.println(x); // 10 — x değişmedi

// Referans — adres kopyalanır
int[] dizi1 = {1, 2, 3};
int[] dizi2 = dizi1;     // Aynı diziyi gösteriyor!
dizi2[0] = 99;
System.out.println(dizi1[0]); // 99 — dizi1 de değişti!

Bu fark özellikle metotlara parametre geçerken çok önemli hale gelir. Primitive geçtiğinde kopya gider — metot orijinali değiştiremez. Referans geçtiğinde adres gider — metot nesneyi değiştirebilir.

public static void artir(int sayi) {
    sayi++; // Kopya üzerinde çalışır
}

public static void main(String[] args) {
    int a = 5;
    artir(a);
    System.out.println(a); // 5 — değişmedi!
}

İkiye Tümleyen (Two's Complement)

Java'da negatif tam sayılar ikiye tümleyen (two's complement) yöntemiyle saklanır. Bu konuyu derinlemesine bilmek gerekmez ama temel mantığını anlamak bazı tuzaklardan kurtarır.

Bir byte için:

  • Pozitif sayılar normal ikili: 5 = 00000101

  • Negatif sayılar: tüm bitleri tersle, 1 ekle

- 5 = 00000101 → tersle: 11111010 → +1: 11111011 = -5

  • En soldaki bit (MSB) işaret biti: 0 = pozitif, 1 = negatif

byte b = 127;  // 01111111
b++;           // 10000000 = -128 (taşma!)
System.out.println(b); // -128

// Bu yüzden byte aralığı -128 ile 127
// 0 pozitif tarafta sayılır: 0..127 = 128 değer, -128..-1 = 128 değer

Bu, overflow'un neden "döndüğünü" açıklar. Max değerden bir fazlası, en küçük negatif sayıya denk gelir.


Tip Promosyonu (Type Promotion)

Farklı tipler bir arada kullanıldığında Java otomatik olarak küçük tipi büyüğe çevirir. Buna tip promosyonu denir.

byte a = 10;
byte b = 20;
// byte c = a + b; // HATA! a + b sonucu int'tir
int c = a + b;     // Doğru

short s = 100;
int i = 200;
long l = s + i;    // short + int = int, int → long

float f = 3.14f;
double d = f + 1;  // float + int = float, float → double

Promosyon kuralları:

  1. byte, short, char → aritmetik işlemde int'e yükseltilir

  2. Operandlardan biri long ise diğeri de long olur

  3. Operandlardan biri float ise diğeri de float olur

  4. Operandlardan biri double ise diğeri de double olur

char c = 'A';   // 65
int sonuc = c + 1; // 66 — char + int = int
System.out.println(sonuc);       // 66
System.out.println((char) sonuc); // B

Bu konu Type Casting dersinde çok daha detaylı işlenecek.


Wrapper Sınıflarla Karşılaştırma

Her primitive tipin bir nesne karşılığı (wrapper class) var. İleride detaylı göreceğiz ama temel farkı şimdi bilelim:

int primitiveInt = 42;           // Primitive — stack'te
Integer wrapperInt = 42;         // Nesne — heap'te (autoboxing)

// Primitive null olamaz
// int x = null; // DERLEME HATASI!

// Wrapper null olabilir
Integer y = null; // OK
// int z = y;     // NullPointerException! (unboxing)
PrimitiveWrapper
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

Koleksiyonlar (List, Map vb.) primitive alamaz, wrapper gerekir:

List<Integer> sayilar = new ArrayList<>(); // int değil, Integer
sayilar.add(42); // Autoboxing: int → Integer

Gerçek Proje Senaryoları

Senaryo 1: Veritabanı ID'leri

// Veritabanı ID'leri genellikle long
long kullaniciId = 1234567890L;
long siparisId = 9876543210L;

// Neden int değil? Çünkü popüler uygulamalarda
// int'in 2.1 milyar sınırı kolayca aşılabilir
// Twitter'ın tweet ID'leri long — trilyonlara ulaştı

Senaryo 2: Dosya İşlemleri

// Dosya boyutunu byte olarak okuma
byte[] buffer = new byte[1024]; // 1KB buffer
// InputStream.read() byte dizisi kullanır

// Dosya boyutu long
long boyut = new java.io.File("video.mp4").length();
System.out.println("Dosya: " + (boyut / 1024 / 1024) + " MB");

Senaryo 3: Oyun Geliştirme

// Oyunda konum — float yeterli, bellek tasarrufu
float oyuncuX = 150.5f;
float oyuncuY = 320.8f;
boolean zipliyor = false;
int can = 3;
char seviye = 'A'; // Seviye göstergesi

// Her frame'de güncelleme
oyuncuX += 2.5f;
if (zipliyor) {
    oyuncuY -= 5.0f;
}

Senaryo 4: Sensör Verileri

// IoT cihazından gelen sıcaklık — double hassasiyet
double sicaklik = 23.456789;
double nem = 65.2;
boolean alarmAktif = sicaklik > 40.0;

// Sensör ID — short yeterli (max 32767 sensör)
short sensorId = 1042;
byte pil = 87; // Pil yüzdesi (0-100)

Min/Max Değerleri Programla Öğrenme

Her primitive tipin sınırlarını Java'nın kendisinden öğrenebilirsin:

System.out.println("byte  : " + Byte.MIN_VALUE + " ~ " + Byte.MAX_VALUE);
System.out.println("short : " + Short.MIN_VALUE + " ~ " + Short.MAX_VALUE);
System.out.println("int   : " + Integer.MIN_VALUE + " ~ " + Integer.MAX_VALUE);
System.out.println("long  : " + Long.MIN_VALUE + " ~ " + Long.MAX_VALUE);
System.out.println("float : " + Float.MIN_VALUE + " ~ " + Float.MAX_VALUE);
System.out.println("double: " + Double.MIN_VALUE + " ~ " + Double.MAX_VALUE);

// Boyutları byte cinsinden
System.out.println("int boyutu: " + Integer.BYTES + " byte");
System.out.println("long boyutu: " + Long.BYTES + " byte");
System.out.println("double boyutu: " + Double.BYTES + " byte");

// Özel double değerler
System.out.println("Pozitif sonsuz: " + Double.POSITIVE_INFINITY);
System.out.println("Negatif sonsuz: " + Double.NEGATIVE_INFINITY);
System.out.println("Sayı değil: " + Double.NaN);

Sık Yapılan Hatalar

1. Aralık dışına çıkmak:

byte b = 130; // HATA! byte max 127

2. Long literal'de L unutmak:

long x = 3000000000; // HATA! int aralığını aşıyor
long y = 3000000000L; // Doğru

3. Float literal'de f unutmak:

float f = 3.14; // HATA! 3.14 double, float'a sığmaz
float g = 3.14f; // Doğru

4. Lokal değişkeni initialize etmeden kullanmak:

int x;
System.out.println(x); // DERLEME HATASI!

5. boolean'a 0 veya 1 atamak:

boolean b = 1; // HATA! Java'da boolean sadece true/false
boolean c = true; // Doğru

C/C++ alışkanlığı olan arkadaşlar bu hatayı sık yapar. Java'da 0 false değildir, 1 true değildir. boolean sadece true ve false kabul eder, nokta.

6. byte ve short ile aritmetik yaparken tip promosyonunu unutmak:

byte a = 10;
byte b = 20;
byte c = a + b; // HATA! Sonuç int'tir
byte d = (byte)(a + b); // Doğru, explicit cast

7. Ondalıklı sayıları == ile karşılaştırmak:

double a = 0.1 + 0.2;
System.out.println(a == 0.3); // false! IEEE 754 hassasiyet sorunu

Performans İpuçları

Primitive tipler bellek ve performans açısından wrapper'lardan çok daha verimli:

// Bellek karşılaştırması (yaklaşık)
// int      → 4 byte
// Integer  → 16 byte (nesne overhead) + 4 byte (değer) = ~20 byte

// 1 milyon int dizisi
int[] primitiveArray = new int[1_000_000];
// Bellek: ~4 MB

// 1 milyon Integer dizisi
Integer[] wrapperArray = new Integer[1_000_000];
// Bellek: ~20 MB (5 kat fazla!)
// Performans farkı
long baslangic = System.nanoTime();

// Primitive ile toplama
long toplam1 = 0L;
for (int i = 0; i < 10_000_000; i++) {
    toplam1 += i;
}
long sure1 = System.nanoTime() - baslangic;

baslangic = System.nanoTime();

// Wrapper ile toplama (autoboxing/unboxing)
Long toplam2 = 0L;
for (int i = 0; i < 10_000_000; i++) {
    toplam2 += i; // Her adımda unbox → topla → autobox
}
long sure2 = System.nanoTime() - baslangic;

System.out.printf("Primitive: %d ms%n", sure1 / 1_000_000);
System.out.printf("Wrapper  : %d ms%n", sure2 / 1_000_000);
// Wrapper tipik olarak 5-10x daha yavaş

Mülakat Soruları — Bunları Bilmeni Beklerler

Teknik mülakata girdiğinde primitive tiplerle ilgili şu sorular sıkça sorulur:

S: Java'da kaç primitive tip var? C: 8 — byte, short, int, long, float, double, char, boolean.

S: String primitive midir? C: Hayır, String bir sınıftır (referans tipi). Ama Java onu özel olarak destekler (literal kullanımı, + operatörü vb.).

S: char kaç byte? C: 2 byte. Java UTF-16 kullandığı için 1 byte değil.

S: boolean kaç byte? C: JVM spesifikasyonu bunu tanımlamaz. Pratikte genellikle 1 byte ama implementasyona bağlı.

S: int ile Integer farkı nedir? C: int primitive, stack'te saklanır, null olamaz. Integer nesne (wrapper), heap'te saklanır, null olabilir, koleksiyonlarda kullanılır.


Özet

  • Java'da 8 primitive tip vardır: byte, short, int, long, float, double, char, boolean

  • Tam sayılar için çoğunlukla `int` yeterlidir; büyük sayılar için `long` kullan

  • Ondalıklı sayılar için `double` varsayılan ve en güvenli seçimdir

  • long literal sonuna `L`, float literal sonuna `f` eklemeyi unutma

  • Sınıf alanları varsayılan değer alır ama lokal değişkenler almaz — mutlaka initialize et

  • Primitive tipler stack'te saklanır ve atamada değer kopyalanır (referans değil)