SEN1002 · 1. hafta · Classes and Objects

Sınıflar ve nesneler: tekrar

Alan, constructor, this, kapsülleme, static ve instance farkı, toString ve equals. Dönemin geri kalanı bu temelin üstüne kurulur.

classobjectinstance variableconstructorthisencapsulationgetter / setterstatictoStringequalsreference

Bu ders SEN1001’in devamı. İlk hafta, nesne yönelimli programlamanın (object oriented programming) temel taşlarını toparlıyoruz. Kalıtım, çok biçimlilik ve arayüzler bu sayfadaki her kavramı tekrar kullanır. Sınavda bu konudan üç tip soru gelir: “What is printed?” (özellikle static sayaçlar, referanslar ve constructor zinciri), “Find the compile error” (private erişim, eksik constructor, static bağlam) ve “Write a class”.

Altın kurallar:

  1. Değişken nesneyi tutmaz, nesnenin adresini (reference) tutar. b = a yeni nesne yaratmaz.
  2. Bir constructor yazdığın an, parametresiz default constructor kaybolur.
  3. Alan ile parametre aynı addaysa this. yazmazsan alan değişmez.
  4. static olan sınıfa aittir, tek kopyadır. static metot instance alana doğrudan erişemez.
  5. == adres karşılaştırır, equals içerik. (equals override edildiyse.)

Sınıf, nesne, alan

Sınıf (class) bir kalıptır: hangi verileri tutacağını (alanlar, fields / instance variables) ve neler yapabileceğini (metotlar) tanımlar. Nesne (object) bu kalıptan new ile üretilen somut örnektir. Her nesnenin alanları kendine aittir.

public class Account {
    private String owner;          // instance variable
    private double balance;
    private static int count = 0;  // sınıfa ait, tek kopya

    public Account(String owner, double balance) {
        this.owner = owner;
        setBalance(balance);       // kontrol tek yerde
        count++;
    }
    public Account(String owner) {
        this(owner, 0.0);          // diğer constructor'a devret
    }
    public void deposit(double amount) {
        if (amount > 0) balance += amount;
    }
    public double getBalance() { return balance; }
    public void setBalance(double b) {
        if (b >= 0) balance = b;
    }
    public static int getCount() { return count; }

    @Override
    public String toString() {
        return owner + " (" + balance + " TL)";
    }
}
Account a = new Account("Ece", 100);
Account b = new Account("Mert");
a.deposit(50);
a.deposit(-20);      // reddedilir
b.setBalance(-5);    // reddedilir
System.out.println(a);
System.out.println(b);
System.out.println(Account.getCount());
Ece (150.0 TL)
Mert (0.0 TL)
2

System.out.println(a) yazınca Java otomatik olarak a.toString() çağırır. toString’i override etmeseydin Account@1b6d3586 gibi sınıf adı ve hash kodu basılırdı; bu sayı her çalıştırmada değişebilir.

Varsayılan değerler

Alanlara değer atamazsan Java varsayılan değer verir. Yerel değişkenlere (metot içindekiler) vermez; atamadan okumak derleme hatasıdır.

Alan tipiVarsayılan
int, long, short, byte0
double, float0.0
booleanfalse
char'\u0000' (kod 0)
Her referans (String, dizi, nesne)null

Constructor

Constructor nesne yaratılırken bir kez çalışır, alanlara başlangıç değeri verir. Adı sınıfla aynıdır ve dönüş tipi yoktur (void bile yazılmaz; yazarsan sıradan bir metot olur).

  • Hiç constructor yazmazsan derleyici parametresiz, boş bir default constructor ekler.
  • Tek bir constructor bile yazarsan bu ekleme yapılmaz. new Car() artık derlenmez.
  • Aynı sınıfta parametre listesi farklı birden fazla constructor olabilir (overloading).

this anahtar kelimesi

this o an üzerinde çalışılan nesnenin kendisidir. İki kullanımı var:

  1. Gölgelemeyi çözmek (shadowing). Parametre adı alan adıyla aynıysa, metot içinde düz name parametreyi gösterir. Alanı göstermek için this.name yazarsın.
  2. Constructor zinciri. this(...) aynı sınıfın başka bir constructor’ını çağırır. Constructor’ın ilk satırı olmak zorundadır. (Java 25 ile gelen flexible constructor bodies özelliği, öncesine this kullanmayan satırlara izin verir. Ders kitabı ve sınav eski kuralı esas alır: ilk satır.)
class Box {
    int w, h;
    Box()             { this(1);    System.out.println("Box()"); }
    Box(int w)        { this(w, w); System.out.println("Box(int)"); }
    Box(int w, int h) { this.w = w; this.h = h;
                        System.out.println("Box(int,int) " + w + "x" + h); }
}
// main:
Box bx = new Box();
System.out.println(bx.w * bx.h);
Box(int,int) 1x1
Box(int)
Box()
1
new Box()
  └─ this(1)            → Box(int) çalışmaya başlar
       └─ this(1, 1)    → Box(int,int) çalışır, BASAR, biter
       Box(int) kalan satırı basar
  Box() kalan satırı basar

TUZAK · zincirde sıra. Çağrı sırası dıştan içe, çıktı sırası içten dışa. En son çağrılan constructor ilk biter. Kalıtımda super(...) zinciri de tam olarak böyle çalışır (2. hafta).

Kapsülleme (encapsulation)

Kapsülleme, nesnenin verisini dışarıdan doğrudan değiştirilemez yapmaktır. Kural basit: alanlar private, erişim public metotlarla. Okuyan metoda getter (getBalance), değiştirene setter (setBalance) denir. Setter’ın asıl işi kontrol etmektir: negatif bakiye, boş isim, geçersiz not gibi değerleri reddeder. Böylece nesne hiçbir zaman bozuk duruma düşmez.

Erişim belirleyiciAynı sınıfAynı paketAlt sınıfHer yer
privateevethayırhayırhayır
(yazılmamış, package-private)evetevetaynı paketteysehayır
protectedevetevetevethayır
publicevetevetevetevet

İPUCU · kontrolü tek yere koy. Constructor da setter’ı çağırsın (setBalance(balance)). Aynı kontrolü iki yere yazarsan biri güncellenir, öteki unutulur.

static ve instance

instance (static değil)static
Kime ait?Her nesneye ayrı kopyaSınıfa, tek kopya
Nasıl çağrılır?nesne.metot()Sinif.metot()
this var mı?VarYok
Neye erişir?Hem instance hem static üyelereSadece static üyelere (doğrudan)
Tipik örnekbalance, getBalance()sayaç count, Math.sqrt, main
class Counter {
    static int total = 0;
    int mine = 0;
    void hit() { total++; mine++; }
}
Counter c1 = new Counter(), c2 = new Counter();
c1.hit(); c1.hit(); c2.hit();
System.out.println(c1.mine + " " + c2.mine + " " + Counter.total);
2 1 3

main static olduğu için, main içinden aynı sınıftaki instance alanı ya da instance metodu doğrudan çağıramazsın. Önce bir nesne yaratman gerekir. Bu, ilk haftaların en sık derleme hatasıdır: non-static variable score cannot be referenced from a static context.

Referanslar ve parametre geçişi

Java’da her şey değer olarak geçer (pass by value). Fark, kopyalanan şeyin ne olduğu:

  • int, double gibi ilkel tiplerde sayının kopyası gider. Metot içindeki değişiklik dışarıyı etkilemez.
  • Nesnelerde adresin kopyası gider. Metot o adresteki nesnenin alanlarını değiştirebilir, dışarıdan görünür. Ama parametreye new ile başka nesne atarsa sadece kendi kopyasını çevirmiş olur.
static void fill(Cup c)    { c.ml = 250; }                   // nesneyi değiştirir
static void swapCup(Cup c) { c = new Cup(999); c.ml = 1; }   // sadece yerel kopya
static void bump(int n)    { n = n + 10; }                    // sadece yerel kopya

Cup cup = new Cup(100);
int n = 5;
fill(cup);
bump(n);
System.out.println(cup.ml + " " + n);
swapCup(cup);
System.out.println(cup.ml);
250 5
250

toString ve equals

Her sınıf, yazmasan da Object sınıfından türer ve ondan toString() ile equals(Object) metotlarını alır. İkisinin de varsayılan hali çoğu zaman işine yaramaz:

  • Varsayılan toString(): SinifAdi@hashkodu.
  • Varsayılan equals(o): this == o, yani adres karşılaştırması.

İçerik karşılaştırmak için equals’ı override edersin. Parametre tipi Object olmalı; equals(Point p) yazarsan override değil overload olur ve beklediğin yerde çağrılmaz.

class Point {
    private final int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;              // aynı nesne
        if (!(o instanceof Point)) return false; // tip yanlış ya da null
        Point p = (Point) o;
        return x == p.x && y == p.y;
    }
    @Override
    public String toString() { return "(" + x + ", " + y + ")"; }
}
Point p1 = new Point(2, 3), p2 = new Point(2, 3);
System.out.println(p1 == p2);
System.out.println(p1.equals(p2));
System.out.println(p1);
false
true
(2, 3)

String’de de aynı mantık var. "java" == new String("java") false verir, equals true. String literal’leri ortak bir havuzda (string pool) tutulduğu için iki literal arasında == bazen true çıkar; bu bir şans eseridir, buna güvenme.

TUZAK · @Override yaz. Metodun başına @Override koyarsan, imzayı yanlış yazdığında (örneğin equals(Point o) ya da tostring()) derleyici seni uyarır. Koymazsan sessizce yeni bir metot yaratmış olursun.

Sık yapılan hatalar

Yanlış düşünceDoğrusu
name = name; alanı doldururParametreyi kendine atar, alan null kalır. this.name = name;
Constructor yazdım, new Car() yine çalışırBir constructor yazınca default constructor eklenmez
Constructor’a void yazmak sorun değilvoid yazarsan sıradan metot olur, constructor değil
b = a; kopya nesne yaratırAynı nesneye ikinci referans; biri değişince öteki de görür
static metottan instance alana erişirimÖnce nesne yarat: obj.alan
static sayaç her nesnede ayrıTek kopya, bütün nesneler paylaşır
String’i == ile karşılaştırmakequals kullan
equals(Point p) override ederParametre Object olmalı, yoksa overload
Metoda int verdim, metot değiştirdiİlkel tip kopyalanır, dışarısı değişmez
this(...) istediğin satırda olabilirSınavın esas aldığı kural: constructor’ın ilk satırı

Kendini test et

Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.

Soru 1kolay

What is printed?

class Counter {
    static int total = 0;
    int mine = 0;
    void hit() { total++; mine++; }
}
// in main:
Counter c1 = new Counter(), c2 = new Counter();
c1.hit(); c1.hit(); c2.hit();
System.out.println(c1.mine + " " + c2.mine + " " + Counter.total);
Cevabı göster
2 1 3

mine instance alanı: her nesnenin kendi kopyası var. c1 iki kez, c2 bir kez arttı: 2 ve 1. total static: tek kopya, üç çağrının hepsi aynı sayacı artırdı: 3.

Soru 2orta

What is printed?

class Student {
    private String name;
    public Student(String name) { name = name; }
    public String getName() { return name; }
}
// in main:
System.out.println(new Student("Ali").getName());
Cevabı göster
null

Constructor içinde name parametresi alanı gölgeler (shadowing). name = name; parametreyi kendisine atar, alana hiç dokunmaz. Alan varsayılan değeri null ile kalır. Düzeltme: this.name = name;.

Soru 3kolay

What is printed?

Account x = new Account("Can", 10);   // deposit adds to balance
Account y = x;
y.deposit(5);
System.out.println(x.getBalance());
System.out.println(x == y);
Cevabı göster
15.0 ve true

y = x yeni nesne yaratmaz, aynı nesnenin adresini kopyalar (aliasing). y üzerinden yapılan deposit, x'in gördüğü nesneyi değiştirir: 15.0 (balance double). İki referans aynı nesneyi gösterdiği için == true.

Soru 4orta

What is printed?

String s1 = "java";
String s2 = new String("java");
String s3 = "java";
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
System.out.println(s1 == s3);
Cevabı göster
false, true, true

== referansları karşılaştırır. new String her zaman yeni nesne yaratır, s1 ile aynı adres değil: false. equals içeriğe bakar: true. s1 ve s3 aynı literal, Java literal'leri string pool'da tek kopya tutar: true. Sınavda String karşılaştırmayı her zaman equals ile yaz.

Soru 5orta

Find the compile error.

class Car {
    private String model;
    public Car(String model) { this.model = model; }
}
public class Test {
    public static void main(String[] args) {
        Car c = new Car();
    }
}
Cevabı göster
new Car() derlenmez: parametresiz constructor yok

Sınıfa tek bir constructor bile yazdığın an derleyici varsayılan (default) constructor'ı eklemez. Car sadece Car(String) biliyor. javac: constructor Car in class Car cannot be applied to given types; required: String, found: no arguments. Çözüm: new Car("Clio") ya da sınıfa public Car() { ... } ekle.

Soru 6orta

Find the compile error.

public class Test {
    private int score = 10;
    public static void main(String[] args) {
        System.out.println(score);
    }
}
Cevabı göster
static main içinden instance alanı score'a erişilemez

main static: çalışırken ortada bir Test nesnesi yok, score ise her nesnenin kendi alanı. javac: non-static variable score cannot be referenced from a static context. Çözüm: Test t = new Test(); System.out.println(t.score); ya da alanı static yap.

Soru 7kolay

What is printed?

class Defaults { int i; double d; boolean b; String s; }
// in main:
Defaults df = new Defaults();
System.out.println(df.i + " " + df.d + " " + df.b + " " + df.s);
Cevabı göster
0 0.0 false null

Alanlar (instance variables) atanmasa da varsayılan değer alır: sayılar 0 / 0.0, boolean false, referanslar null. Yerel değişkenler ise varsayılan almaz; atamadan okursan derleme hatası.

Soru 8zor

What is printed?

class Box {
    int w, h;
    Box() { this(1); System.out.println("Box()"); }
    Box(int w) { this(w, w); System.out.println("Box(int)"); }
    Box(int w, int h) { this.w = w; this.h = h;
        System.out.println("Box(int,int) " + w + "x" + h); }
}
// in main:
Box bx = new Box();
System.out.println(bx.w * bx.h);
Cevabı göster
Box(int,int) 1x1 / Box(int) / Box() / 1

this(...) constructor'ın ilk satırı ve önce o çalışır. Box() → Box(1) → Box(1, 1). En içteki önce biter ve basar, sonra geri dönerken Box(int), en son Box(). Alanlar 1 ve 1: 1 * 1 = 1.

Soru 9zor

What is printed?

class Cup { int ml; Cup(int ml) { this.ml = ml; } }
static void fill(Cup c) { c.ml = 250; }
static void swapCup(Cup c) { c = new Cup(999); c.ml = 1; }
static void bump(int n) { n = n + 10; }
// in main:
Cup cup = new Cup(100);
int n = 5;
fill(cup);
bump(n);
System.out.println(cup.ml + " " + n);
swapCup(cup);
System.out.println(cup.ml);
Cevabı göster
250 5, sonra 250

Java her zaman değer kopyalar (pass by value). int için sayının kopyası gider: bump dışarıdaki n'yi değiştirmez, 5 kalır. Nesne için adresin kopyası gider: fill aynı nesnenin alanını değiştirir, 250. swapCup parametreye yeni nesne atar; bu sadece yerel kopyayı başka yere çevirir, main'deki cup hâlâ eski nesneyi gösterir: 250.

Soru 10orta

What is printed?

class P { int x, y; P(int x, int y) { this.x = x; this.y = y; } }
// in main:
P a = new P(2, 3), b = new P(2, 3);
System.out.println(a.equals(b));
Cevabı göster
false

P sınıfı equals'ı override etmedi, Object'ten gelen equals çalışır. Object'in equals'ı == ile aynı şeyi yapar: iki ayrı nesne, false. İçerik karşılaştırması istiyorsan equals'ı kendin yazmalısın.

Soru 11kolay

Find the compile error.

class Wallet {
    private double money = 50;
}
public class Test {
    public static void main(String[] args) {
        Wallet w = new Wallet();
        w.money = 100;
    }
}
Cevabı göster
money private, Test sınıfından erişilemez

private alan sadece kendi sınıfının içinden görülür. javac: money has private access in Wallet. Kapsüllemenin amacı bu: dışarıdan değiştirmek için Wallet bir setter (örneğin setMoney) sunmalı ve değeri kontrol etmeli.

Soru 12orta

Write a class Rectangle with private width and height (double), a constructor that rejects non-positive values with IllegalArgumentException, a getArea() method and a toString() that returns Rectangle[3.0 x 4.5] style text.

Cevabı göster
Aşağıdaki sınıf

Alanlar private, kontrol setter'da, constructor setter'ları çağırıyor (kontrol tek yerde):

public class Rectangle {
    private double width, height;

    public Rectangle(double width, double height) {
        setWidth(width);
        setHeight(height);
    }
    public void setWidth(double width) {
        if (width <= 0) throw new IllegalArgumentException("width must be > 0");
        this.width = width;
    }
    public void setHeight(double height) {
        if (height <= 0) throw new IllegalArgumentException("height must be > 0");
        this.height = height;
    }
    public double getArea() { return width * height; }

    @Override
    public String toString() {
        return "Rectangle[" + width + " x " + height + "]";
    }
}
new Rectangle(3, 4.5) basılınca Rectangle[3.0 x 4.5] area=13.5 (alan ile birlikte); new Rectangle(-1, 2) istisna fırlatır, mesajı width must be > 0.

Doğrulama: Tüm kod örnekleri ve cevaplar JDK 26 ile çalıştırıldı (java verify/SEN1002/W01.java); derleme hatası soruları javac ile denendi (verify/SEN1002/err/E01a-d.java; E01d flexible constructor kuralı için --release 21 ile de).

Deftere not kartı

class = kalıp, object = new ile yaratılan örnek. Değişken nesneyi değil ADRESİ tutar. Alan varsayılanları: int 0, double 0.0, boolean false, referans null. Yerel değişken: varsayılan YOK. Constructor: sınıfla aynı ad, dönüş tipi yok. Bir tane yazarsan default constructor GİDER. this.x = x → gölgelemeyi çöz. name = name; alanı değiştirmez (null kalır). this(...) ilk satır olmalı (Java 25+ öncesine this kullanmayan satır izin verir); en içteki önce biter. Kapsülleme: alan private, erişim get/set, kontrol setter'da. static alan: sınıfa ait tek kopya (sayaç). instance alan: her nesneye ayrı. static metot içinde this yok, instance alana doğrudan erişemez. b = a → aynı nesne (alias). Biri değişirse öteki de görür. Java pass-by-value: int kopyası / adres kopyası. Parametreye new atamak dışarıyı değiştirmez. == adres karşılaştırır, equals içerik (override edildiyse). String: hep equals. Override edilmemiş equals = ==. toString yoksa SinifAdi@hash basılır.