Değişken Kapsamı ve Sabitler
Bir değişken nerede tanımlandıysa, orada yaşar ve orada ölür. Buna kapsam (scope) diyoruz. Kapsam kurallarını bilmezsen "değişken bulunamadı" hataları alırsın ya da daha kötüsü, yanlış değişkeni kullanırsın farkında olmadan.
Bu derste değişkenlerin nerede yaşadığını, final ile sabitleri, isimlendirme kurallarını ve Java 10+ ile gelen var keyword'ünü öğreneceğiz.
Kapsam (Scope) Nedir?
Kapsam, bir değişkenin erişilebilir olduğu kod bölgesidir. Genel kural basit: bir değişken tanımlandığı süslü parantezler `{}` içinde yaşar.
Bunu şöyle düşün: Her süslü parantez çifti bir oda. Bir odada tanımlanan eşya sadece o odada kullanılabilir. İç odadan dış odayı görebilirsin ama dış odadan iç odayı göremezsin.
Lokal Değişkenler (Local Variables)
Bir metot veya blok içinde tanımlanan değişkenler:
public void hesapla() {
int x = 10; // x burada doğar
if (x > 5) {
int y = 20; // y burada doğar
System.out.println(x + y); // 30 — x erişilebilir
} // y burada ölür
// System.out.println(y); // HATA! y bu scope'ta yok
System.out.println(x); // OK — x hâlâ yaşıyor
} // x burada ölürBlok Kapsamı
Her {} yeni bir kapsam oluşturur:
public void ornekMetot() {
int a = 1;
{ // Anonim blok — yeni kapsam
int b = 2;
System.out.println(a + b); // 3 — dış değişkene erişir
}
// System.out.println(b); // HATA! b dış kapsamda yok
}Döngü Kapsamı
// i sadece for döngüsü içinde yaşar
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
// System.out.println(i); // HATA! i burada yok
// Her iterasyonda yeni değişken
for (int j = 0; j < 3; j++) {
int temp = j * 2; // Her iterasyonda yeni temp
System.out.println(temp);
} // temp her iterasyon sonunda ölür// while ile karşılaştır
int k = 0; // Döngü dışında tanımlanmalı
while (k < 5) {
System.out.println(k);
k++;
}
System.out.println(k); // 5 — k hâlâ erişilebilir💡 Değişkeni mümkün olan en dar kapsamda tanımla.
fordöngüsünde sayaç gerekiyorsa döngü içinde tanımla — dışarıda gereksiz yere yaşamasın. Bu hem okunabilirliği artırır hem hata riskini azaltır.
Sınıf Alanları (Instance Variables / Fields)
Sınıf içinde ama metot dışında tanımlanan değişkenler. Nesne yaşadığı sürece yaşarlar.
public class Ogrenci {
// Instance variables (alanlar)
String isim; // Tüm metotlardan erişilebilir
int yas;
double ortalama;
// Varsayılan değer alırlar
boolean aktif; // false
String adres; // null
public void bilgiYazdir() {
// Alanlar metotların hepsinden erişilebilir
System.out.println(isim + " - " + yas);
}
public void yasGuncelle(int yeniYas) {
yas = yeniYas; // Alan'a erişim
}
}Lokal vs Alan — İsim Çakışması
Lokal değişken, aynı isimli alanı gölgeler (shadow):
public class Ornek {
int x = 10; // Alan (field)
public void metot() {
int x = 20; // Lokal değişken — alanı gölgeler
System.out.println(x); // 20 (lokal)
System.out.println(this.x); // 10 (alan)
}
}this keyword'ü ile alana açıkça erişebilirsin. Bu özellikle constructor'larda çok yaygın:
public class Kullanici {
String isim;
int yas;
public Kullanici(String isim, int yas) {
this.isim = isim; // this.isim = alan, isim = parametre
this.yas = yas;
}
}Static Alanlar (Sınıf Değişkenleri)
static ile tanımlanan alanlar nesneye değil sınıfa aittir. Tüm nesneler arasında paylaşılır.
public class Sayac {
static int toplamNesne = 0; // Sınıf değişkeni
String isim; // Nesne değişkeni
public Sayac(String isim) {
this.isim = isim;
toplamNesne++; // Her nesne oluştuğunda artar
}
public static void main(String[] args) {
Sayac a = new Sayac("A");
Sayac b = new Sayac("B");
Sayac c = new Sayac("C");
System.out.println(Sayac.toplamNesne); // 3
}
}final Keyword — Sabitler
final bir değişkenin değerinin bir kez atanıp bir daha değiştirilememesini sağlar.
final int MAX_PUAN = 100;
// MAX_PUAN = 200; // DERLEME HATASI!
final double PI = 3.14159265358979;
final String VARSAYILAN_DIL = "tr";final Kullanım Yerleri
1. Sabit değerler (constant):
public class Ayarlar {
// static final → gerçek sabit
public static final int MAX_KULLANICI = 1000;
public static final double KDV_ORANI = 0.18;
public static final String API_URL = "https://api.example.com";
}
// Kullanım
double kdv = fiyat * Ayarlar.KDV_ORANI;static final ile tanımlanan sabitler büyük harfle ve alt çizgiyle yazılır. Bu bir konvansiyon.
2. Metot parametreleri:
public void hesapla(final int deger) {
// deger = 42; // HATA! final parametresi değiştirilemez
System.out.println(deger * 2);
}3. Lokal değişkenler:
public void islem() {
final int sonuc = karmasikHesaplama();
// sonuc artık değişmez — okuyucu bunu bilir
System.out.println(sonuc);
}final ve Nesneler — Dikkat!
final referansı sabitler, nesnenin içeriğini değil:
final List<String> liste = new ArrayList<>();
liste.add("Merhaba"); // OK! İçerik değişebilir
liste.add("Dünya"); // OK!
// liste = new ArrayList<>(); // HATA! Referans değişemezfinal int[] dizi = {1, 2, 3};
dizi[0] = 99; // OK! Dizi içeriği değişebilir
// dizi = new int[]{4, 5}; // HATA! Referans değişemez⚠️ final "bu değişken hep aynı nesneye işaret edecek" demektir, "bu nesne değişmeyecek" değil. Gerçek immutability için nesnenin kendisi immutable olmalı (String gibi).
İsimlendirme Kuralları (Naming Conventions)
Java'da yazılmamış ama herkesin uyduğu kurallar:
| Ne | Kural | Örnek |
|---|---|---|
| Değişken | camelCase | ogrenciSayisi, toplamFiyat |
| Sabit | SCREAMING_SNAKE_CASE | MAX_BOYUT, PI, KDV_ORANI |
| Metot | camelCase | hesaplaOrtalama(), getIsim() |
| Sınıf | PascalCase | OgrenciListesi, HttpClient |
| Paket | lowercase | com.sirket.proje |
// İyi isimlendirme
int ogrenciSayisi = 42;
double toplamFiyat = 199.99;
boolean aktifMi = true;
String kullaniciAdi = "ahmet";
final int MAX_DENEME = 3;
// Kötü isimlendirme
int x = 42; // Ne bu?
double tp = 199.99; // Kısaltma anlaşılmıyor
boolean flag = true; // Ne flag'ı?
int SAYI = 42; // Sabit değilse büyük harf kullanmaİsimlendirme İpuçları
// Boolean değişkenler soru gibi olsun
boolean ogrenciMi = true;
boolean aktif = false;
boolean girisYapildi = true;
// Kötü: boolean durum = true; (ne durumu?)
// Koleksiyonlar çoğul olsun
List<String> isimler = new ArrayList<>(); // tek: isim
Map<String, Integer> puanlar = new HashMap<>();
// Geçici değişkenler kısa olabilir (dar kapsam)
for (int i = 0; i < 10; i++) { ... } // OK — herkes i'yi bilir
for (String s : liste) { ... } // OK — kısa döngüvar Keyword (Java 10+)
Java 10 ile gelen var, lokal değişkenlerde tip çıkarımı (type inference) sağlar. Derleyici tipi sağ taraftan anlar.
// Geleneksel
String isim = "Ahmet";
int yas = 25;
List<String> liste = new ArrayList<>();
Map<String, List<Integer>> karmasik = new HashMap<>();
// var ile
var isim = "Ahmet"; // String olduğu belli
var yas = 25; // int olduğu belli
var liste = new ArrayList<String>();
var karmasik = new HashMap<String, List<Integer>>();var özellikle uzun generic tiplerde çok işe yarar:
// Eski
Map<String, List<Map<Integer, String>>> veri = new HashMap<String, List<Map<Integer, String>>>();
// var ile
var veri = new HashMap<String, List<Map<Integer, String>>>();
// Tip hâlâ tam olarak belirleniyor, sadece yazmıyorsunvar Kuralları
// ✅ Lokal değişkenlerde kullanılabilir
var x = 42;
var s = "merhaba";
var liste = List.of(1, 2, 3);
// ✅ Enhanced for ve normal for döngüsünde
for (var eleman : liste) {
System.out.println(eleman);
}
for (var i = 0; i < 10; i++) {
System.out.println(i);
}
// ✅ try-with-resources'ta
try (var stream = new FileInputStream("dosya.txt")) {
// ...
}// ❌ Sınıf alanlarında KULLANILMAZ
// var alan = 42; // Derleme hatası!
// ❌ Metot parametrelerinde KULLANILMAZ
// public void metot(var x) { } // Hata!
// ❌ Dönüş tipinde KULLANILMAZ
// public var getIsim() { } // Hata!
// ❌ null ile KULLANILMAZ (tip çıkarılamaz)
// var x = null; // Hata!
// ❌ İlk değer vermeden KULLANILMAZ
// var x; x = 42; // Hata!var Ne Zaman Kullanmalı?
// İYİ: Tip sağ taraftan açıkça belli
var isim = "Ahmet"; // String belli
var sayilar = new ArrayList<Integer>(); // Tip belli
var dosya = new File("test.txt"); // File belli
// TARTIŞMALI: Tip metot adından anlaşılıyor
var sonuc = hesaplaOrtalama(notlar); // double mı? int mi?
var veri = servis.getKullanici(id); // Kullanici mı? Optional mı?
// KÖTÜ: Tip hiç belli değil
var x = metot(); // Ne dönüyor? Okunmuyor.
var sonuc = isle(); // ???💡 Kural:
varkullan ama okunabilirliği bozmadan. Sağ taraftan tip anlaşılmıyorsa açık tip yaz.vardaha az yazmak için değil, daha okunabilir kod için kullanılmalı.
Değişken Tanımlama Best Practice'leri
1. Mümkün olan en dar kapsamda tanımla:
// Kötü — gereksiz geniş kapsam
int sonuc;
// ... 50 satır kod ...
sonuc = hesapla();
// ... 30 satır daha ...
System.out.println(sonuc);
// İyi — kullanıma yakın tanımla
// ... 50 satır kod ...
int sonuc = hesapla();
// hemen kullan
System.out.println(sonuc);2. Değişmeyecekse final yap:
final int port = config.getPort();
final String url = config.getUrl();
// Kodun geri kalanında bunların değişmeyeceğini biliyorsun3. Bir değişkeni birden fazla amaç için kullanma:
// Kötü
int temp = kullanici.getYas();
// ... bir şeyler yap ...
temp = urun.getFiyat(); // Aynı değişken, farklı amaç!
// İyi
int yas = kullanici.getYas();
// ... bir şeyler yap ...
double fiyat = urun.getFiyat(); // Farklı değişken4. Anlamlı isim ver:
// Kötü
int d; // gün mü? mesafe mi? fark mı?
// İyi
int gunSayisi;
int mesafeKm;
int farkDakika;5. Magic number kullanma — sabit tanımla:
// Kötü — 86400 ne demek?
if (gecenSure > 86400) {
System.out.println("Süre aşıldı");
}
// İyi — anlaşılır
final int BIR_GUN_SANIYE = 86400;
if (gecenSure > BIR_GUN_SANIYE) {
System.out.println("Süre aşıldı");
}
// Daha iyi — açıklayıcı hesaplama
final int BIR_GUN_SANIYE = 24 * 60 * 60; // 86400Magic number, kodda açıklamasız kullanılan sayısal değerlerdir. Okuyucu ne anlama geldiğini bilemez. Sabit tanımlayarak kodun kendini açıklamasını sağla.
Record Sınıflar ve Kapsam (Java 16+)
Record sınıfları otomatik olarak final alanlar oluşturur:
// Record — tüm alanlar otomatik final ve private
record Koordinat(double x, double y) {
// x ve y otomatik final — değiştirilemez
// Constructor, getter, equals, hashCode, toString otomatik
}
Koordinat nokta = new Koordinat(3.5, 7.2);
System.out.println(nokta.x()); // 3.5
System.out.println(nokta.y()); // 7.2
System.out.println(nokta); // Koordinat[x=3.5, y=7.2]
// nokta.x = 5.0; // HATA! final alanRecord'lar immutable veri taşıyıcıları oluşturmanın en kısa yolu. Tüm alanlar final olduğu için thread-safe'dir.
Pratik Örnek: Kapsam ve Sabitler Bir Arada
public class SicaklikIstasyonu {
// Sabitler
static final double MUTLAK_SIFIR_CELSIUS = -273.15;
static final String BIRIM = "°C";
// Alan
private final String istasyonAdi;
private double sonOlcum;
public SicaklikIstasyonu(String istasyonAdi) {
this.istasyonAdi = istasyonAdi; // final alan, constructor'da atanır
}
public void olcumYap(double sicaklik) {
// Lokal değişken — sadece bu metotta yaşar
final boolean gecerli = sicaklik > MUTLAK_SIFIR_CELSIUS;
if (gecerli) {
this.sonOlcum = sicaklik;
// Blok kapsamı
var mesaj = String.format("%s: %.1f%s",
istasyonAdi, sicaklik, BIRIM);
System.out.println(mesaj);
} else {
System.out.println("Geçersiz ölçüm: " + sicaklik);
}
// mesaj burada erişilemez — if bloğu kapsamında kaldı
}
public static void main(String[] args) {
var istasyon = new SicaklikIstasyonu("Ankara");
istasyon.olcumYap(23.5); // Ankara: 23.5°C
istasyon.olcumYap(-300); // Geçersiz ölçüm: -300.0
}
}Metot Parametresi Kapsamı
Metot parametreleri de lokal değişken gibidir — sadece o metot içinde geçerlidir:
public void selamla(String isim, int tekrar) {
// isim ve tekrar sadece burada yaşar
for (int i = 0; i < tekrar; i++) {
System.out.println("Merhaba " + isim);
}
}
// isim ve tekrar burada erişilemezParametre ismi ile alan ismi aynı olabilir — bu durumda parametre alanı gölgeler:
public class Kullanici {
private String isim;
// Parametre "isim" alanı gölgeler
public void setIsim(String isim) {
// isim → parametre
// this.isim → alan
this.isim = isim;
}
}Switch İçinde Kapsam
switch bloğundaki kapsam kuralları dikkat ister:
// Eski stil switch — case'ler aynı kapsamı paylaşır!
switch (gun) {
case 1:
String mesaj = "Pazartesi"; // mesaj burada tanımlanır
System.out.println(mesaj);
break;
case 2:
// String mesaj = "Salı"; // HATA! mesaj zaten tanımlı
mesaj = "Salı"; // Mevcut mesaj'ı kullan
System.out.println(mesaj);
break;
}
// Süslü parantez ile kapsam oluştur
switch (gun) {
case 1: {
String mesaj = "Pazartesi"; // Bu mesaj sadece bu blokta
System.out.println(mesaj);
break;
}
case 2: {
String mesaj = "Salı"; // OK! Farklı kapsam
System.out.println(mesaj);
break;
}
}
// Java 14+ switch expression — her case kendi kapsamında
String mesaj = switch (gun) {
case 1 -> "Pazartesi";
case 2 -> "Salı";
default -> "Bilinmeyen";
};Try-Catch İçinde Kapsam
// Yanlış — değişken try bloğunda kaldı
try {
int sonuc = riskliBirIslem();
} catch (Exception e) {
System.out.println("Hata: " + e.getMessage());
}
// System.out.println(sonuc); // HATA! sonuc burada yok
// Doğru — dışarıda tanımla
int sonuc = 0;
try {
sonuc = riskliBirIslem();
} catch (Exception e) {
System.out.println("Hata: " + e.getMessage());
}
System.out.println(sonuc); // OK// try-with-resources kapsamı
try (var reader = new BufferedReader(new FileReader("dosya.txt"))) {
String satir = reader.readLine();
System.out.println(satir);
} // reader burada otomatik kapatılır ve kapsam dışına çıkar
// reader burada erişilemezEffectively Final (Java 8+)
Lambda ifadelerinde ve anonim sınıflarda dış kapsamdaki değişkenleri kullanabilirsin — ama sadece effectively final olanlları (değeri atandıktan sonra değişmeyen):
String sehir = "İstanbul"; // effectively final — değeri hiç değişmiyor
List<String> isimler = List.of("Ali", "Veli", "Ayşe");
isimler.forEach(isim -> {
System.out.println(isim + " - " + sehir); // OK! sehir effectively final
});
// Ama bu olmaz:
int sayac = 0;
isimler.forEach(isim -> {
// sayac++; // HATA! sayac effectively final değil
System.out.println(isim);
});"Effectively final" demek: final yazmamışsın ama yazsan da derleme hatası vermeyecek — yani değeri hiç değişmiyor.
// Bu ikisi eşdeğer:
String a = "test"; // effectively final
final String b = "test"; // explicitly final
// İkisi de lambda'da kullanılabilir
Runnable r = () -> System.out.println(a + b);Gerçek Proje Örneği: Konfigürasyon Sabitleri
public class AppConfig {
// Uygulama sabitleri — değişmez, paylaşılır
public static final String APP_NAME = "MyApp";
public static final String VERSION = "2.1.0";
public static final int MAX_CONNECTIONS = 100;
public static final int TIMEOUT_MS = 30_000;
public static final String DB_URL = "jdbc:mysql://localhost:3306/mydb";
// Environment'tan okunan "sabit" — runtime'da belirlenir
public static final String API_KEY;
// Static initializer block
static {
String key = System.getenv("API_KEY");
API_KEY = (key != null) ? key : "default-key";
}
// Private constructor — instance oluşturulmasın
private AppConfig() {
throw new AssertionError("Instance oluşturulamaz");
}
}
// Kullanım
System.out.println(AppConfig.APP_NAME + " v" + AppConfig.VERSION);Bu pattern gerçek projelerde çok yaygın. Sabitler static final olarak bir sınıfta toplanır, kodun her yerinden erişilir.
Sık Yapılan Hatalar
1. Kapsam dışında erişim:
if (true) {
int x = 10;
}
// System.out.println(x); // HATA! x if bloğunda kaldı
// Çözüm: dışarıda tanımla
int x = 0;
if (true) {
x = 10;
}
System.out.println(x); // 102. Lokal değişkeni initialize etmeden kullanmak:
int x;
// System.out.println(x); // HATA! Atanmamış
// Tüm yollar initialize etmeli
int y;
if (kosul) {
y = 10;
} else {
y = 20;
}
System.out.println(y); // OK — her durumda atanmış3. final nesne içeriğinin değişebileceğini unutmak:
final List<String> list = new ArrayList<>();
list.add("test"); // Bu çalışır! final referans, içerik değil
// Gerçek immutability istiyorsan:
final List<String> immutable = List.of("a", "b", "c");
// immutable.add("d"); // UnsupportedOperationException!4. var'ı her yerde kullanmak:
var s = servis.isle(); // Ne dönüyor? Object? String? List?
// Tip belli değilse var kullanma
// İyi: tip sağ taraftan belli
var users = new ArrayList<String>();
var config = new HashMap<String, Object>();5. Aynı isimli iç içe değişken (shadowing farkında olmamak):
int x = 10;
if (true) {
// int x = 20; // HATA! Java'da aynı isimli lokal değişken
// (C/C++'tan farklı olarak Java buna izin vermez)
x = 20; // Bu OK — mevcut x'i günceller
}Özet
Değişken tanımlandığı `{}` bloğu içinde yaşar — bu kapsam (scope) kuralıdır
Lokal değişkenler varsayılan değer almaz, kullanmadan önce mutlaka initialize et
`final` değişkeni sabitler — bir kez atandıktan sonra değiştirilemez.
static finalile gerçek sabitler oluşturfinalreferansı sabitler, nesne içeriğini değil —final Listiçine eleman eklenebilirİsimlendirme: değişkenler
camelCase, sabitlerSCREAMING_SNAKE_CASE, sınıflarPascalCase`var` (Java 10+) lokal değişkenlerde tip çıkarımı sağlar — okunabilirliği bozmadan kullan, her yerde değil
Effectively final değişkenler lambda ifadelerinde kullanılabilir — değeri atandıktan sonra değişmeyen lokal değişkenler
Magic number kullanma — açıklamasız sayısal değerler yerine anlamlı sabitler tanımla
Mülakat Soruları
S: Lokal değişken ile instance variable farkı? C: Lokal değişkenler metot/blok içinde tanımlanır, varsayılan değer almaz, stack'te yaşar. Instance variable'lar sınıfta tanımlanır, varsayılan değer alır (0, null, false), heap'te nesneyle birlikte yaşar.
S: final ile immutable farkı? C: final referansı sabitler — değişken başka nesneye atanamaz. Ama nesnenin içeriği değişebilir (final List'e eleman eklenebilir). Immutable ise nesnenin kendisinin değişmez olması demek (String gibi).
S: var keyword'ü tipi kaldırır mı? C: Hayır. Java hâlâ statik tipli. var sadece derleyicinin tipi sağ taraftan çıkarmasını sağlar. Derleme sonrası tam tip bilgisi vardır. Runtime'da fark yok.
AI Asistan
Sorularını yanıtlamaya hazır