3. Sınıflar ve arayüzler
3.1. Örneklerle nesne
3.1.1. Genel Bilgiler
Şimdi örnekler üzerinden nesne yönelimli programlamayı ele alacağız. Bir nesne, durumunu tanımlayan verileri (bunlara öznitelikler veya özellikler denir) ve işlevleri (bunlara yöntemler denir) içeren bir varlıktır. Bir nesne, sınıf adı verilen bir modele göre oluşturulur:
public class C1{
type1 p1; // p1 özelliği
type2 p2; // p2 özelliği
…
type3 m3(…){ // m3 yöntemi
…
}
type4 m4(…){ // m4 yöntemi
…
}
…
}
Önceki C1 sınıfından yola çıkarak, çok sayıda O1, O2,… nesnesi oluşturulabilir. Hepsi p1, p2,… özelliklerine ve m3, m4,… yöntemlerine sahip olacaktır. pi nesnelerinin özellikleri farklı değerlere sahip olacak ve böylece her biri kendine özgü bir duruma sahip olacaktır.
O1, C1 türünde bir nesne ise, O1.p1, O1’in p1 özelliğini, O1.m1 ise O1'in yöntemini ifade eder.
İlk nesne modelini ele alalım: personne sınıfı.
3.1.2. Kişi sınıfının tanımı
personne sınıfının tanımı şu şekilde olacaktır:
import java.io.*;
public class personne{
// özellikler
private String prenom;
private String nom;
private int age;
// yöntem
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
// yöntem
public void identifie(){
System.out.println(prenom+","+nom+","+age);
}
}
Burada bir sınıfın, yani bir veri türünün tanımını görüyoruz. Bu türden değişkenler oluşturduğumuzda, bunlara nesne adını vereceğiz. Dolayısıyla bir sınıf, nesnelerin oluşturulduğu bir kalıptır.
Bir sınıfın üyeleri veya alanları, veriler veya yöntemler (fonksiyonlar) olabilir. Bu alanlar, aşağıdaki üç öznitelikten birine sahip olabilir:
privé: Özel (private) bir alana yalnızca sınıfın iç yöntemleri erişebilir
public: Bir public alan, sınıf içinde tanımlanmış olsun ya da olmasın tüm fonksiyonlar tarafından erişilebilir
protégé: Korumalı (protected) bir alana yalnızca sınıfın veya türetilmiş bir nesnenin (daha sonra miras kavramına bakınız) iç yöntemleri erişebilir.
Genel olarak, bir sınıfın verileri özel olarak, yöntemleri ise genel olarak tanımlanır. Bu, bir nesnenin kullanıcısının (programcı)
a: nesnenin özel verilerine doğrudan erişemez
b: nesnenin genel yöntemlerini ve özellikle de özel verilerine erişim sağlayan yöntemleri kullanabilir.
Bir nesnenin bildirim sözdizimi şöyledir:
public class nomClasse{
private donnée ou méthode privée
public donnée ou méthode publique
protected donnée ou méthode protégée
}
Remarques
- private, protected ve public özniteliklerinin bildirilme sırası herhangi bir şekilde olabilir.
3.1.3. Yöntem,
Şu şekilde tanımlanan kişi sınıfımıza geri dönelim:
import java.io.*;
public class personne{
// özellikler
private String prenom;
private String nom;
private int age;
// yöntem
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
// yöntem
public void identifie(){
System.out.println(prenom+","+nom+","+age);
}
}
initialise yönteminin rolü nedir? Soyadı, adı ve yaş, “kişi” sınıfına ait özel veriler olduğundan, şu komutlar
komutları geçersizdir. personne türünde bir nesneyi genel bir yöntem aracılığıyla başlatmamız gerekir. Bu, initialise yönteminin görevidir. Şöyle yazacağız:
p1.initialise yazımı, initialise'in erişimi herkese açık olduğu için geçerlidir.
3.1.4. new operatörü
Komut dizisi
hatalıdır.
komutu, p1'i personne türünde bir nesneye referans olarak tanımlamaktadır. Bu nesne henüz mevcut değildir ve bu nedenle p1 başlatılmamıştır. Bu, sanki şunu yazmış gibi bir durumdur:
burada null anahtar sözcüğüyle, p1 değişkeninin henüz hiçbir nesneye referans vermediğini açıkça belirtmiş oluruz.
Daha sonra şunu yazdığımızda
yazdığımızda, p1 tarafından referans verilen nesnenin initialise yöntemini çağırmış oluruz. Ancak bu nesne henüz mevcut değildir ve derleyici bir hata bildirir. p1'in bir nesneye referans vermesi için şunu yazmak gerekir:
Bu, henüz başlatılmamış bir personne türü nesne oluşturur: nom ve prenom öznitelikleri, String türündeki nesnelere referanslar olduğundan null değerini alacak, ve age ise 0 değerini alacaktır. Dolayısıyla varsayılan bir başlatma söz konusudur. Artık p1 bir nesneye referans verdiğine göre, bu nesnenin başlatma komutu
geçerlidir.
3.1.5. this anahtar sözcüğü
initialise yönteminin koduna bakalım:
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
this.prenom=P komutu, geçerli nesnenin (this) prenom özniteliğinin P değerini aldığını ifade eder. this anahtar sözcüğü, geçerli nesneyi belirtir: yani, yürütülen yöntemin bulunduğu nesneyi. Bunu nasıl anlarız? Çağıran programda p1 tarafından referans verilen nesnenin nasıl başlatıldığını inceleyelim:
Çağrılan, p1 nesnesinin initialise yöntemidir. Bu yöntem içinde this nesnesine atıfta bulunulduğunda, aslında p1 nesnesine atıfta bulunulur. initialise yöntemi şu şekilde de yazılabilirdi:
public void initialise(String P, String N, int age){
prenom=P;
nom=N;
this.age=age;
}
Bir nesnenin yöntemi, bu nesnenin A özniteliğine başvurduğunda, this.A yazımı örtük olarak kullanılır. Tanımlayıcı çakışması olduğunda ise bu yazım açıkça kullanılmalıdır. Aşağıdaki komutda durum böyledir:
this.age=age;
burada age, hem geçerli nesnenin bir özniteliğini hem de yöntem tarafından alınan age parametresini ifade eder. Bu durumda, age özniteliğini this.age olarak belirtmek suretiyle belirsizliği gidermek gerekir.
3.1.6. Bir test programı
İşte bir test programı:
public class test1{
public static void main(String arg[]){
personne p1=new personne();
p1.initialise("Jean","Dupont",30);
p1.identifie();
}
}
personne sınıfı, personne.java kaynak dosyasında tanımlanmıştır ve derlenmiştir:
E:\data\serge\JAVA\BASES\OBJETS\2>javac personne.java
E:\data\serge\JAVA\BASES\OBJETS\2>dir
10/06/2002 09:21 473 personne.java
10/06/2002 09:22 835 personne.class
10/06/2002 09:23 165 test1.java
Aynı işlemi test programı için de yapıyoruz:
E:\data\serge\JAVA\BASES\OBJETS\2>javac test1.java
E:\data\serge\JAVA\BASES\OBJETS\2>dir
10/06/2002 09:21 473 personne.java
10/06/2002 09:22 835 personne.class
10/06/2002 09:23 165 test1.java
10/06/2002 09:25 418 test1.class
test1.java programının, personne sınıfını şu komutla içe aktarmaması şaşırtıcı olabilir:
Derleyici, kaynak kodda aynı kaynak dosyada tanımlanmamış bir sınıf referansı ile karşılaştığında, sınıfı çeşitli yerlerde arar:
- import komutlarıyla içe aktarılan paketlerde
- derleyicinin başlatıldığı dizinde
Örneğimizde, derleyici personne.class dosyasını içeren dizinden başlatılmıştır; bu da personne sınıfının tanımını bulmasını açıklamaktadır. Bu durumda bir import komutu eklenmesi, derleme hatasına neden olur:
E:\data\serge\JAVA\BASES\OBJETS\2>javac test1.java
test1.java:1: '.' expected
import personne;
^
1 error
Bu hatayı önlemek ve "person" sınıfının içe aktarılması gerektiğini hatırlatmak için, bundan sonra programın başına şunu yazacağız:
Artık test1.class dosyasını çalıştırabiliriz:
Aynı kaynak dosyada birden fazla sınıf bir araya getirilebilir. Öyleyse, personne ve test1 sınıflarını test2.java kaynak dosyasında bir araya getirelim. test1 sınıfı, kaynak dosyanın adındaki değişikliği yansıtmak için test2 olarak yeniden adlandırılır:
// ithal edilen paketler
import java.io.*;
class personne{
// özellikler
private String prenom; // kişimin adı
private String nom; // soyadı
private int age; // yaşı
// yöntem
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}//başlatır
// yöntem
public void identifie(){
System.out.println(prenom+","+nom+","+age);
}//tanımlar
}//sınıf
public class test2{
public static void main(String arg[]){
personne p1=new personne();
p1.initialise("Jean","Dupont",30);
p1.identifie();
}
}
personne sınıfının artık public özniteliğine sahip olmadığına dikkat edilmelidir. Aslında, bir Java kaynak dosyasında yalnızca bir sınıf public özniteliğine sahip olabilir. Bu, main işlevine sahip olan sınıftır. Ayrıca, kaynak dosyanın adı da bu işlevin adını taşımalıdır. test2.java dosyasını derleyelim:
E:\data\serge\JAVA\BASES\OBJETS\3>dir
10/06/2002 09:36 633 test2.java
E:\data\serge\JAVA\BASES\OBJETS\3>javac test2.java
E:\data\serge\JAVA\BASES\OBJETS\3>dir
10/06/2002 09:36 633 test2.java
10/06/2002 09:41 832 personne.class
10/06/2002 09:41 418 test2.class
Kaynak dosyada bulunan her sınıf için bir .class dosyası oluşturulduğu görülecektir. Şimdi test2.class dosyasını çalıştıralım:
Bundan sonra, her iki yöntemi de aynı şekilde kullanacağız:
- tek bir kaynak dosyada toplanan sınıflar
- her kaynak dosyada bir sınıf
3.1.7. Başka bir yöntem başlatılır
Yine personne sınıfını ele alalım ve ona aşağıdaki yöntemi ekleyelim:
public void initialise(personne P){
prenom=P.prenom;
nom=P.nom;
this.age=P.age;
}
Şu anda initialise adını taşıyan iki yöntemimiz var: farklı parametreleri kabul ettikleri sürece bu durum geçerlidir. Burada da durum böyledir. Parametre artık bir kişiye ait P referansıdır. P kişisinin öznitelikleri, bu durumda geçerli nesneye (this) atanır. initialise yönteminin, P nesnesinin özniteliklerine doğrudan erişimi olduğu, ancak bu özniteliklerin private türünde olduğu dikkatinizi çekecektir. Bu her zaman geçerlidir: C sınıfına ait bir O1 nesnesinin yöntemleri, her zaman aynı C sınıfına ait diğer nesnelerin özel özniteliklerine erişebilir.
İşte yeni personne sınıfının bir testi:
// kişi içe aktar;
import java.io.*;
public class test1{
public static void main(String arg[]){
personne p1=new personne();
p1.initialise("Jean","Dupont",30);
System.out.print("p1=");
p1.identifie();
personne p2=new personne();
p2.initialise(p1);
System.out.print("p2=");
p2.identifie();
}
}
ve sonuçları:
3.1.8. Person sınıfının oluşturucuları
Oluşturucu, sınıfın adını taşıyan ve nesnenin oluşturulması sırasında çağrılan bir yöntemdir. Genellikle nesneyi başlatmak için kullanılır. Bu yöntem, argümanları kabul edebilir ancak herhangi bir sonuç döndürmez. Prototipi veya tanımının önünde herhangi bir tür belirtilmez (void bile).
Bir sınıfın n adet argi argümanı kabul eden bir oluşturucusu varsa, bu sınıfa ait bir nesnenin bildirilmesi ve başlatılması şu şekilde yapılabilir:
classe objet =new classe(arg1,arg2, ... argn);
veya
classe objet;
…
objet=new classe(arg1,arg2, ... argn);
Bir sınıfın bir veya daha fazla yapıcı işlevi varsa, bu sınıftan bir nesne oluşturmak için bu yapıcı işlevlerden birinin kullanılması zorunludur. C sınıfının hiçbir yapıcı işlevi yoksa, varsayılan olarak parametresiz yapıcı işlevi olan public C() kullanılır. Bu durumda nesnenin özellikleri varsayılan değerlerle başlatılır. Önceki programlarda şu kod yazıldığında da bu durum gerçekleşmiştir:
personne sınıfımıza iki yapıcı oluşturalım:
public class personne{
// özellikler
private String prenom;
private String nom;
private int age;
// yapıcılar
public personne(String P, String N, int age){
initialise(P,N,age);
}
public personne(personne P){
initialise(P);
}
// yöntem
public void initialise(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
public void initialise(personne P){
this.prenom=P.prenom;
this.nom=P.nom;
this.age=P.age;
}
// yöntem
public void identifie(){
System.out.println(prenom+","+nom+","+age);
}
}
Her iki yapıcı da sadece ilgili initialise yöntemlerini çağırmakla yetinir. Bir yapıcıda, örneğin initialise(P) notasyonu bulunduğunda, derleyicinin bunu this.initialise(P) olarak çevirdiğini hatırlatırız. Dolayısıyla, oluşturucuda initialise yöntemi, this tarafından referans verilen nesne, yani o anda oluşturulmakta olan nesne üzerinde işlem yapmak üzere çağrılır.
İşte bir test programı:
// person import;
import java.io.*;
public class test1{
public static void main(String arg[]){
personne p1=new personne("Jean","Dupont",30);
System.out.print("p1=");
p1.identifie();
personne p2=new personne(p1);
System.out.print("p2=");
p2.identifie();
}
}
ve elde edilen sonuçlar:
3.1.9. Nesne referansları
Her zaman aynı personne sınıfını kullanıyoruz. Test programı şu şekilde olur:
// person'u içe aktar;
import java.io.*;
public class test1{
public static void main(String arg[]){
// p1
personne p1=new personne("Jean","Dupont",30);
System.out.print("p1="); p1.identifie();
// p2, p1 ile aynı nesneye başvurur
personne p2=p1;
System.out.print("p2="); p2.identifie();
// p3, p1'in işaret ettiği nesnenin bir kopyası olacak bir nesneye işaret eder
personne p3=new personne(p1);
System.out.print("p3="); p3.identifie();
// p1 tarafından referans verilen nesnenin durumu değiştirilir
p1.initialise("Micheline","Benoît",67);
System.out.print("p1="); p1.identifie();
// p2=p1 olduğu için, p2'nin işaret ettiği nesnenin durumu değişmiş olmalıdır
System.out.print("p2="); p2.identifie();
// p3, p1 ile aynı nesneye referans vermediğinden, p3 tarafından referans verilen nesnenin durumu değişmemiş olmalıdır
System.out.print("p3="); p3.identifie();
}
}
Elde edilen sonuçlar şunlardır:
p1=Jean,Dupont,30
p2=Jean,Dupont,30
p3=Jean,Dupont,30
p1=Micheline,Benoît,67
p2=Micheline,Benoît,67
p3=Jean,Dupont,30
p1 değişkeni şu şekilde tanımlandığında
p1, personne("Jean","Dupont",30) nesnesine başvurur, ancak nesnenin kendisi değildir. C dilinde buna bir işaretçi denir; c.a.d, oluşturulan nesnenin adresidir. Ardından şunu yazarsak:
Değiştirilen personne("Jean","Dupont",30) nesnesi değil, değeri değişen p1 referansıdır. "Jean", "Dupont", 30 nesnesi, başka hiçbir değişken tarafından referanslanmazsa "kaybolur".
Şu kod yazıldığında:
yazıldığında, p2 işaretçisi başlatılır: bu işaretçi, p1 işaretçisiyle aynı nesneye "işaret eder" (aynı nesneyi gösterir). Dolayısıyla, p1 tarafından "işaret edilen" (veya referans verilen) nesne değiştirilirse, p2 tarafından referans verilen nesne de değiştirilmiş olur.
Şu kod yazıldığında:
yazıldığında, p1 tarafından referans verilen nesnenin bir kopyası olan yeni bir nesne oluşturulur. Bu yeni nesne, p3 tarafından referans verilecektir. p1 tarafından "işaret edilen" (veya referans verilen) nesne değiştirilirse, p3 tarafından referans verilen nesne hiçbir şekilde değiştirilmez. Elde edilen sonuçlar da bunu göstermektedir.
3.1.10. Geçici nesneler
Bir ifadede, bir nesnenin oluşturucusuna açıkça başvurulabilir: nesne oluşturulur, ancak ona erişimimiz yoktur (örneğin, onu değiştirmek için). Bu geçici nesne, ifadenin değerlendirilmesi için oluşturulur ve ardından terk edilir. İşgal ettiği bellek alanı, daha sonra "çöp toplayıcı" adı verilen bir program tarafından otomatik olarak geri kazanılır; bu programın görevi, program verileri tarafından artık referans gösterilmeyen nesnelerin işgal ettiği bellek alanını geri kazanmaktır.
Şu örneği ele alalım:
// import person;
public class test1{
public static void main(String arg[]){
new personne(new personne("Jean","Dupont",30)).identifie();
}
}
ve personne sınıfındaki oluşturucuları, bir mesaj görüntüleyecek şekilde değiştirelim:
// üreticiler
public personne(String P, String N, int age){
System.out.println("Constructeur personne(String, String, int)");
initialise(P,N,age);
}
public personne(personne P){
System.out.println("Constructeur personne(personne)");
initialise(P);
}
Şu sonuçları elde ediyoruz:
Bu, iki geçici nesnenin art arda oluşturulduğunu göstermektedir.
3.1.11. Özel özniteliklerin okunması ve yazılması yöntemleri
personne sınıfına, nesnelerin özniteliklerinin durumunu okumak veya değiştirmek için gerekli yöntemleri ekliyoruz:
public class personne{
private String prenom;
private String nom;
private int age;
public personne(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
public personne(personne P){
this.prenom=P.prenom;
this.nom=P.nom;
this.age=P.age;
}
public void identifie(){
System.out.println(prenom+","+nom+","+age);
}
// donanım üreticileri
public String getPrenom(){
return prenom;
}
public String getNom(){
return nom;
}
public int getAge(){
return age;
}
// değiştiriciler
public void setPrenom(String P){
this.prenom=P;
}
public void setNom(String N){
this.nom=N;
}
public void setAge(int age){
this.age=age;
}
}
Yeni sınıfı aşağıdaki programla test ediyoruz:
// kişi içe aktar;
public class test1{
public static void main(String[] arg){
personne P=new personne("Jean","Michelin",34);
System.out.println("P=("+P.getPrenom()+","+P.getNom()+","+P.getAge()+")");
P.setAge(56);
System.out.println("P=("+P.getPrenom()+","+P.getNom()+","+P.getAge()+")");
}
}
ve şu sonuçları elde ediyoruz:
3.1.12. Yöntemler ve sınıf öznitelikleri
Bir uygulamada oluşturulan personne nesnelerinin sayısını saymak istediğimizi varsayalım. Bir sayacı kendimiz yönetebiliriz, ancak burada veya orada oluşturulan geçici nesneleri unutma riski vardır. personne sınıfının oluşturucularına, bir sayacı artıran bir komut eklemek daha güvenli görünmektedir. Sorun, oluşturucunun sayacı artırabilmesi için bu sayaca bir referans aktarmaktır: onlara yeni bir parametre aktarmak gerekir. Sayacı sınıf tanımına da dahil edebiliriz. Bu, sınıfın kendisinin bir özniteliği olduğu ve bu sınıfa ait belirli bir nesnenin özniteliği olmadığı için, static anahtar sözcüğüyle farklı şekilde tanımlanır:
Buna başvurmak için, personne.nbPersonnes yazarak bunun personne sınıfının kendisinin bir özniteliği olduğunu gösteririz. Burada, sınıfın dışından doğrudan erişilemeyen özel bir öznitelik oluşturduk. Bu nedenle, nbPersonnes sınıf özniteliğine erişim sağlamak için genel bir yöntem oluşturuyoruz. nbPersonnes değerini almak için yöntemin belirli bir nesneye ihtiyacı yoktur: zira nbPersonnes belirli bir nesnenin özniteliği değil, tüm sınıfın özniteliğidir. Bu nedenle, yine static olarak tanımlanmış bir sınıf yöntemine ihtiyaç vardır:
Bu yöntem, dışarıdan personne.getNbPersonnes() sözdizimiyle çağrılacaktır. İşte bir örnek.
personne sınıfı şu şekilde olur:
public class personne{
// sınıf özniteliği
private static long nbPersonnes=0;
// nesne öznitelikleri
…
// yapıcılar
public personne(String P, String N, int age){
initialise(P,N,age);
nbPersonnes++;
}
public personne(personne P){
initialise(P);
nbPersonnes++;
}
// yöntem
…
// sınıf yöntemi
public static long getNbPersonnes(){
return nbPersonnes;
}
}// sınıf
Aşağıdaki programla:
// person'ı içe aktar;
public class test1{
public static void main(String arg[]){
personne p1=new personne("Jean","Dupont",30);
personne p2=new personne(p1);
new personne(p1);
System.out.println("Nombre de personnes créées : "+personne.getNbPersonnes());
}// ana işlev
}//test1
şu sonuçlar elde edilir:
3.1.13. Bir nesnenin bir fonksiyona aktarılması
Daha önce de belirttiğimiz gibi, Java bir fonksiyonun gerçek parametrelerini değer olarak aktarır: gerçek parametrelerin değerleri, biçimsel parametrelere kopyalanır. Dolayısıyla bir fonksiyon, gerçek parametreleri değiştiremez.
Bir nesne söz konusu olduğunda, "nesne referansı" yerine sistematik olarak "nesne" denerek yapılan dil hatasına aldanmamak gerekir. Bir nesne, yalnızca o nesneye ait bir referans (işaretçi) aracılığıyla işlenir. Dolayısıyla bir fonksiyona aktarılan, nesnenin kendisi değil, o nesneye ait bir referanstır. Bu nedenle, biçimsel parametreye kopyalanan nesnenin kendisinin değeri değil, referansın değeridir: yeni bir nesne oluşturulmaz.
R1 nesne referansı bir fonksiyona aktarılırsa, bu referans karşılık gelen biçimsel parametreye (R2) kopyalanır. Dolayısıyla, R2 ve R1 referansları aynı nesneyi işaret eder. İşlev, R2 tarafından işaret edilen nesneyi değiştirirse, R1 tarafından referans verilen nesneyi de değiştirir; çünkü ikisi aynıdır.

Aşağıdaki örnek bunu göstermektedir:
// person import;
public class test1{
public static void main(String arg[]){
personne p1=new personne("Jean","Dupont",30);
System.out.print("Paramètre effectif avant modification : ");
p1.identifie();
modifie(p1);
System.out.print("Paramètre effectif après modification : ");
p1.identifie();
}// ana
private static void modifie(personne P){
System.out.print("Paramètre formel avant modification : ");
P.identifie();
P.initialise("Sylvie","Vartan",52);
System.out.print("Paramètre formel après modification : ");
P.identifie();
}// değiştir
}// sınıf
modifie yöntemi, bir sınıf yöntemi olduğu için static olarak bildirilmiştir: bu yöntemi çağırmak için önüne bir nesne öneki eklemek gerekmez. Elde edilen sonuçlar şunlardır:
Constructeur personne(String, String, int)
Paramètre effectif avant modification : Jean,Dupont,30
Paramètre formel avant modification : Jean,Dupont,30
Paramètre formel après modification : Sylvie,Vartan,52
Paramètre effectif après modification : Sylvie,Vartan,52
Görüldüğü gibi, yalnızca bir nesne oluşturulmuştur: p1 işlevinin main nesnesi ve bu nesne, modifie işlevi tarafından değiştirilmiştir.
3.1.14. Bir fonksiyonun çıktı parametrelerini bir nesne içinde kapsüllemek
Değer olarak parametre aktarımı nedeniyle, Java'da örneğin int türünde çıkış parametrelerine sahip bir fonksiyon yazmak mümkün değildir; çünkü nesne olmayan int türündeki bir referansı aktaramayız. Bu durumda, int türünü kapsülleyen bir sınıf oluşturabiliriz:
public class entieres{
private int valeur;
public entieres(int valeur){
this.valeur=valeur;
}
public void setValue(int valeur){
this.valeur=valeur;
}
public int getValue(){
return valeur;
}
}
Yukarıdaki sınıf, bir tamsayıyı başlatmaya yarayan bir oluşturucuya ve bu tamsayının değerini okumaya ve değiştirmeye yarayan iki yönteme sahiptir. Bu sınıfı aşağıdaki programla test ediyoruz:
// tamsayıları içe aktar;
public class test2{
public static void main(String[] arg){
entieres I=new entieres(12);
System.out.println("I="+I.getValue());
change(I);
System.out.println("I="+I.getValue());
}
private static void change(entieres entier){
entier.setValue(15);
}
}
ve şu sonuçlar elde edilir:
3.1.15. Kişilerden oluşan bir dizi
Bir nesne, diğer veriler gibi bir veridir ve bu nedenle birden fazla nesne bir dizide bir araya getirilebilir:
// person'u içe aktar;
public class test1{
public static void main(String arg[]){
personne[] amis=new personne[3];
System.out.println("----------------");
amis[0]=new personne("Jean","Dupont",30);
amis[1]=new personne("Sylvie","Vartan",52);
amis[2]=new personne("Neil","Armstrong",66);
int i;
for(i=0;i<amis.length;i++)
amis[i].identifie();
}
}
person[] friends = new person[3]; komutu, personne türünde 3 elemanlı bir dizi oluşturur. Bu 3 eleman burada null ve c.a.d değerleriyle başlatılır; bu değerler herhangi bir nesneye referans vermez. Yine, dilin yanlış kullanımı nedeniyle, nesne dizisi denilse de bu aslında sadece nesne referanslarından oluşan bir dizidir. Nesne dizisinin oluşturulması —ki bu dizi de başlı başına bir nesnedir (new'in varlığı)— kendi başına elemanlarının türünde hiçbir nesne oluşturmaz: bunu daha sonra yapmak gerekir.
Aşağıdaki sonuçlar elde edilir:
----------------
Constructeur personne(String, String, int)
Constructeur personne(String, String, int)
Constructeur personne(String, String, int)
Jean,Dupont,30
Sylvie,Vartan,52
Neil,Armstrong,66
3.2. Örnek Olarak Miras
3.2.1. Genel Bilgiler
Burada miras kavramını ele alıyoruz. Mirasın amacı, mevcut bir sınıfı ihtiyaçlarımıza uygun hale getirmek için "kişiselleştirmektir". enseignant sınıfını oluşturmak istediğimizi varsayalım: bir öğretmen özel bir kişidir. Başka bir kişinin sahip olmayacağı özellikleri vardır: örneğin, öğrettiği ders. Ancak aynı zamanda her insanın sahip olduğu özelliklere de sahiptir: adı, soyadı ve yaşı. Dolayısıyla bir öğretmen, personne sınıfının tam bir parçasıdır, ancak ek özelliklere de sahiptir. Sıfırdan bir enseignant sınıfı yazmak yerine, personne sınıfının mevcut özelliklerini alıp bunları öğretmenlerin kendine özgü özelliklerine uyarlamayı tercih ederiz. Bunu mümkün kılan şey, miras alma kavramıdır.
enseignant sınıfının personne sınıfının özelliklerini miras aldığını ifade etmek için şunu yazacağız:
public class enseignant extends personne
personne'e üst sınıf (veya ana sınıf), enseignant'e ise türetilmiş sınıf (veya alt sınıf) denir. Bir enseignant nesnesi, bir personne nesnesinin tüm özelliklerine sahiptir: aynı özniteliklere ve yöntemlere sahiptir. Ana sınıfın bu öznitelikleri ve yöntemleri, alt sınıfın tanımında tekrarlanmaz; sadece alt sınıf tarafından eklenen öznitelikler ve yöntemler belirtilir:
class enseignant extends personne{
// özellikler
private int section;
// yapıcı
public enseignant(String P, String N, int age,int section){
super(P,N,age);
this.section=section;
}
}
personne sınıfının şu şekilde tanımlandığını varsayıyoruz:
public class personne{
private String prenom;
private String nom;
private int age;
public personne(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
public personne(personne P){
this.prenom=P.prenom;
this.nom=P.nom;
this.age=P.age;
}
public String identite(){
return "personne("+prenom+","+nom+","+age+")";
}
// erişim yöntemleri
public String getPrenom(){
return prenom;
}
public String getNom(){
return nom;
}
public int getAge(){
return age;
}
//değiştiriciler
public void setPrenom(String P){
this.prenom=P;
}
public void setNom(String N){
this.nom=N;
}
public void setAge(int age){
this.age=age;
}
}
Kişiyi tanımlayan bir karakter dizisi döndürmek üzere identifie yöntemi hafifçe değiştirilmiştir ve artık identite adını taşımaktadır. Burada enseignant sınıfı, personne sınıfının yöntemlerine ve özniteliklerine şunları ekler:
- section özniteliği; bu, öğretmenin öğretmen kadrosunda ait olduğu bölüm numarasıdır (genel olarak her ders dalı için bir bölüm)
- bir öğretmenin tüm özniteliklerini başlatmaya yarayan yeni bir oluşturucu
3.2.2. Bir öğretmen nesnesinin oluşturulması
enseignant sınıfının oluşturucusu şöyledir:
// yapıcı
public enseignant(String P, String N, int age,int section){
super(P,N,age);
this.section=section;
}
super(P,N,age) komutu, üst sınıfın oluşturucusuna yapılan bir çağrıdır; buradaki üst sınıf personne sınıfıdır. Bu oluşturucunun, étudiant nesnesinin içinde bulunan personne nesnesinin prenom, nom ve age alanlarını başlattığı bilinmektedir. Bu oldukça karmaşık görünebilir ve şu şekilde yazmayı tercih edebiliriz:
// oluşturucu
public enseignant(String P, String N, int age,int section){
this.prenom=P;
this.nom=N
this.age=age
this.section=section;
}
Bu imkansız. personne sınıfı, prenom, nom ve age adlı üç alanını özel (private) olarak tanımlamıştır. Yalnızca aynı sınıfa ait nesneler bu alanlara doğrudan erişebilir. Diğer tüm nesneler, buradaki gibi alt sınıf nesneleri de dahil olmak üzere, bu alanlara erişmek için genel yöntemleri kullanmak zorundadır. personne sınıfı bu üç alanı korumalı (protected) olarak tanımlamış olsaydı durum farklı olurdu: bu durumda, türetilmiş sınıfların bu üç alana doğrudan erişmesine izin verirdi. Örneğimizde, üst sınıfın oluşturucusunu kullanmak doğru çözümdü ve bu genel olarak uygulanan yöntemdir: bir alt sınıf nesnesi oluşturulurken, önce üst sınıf nesnesinin oluşturucusu çağrılır, ardından bu sefer alt sınıf nesnesine (örneğimizde section) özgü başlatma işlemleri tamamlanır.
İlk programımızı deneyelim:
// kişi içe aktar;
// öğretmen içe aktar;
public class test1{
public static void main(String arg[]){
System.out.println(new enseignant("Jean","Dupont",30,27).identite());
}
}
Bu program, yalnızca bir enseignant (new) nesnesi oluşturup onu tanımlamakla yetinir. enseignant sınıfında identité adlı bir yöntem bulunmamaktadır, ancak üst sınıfında bu yöntem mevcuttur ve üstelik bu yöntem genel (public) niteliğindedir: miras yoluyla bu yöntem, enseignant sınıfının genel bir yöntemi haline gelir.
Sınıfların kaynak dosyaları aynı dizinde toplanır ve ardından derlenir:
E:\data\serge\JAVA\BASES\OBJETS\4>dir
10/06/2002 10:00 765 personne.java
10/06/2002 10:00 212 enseignant.java
10/06/2002 10:01 192 test1.java
E:\data\serge\JAVA\BASES\OBJETS\4>javac *.java
E:\data\serge\JAVA\BASES\OBJETS\4>dir
10/06/2002 10:00 765 personne.java
10/06/2002 10:00 212 enseignant.java
10/06/2002 10:01 192 test1.java
10/06/2002 10:02 316 enseignant.class
10/06/2002 10:02 1 146 personne.class
10/06/2002 10:02 550 test1.class
test1.class dosyası çalıştırılıyor:
3.2.3. Bir yöntemin aşırı yüklenmesi
Önceki örnekte, öğretmenin personne kısmının kimliğini elde ettik, ancak enseignant sınıfına (bölüm) özgü bazı bilgiler eksik. Bu nedenle, öğretmeni tanımlamaya yarayan bir yöntem yazmamız gerekiyor:
class enseignant extends personne{
int section;
public enseignant(String P, String N, int age,int section){
super(P,N,age);
this.section=section;
}
public String identite(){
return "enseignant("+super.identite()+","+section+")";
}
}
enseignant sınıfındaki identite yöntemi, üst sınıfındaki identite yöntemini kullanır (super.identite) yöntemini temel alarak "personne" kısmını görüntüler ve ardından enseignant sınıfına özgü olan section alanıyla tamamlar.
enseignant sınıfı artık iki identite yöntemine sahiptir:
- ana sınıf personne'ten miras alınan yöntem
- kendisine ait olan
E, bir enseignant nesnesi ise, E.identite, enseignant sınıfının identite yöntemini ifade eder. Ana sınıfın identite yönteminin, alt sınıfın identite yöntemi tarafından "aşırı yüklenmiş" olduğu söylenir. Genel olarak, O bir nesne ve M bir yöntem ise, O.M yöntemini çalıştırmak için sistem, M yöntemini aşağıdaki sırayla arar:
- O nesnesinin sınıfında
- varsa, üst sınıfında
- varsa, ana sınıfının ana sınıfında
- vb…
Dolayısıyla miras, alt sınıfta üst sınıftaki aynı isimli yöntemlerin yeniden tanımlanmasına olanak tanır. Bu, alt sınıfın kendi ihtiyaçlarına göre uyarlanmasını sağlar. Biraz sonra ele alacağımız polimorfizmle birleştiğinde, yöntemlerin yeniden tanımlanması mirasın en önemli avantajıdır.
Önceki örnekle devam edelim:
// kişi içe aktar;
// öğretmen içe aktar;
public class test1{
public static void main(String arg[]){
System.out.println(new enseignant("Jean","Dupont",30,27).identite());
}
}
Bu sefer elde edilen sonuçlar şunlardır:
3.2.4. Polimorfizm
Şu sınıf dizisini ele alalım: C0 C1 C2 … Cn
burada Ci Cj, Cj sınıfının Ci sınıfından türetildiğini gösterir. Bu, Cj sınıfının Ci sınıfının tüm özelliklerine ek olarak başka özelliklere de sahip olduğu anlamına gelir. Ci türündeki Oi nesneleri olsun. Şu şekilde yazmak geçerlidir:
Nitekim, miras yoluyla Cj sınıfı, Ci sınıfının tüm özelliklerine ek olarak başka özelliklere de sahiptir. Dolayısıyla, Cj türündeki bir Oj nesnesi, kendi içinde Ci türünde bir nesne barındırır. Şu işlem:
işlemi, Oi'nin Oj nesnesinde bulunan Ci türündeki nesneye bir referans olmasını sağlar.
Şu gerçek:Ci sınıfına ait bir Oi değişkeninin, aslında sadece Ci sınıfına ait bir nesneye değil, aynı zamanda Ci sınıfından türetilen herhangi bir nesneye de başvurabilmesi durumuna polimorfizm denir: bir değişkenin farklı nesne türlerine başvurabilme özelliği.
Bir örnek verelim ve herhangi bir sınıftan bağımsız olan aşağıdaki fonksiyonu ele alalım:
Object sınıfı, tüm Java sınıflarının "ana" sınıfıdır. Dolayısıyla şu kod yazıldığında:
yazdığımızda, dolaylı olarak şunu yazmış oluruz:
Dolayısıyla her Java nesnesi, içinde Object türünde bir bölüm barındırır. Böylece şunu yazabiliriz:
display işlevinin Object türündeki biçimsel parametresi, enseignant türünde bir değer alacaktır. Teacher, Object'ten türediği için bu geçerlidir.
3.2.5. Aşırı yükleme ve polimorfizm
affiche fonksiyonumuzu tamamlayalım:
obj.toString() yöntemi, obj nesnesini nom_de_la_classe@adresse_de_l'nesne biçiminde tanımlayan bir karakter dizisi döndürür. Önceki örneğimizde ne olur:
Sistem, System.out.println(e.toString()) komutunu çalıştırmalıdır; burada e bir öğretmen nesnesidir. Sistem, enseignant sınıfına giden sınıf hiyerarşisinde, en sondan başlayarak toString yöntemini arar:
- enseignant sınıfında, toString() yöntemini bulamaz
- üst sınıf personne'te toString() yöntemini bulamaz
- üst sınıf Object'te, toString() yöntemini bulur ve bunu yürütür
Aşağıdaki programda bu durum gösterilmektedir:
// kişi içe aktar;
// öğretmen içe aktar;
public class test1{
public static void main(String arg[]){
enseignant e=new enseignant("Lucile","Dumas",56,61);
affiche(e);
personne p=new personne("Jean","Dupont",30);
affiche(p);
}
public static void affiche(Object obj){
System.out.println(obj.toString());
}
}
Elde edilen sonuçlar şunlardır:
Yani nom_de_la_classe@adresse_de_l nesnesi. Bu çok açık olmadığı için, personne ve etudiant sınıfları için, ana sınıf Object'in toString yöntemini geçersiz kılacak bir toString yöntemi tanımlamak cazip gelebilirQX sınıfındaki toString yöntemini geçersiz kılacak bir toString yöntemi tanımlamak cazip gelebilir. personne ve enseignant sınıflarında zaten mevcut olan identite yöntemlerine benzer yöntemler yazmak yerine, toString yöntemlerini identite olarak yeniden adlandıralım:
public class personne{
...
public String toString(){
return "personne("+prenom+","+nom+","+age+")";
}
...
}
class enseignant extends personne{
int section;
…
public String toString(){
return "enseignant("+super.toString()+","+section+")";
}
}
Daha önce kullanılan test programıyla elde edilen sonuçlar şunlardır:
3.3. İç sınıflar
Bir sınıf, başka bir sınıfın tanımını içerebilir. Aşağıdaki örneği ele alalım:
// ithal edilen sınıflar
import java.io.*;
public class test1{
// dahili sınıf
private class article{
// yapı tanımlanıyor
private String code;
private String nom;
private double prix;
private int stockActuel;
private int stockMinimum;
// yapıcı
public article(String code, String nom, double prix, int stockActuel, int stockMinimum){
// özelliklerin başlatılması
this.code=code;
this.nom=nom;
this.prix=prix;
this.stockActuel=stockActuel;
this.stockMinimum=stockMinimum;
}//oluşturucu
//toString
public String toString(){
return "article("+code+","+nom+","+prix+","+stockActuel+","+stockMinimum+")";
}//toString
}//ürün sınıfı
// yerel veriler
private article art=null;
// üretici
public test1(String code, String nom, double prix, int stockActuel, int stockMinimum){
// öznitelik tanımı
art=new article(code, nom, prix, stockActuel,stockMinimum);
}//test1
// erişim yöntemi
public article getArticle(){
return art;
}//getArticle
public static void main(String arg[]){
// test1 örneğinin oluşturulması
test1 t1=new test1("a100","velo",1000,10,5);
// görüntüleme test1.art
System.out.println("art="+t1.getArticle());
}//ana sayfa
}// class sonu
test1 sınıfı, başka bir sınıf olan article sınıfının tanımını içerir. article sınıfının, test1 sınıfının iç sınıfı olduğu söylenir. Bu, iç sınıfın yalnızca onu içeren sınıfta kullanılması durumunda yararlı olabilir. Yukarıdaki test1.java kaynak kodunun derlenmesi sırasında iki .class dosyası elde edilir:
E:\data\serge\JAVA\classes\interne>dir
05/06/2002 17:26 1 362 test1.java
05/06/2002 17:26 941 test1$article.class
05/06/2002 17:26 1 020 test1.class
test1 sınıfının iç sınıfı olan article için test1$article.class dosyası oluşturulmuştur. Yukarıdaki programı çalıştırdığımızda şu sonuçlar elde edilir:
3.4. Arayüzler
Bir arayüz, bir sözleşme oluşturan yöntem veya özellik prototiplerinden oluşan bir kümedir. Bir arayüzü uygulamaya karar veren bir sınıf, arayüzde tanımlanan tüm yöntemlerin bir uygulamasını sağlamayı taahhüt eder. Bu uygulamayı denetleyen derleyicidir.
Örneğin, java.util.Enumeration arayüzünün tanımı şöyledir:
Yöntem Özeti | ||
boolean | hasMoreElements() Bu sıralama yapısının daha fazla öğe içerip içermediğini kontrol eder. | |
Nesne | nextElement() Bu sıralama nesnesinin sunabileceği en az bir eleman daha varsa, bu sıralamanın bir sonraki elemanını döndürür. | |
Bu arayüzü uygulayan tüm sınıflar şu şekilde tanımlanacaktır:
hasMoreElements() ve nextElement() yöntemleri C sınıfında tanımlanmalıdır.
Bir öğrencinin adını ve bir dersteki notunu tanımlayan élève sınıfını tanımlayan aşağıdaki kodu ele alalım:
// bir öğrenci sınıfı
public class élève{
// genel özellikler
public String nom;
public double note;
// yapıcı
public élève(String NOM, double NOTE){
nom=NOM;
note=NOTE;
}//oluşturucu
}//öğrenci
Bir dersteki tüm öğrencilerin notlarını bir araya getiren notes sınıfını tanımlıyoruz:
// ithal edilen sınıflar
// öğrenci içe aktarımı
// not sınıfı
public class notes{
// özellikler
protected String matière;
protected élève[] élèves;
// oluşturucu
public notes (String MATIERE, élève[] ELEVES){
// öğrenci ve ders kaydı
matière=MATIERE;
élèves=ELEVES;
}//notlar
// toString
public String toString(){
String valeur="matière="+matière +", notes=(";
int i;
// tüm notları birleştirir
for (i=0;i<élèves.length-1;i++){
valeur+="["+élèves[i].nom+","+élèves[i].note+"],";
};
//son not
if(élèves.length!=0){ valeur+="["+élèves[i].nom+","+élèves[i].note+"]";}
valeur+=")";
// son
return valeur;
}//toString
}//sınıf
matière ve élèves öznitelikleri, türetilmiş bir sınıftan erişilebilir olması için protected olarak tanımlanmıştır. notes sınıfını, notların ortalaması ve standart sapması olmak üzere iki ek özniteliğe sahip notesStats sınıfından türetmeye karar veriyoruz:
public class notesStats extends notes implements Istats {
// özellikler
private double _moyenne;
private double _écartType;
notesStats sınıfı, notes sınıfından türetilmiştir ve aşağıdaki Istats arayüzünü uygular:
// bir arayüz
public interface Istats{
double moyenne();
double écartType();
}//
Bu, notesStats sınıfının, moyenne ve écartType adlı iki yönteme sahip olması gerektiği anlamına gelir; bu yöntemlerin imzaları Istats arayüzünde belirtilmiştir. notesStats sınıfı şu şekildedir:
// ithal edilen sınıflar
// notları içe aktar;
// Istats içe aktarımı;
// öğrenci içe aktarımı;
public class notesStats extends notes implements Istats {
// özellikler
private double _moyenne;
private double _écartType;
// oluşturucu
public notesStats (String MATIERE, élève[] ELEVES){
// üst sınıfın oluşturulması
super(MATIERE,ELEVES);
// not ortalamasının hesaplanması
double somme=0;
for (int i=0;i<élèves.length;i++){
somme+=élèves[i].note;
}
if(élèves.length!=0) _moyenne=somme/élèves.length;
else _moyenne=-1;
// standart sapma
double carrés=0;
for (int i=0;i<élèves.length;i++){
carrés+=Math.pow((élèves[i].note-_moyenne),2);
}//for
if(élèves.length!=0) _écartType=Math.sqrt(carrés/élèves.length);
else _écartType=-1;
}//oluşturucu
// ToString
public String toString(){
return super.toString()+",moyenne="+_moyenne+",écart-type="+_écartType;
}//ToString
// Istats arayüzü yöntemleri
public double moyenne(){
// notların ortalamasını hesaplar
return _moyenne;
}//ortalama
public double écartType(){
// standart sapmayı hesaplar
return _écartType;
}//écartType
}//sınıfı
_moyenne ortalaması ve _ecartType standart sapması, nesnenin oluşturulduğu anda hesaplanır. Bu nedenle, moyenne ve écartType yöntemleri, yalnızca _moyenne ve _ecartType özniteliklerinin değerlerini döndürür. Öğrenci tablosu boşsa, her iki yöntem de -1 değerini döndürür.
Aşağıdaki test sınıfı:
// ithal edilen sınıflar
// öğrenci içe aktarımı;
// Istats içe aktarımı;
// notları içe aktar;
// notesStats'i içe aktar;
// test sınıfı
public class test{
public static void main(String[] args){
// bazı öğrenciler ve notlar
élève[] ELEVES=new élève[] { new élève("paul",14),new élève("nicole",16), new élève("jacques",18)};
// bunlar bir not nesnesine kaydedilir
notes anglais=new notes("anglais",ELEVES);
// ve görüntülenir
System.out.println(""+anglais);
// ortalama ve standart sapma ile aynı şekilde
anglais=new notesStats("anglais",ELEVES);
System.out.println(""+anglais);
}//ana sayfa
}//sınıf
sonuçlar şunlardır:
matière=anglais, notes=([paul,14.0],[nicole,16.0],[jacques,18.0])
matière=anglais, notes=([paul,14.0],[nicole,16.0],[jacques,18.0]),moyenne=16.0,écart-type=1.632993161855452
Bu örnekteki farklı sınıfların her biri ayrı bir kaynak dosyada yer almaktadır:
E:\data\serge\JAVA\interfaces\notes>dir
06/06/2002 14:06 707 notes.java
06/06/2002 14:06 878 notes.class
06/06/2002 14:07 1 160 notesStats.java
06/06/2002 14:02 101 Istats.java
06/06/2002 14:02 138 Istats.class
06/06/2002 14:05 247 élève.java
06/06/2002 14:05 309 élève.class
06/06/2002 14:07 1 103 notesStats.class
06/06/2002 14:10 597 test.java
06/06/2002 14:10 931 test.class
notesStats sınıfı, Istats arayüzünü uyguladığını belirtmeden, moyenne ve écartType yöntemlerini kendisi için gayet iyi bir şekilde uygulayabilirdi. Peki arayüzlerin ne gibi bir faydası vardır? Şu şekildedir: Bir fonksiyon, I arayüzünün türünde bir veriyi parametre olarak kabul edebilir. Böylece, I arayüzünü uygulayan C sınıfına ait herhangi bir nesne, bu fonksiyonun parametresi olabilir. Şu arayüzü ele alalım:
// bir Iexample arayüzü
public interface Iexemple{
int ajouter(int i,int j);
int soustraire(int i,int j);
}//arayüz
Iexemple arayüzü, ajouter ve soustraire adlı iki yöntem tanımlar. Aşağıdaki classe1 ve classe2 sınıfları bu arayüzü uygular.
// ithal edilen sınıflar
// Iexample'ı içe aktar;
public class classe1 implements Iexemple{
public int ajouter(int a, int b){
return a+b+10;
}
public int soustraire(int a, int b){
return a-b+20;
}
}//sınıf
// ithal edilen sınıflar
// Iexemple'i içe aktarma;
public class classe2 implements Iexemple{
public int ajouter(int a, int b){
return a+b+100;
}
public int soustraire(int a, int b){
return a-b+200;
}
}//sınıf
Örneği basitleştirmek amacıyla, sınıflar Iexemple arayüzünü uygulamaktan başka bir şey yapmamaktadır. Şimdi şu örneği ele alalım:
// ithal edilen sınıflar
// class1'i içe aktar;
// class2'yi içe aktar;
// test sınıfı
public class test{
// statik bir işlev
private static void calculer(int i, int j, Iexemple inter){
System.out.println(inter.ajouter(i,j));
System.out.println(inter.soustraire(i,j));
}//hesapla
// main işlevi
public static void main(String[] arg){
// sınıf1 ve sınıf2 adlı iki nesnenin oluşturulması
classe1 c1=new classe1();
classe2 c2=new classe2();
// calculer statik işlevinin çağrılması
calculer(4,3,c1);
calculer(14,13,c2);
}//main
}//test sınıfı
calculer statik fonksiyonu, Iexemple türünde bir öğeyi parametre olarak kabul eder. Dolayısıyla, bu parametre için hem classe1 hem de classe2 türünde bir nesne alabilir. main fonksiyonunda bu işlem gerçekleştirilir ve aşağıdaki sonuçlar elde edilir:
Görüldüğü üzere, burada sınıflar için ele alınan polimorfizme benzer bir özellik söz konusudur. Dolayısıyla, birbirleriyle miras yoluyla bağlantılı olmayan (yani miras polimorfizmi kullanılamayan) bir Ci sınıflar kümesi, aynı imzaya sahip bir yöntemler kümesi sunuyorsa, bu yöntemleri ilgili tüm sınıfların miras alacağı bir I arayüzünde bir araya getirmek faydalı olabilir. Bu Ci sınıflarının örnekleri, I türünde bir parametre kabul eden fonksiyonların parametreleri olarak kullanılabilir, c.a.d. Bu fonksiyonlar, farklı Ci sınıflarının kendine özgü özelliklerini ve yöntemlerini değil, yalnızca I arayüzünde tanımlanan Ci nesnelerinin yöntemlerini kullanır.
Önceki örnekte, her sınıf veya arayüz ayrı bir kaynak dosyaya sahipti:
E:\data\serge\JAVA\interfaces\opérations>dir
06/06/2002 14:33 128 Iexemple.java
06/06/2002 14:34 218 classe1.java
06/06/2002 14:32 220 classe2.java
06/06/2002 14:33 144 Iexemple.class
06/06/2002 14:34 325 classe1.class
06/06/2002 14:34 326 classe2.class
06/06/2002 14:36 583 test.java
06/06/2002 14:36 628 test.class
Son olarak, arayüz mirasının birden fazla olabileceğini belirtelim: c.a.d. Şu şekilde yazılabilir:
burada ij'ler arayüzlerdir.
3.5. Anonim sınıflar
Önceki örnekte, classe1 ve classe2 sınıfları açıkça tanımlanmamış olabilirdi. Aşağıdaki programı ele alalım; bu program, önceki programla hemen hemen aynı işlevi görür, ancak classe1 ve classe2 sınıflarının açıkça tanımlanmasına gerek kalmaz:
// ithal edilen sınıflar
// Iexemple'in içe aktarılması;
// test sınıfı
public class test2{
// bir iç sınıf
private static class classe3 implements Iexemple{
public int ajouter(int a, int b){
return a+b+1000;
}
public int soustraire(int a, int b){
return a-b+2000;
}
};//sınıf3 tanımı
// statik bir işlev
private static void calculer(int i, int j, Iexemple inter){
System.out.println(inter.ajouter(i,j));
System.out.println(inter.soustraire(i,j));
}//hesapla
// main işlevi
public static void main(String[] arg){
// Iexemple arayüzünü uygulayan iki nesnenin oluşturulması
Iexemple i1=new Iexemple(){
public int ajouter(int a, int b){
return a+b+10;
}
public int soustraire(int a, int b){
return a-b+20;
}
};//i1 tanımı
Iexemple i2=new Iexemple(){
public int ajouter(int a, int b){
return a+b+100;
}
public int soustraire(int a, int b){
return a-b+200;
}
};//i2 tanımı
// başka bir Iexemple nesnesi
Iexemple i3=new classe3();
// calculer statik işlevinin çağrılması
calculer(4,3,i1);
calculer(14,13,i2);
calculer(24,23,i3);
}//main
}//test sınıfı
Özelliği kodda yatıyor:
// Iexample arayüzünü uygulayan iki nesnenin oluşturulması
Iexemple i1=new Iexemple(){
public int ajouter(int a, int b){
return a+b+10;
}
public int soustraire(int a, int b){
return a-b+20;
}
};//i1 tanımı
Tek işlevi Iexemple arayüzünü uygulamak olan bir i1 nesnesi oluşturulur. Bu nesne, Iexemple türündedir. Dolayısıyla, arayüz türünde nesneler oluşturulabilir. Çok sayıda Java sınıfı yöntemi, c.a.d türünde nesneler döndürür; bu nesnelerin tek işlevi, bir arayüzün yöntemlerini uygulamaktır. i1 nesnesini oluşturmak için şu şekilde yazmak cazip gelebilir:
Iexemple i1=new Iexemple()
Ancak bir arayüzün örneği oluşturulamaz. Yalnızca bu arayüzü uygulayan bir sınıfın örneği oluşturulabilir. Burada, i1 nesnesinin tanımının gövdesinde "anında" böyle bir sınıf tanımlıyoruz:
Iexemple i1=new Iexemple(){
public int ajouter(int a, int b){
// "ekle" tanımlaması
}
public int soustraire(int a, int b){
// çıkarma tanımlaması
}
};//i1 tanımı
Bu tür bir komutun anlamı şu diziyle benzerdir:
public class test2{
................
// bir iç sınıf
private static class classe1 implements Iexemple{
public int ajouter(int a, int b){
// "eklemek" tanımı
}
public int soustraire(int a, int b){
// çıkarma tanımı
}
};//sınıf1 tanımı
.................
public static void main(String[] arg){
...........
Iexemple i1=new classe1();
}//ana işlev
}//sınıf
Yukarıdaki örnekte, bir arayüz değil, bir sınıfın örneği oluşturulmaktadır. "Anında" tanımlanan bir sınıfa anonim sınıf denir. Bu yöntem, tek işlevi bir arayüzü uygulamak olan nesnelerin örneklerini oluşturmak için sıklıkla kullanılır.
Yukarıdaki programın çalıştırılması şu sonuçları verir:
Yukarıdaki örnekte, bir arayüzü uygulamak için isimsiz sınıflar kullanılmıştı. Bu sınıflar, parametreli yapıcıları olmayan sınıfların türetilmesinde de kullanılabilir. Aşağıdaki örneği ele alalım:
// ithal edilen sınıflar
// Iexample'ı içe aktar;
class classe3 implements Iexemple{
public int ajouter(int a, int b){
return a+b+1000;
}
public int soustraire(int a, int b){
return a-b+2000;
}
};//sınıf3 tanımı
public class test4{
// statik bir işlev
private static void calculer(int i, int j, Iexemple inter){
System.out.println(inter.ajouter(i,j));
System.out.println(inter.soustraire(i,j));
}//hesapla
// main yöntemi
public static void main(String args[]){
// sınıf3'ten türetilen isimsiz bir sınıfın tanımı
// çıkarma işlemini yeniden tanımlamak için
classe3 i1=new classe3(){
public int ajouter(int a, int b){
return a+b+10000;
}//çıkarmak
};//i1
// calculate statik işlevinin çağrılması
calculer(4,3,i1);
}//main
}//sınıf
Burada, Iexemple arayüzünü uygulayan classe3 sınıfını görüyoruz. main işlevinde, türü classe3 sınıfından türetilmiş bir sınıf olan i1 değişkenini tanımlıyoruz. Bu türetilmiş sınıf, anonim bir sınıf içinde "anında" tanımlanır ve classe3 sınıfının ajouter yöntemini yeniden tanımlar. Sözdizimi, bir arayüzü uygulayan anonim sınıfınkiyle aynıdır. Ancak burada derleyici, classe3'in bir arayüz değil, bir sınıf olduğunu algılar. Derleyici için bu, bir sınıf türetmesidir. Anonim sınıfın gövdesinde bulduğu tüm yöntemler, temel sınıftaki aynı isimli yöntemlerin yerini alacaktır.
Yukarıdaki programın çalıştırılması şu sonuçları verir:
3.6. Paketler
3.6.1. Bir pakette sınıflar oluşturma
Ekrana bir satır yazmak için şu komutu kullanırız
System sınıfının tanımına bakarsak, aslında java.lang.System olarak adlandırıldığını görürüz:

Bunu bir örnekle kontrol edelim:
public class test1{
public static void main(String[] args){
java.lang.System.out.println("Coucou");
}//ana işlev
}//sınıf
Bu programı derleyip çalıştıralım:
E:\data\serge\JAVA\classes\paquetages>javac test1.java
E:\data\serge\JAVA\classes\paquetages>dir
06/06/2002 15:40 127 test1.java
06/06/2002 15:40 410 test1.class
E:\data\serge\JAVA\classes\paquetages>java test1
Coucou
Peki neden şöyle yazabiliyoruz?
System.out.println("Coucou");
yerine
java.lang.System.out.println("Coucou");
Çünkü tüm Java programlarında, java.lang "paketinin" sistematik olarak içe aktarılması varsayılır. Dolayısıyla, sanki her programın başında şu komut varmış gibi işler yürür:
Bu komut ne anlama gelir? java.lang paketindeki tüm sınıflara erişim sağlar. Derleyici, burada System.class dosyasını bulacak ve bu dosya System sınıfını tanımlar. Derleyicinin java.lang paketini nerede bulacağı ve bir paketin neye benzediği henüz bilinmiyor. Bu konuya daha sonra tekrar döneceğiz. Bir pakette sınıf oluşturmak için şunu yazarız:
Örnek olarak, daha önce incelediğimiz personne sınıfımızı bir pakette oluşturalım. Paket adı olarak istia.st'i seçeceğiz. personne sınıfı şu şekilde olur:
// "kişi" sınıfının oluşturulacağı paket adı
package istia.st;
// "kişi" sınıfı
public class personne{
// soyadı, adı, yaşı
private String prenom;
private String nom;
private int age;
// yapıcı 1
public personne(String P, String N, int age){
this.prenom=P;
this.nom=N;
this.age=age;
}
// toString
public String toString(){
return "personne("+prenom+","+nom+","+age+")";
}
}//sınıf
Bu sınıf derlenir ve ardından geçerli dizindeki istia\st dizinine yerleştirilir. Neden istia\st? Çünkü paketin adı istia.st'tir.
E:\data\serge\JAVA\classes\paquetages\personne>dir
06/06/2002 16:28 467 personne.java
06/06/2002 16:04 <DIR> istia
E:\data\serge\JAVA\classes\paquetages\personne>dir istia
06/06/2002 16:04 <DIR> st
E:\data\serge\JAVA\classes\paquetages\personne>dir istia\st
06/06/2002 16:28 675 personne.class
Şimdi personne sınıfını ilk test sınıfımızda kullanalım:
public class test{
public static void main(String[] args){
istia.st.personne p1=new istia.st.personne("Jean","Dupont",20);
System.out.println("p1="+p1);
}//el
}//test sınıfı
personne sınıfının artık istia.st paket adıyla ön ekle donatıldığı dikkatinizi çekecektir. Derleyici, istia.st.personne sınıfını nereden bulacaktır? Derleyici, ihtiyaç duyduğu sınıfları önceden tanımlanmış bir dizin listesinde ve geçerli dizinden başlayan bir dizin ağacında arar. Burada, istia.st.personne sınıfını istia\st\personne.class dosyasında arayacaktır. Bu nedenle personne.class dosyasını istia\st dizinine yerleştirdik. Test programını derleyip çalıştıralım:
E:\data\serge\JAVA\classes\paquetages\personne>dir
06/06/2002 16:28 467 personne.java
06/06/2002 16:06 246 test.java
06/06/2002 16:04 <DIR> istia
06/06/2002 16:06 738 test.class
E:\data\serge\JAVA\classes\paquetages\personne>java test
p1=personne(Jean,Dupont,20)
Yazmaktan kaçınmak için
istia.st.personne p1=new istia.st.personne("Jean","Dupont",20);
istia.st.personne sınıfını, import koşulu ile içe aktarabiliriz:
import istia.st.personne;
Böylece şunu yazabiliriz:
personne p1=new personne("Jean","Dupont",20);
ve derleyici bunu şu şekilde çevirecektir:
istia.st.personne p1=new istia.st.personne("Jean","Dupont",20);
Test programı şu şekilde olur:
// ithal edilen ad alanları
import istia.st.personne;
public class test2{
public static void main(String[] args){
personne p1=new personne("Jean","Dupont",20);
System.out.println("p1="+p1);
}//ana
}//test2 sınıfı
Bu yeni programı derleyip çalıştıralım:
E:\data\serge\JAVA\classes\paquetages\personne>javac test2.java
E:\data\serge\JAVA\classes\paquetages\personne>dir
06/06/2002 16:28 467 personne.java
06/06/2002 16:06 246 test.java
06/06/2002 16:04 <DIR> istia
06/06/2002 16:06 738 test.class
06/06/2002 16:47 236 test2.java
06/06/2002 16:50 740 test2.class
E:\data\serge\JAVA\classes\paquetages\personne>java test2
p1=personne(Jean,Dupont,20)
istia.st paketini geçerli dizine yerleştirdik. Bu zorunlu değildir. Paketi, yine geçerli dizinde bulunan mesClasses adlı bir klasöre yerleştirelim. istia.st paketindeki sınıfların istia\st adlı bir klasöre yerleştirildiğini hatırlayalım. Geçerli dizinin dizin yapısı şu şekildedir:
E:\data\serge\JAVA\classes\paquetages\personne>dir
06/06/2002 16:28 467 personne.java
06/06/2002 16:06 246 test.java
06/06/2002 16:06 738 test.class
06/06/2002 16:47 236 test2.java
06/06/2002 16:50 740 test2.class
06/06/2002 16:21 <DIR> mesClasses
E:\data\serge\JAVA\classes\paquetages\personne>dir mesClasses
06/06/2002 16:22 <DIR> istia
E:\data\serge\JAVA\classes\paquetages\personne>dir mesClasses\istia
06/06/2002 16:22 <DIR> st
E:\data\serge\JAVA\classes\paquetages\personne>dir mesClasses\istia\st
06/06/2002 16:01 1 153 personne.class
Şimdi test2.java programını yeniden derleyelim:
E:\data\serge\JAVA\classes\paquetages\personne>javac test2.java
test2.java:2: package istia.st does not exist
import istia.st.personne;
Paket istia.st taşındığından beri derleyici artık onu bulamıyor. import komutu nedeniyle onu aradığına dikkat edelim. Varsayılan olarak, derleyici geçerli dizinden artık mevcut olmayan istia\st adlı klasörde bu paketi arıyor. Derleyicinin seçeneklerini inceleyelim:
E:\data\serge\JAVA\classes\paquetages\personne>javac
Usage: javac <options> <source files>
where possible options include:
-g Generate all debugging info
-g:none Generate no debugging info
-g:{lines,vars,source} Generate only some debugging info
-O Optimize; may hinder debugging or enlarge class file
-nowarn Generate no warnings
-verbose Output messages about what the compiler is doing
-deprecation Output source locations where deprecated APIs are used
-classpath <path> Specify where to find user class files
-sourcepath <path> Specify where to find input source files
-bootclasspath <path> Override location of bootstrap class files
-extdirs <dirs> Override location of installed extensions
-d <directory> Specify where to place generated class files
-encoding <encoding> Specify character encoding used by source files
-source <release> Provide source compatibility with specified release
-target <release> Generate class files for specific VM version
-help Print a synopsis of standard options
Burada -classpath seçeneği işimize yarayabilir. Bu seçenek, derleyiciye sınıflarını ve paketlerini nerede arayacağını belirtmemizi sağlar. Hadi deneyelim. Derleyiciye, istia.st paketinin artık mesClasses klasöründe olduğunu söyleyerek derlemeyi yapalım:
E:\data\serge\JAVA\classes\paquetages\personne>javac -classpath mesClasses test2.java
E:\data\serge\JAVA\classes\paquetages\personne>dir
06/06/2002 16:47 236 test2.java
06/06/2002 17:03 740 test2.class
06/06/2002 16:21 <DIR> mesClasses
Derleme bu sefer sorunsuz gerçekleşti. test2.class programını çalıştıralım:
E:\data\serge\JAVA\classes\paquetages\personne>java test2
Exception in thread "main" java.lang.NoClassDefFoundError: istia/st/personne
at test2.main(test2.java:6)
Şimdi de Java sanal makinesi, istia/st/personne sınıfını bulamıyor. Sınıf artık mesClasses dizinindeyken, sanal makine onu geçerli dizinde arıyor. Java sanal makinesinin seçeneklerine bir göz atalım:
E:\data\serge\JAVA\classes\paquetages\personne>java
Usage: java [-options] class [args...]
(to execute a class)
or java -jar [-options] jarfile [args...]
(to execute a jar file)
where options include:
-client to select the "client" VM
-server to select the "server" VM
-hotspot is a synonym for the "client" VM [deprecated]
The default VM is client.
-cp -classpath <directories and zip/jar files separated by ;>
set search path for application classes and resources
-D<name>=<value>
set a system property
-verbose[:class|gc|jni]
enable verbose output
-version print product version and exit
-showversion print product version and continue
-? -help print this help message
-X print help on non-standard options
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
enable assertions
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
disable assertions
-esa | -enablesystemassertions
enable system assertions
-dsa | -disablesystemassertions
disable system assertions
Görüldüğü gibi, JVM'in de derleyici gibi bir classpath seçeneği var. Bunu kullanarak istia.st paketinin nerede olduğunu belirtelim:
E:\data\serge\JAVA\classes\paquetages\personne>java.bat -classpath mesClasses test2
Exception in thread "main" java.lang.NoClassDefFoundError: test2
Pek ilerleme kaydedemedik. Şimdi de test2 sınıfının kendisi bulunamıyor. Bunun nedeni şudur: classpath anahtar sözcüğü olmadığında, sınıf arama işlemi sırasında geçerli dizin sistematik olarak taranır; ancak bu anahtar sözcük mevcut olduğunda tarama yapılmaz. Bu nedenle, geçerli dizinde bulunan test2.class sınıfı bulunamıyor. Çözüm nedir? Geçerli dizini classpath'e eklemek. Geçerli dizin, . sembolüyle gösterilir.
E:\data\serge\JAVA\classes\paquetages\personne>java -classpath mesClasses;. test2
p1=personne(Jean,Dupont,20)
Neden bu kadar karmaşık? Paketlerin amacı, sınıflar arasında isim çakışmalarını önlemektir. E1 ve E2 adlı iki şirketi ele alalım; bu şirketler sırasıyla com.e1 ve com.e2 paketlerinde paketlenmiş sınıflar dağıtmaktadır. C adlı bir müşteri, her iki şirketin de personne adlı bir sınıf tanımladığı bu iki sınıf kümesini satın alsın. Müşteri C, personne sınıfınıE1 şirketine ait personne sınıfına com.e1.personne olarak, E2 şirketine ait sınıfa ise com.e2.personne olarak referans verecek ve böylece isim çakışmasını önleyecektir.
3.6.2. Paket Arama
Bir programa yazarken
yazdığımızda, java.util paketindeki tüm sınıflara erişmek için bu paket nerede bulunur? Paketlerin varsayılan olarak geçerli dizinde veya derleyicinin classpath seçeneğinde belirtilen dizinler listesinde arandığını belirtmiştik; bu seçeneğin mevcut olması durumunda ise JVM seçeneğinde belirtilen dizinlerde aranır. Ayrıca, lib'in kurulum dizinindeki JDK dizinlerinde de aranırlar. Şu dizini ele alalım:

Bu örnekte, jdk14\lib ve jdk14\jre\lib dizin ağaçları taranacak ve bu dizinlerde .class dosyaları ya da sınıf arşivleri olan .jar veya .zip dosyaları aranacaktır. Örneğin, bir önceki jdk14 dizini altında bulunan .jar dosyalarını arayalım:

Bunlardan onlarca var. Bir .jar dosyası, winzip yardımcı programı ile açılabilir. Yukarıdaki rt.jar dosyasını (rt=RunTime) açalım. Burada, .class uzantılı yüzlerce dosya bulunuyor; bunlardan bazıları java.util paketine ait:

Paketleri yönetmenin basit bir yöntemi, bunları <jdk>\jre\lib dizinine yerleştirmektir; burada <jdk>, JDK'in kurulum dizinidir. Genellikle bir paket birden fazla sınıf içerir ve bunları tek bir .jar dosyasında (JAR=Java ARchive dosyası) bir araya getirmek pratiktir. jar.exe yürütülebilir dosyası, <jdk>\bin klasöründe bulunur:
E:\data\serge\JAVA\classes\paquetages\personne>dir "e:\program files\jdk14\bin\jar.exe"
07/02/2002 12:52 28 752 jar.exe
jar programının kullanımına ilişkin yardım, programı parametre olmadan çalıştırarak alınabilir:
E:\data\serge\JAVA\classes\paquetages\personne>"e:\program files\jdk14\bin\jar.exe"
Syntaxe : jar {ctxu}[vfm0M] [fichier-jar] [fichier-manifest] [rÚp -C] fichiers ...
Options :
-c crÚer un nouveau fichier d''archives
-t gÚnÚrer la table des matiÞres du fichier d''archives
-x extraire les fichiers nommÚs (ou tous les fichiers) du fichier d''archives
-u mettre Ó jour le fichier d''archives existant
-v gÚnÚrer des informations verbeuses sur la sortie standard
-f spÚcifier le nom du fichier d''archives
-m inclure les informations manifest provenant du fichier manifest spÚcifiÚ
-0 stocker seulement ; ne pas utiliser la compression ZIP
-M ne pas crÚer de fichier manifest pour les entrÚes
-i gÚnÚrer l''index pour les fichiers jar spÚcifiÚs
-C passer au rÚpertoire spÚcifiÚ et inclure le fichier suivant
Si un rÚpertoire est spÚcifiÚ, il est traitÚ rÚcursivement.
Les noms des fichiers manifest et d''archives doivent Ûtre spÚcifiÚs
dans l''ordre des indicateurs ''m'' et ''f''.
Exemple 1 : pour archiver deux fichiers de classe dans le fichier d''archives classes.jar :
jar cvf classes.jar Foo.class Bar.class
Exemple 2 : utilisez le fichier manifest existant ''monmanifest'' pour archiver tous les fichiers du
rÚpertoire foo/ dans ''classes.jar'':
jar cvfm classes.jar monmanifest -C foo/ .
Daha önce istia.st paketinde oluşturulan personne.class sınıfına geri dönelim:
E:\data\serge\JAVA\classes\paquetages\personne>dir
06/06/2002 16:28 467 personne.java
06/06/2002 17:36 195 test.java
06/06/2002 16:04 <DIR> istia
06/06/2002 16:06 738 test.class
06/06/2002 16:47 236 test2.java
06/06/2002 18:15 740 test2.class
E:\data\serge\JAVA\classes\paquetages\personne>dir istia
06/06/2002 16:04 <DIR> st
E:\data\serge\JAVA\classes\paquetages\personne>dir istia\st
06/06/2002 16:28 675 personne.class
istia.st.jar paketindeki tüm sınıfları, yani yukarıdaki istia\st dizin ağacındaki tüm sınıfları içeren bir istia.st.jar dosyası oluşturalım:
E:\data\serge\JAVA\classes\paquetages\personne>"e:\program files\jdk14\bin\jar" cvf istia.st.jar istia\st\*
E:\data\serge\JAVA\classes\paquetages\personne>dir
06/06/2002 16:28 467 personne.java
06/06/2002 17:36 195 test.java
06/06/2002 16:04 <DIR> istia
06/06/2002 16:06 738 test.class
06/06/2002 16:47 236 test2.java
06/06/2002 18:15 740 test2.class
06/06/2002 18:08 874 istia.st.jar
winzip ile istia.st.jar dosyasının içeriğini inceleyelim:

istia.st.jar dosyasını <jdk>\jre\lib\perso dizinine yerleştirelim:
E:\data\serge\JAVA\classes\paquetages\personne>dir "e:\program files\jdk14\jre\lib\perso"
06/06/2002 18:08 874 istia.st.jar
Şimdi test2.java programını derleyelim ve çalıştıralım:
E:\data\serge\JAVA\classes\paquetages\personne>javac -classpath istia.st.jar test2.java
E:\data\serge\JAVA\classes\paquetages\personne>java -classpath istia.st.jar;. test2
p1=personne(Jean,Dupont,20)
Görüldüğü gibi, arşivin konumunu açıkça belirtmeye gerek kalmadan sadece taranacak arşivin adını belirtmemiz yeterli oldu. <jdk>\jre\lib dizin ağacındaki tüm dizinler, istenen .jar dosyasını bulmak için taranır.
3.7. IMPÔTS örneği
Bir önceki bölümde incelediğimiz vergi hesaplamasını tekrar ele alalım ve bunu bir sınıf kullanarak işleyelim. Sorunu hatırlayalım:
Sadece maaşını beyan edecek bir vergi mükellefinin basitleştirilmiş durumunu ele alalım:
- çalışanın pay sayısı şu şekilde hesaplanır: nbParts = nbEnfants/2 + 1 (evli değilse), evliyse nbEnfants/2+2 olarak hesaplanır; burada nbEnfants, çocuk sayısıdır.
- En az üç çocuğu varsa, yarım pay daha eklenir
- vergilendirilebilir geliri R=0,72*S olarak hesaplanır; burada S, yıllık maaşıdır
- aile katsayısı QF = R / nbParts olarak hesaplanır
- Vergisi I hesaplanır. Aşağıdaki tabloyu ele alalım:
12620,0 | 0 | 0 |
13190 | 0,05 | 631 |
15640 | 0,1 | 1290,5 |
24.740 | 0,15 | 2072,5 |
31810 | 0,2 | 3309,5 |
39.970 | 0,25 | 4900 |
48360 | 0,3 | 6898,5 |
55790 | 0,35 | 9316,5 |
92970 | 0,4 | 12106 |
127.860 | 0,45 | 16754,5 |
151250 | 0,50 | 23147,5 |
172.040 | 0,55 | 30710 |
195.000 | 0,60 | 39312 |
0 | 0,65 | 49062 |
Her satırda 3 alan vardır. I vergisini hesaplamak için, QF<=alan1 olan ilk satır aranır. Örneğin, QF=23000 ise şu satır bulunur:
bulunur. I vergisi bu durumda 0,15*R - 2072,5*nbParts'e eşittir. Eğer QF, QF<=alan1 ilişkisi hiçbir zaman geçerli olmayacak şekilde belirlenmişse, o zaman son satırdaki katsayılar kullanılır. Burada:
bu da I=0,65*R - 49062*nbParts vergi tutarını verir.
impots sınıfı şu şekilde tanımlanacaktır:
// vergi sınıfı oluşturma
public class impots{
// vergi hesaplaması için gerekli veriler
// harici bir kaynaktan alınır
private double[] limites, coeffR, coeffN;
// üretici
public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception{
// 3 tablonun da aynı boyutta olup olmadığı kontrol edilir
boolean OK=LIMITES.length==COEFFR.length && LIMITES.length==COEFFN.length;
if (! OK) throw new Exception ("Les 3 tableaux fournis n'ont pas la même taille("+
LIMITES.length+","+COEFFR.length+","+COEFFN.length+")");
// tamam
this.limites=LIMITES;
this.coeffR=COEFFR;
this.coeffN=COEFFN;
}//üretici
// vergi hesaplaması
public long calculer(boolean marié, int nbEnfants, int salaire){
// pay sayısının hesaplanması
double nbParts;
if (marié) nbParts=(double)nbEnfants/2+2;
else nbParts=(double)nbEnfants/2+1;
if (nbEnfants>=3) nbParts+=0.5;
// vergilendirilebilir gelir ve aile katsayısı hesaplaması
double revenu=0.72*salaire;
double QF=revenu/nbParts;
// vergi hesaplaması
limites[limites.length-1]=QF+1;
int i=0;
while(QF>limites[i]) i++;
// sonuçun döndürülmesi
return (long)(revenu*coeffR[i]-nbParts*coeffN[i]);
}//hesapla
}//sınıf
Bir vergi nesnesi, bir vergi mükellefinin vergisinin hesaplanmasına olanak tanıyan verilerle oluşturulur. Bu, nesnenin sabit kısmıdır. Bu nesne oluşturulduktan sonra, vergi mükellefinin medeni durumu (evli veya bekar), çocuk sayısı ve yıllık maaşı temel alınarak vergisini hesaplayan "calculer" yöntemini tekrar tekrar çağırabiliriz.
Bir test programı şu şekilde olabilir:
//içe aktarılan sınıflar
// vergilerin içe aktarılması;
import java.io.*;
public class test
{
public static void main(String[] arg) throws IOException
{
// etkileşimli vergi hesaplama programı
// kullanıcı klavyeden üç veri girer: evli nbEnfants maaş
// program daha sonra ödenecek vergiyi gösterir
final String syntaxe="syntaxe : marié nbEnfants salaire\n"
+"marié : o pour marié, n pour non marié\n"
+"nbEnfants : nombre d'enfants\n"
+"salaire : salaire annuel en F";
// vergi hesaplaması için gerekli veri tabloları
double[] limites=new double[] {12620,13190,15640,24740,31810,39970,48360,55790,92970,127860,151250,172040,195000,0};
double[] coeffR=new double[] {0,0.05,0.1,0.15,0.2,0.25,0.3,0.35,0.4,0.45,0.5,0.55,0.6,0.65};
double[] coeffN=new double[] {0,631,1290.5,2072.5,3309.5,4900,6898.5,9316.5,12106,16754.5,23147.5,30710,39312,49062};
// okuma akışının oluşturulması
BufferedReader IN=new BufferedReader(new InputStreamReader(System.in));
// bir vergi nesnesinin oluşturulması
impots objImpôt=null;
try{
objImpôt=new impots(limites,coeffR,coeffN);
}catch (Exception ex){
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
System.exit(1);
}//try-catch
// sonsuz döngü
while(true){
// vergi hesaplaması parametreleri isteniyor
System.out.print("Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :");
String paramètres=IN.readLine().trim();
// Yapılması gereken bir şey var mı?
if(paramètres==null || paramètres.equals("")) break;
// giriş satırındaki argüman sayısının kontrolü
String[] args=paramètres.split("\\s+");
int nbParamètres=args.length;
if (nbParamètres!=3){
System.err.println(syntaxe);
continue;
}//if
// parametrelerin geçerliliğinin kontrolü
// evli
String marié=args[0].toLowerCase();
if (! marié.equals("o") && ! marié.equals("n")){
System.err.println(syntaxe+"\nArgument marié incorrect : tapez o ou n");
continue;
}//if
// nbEnfants
int nbEnfants=0;
try{
nbEnfants=Integer.parseInt(args[1]);
if(nbEnfants<0) throw new Exception();
}catch (Exception ex){
System.err.println(syntaxe+"\nArgument nbEnfants incorrect : tapez un entier positif ou nul");
continue;
}//if
// maaş
int salaire=0;
try{
salaire=Integer.parseInt(args[2]);
if(salaire<0) throw new Exception();
}catch (Exception ex){
System.err.println(syntaxe+"\nArgument salaire incorrect : tapez un entier positif ou nul");
continue;
}//if
// parametreler doğruysa - vergi hesaplanır
System.out.println("impôt="+objImpôt.calculer(marié.equals("o"),nbEnfants,salaire)+" F");
// sonraki vergi mükellefi
}//while
}//ana
}//sınıf
İşte önceki programın çalıştırılmasına ilişkin bir örnek:
E:\data\serge\MSNET\c#\impots\3>java testi
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :q s d
syntaxe : marié nbEnfants salaire
marié : o pour marié, n pour non marié
nbEnfants : nombre d'enfants
salaire : salaire annuel en F
Argument marié incorrect : tapez o ou n
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o s d
syntaxe : marié nbEnfants salaire
marié : o pour marié, n pour non marié
nbEnfants : nombre d'enfants
salaire : salaire annuel en F
Argument nbEnfants incorrect : tapez un entier positif ou nul
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 d
syntaxe : marié nbEnfants salaire
marié : o pour marié, n pour non marié
nbEnfants : nombre d'enfants
salaire : salaire annuel en F
Argument salaire incorrect : tapez un entier positif ou nul
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :q s d f
syntaxe : marié nbEnfants salaire
marié : o pour marié, n pour non marié
nbEnfants : nombre d'enfants
salaire : salaire annuel en F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 200000
impôt=22504 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :