this, static üyeler, final sabitler ve toString
Parametre alanı gölgelediğinde this, kurucuları birbirine bağlayan this(...), bütün nesnelerin paylaştığı static alanlar, değişmeyen final değerler ve nesneyi okunur yazdıran toString. Final sınavında 'what is printed' sorularının favorisi.
Sınıf yazmayı biliyorsun. Bu hafta sınıfların içinde sık kullanılan dört araç var: this, static, final ve toString. Her biri birkaç kurala dayanıyor ama sınavda bu kurallar küçük farklarla tuzak olarak kurulur: kurucuda this unutulmuş bir sınıf, static bir sayaç, toString’i otomatik çağrılan bir println.
Altın kurallar:
- Parametre alanla aynı addaysa
this.olmadan alana ulaşamazsın. - static alan tek kopya; bütün nesneler paylaşır. Örnek alanı her nesnede ayrı.
- static metot örnek alanlarına doğrudan erişemez.
- final bir kez atanır. Sabitler
static finalve BÜYÜK_HARF. - println(nesne) otomatik
toString()çağırır.
this referansı
Her örnek metodunun içinde gizli bir değişken var: this. Metodu çağıran nesneyi gösterir. p.getName() çağrısında this, p’dir.
Gölgeleme (shadowing)
Kurucu parametrelerine alanlarla aynı adı vermek yaygın bir alışkanlık. Ama o zaman metodun içinde o isim parametreyi gösterir:
class BadPlayer {
private String name;
private int number;
public BadPlayer(String name, int number) {
name = name; // parametre = parametre
number = number;
}
...
}
class Player {
private String name;
private int number;
public Player(String name, int number) {
this.name = name; // alan = parametre
this.number = number;
}
...
}
// main içinde:
BadPlayer bp = new BadPlayer("Deniz", 22);
System.out.println(bp.getName() + " " + bp.getNumber());
Player p = new Player("Deniz", 22);
System.out.println(p.getName() + " " + p.getNumber());
null 0
Deniz 22
BadPlayer derlenir ve çalışır, ama alanlar hiç atanmaz. Derleyici uyarmaz; tipik bir mantık hatası.
this(…) ile kurucu zinciri
Birden çok kurucu varsa ortak işi bir tanesinde yapıp diğerlerinden onu çağırabilirsin:
class Time {
private int hour, minute;
public Time() { this(0, 0); }
public Time(int hour) { this(hour, 0); }
public Time(int hour, int minute) {
this.hour = hour;
this.minute = minute;
}
@Override
public String toString() {
return String.format("%02d:%02d", hour, minute);
}
}
// main: System.out.println(new Time() + " " + new Time(9) + " " + new Time(14, 30));
00:00 09:00 14:30
this(...) çağrısını kurucunun ilk satırına yaz. Ders kitabının ve Java 21’e kadarki sürümlerin kuralı bu; javac --release 21 öncesine bir satır yazılmış kurucuyu flexible constructors is not supported in -source 21 diyerek reddetti. (Java 25 ile bu kural gevşedi, ama sınavda klasik kuralı uygula.) Doğrulama gibi kurallar sadece bir kurucuda yazılır, tekrar edilmez.
return this
Bir metot this döndürürse çağrılar zincirlenebilir. Soru bankasındaki Temp örneği bunu gösteriyor: new Temp(20).add(5).add(3) aynı nesneye art arda ekler.
static üyeler
Örnek değişkeni (instance variable) her nesnede ayrıdır. static bir alan ise sınıfa aittir ve bellekte tek kopya vardır; bütün nesneler onu paylaşır. Bu yüzden ona sınıf değişkeni (class variable) da denir.
class Ticket {
private static int count = 0; // kaç bilet oluşturuldu (paylaşılan)
private int id; // bu biletin numarası (her nesnede ayrı)
private String owner;
public Ticket(String owner) {
this.owner = owner;
count++;
id = count;
}
public static int getCount() { return count; }
public int getId() { return id; }
public int getCountViaInstance() { return count; }
}
// main içinde:
System.out.println(Ticket.getCount());
Ticket a = new Ticket("A");
Ticket b = new Ticket("B");
Ticket c = new Ticket("C");
System.out.println(Ticket.getCount() + " " + a.getId() + " " + b.getId() + " " + c.getId());
System.out.println(a.getCountViaInstance());
0
3 1 2 3
3
Ticket sınıfı: count = 3 (tek kopya)
a: id = 1 b: id = 2 c: id = 3 (her nesnede ayrı)
- Hiç nesne yokken bile
Ticket.getCount()çalıştı: static üyeler nesneye ihtiyaç duymaz, sınıf adıyla kullanılır. - Örnek metodu (
getCountViaInstance) static alanı okuyabilir. - Ters yön çalışmaz: static bir metot
this’e sahip değildir, hangi nesnenin alanını kastettiğini bilemez.
public class Main {
private int score = 5;
public static void main(String[] args) {
System.out.println(score);
}
}
Main.java:5: error: non-static variable score cannot be referenced from a static context
Bu hatayı ilk haftalardan beri main’e yardımcı metot yazarken görmüş olabilirsin: main static olduğu için çağırdığı metotlar da static olmalıydı.
Soru bankasındaki Counter örneğini iz tablosuyla çöz: static alan için sınıfın yanına tek bir kutu, örnek alanı için her nesnenin yanına ayrı bir kutu çiz.
çağrı x.mine y.mine Counter.total
başlangıç 0 0 0
x.hit() 1 0 1
y.hit() 1 1 2
y.hit() 1 2 3
Her hit iki kutuyu birden artırıyor: çağıran nesnenin kendi kutusunu ve herkesin paylaştığı tek kutuyu.
Ne zaman static? Kendine şunu sor: “Bu bilgi tek bir nesneye mi ait, yoksa bütün sınıfa mı?” Bir öğrencinin notu o öğrenciye aittir: örnek alanı. Toplam öğrenci sayısı, KDV oranı ya da bir sonraki verilecek numara bütün sınıfa aittir: static. Metotlar için de aynı soru: metot bir nesnenin alanlarını kullanıyorsa örnek metodu, sadece parametreleriyle çalışıyorsa (Math.max gibi) static olabilir.
| static | static değil | |
|---|---|---|
| Kime ait | sınıfa | her nesneye |
| Kaç kopya | 1 | nesne sayısı kadar |
| Erişim | Ticket.getCount() | a.getId() |
| Örnek | sayaç, sabitler, Math.sqrt | ad, bakiye, not |
final ve sabitler
final bir değişkene bir kez değer atanabileceğini söyler.
class Shop {
public static final double TAX_RATE = 0.20; // sabit (constant)
public static double priceWithTax(double price) {
return price * (1 + TAX_RATE);
}
}
class Circle {
private final double radius; // kurucuda bir kez atanır
public Circle(double radius) { this.radius = radius; }
public double area() { return Math.PI * radius * radius; }
}
// main: Shop.priceWithTax(100), Shop.TAX_RATE, new Circle(2).area() (%.2f)
120.0
0.2
12.57
- Sabit (constant) için
static finalbirlikte kullanılır: tek kopya ve değişmez. Adı büyük harf ve alt çizgiyle yazılır:TAX_RATE,MAX_SIZE.Math.PIde böyle bir sabit. - final örnek alanı her nesnede farklı olabilir ama nesne oluştuktan sonra değişmez. Kurucuda atanır.
Atamaya çalışırsan derlenmez:
Main.java:6: error: cannot assign a value to static final variable MAX
Main2.java:4: error: cannot assign a value to final variable radius
toString
Her sınıf, Java’nın kök sınıfı Object’ten bir toString() metodu alır. Varsayılan sürüm işe yaramaz bir metin döndürür: sınıf adı, @ ve bir hash kodu (NoToString@1b6d3586 gibi). Kendi sürümünü yazarsan (override):
@Override
public String toString() {
return name + " #" + number;
}
// main içinde:
Player p2 = new Player("Can", 7);
System.out.println(p2); // println otomatik toString çağırır
String msg = "Kaptan: " + p2; // birleştirme de çağırır
System.out.println(msg);
Can #7
Kaptan: Can #7
Kurallar:
- İmza tam olarak
public String toString(). Parametre yok, dönüş tipi String, public. - Metot yazdırmaz, döndürür. İçine println koymak yanlış bir alışkanlık.
@Overrideetiketi zorunlu değil ama iyi: imzayı yanlış yazarsan derleyici uyarır.String.formatprintf ile aynı belirteçleri kullanır ama ekrana basmak yerine String döndürür; toString içinde çok işe yarar.
Sık yapılan hatalar
| Yanlış düşünce | Doğrusu |
|---|---|
name = name; alanı atar | Parametreyi kendine atar; this.name = name; |
this(...) kurucunun herhangi bir yerinde | İlk satıra yaz (Java 21 ve öncesinde zorunlu) |
static metotta this kullanılabilir | static’te this yok |
| static alan her nesnede ayrı | Tek kopya, paylaşılır |
| static metot için nesne şart | Sinif.metot() yeter |
| main’den örnek alanına doğrudan erişilir | Nesne üzerinden: obj.alan |
| final alan hiç atanamaz | Bir kez atanır (tanımda ya da kurucuda) |
| Sabit adı camelCase | BÜYÜK_HARF: TAX_RATE |
| toString içinde println | return ile String döndür |
println(obj) alanları basar | toString yazılmadıysa SinifAdi@... |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
What is printed?
class Box {
private int size;
public Box(int size) { size = size * 2; }
public int getSize() { return size; }
}
// in main:
Box b = new Box(5);
System.out.println(b.getSize());Cevabı göster
Kurucunun içinde size adı parametreyi gösterir (gölgeleme, shadowing). size = size * 2 sadece parametreyi 10 yapar; alan hiç değişmez, varsayılan 0'da kalır. Doğrusu this.size = size * 2;.
Same class, but the constructor body is this.size = size * 2;. What is printed for new Box2(5).getSize()?
Cevabı göster
this.size nesnenin alanı, sağdaki size parametre. Alan 5 * 2 = 10 olur.
What is printed?
class Counter {
static int total = 0;
private int mine = 0;
public void hit() { mine++; total++; }
public int getMine() { return mine; }
}
// in main:
Counter x = new Counter();
Counter y = new Counter();
x.hit();
y.hit();
y.hit();
System.out.println(x.getMine() + " " + y.getMine() + " " + Counter.total);Cevabı göster
mine örnek değişkeni: her nesnede ayrı (x'te 1, y'de 2). total static: sınıfa ait, tek bir kopya var ve bütün nesneler onu paylaşıyor. Üç hit, total 3.
Time has Time(int hour) which calls this(hour, 0), and toString returns String.format("%02d:%02d", hour, minute). What is printed?
Time t = new Time(7);
System.out.println(t);Cevabı göster
this(hour, 0) aynı sınıftaki iki parametreli kurucuyu çağırır: hour = 7, minute = 0. println bir nesne alınca otomatik toString() çağırır. %02d sayıyı iki haneye sıfırla tamamlar: 07.
Player.toString() returns name + " #" + number. What is printed?
Player p = new Player("Ali", 10);
System.out.println("#" + p + "#");Cevabı göster
String birleştirmede nesne kullanılınca Java onun toString() metodunu çağırır: "Ali #10". Başına ve sonuna # eklenir.
Ticket has private static int count; each constructor call does count++; id = count;. Assume count starts at 0. What is printed?
Ticket t = new Ticket("X");
Ticket u = new Ticket("Y");
System.out.println(t.getId() + u.getId() + Ticket.getCount());Cevabı göster
t'nin id'si 1, u'nunki 2, count 2. Üçü de int ve başta String yok: toplama yapılır, 1 + 2 + 2 = 5. Birleştirme isteseydin başa "" + koyardın: "122".
Shop has public static final double TAX_RATE = 0.20; and priceWithTax(p) returns p * (1 + TAX_RATE). What is printed?
System.out.println(Shop.priceWithTax(50) + " " + (int) Shop.priceWithTax(10));Cevabı göster
Static metot sınıf adıyla çağrılır, nesne gerekmez. 50 * 1.2 = 60.0 (double). 10 * 1.2 = 12.0, cast ile 12.
What is printed?
class Temp {
private int value;
public Temp(int value) { this.value = value; }
public Temp add(int d) { value += d; return this; }
public String toString() { return value + "C"; }
}
// in main:
Temp tm = new Temp(20).add(5).add(3);
System.out.println(tm);Cevabı göster
return this metodu çağıran nesnenin kendisini döndürür, bu yüzden çağrılar zincirlenebilir (method chaining). Aynı nesneye önce 5, sonra 3 eklenir: 28.
Which lines do not compile?
public class Main {
private int score = 5;
public static final int MAX = 10;
public static void main(String[] args) {
System.out.println(score); // line A
System.out.println(MAX); // line B
MAX = 20; // line C
}
}Cevabı göster
A: main static; hangi nesnenin score'u olduğu belli değil → non-static variable score cannot be referenced from a static context. B: static alan static metottan okunabilir. C: final değişkene ikinci kez değer atanamaz → cannot assign a value to static final variable MAX.
A class has no toString method. What does System.out.println(obj) print?
- Nothing
- The values of all fields
- Class name, @ and a hexadecimal hash code (e.g. NoToString@1b6d3586)
- A compile-time error
Cevabı göster
Her sınıf Object'ten varsayılan bir toString alır: SınıfAdı@hashKodu. Kod her çalıştırmada değişebilir. Alanları görmek istiyorsan toString'i kendin yazmalısın (override).
Write a constructor for Player (fields private String name; private int number;) whose parameters have the same names as the fields.
Cevabı göster
Parametre adları alanları gölgeler; alana ulaşmak için this. şart.public Player(String name, int number) { this.name = name; this.number = number;}this olmadan (name = name;) yazılan sürüm doğrulamada null 0 bastı.
Write a toString method for Player that returns the name, a space, # and the number (e.g. Can #7).
Cevabı göster
İmza tam olarak public String toString() olmalı: public, dönüş tipi String, parametre yok. Metot yazdırmaz, döndürür.@Overridepublic String toString() { return name + " #" + number;}System.out.println(new Player("Can", 7)); → Can #7.
Doğrulama: Tüm çıktılar JDK 26 ile çalıştırıldı (verify/SEN1001/W12.java; Player, BadPlayer, Time, Ticket, Shop, Circle, Box, Box2, Counter ve Temp sınıfları aynı dosyada). static ve final derleme hataları verify/SEN1001/err/st/ altında javac ile alındı; this(...) kuralı javac --release 21 ile ayrıca denendi.