Kalıtım: extends, super ve constructor zinciri
Alt sınıf üst sınıftan ne alır, constructor'lar hangi sırayla çalışır, overriding ile overloading farkı. Çıktı sorularının favorisi.
Kalıtım (inheritance), var olan bir sınıfın üstüne yeni bir sınıf kurmaktır. Yeni sınıf (alt sınıf, subclass) eskisinin (üst sınıf, superclass) alanlarını ve metotlarını alır, üstüne kendi şeylerini ekler ya da bazı metotları değiştirir. Sınavda bu konu en çok iki biçimde gelir: constructor’ların hangi sırayla bastığını soran çıktı soruları ve super ile ilgili derleme hataları.
Altın kurallar:
- Her constructor önce üst sınıfın constructor’ını çağırır. Yazmazsan derleyici
super();ekler. - Çıktı sırası tepeden aşağı: önce Object, sonra üst sınıf, en son alt sınıf.
- private alan miras alınır ama adıyla erişilemez. Alt sınıf getter kullanır ya da alan
protectedolur. - Override: aynı imza, nesnenin gerçek tipine göre çalışır. Overload: farklı parametreler, derleme zamanında seçilir.
- Alanlar override edilmez. Sadece metotlar.
extends ve “is-a” ilişkisi
class Manager extends Employee yazınca “her Manager bir Employee’dir” demiş olursun (is-a relationship). Manager, Employee’nin bütün public ve protected üyelerini kendi üyesiymiş gibi kullanır. Java’da bir sınıf sadece bir sınıftan türeyebilir (single inheritance). Birden fazla tip davranışı istiyorsan arayüz (interface) kullanırsın (4. hafta).
Kalıtımı ne zaman kullanacağını “is-a” testiyle anlarsın. “Bir Manager bir Employee’dir” doğru, kalıtım uygun. “Bir Car bir Engine’dir” yanlış; arabanın bir motoru vardır (has-a). O durumda Car içine bir Engine alanı koyarsın (composition, 7. hafta).
class Employee {
private String name;
protected double salary; // alt sınıflar doğrudan görebilir
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
System.out.println("Employee ctor: " + name);
}
public String getName() { return name; }
public double pay() { return salary; }
@Override
public String toString() { return "Employee " + name + " " + pay(); }
}
class Manager extends Employee {
private double bonus; // Manager'a özel
public Manager(String name, double salary, double bonus) {
super(name, salary); // üst sınıfın constructor'ı
this.bonus = bonus;
System.out.println("Manager ctor: bonus " + bonus);
}
@Override
public double pay() { return super.pay() + bonus; }
@Override
public String toString() { return "Manager " + getName() + " " + pay(); }
}
Manager m = new Manager("Selin", 5000, 1500);
System.out.println(m);
System.out.println(m.pay());
Employee ctor: Selin
Manager ctor: bonus 1500.0
Manager Selin 6500.0
6500.0
Manager’ın toString’inde name değil getName() yazdık. name Employee’de private; Manager içinde name yazsaydın name has private access in Employee derleme hatası alırdın.
Constructor zinciri (constructor chaining)
Constructor’lar miras alınmaz. Ama alt sınıf nesnesi yaratılırken üst sınıfın parçası da kurulmak zorunda. Bu yüzden her constructor, ilk satırında üst sınıfın bir constructor’ını çağırır:
super(argümanlar)yazarsan o constructor çağrılır.- Hiçbir şey yazmazsan derleyici gizlice
super();ekler (parametresiz). - Üst sınıfta parametresiz constructor yoksa bu gizli çağrı derleme hatası verir.
class A { A() { System.out.println("A"); } }
class B extends A { B() { System.out.println("B"); } } // gizli super()
class C extends B { C() { super(); System.out.println("C"); } }
new C();
A
B
C
new C()
C() çağrıldı → ilk iş super() → B()
B() çağrıldı → ilk iş super() → A()
A() çağrıldı → super() → Object() (sessiz)
A gövdesi: "A"
B gövdesi: "B"
C gövdesi: "C"
TUZAK ·
this(...)vesuper(...)birlikte. Bir constructor yathis(...)yasuper(...)ile başlar, ikisi birden olmaz.this(...)ile başlayan constructor üst sınıfı kendisi çağırmaz; zincirin sonundaki constructor çağırır. Aşağıdaki Truck örneği bunu gösteriyor.
class Vehicle {
int wheels;
Vehicle(int w) { wheels = w; System.out.println("Vehicle " + w); }
Vehicle() { this(4); System.out.println("Vehicle()"); }
}
class Bike extends Vehicle { Bike() { super(2); System.out.println("Bike"); } }
class Truck extends Vehicle { Truck() { System.out.println("Truck"); } }
Bike b = new Bike();
Truck t = new Truck();
System.out.println(b.wheels + " " + t.wheels);
Vehicle 2
Bike
Vehicle 4
Vehicle()
Truck
2 4
Alan ilk değerleri ne zaman atanır?
Nesne yaratılırken sıra şudur: (1) bütün alanlar varsayılan değer alır (0, null, false), (2) super(...) çalışır, (3) bu sınıfın alan ilk değerleri (int x = 7;) atanır, (4) constructor gövdesinin kalanı çalışır. Üst sınıfın constructor’ı override edilmiş bir metodu çağırırsa, o metot alt sınıfın alanlarını henüz varsayılan değerinde görür:
class Base {
Base() { System.out.println("Base ctor"); hello(); }
void hello() { System.out.println("Base hello"); }
}
class Derived extends Base {
int x = 7;
Derived() { System.out.println("Derived ctor x=" + x); }
@Override void hello() { System.out.println("Derived hello x=" + x); }
}
new Derived();
Base ctor
Derived hello x=0
Derived ctor x=7
TUZAK ·
super(...)ilk satır kuralı. Ders kitabı ve sınav,super(...)çağrısının constructor’ın ilk satırı olması gerektiğini esas alır; Java 24 ve öncesinde öyledir. Java 25’teki flexible constructor bodies özelliği, öncesinethis’e dokunmayan satırlara izin verdi (--release 21ile derlersenflexible constructors is not supportedhatası alırsın). Sınavda “super ilk satırda değil” diye bir şık görürsen eski kurala göre cevapla: derleme hatası.
protected ve erişim
| Üst sınıftaki üye | Alt sınıf içinden erişim |
|---|---|
public | evet |
protected | evet (alt sınıf başka pakette olsa bile) |
| belirleyicisiz (package-private) | sadece aynı paketteyse |
private | hayır (nesnede var, adıyla erişilemez) |
protected kolaylık sağlar ama kapsüllemeyi biraz gevşetir. Çoğu durumda alanı private tutup getter vermek daha iyidir; sınavda iki çözüm de kabul görür.
Overriding ve overloading
İki kavram adları yüzünden karıştırılır ama tamamen farklı mekanizmalar:
| Overriding (geçersiz kılma) | Overloading (aşırı yükleme) | |
|---|---|---|
| Nerede? | Alt sınıfta, üst sınıftaki metodu yeniden yazarsın | Aynı sınıfta (ya da miras alınanla) aynı ad |
| Parametre listesi | Aynı | Farklı (sayı, tip ya da sıra) |
| Dönüş tipi | Aynı ya da alt tipi | Önemli değil, tek başına ayırt etmez |
| Erişim | Daralamaz (public → public) | Serbest |
| Seçim ne zaman? | Çalışma zamanında, nesnenin gerçek tipine göre | Derleme zamanında, argüman tiplerine göre |
@Override | Yazılır | Yazılmaz |
class Printer {
void show(int a) { System.out.println("int " + a); }
void show(double a) { System.out.println("double " + a); }
void show(String s) { System.out.println("String " + s); }
}
Printer pr = new Printer();
pr.show(3);
pr.show(3.0);
pr.show('A'); // char → int'e genişler
pr.show(5L); // long → double'a genişler
pr.show("3");
int 3
double 3.0
int 65
double 5.0
String 3
Override edilen metodun içinde super.metot() ile üst sınıfın sürümünü çağırabilirsin. Bu, kodu tekrar yazmadan üstüne eklemenin standart yolu:
class Shape { String name() { return "shape"; } }
class Circle extends Shape {
@Override String name() { return "circle<" + super.name() + ">"; }
}
System.out.println(new Circle().name());
circle<shape>
Alan gizleme (field hiding)
Alt sınıf üst sınıfla aynı adda bir alan tanımlarsa iki ayrı alan olur. Hangi alanın okunacağına değişkenin tipi karar verir, nesnenin değil. Metotlarda ise nesnenin gerçek tipi karar verir.
class P { String tag = "P"; String getTag() { return tag; } }
class Q extends P { String tag = "Q"; @Override String getTag() { return tag; } }
P obj = new Q();
System.out.println(obj.tag + " " + obj.getTag());
P Q
Object sınıfı
extends yazmadığın her sınıf otomatik olarak java.lang.Object’ten türer. Yani her nesne şu metotlara sahiptir:
| Metot | Varsayılan davranış | Genelde ne yapılır? |
|---|---|---|
toString() | SinifAdi@hash | Okunabilir metin için override |
equals(Object o) | this == o | İçerik karşılaştırması için override |
hashCode() | Nesneye özgü sayı | equals override edilirse bu da edilir |
getClass() | Çalışma zamanındaki sınıf | getClass().getSimpleName() gibi kullanılır |
Book b1 = new Book("Dune"), b2 = new Book("Dune"); // equals override YOK
System.out.println(b1.equals(b2));
System.out.println(b1.getClass().getSimpleName());
System.out.println(b1 instanceof Object);
false
Book
true
final
final class: kimse ondan türeyemez (String,Integerböyle).finalmetot: alt sınıf override edemez, ama overload serbest.finalalan: bir kez atanır, sonra değişmez.
Sık yapılan hatalar
| Yanlış düşünce | Doğrusu |
|---|---|
| Constructor’lar miras alınır | Alınmaz; her sınıf kendi constructor’ını yazar, super(...) ile üsttekini çağırır |
| super yazmazsam üst sınıf kurulmaz | Derleyici gizli super() ekler; üstte parametresiz constructor yoksa hata |
| Çıktı alt sınıftan başlar | Gövdeler tepeden aşağı çalışır: A, B, C |
| Alt sınıf private alanı kullanabilir | Adıyla erişemez; getter ya da protected |
| Aynı ad, farklı parametre = override | Bu overload; override için parametreler aynı olmalı |
| Override ederken public’i silebilirim | Erişim daraltılamaz |
| Alanlar da override edilir | Gizlenir; ref.alan değişkenin tipine bakar |
| Üst constructor’ın çağırdığı metot üst sınıfınki | Override edilmişse alt sınıfınki çalışır, alanları henüz varsayılanda |
super.super.metot() | Böyle bir şey yok, sadece bir üst |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
What is printed by new C();?
class A { A() { System.out.println("A"); } }
class B extends A { B() { System.out.println("B"); } }
class C extends B { C() { super(); System.out.println("C"); } }Cevabı göster
Her constructor'ın ilk işi üst sınıfın constructor'ını çağırmak. B'de super() yazılmasa da derleyici ekler. Çağrı C → B → A diye iner, ama gövdeler en tepeden başlayarak çalışır: önce A basılır, sonra B, en son C.
What is printed?
class Employee {
private String name; protected double salary;
Employee(String name, double salary) {
this.name = name; this.salary = salary;
System.out.println("Employee ctor: " + name);
}
String getName() { return name; }
double pay() { return salary; }
}
class Manager extends Employee {
private double bonus;
Manager(String n, double s, double b) {
super(n, s); bonus = b;
System.out.println("Manager ctor: bonus " + bonus);
}
@Override double pay() { return super.pay() + bonus; }
}
// in main:
Manager m = new Manager("Selin", 5000, 1500);
System.out.println(m.pay());Cevabı göster
super(n, s) önce Employee'nin constructor'ını çalıştırır: Employee ctor: Selin. Sonra Manager'ın gövdesi: bonus double olduğu için 1500.0. pay() override edilmiş: super.pay() 5000.0 döner, üstüne 1500 eklenir: 6500.0.
Find the compile error.
class Vehicle {
int wheels;
Vehicle(int w) { wheels = w; }
}
class Car extends Vehicle {
Car() { System.out.println("Car"); }
}Cevabı göster
super(...) yazmazsan derleyici super(); ekler. Vehicle sadece Vehicle(int) tanımladığı için parametresiz constructor yok. javac: constructor Vehicle in class Vehicle cannot be applied to given types; required: int. Çözüm: Car'ın ilk satırına super(4); yaz.
Find the compile error.
class Employee {
private double salary = 100;
}
class Manager extends Employee {
double pay() { return salary * 2; }
}Cevabı göster
Alt sınıf private alanı miras alır (nesnenin içinde vardır) ama adıyla erişemez. javac: salary has private access in Employee. Çözüm: alanı protected yap ya da Employee'ye getSalary() ekleyip onu çağır.
What is printed?
class Printer {
void show(int a) { System.out.println("int " + a); }
void show(double a) { System.out.println("double " + a); }
void show(String s) { System.out.println("String " + s); }
}
// in main:
Printer p = new Printer();
p.show('A');
p.show(5L);Cevabı göster
Overloading'de hangi metodun çağrılacağını derleyici argüman tipine bakarak seçer. 'A' bir char, birebir eşleşme yok; char'ın genişleyebildiği en yakın tip int: int 65 (A'nın kodu). 5L long; long int'e daralamaz, double'a genişler: double 5.0.
What is printed?
class Animal {
String sound() { return "..."; }
String describe() { return "I say " + sound(); }
}
class Dog extends Animal {
@Override String sound() { return "Woof"; }
}
// in main:
Animal d = new Dog();
System.out.println(d.describe());Cevabı göster
describe() Animal'da yazılı ama içindeki sound() çağrısı o anki nesnenin, yani Dog'un sürümünü çalıştırır. Override edilmiş metot her zaman nesnenin gerçek sınıfından seçilir (3. haftada dynamic binding).
Find the compile error.
class Animal {
public String sound() { return "..."; }
}
class Cat extends Animal {
String sound() { return "Meow"; }
}Cevabı göster
Override ederken erişim belirleyici aynı kalabilir ya da genişleyebilir, daralamaz. Animal'da public olan metot Cat'te belirleyicisiz (package-private) yazılmış. javac: attempting to assign weaker access privileges; was public. Çözüm: Cat'te de public yaz.
What is printed?
class Vehicle {
int wheels;
Vehicle(int w) { wheels = w; System.out.println("Vehicle " + w); }
Vehicle() { this(4); System.out.println("Vehicle()"); }
}
class Bike extends Vehicle { Bike() { super(2); System.out.println("Bike"); } }
class Truck extends Vehicle { Truck() { System.out.println("Truck"); } }
// in main:
Bike b = new Bike();
Truck t = new Truck();
System.out.println(b.wheels + " " + t.wheels);Cevabı göster
Bike açıkça super(2) çağırır: Vehicle 2, sonra Bike. Truck'ta super yazılmamış, gizli super() parametresiz Vehicle'ı çağırır; o da this(4) ile Vehicle(int)'e devreder: önce Vehicle 4, sonra Vehicle(), en son Truck. Tekerlekler 2 ve 4.
What is printed?
class Base {
Base() { System.out.println("Base ctor"); hello(); }
void hello() { System.out.println("Base hello"); }
}
class Derived extends Base {
int x = 7;
Derived() { System.out.println("Derived ctor x=" + x); }
@Override void hello() { System.out.println("Derived hello x=" + x); }
}
// in main:
new Derived();Cevabı göster
Nesne Derived olduğu için Base'in constructor'ı içindeki hello() çağrısı bile Derived'ın sürümünü çalıştırır. Ama o an Derived'ın alan ilk değerleri (x = 7) henüz atanmadı; bu atama super() bittikten sonra yapılır. x varsayılan değeri 0 ile basılır. Sonra x = 7 olur ve Derived'ın gövdesi çalışır.
What is printed?
class P { String tag = "P"; String getTag() { return tag; } }
class Q extends P { String tag = "Q"; @Override String getTag() { return tag; } }
// in main:
P obj = new Q();
System.out.println(obj.tag + " " + obj.getTag());Cevabı göster
Alanlar override edilmez, gizlenir (field hiding). obj.tag derleme zamanındaki tipe (P) bakar: "P". Metotlar ise override edilir: getTag() Q'nun sürümü çalışır ve Q'nun tag'ini döner: "Q". Bu yüzden alt sınıfta üst sınıfla aynı adlı alan açma.
Which statement about final is true? (a) A final method cannot be overloaded. (b) A final class cannot be extended. (c) A final field can be changed in a subclass.
- (a)
- (b)
- (c)
Cevabı göster
final class miras alınamaz: class Key extends Locked için javac cannot inherit from final Locked der (String de final bir sınıftır). final metot override edilemez ama overload edilebilir; (a) yanlış. final alan bir kez atanır, kimse değiştiremez; (c) yanlış.
Write a class Student that extends Person (which has a private name, a constructor Person(String name), getName() and toString() returning Person: <name>). Student adds a private double gpa, a constructor Student(String name, double gpa), and overrides toString() to return Person: Deniz, GPA 3.4 style text.
Cevabı göster
name Person'da private, bu yüzden Student onu ancak super(name) ile doldurabilir. toString'de üst sınıfın metnini tekrar yazmak yerine super.toString() çağır:
public class Student extends Person {
private double gpa; public Student(String name, double gpa) {
super(name); // ilk satır
this.gpa = gpa;
}
public double getGpa() { return gpa; } @Override
public String toString() {
return super.toString() + ", GPA " + gpa;
}
}new Student("Deniz", 3.4) basılınca: Person: Deniz, GPA 3.4.Doğrulama: Tüm kod örnekleri ve cevaplar JDK 26 ile çalıştırıldı (java verify/SEN1002/W02.java); derleme hataları javac ile denendi (verify/SEN1002/err/E02a-e.java, E02d ayrıca --release 21 ile).