Sınıflar ve nesneler: alanlar, metotlar, get/set
Sınıf bir kalıp, nesne o kalıptan üretilen örnek. new ile nesne oluşturma, örnek değişkenleri, nokta operatörü, private alanlar ve get/set metotları, referans değişkenler ve null. Finalin en büyük konusu burada başlıyor.
Şimdiye kadar her şeyi main’in içine ve static metotlara yazdın. Gerçek programlar ise nesnelerden (objects) oluşur: bir banka uygulamasında hesaplar, bir oyunda oyuncular, bir okul sisteminde öğrenciler. Her nesnenin kendi verisi (alanları) ve kendi davranışı (metotları) vardır. Bu hafta ilk sınıflarını yazacaksın. Finalde bu konu yazma sorusu olarak gelir: “şu alanları ve metotları olan bir sınıf yaz, main’de iki nesne oluştur”.
Altın kurallar:
- Sınıf kalıp, nesne örnek. Bir
Dogsınıfından istediğin kadar köpek nesnesi üretirsin. - Her nesnenin kendi alanları var. Birini değiştirmek diğerini etkilemez.
- Değişken nesneyi değil, nesnenin referansını tutar.
b = ayeni nesne yaratmaz. - Alanlar private, erişim public metotlarla (get/set).
- Örnek metotları static değildir ve bir nesne üzerinden çağrılır.
İlk sınıf
class Dog {
String name; // örnek değişkenleri (instance variables / fields)
int age;
void bark() { // örnek metodu (instance method): static YOK
System.out.println(name + " (" + age + ") says Hav!");
}
}
Sınıf bir tip tanımlar. Kendi başına hiçbir şey çalıştırmaz; içinde main yok. Onu kullanan kod başka bir sınıfın main’inde:
Dog d1 = new Dog(); // yeni nesne oluştur (instantiation)
d1.name = "Karabas";
d1.age = 3;
d1.bark();
Dog d2 = new Dog();
System.out.println(d2.name + " " + d2.age);
d2.name = "Pamuk";
d2.bark();
d1.bark();
Karabas (3) says Hav!
null 0
Pamuk (0) says Hav!
Karabas (3) says Hav!
Gözlemler:
new Dog()bellekte yeni bir Dog nesnesi oluşturur ve adresini döndürür.d1bu adresi tutar.- Nokta operatörü (dot operator) ile nesnenin alanına ve metoduna erişilir:
d1.name,d1.bark(). bark()içindekiname, metodu çağıran nesnenin alanıdır.d1.bark()Karabas’ı,d2.bark()Pamuk’u yazar.- d2’ye değer verilmeden önce alanları varsayılan değerlerde: String null, int 0. (Metot içindeki yerel değişkenlerde böyle bir varsayılan yoktur.)
Bir dosyada birden çok sınıf olabilir, ama sadece biri public olabilir ve dosya adı onun adıdır. Gerçek projelerde her sınıf kendi dosyasına konur: Dog.java, Main.java.
Kapsülleme: private alanlar, get ve set
Alanlar public olsaydı herkes acc.balance = -5000; yazabilirdi. Bunu engellemek için alanlar private yapılır ve dışarıya kontrollü public metotlar açılır. Buna kapsülleme (encapsulation) denir.
class Account {
private String owner;
private double balance;
public String getOwner() { return owner; } // get (accessor)
public void setOwner(String newOwner) { owner = newOwner; } // set (mutator)
public double getBalance() { return balance; } // balance için set YOK
public void deposit(double amount) {
if (amount > 0) balance += amount;
}
public void withdraw(double amount) {
if (amount <= balance) balance -= amount;
else System.out.println("Insufficient funds");
}
}
Account acc = new Account();
acc.setOwner("Deniz");
acc.deposit(500);
acc.withdraw(200);
acc.withdraw(1000);
System.out.println(acc.getOwner() + " " + acc.getBalance());
acc.deposit(-50);
System.out.println(acc.getBalance());
Insufficient funds
Deniz 300.0
300.0
Bakiye sadece deposit ve withdraw ile değişebiliyor, ikisi de kural kontrol ediyor. Dışarıdan doğrudan erişmeye çalışırsan:
Acc a = new Acc();
a.balance = 1000;
Main.java:7: error: balance has private access in Acc
| Erişim belirleyici (access modifier) | Kim erişir? |
|---|---|
private | sadece aynı sınıfın içi |
public | her yer |
| (hiçbiri) | aynı paketteki (package) sınıflar |
get/set adlandırma
| Alan | get | set |
|---|---|---|
private String name | public String getName() | public void setName(String n) |
private int age | public int getAge() | public void setAge(int a) |
private boolean active | public boolean isActive() | public void setActive(boolean a) |
get metodunun dönüş tipi alanın tipi, parametresi yok. set metodu void, parametresi alanın tipinde. Her alan için set yazmak zorunlu değil: bakiye gibi doğrudan değiştirilmemesi gereken alanlar için set yazılmaz.
Doğrulama (validation)
class Thermostat {
private double celsius;
public void setCelsius(double value) {
if (value >= 5 && value <= 35) celsius = value; // aralık dışı reddedilir
}
public double getCelsius() { return celsius; }
public double getFahrenheit() { return celsius * 9 / 5 + 32; }
}
// main içinde:
Thermostat t = new Thermostat();
t.setCelsius(45);
System.out.println(t.getCelsius());
t.setCelsius(22.5);
System.out.println(t.getCelsius() + " " + t.getFahrenheit());
0.0
22.5 72.5
45 reddedildi, alan varsayılan 0.0’da kaldı. getFahrenheit bir alan değil, alandan hesaplanan bir değer; ayrıca saklamaya gerek yok.
Referans değişkenler
Primitive bir değişken değerin kendisini tutar. Nesne değişkeni ise nesnenin referansını (bellekteki yerini) tutar.
Account a = new Account();
a.deposit(100);
Account b = a; // referans kopyalandı, nesne değil
b.deposit(50);
System.out.println(a.getBalance() + " " + b.getBalance());
System.out.println(a == b);
Account c = new Account();
c.deposit(150);
System.out.println(a == c);
150.0 150.0
true
false
a ──┐
├──► [Account: balance 150.0]
b ──┘
c ─────► [Account: balance 150.0] başka bir nesne
== nesnelerde içeriğe değil, aynı nesne olup olmadığına bakar. a ve c’nin bakiyeleri eşit ama farklı nesneler: false. (String’lerde equals kullanmamızın sebebi de bu.)
null
Hiçbir nesneyi göstermeyen referansın değeri null’dır. null bir referans üzerinden metot çağırmak programı çökertir:
Account n = null;
n.deposit(10);
NullPointerException
Kod derlenir, çünkü derleyici sadece tipe bakar (Account’ta deposit var). Hata çalışma anında çıkar.
static ile örnek metodu farkı
main static’tir; bir nesneye ait değildir. Örnek metotları ise bir nesneye aittir. Bu yüzden main’den örnek metodu bir nesne üzerinden çağrılır:
Acc a = new Acc();
System.out.println(Acc.getBalance()); // sınıf adıyla: HATALI
Main2.java:8: error: non-static method getBalance() cannot be referenced from a static context
Doğrusu a.getBalance(). Kimin bakiyesi? a’nın. Sınıfın tek bir bakiyesi yok; her nesnenin kendisi var. Bütün nesnelerin paylaştığı static alanları 12. haftada göreceksin.
| static metot | örnek metodu | |
|---|---|---|
| Nasıl çağrılır | Math.sqrt(4), ClassName.m() | nesne.m() |
| Nesne gerekir mi | Hayır | Evet |
| Alanlara erişim | Örnek alanlarına doğrudan erişemez | Kendi nesnesinin alanlarına erişir |
| Örnek | isPrime(int n), main | deposit, getBalance |
Çözümlü örnek: sınıf yazma sorusu
Soru: “Rectangle sınıfı yaz: private int width ve height, set metotları, getWidth(), alanı ve çevreyi döndüren metotlar.”
class Rectangle {
private int width, height;
public void setWidth(int w) { width = w; }
public void setHeight(int h) { height = h; }
public int getWidth() { return width; }
public int area() { return width * height; }
public int perimeter() { return 2 * (width + height); }
}
Puanlama genelde şöyle: alanların private tanımı, her metodun doğru başlığı (dönüş tipi!), gövdede doğru formül, main’de new ile nesne oluşturup metotları nokta ile çağırmak. 3’e 4’lük bir dikdörtgende alan 12, çevre 14 çıkar.
Sık yapılan hatalar
| Yanlış düşünce | Doğrusu |
|---|---|
Dog d; nesne oluşturur | Sadece referans tanımlar; nesne new Dog() ile |
Örnek metodu static yazılır | Örnek metotları static değildir |
Account.getBalance() | Nesne üzerinden: acc.getBalance() |
| Alanlar başlangıçta tanımsız, derlenmez | Varsayılan değer alır: 0, null, false |
| İki nesne aynı alanları paylaşır | Her nesnenin kendi kopyası |
b = a; nesneyi kopyalar | Referansı kopyalar; aynı nesne |
a == b içerik karşılaştırır | Aynı nesne mi diye bakar |
| Alanlar public olsun, kolay olur | private + get/set, doğrulama için |
| get metodu void | Alanın tipini döndürür |
| set metodu değer döndürür | Genellikle void |
| null referansla metot çağrısı derlenmez | Derlenir, çalışırken NullPointerException |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
Counter has a private int count, increment() adds 1 to it and getCount() returns it. What is printed?
Counter c1 = new Counter();
Counter c2 = new Counter();
c1.increment();
c1.increment();
c2.increment();
System.out.println(c1.getCount() + " " + c2.getCount());Cevabı göster
İki ayrı new, iki ayrı nesne. Her nesnenin kendi count alanı var. c1'in sayacı 2, c2'ninki 1.
Using the same Counter class, what is printed?
Counter c3 = new Counter();
Counter c4 = c3;
c3.increment();
c4.increment();
c4.increment();
System.out.println(c3.getCount());Cevabı göster
c4 = c3 yeni nesne oluşturmaz; ikisi de aynı nesneyi gösterir. Üç increment çağrısı aynı count'u artırır: 3.
deposit adds the amount only if it is positive; withdraw subtracts only if the amount is not more than the balance, otherwise it prints Insufficient funds. What is printed?
Account a = new Account();
a.deposit(300);
a.withdraw(100);
a.withdraw(250);
a.deposit(-20);
System.out.println(a.getBalance());Cevabı göster
200.0
300 yatır → 300. 100 çek → 200. 250 > 200 → çekilmez, mesaj basılır. -20 pozitif değil → yok sayılır. balance double olduğu için 200.0 basılır.
Dog has fields String name; and int age; with no initial values. What is printed?
Dog d = new Dog();
System.out.println(d.name + " " + d.age);Cevabı göster
Örnek değişkenleri (instance variables), yerel değişkenlerin aksine varsayılan değer alır: nesne referansları (String) null, int 0. Bu yüzden derleme hatası yok.
What is printed?
class Box {
private int size = 1;
public void setSize(int s) { if (s > 0) size = s; }
public int getSize() { return size; }
public int volume() { return size * size * size; }
}
// in main:
Box bx = new Box();
bx.setSize(4);
bx.setSize(-2);
System.out.println(bx.getSize() + " " + bx.volume());Cevabı göster
set metodu doğrulama (validation) yapıyor: -2 geçersiz, alan değişmez. size 4 kalır, hacim 4 * 4 * 4 = 64. private alanın asıl faydası bu: geçersiz değer içeri giremez.
Using Account (deposit adds positive amounts), what is printed?
Account p = new Account();
Account q = new Account();
p.deposit(100);
q = p;
q.deposit(25);
p = new Account();
p.deposit(5);
System.out.println(p.getBalance() + " " + q.getBalance());Cevabı göster
q = p sonrası ikisi de ilk hesabı gösterir; 100 + 25 = 125. Sonra p yeni bir hesaba yönlendirilir (q hâlâ eskisinde) ve 5 yatırılır. q'nun ilk gösterdiği boş nesne artık kimse tarafından gösterilmiyor.
Rectangle has private int width and height, setters, getWidth(), area() and perimeter(). What is printed?
Rectangle r = new Rectangle();
r.setWidth(3);
r.setHeight(4);
System.out.println(r.area() + " " + r.perimeter());
r.setWidth(r.getWidth() * 2);
System.out.println(r.area());Cevabı göster
24
Alan 3 * 4 = 12, çevre 2 * (3 + 4) = 14. Genişlik get ile okunup 2 katı set edilir: 6. Yeni alan 6 * 4 = 24.
What is printed?
class Light {
private boolean on;
public void toggle() { on = !on; }
public boolean isOn() { return on; }
}
// in main:
Light l = new Light();
l.toggle();
l.toggle();
l.toggle();
System.out.println(l.isOn());Cevabı göster
boolean alan false ile başlar. Her toggle tersine çevirir: true, false, true. boolean getter'lar geleneksel olarak is ile başlar: isOn().
balance is declared private in class Acc. Which statement in main (another class) does not compile, and why?
Acc a = new Acc();a.balance = 1000;System.out.println(a.getBalance());a.deposit(50);
Cevabı göster
private bir alana sadece kendi sınıfının içinden erişilir. javac: balance has private access in Acc. Dışarıdan erişim public metotlarla (getBalance, deposit) yapılır.
What happens when this code runs?
Account n = null;
n.deposit(10);Cevabı göster
null, referansın hiçbir nesneyi göstermediği anlamına gelir. Derleyici tipe baktığı için kod derlenir, ama çalışırken olmayan bir nesnenin metodu çağrılınca NullPointerException fırlatılır.
Write the withdraw method of Account (private double balance): subtract amount only if it is positive and not more than the balance; otherwise print Insufficient funds.
Cevabı göster
Örnek metodu (instance method) static değildir; doğrudan nesnenin alanını kullanır.public void withdraw(double amount) { if (amount > 0 && amount <= balance) { balance -= amount; } else { System.out.println("Insufficient funds"); }}Bu sayfadaki örnek sürüm sadece amount <= balance kontrol ediyor; negatif çekimi de engellemek için amount > 0 eklendi.
Write a class Thermostat with a private double celsius, a setter that accepts only values from 5 to 35 (inclusive), a getter, and a method getFahrenheit().
Cevabı göster
class Thermostat { private double celsius; public void setCelsius(double value) { if (value >= 5 && value <= 35) celsius = value; } public double getCelsius() { return celsius; } public double getFahrenheit() { return celsius * 9 / 5 + 32; }}Test: setCelsius(45) reddedilir, getCelsius() → 0.0 (varsayılan). setCelsius(22.5) → 22.5, getFahrenheit() → 72.5. celsius double olduğu için * 9 / 5 int bölme tuzağına düşmez.
Doğrulama: Tüm çıktılar JDK 26 ile çalıştırıldı (verify/SEN1001/W10.java; Dog, Account, Thermostat, Counter, Box, Rectangle ve Light sınıfları aynı dosyada). withdraw yazma sorusunun cevabı W11.java içindeki Account2 sınıfında aynen çalıştırıldı. private erişim ve static bağlam hataları verify/SEN1001/err/priv/ altında javac ile alındı.