Bölüm 10 Statik ve Anlık Öğeler

advertisement
Bölüm 10
Statik ve Anlık Öğeler
Statik ve Anlık Öğeler
Statik öğeler, bir sınıfta static sıfatıyla nitelendirilen değişkenler ve metotlardır. Bunlara sınıf değişkenleri de
denilir. static nitelemesi almayan değişkenler ve metotlar ise anlık (dinamik, statik olmayan) öğelerdir.
Örneğin,
static int x;
static double ÜcretHesapla(){ ... };
deyimleri, sırasıyla, int tipinden static
ÜcretHesapla() metodunun bildirimidir.
x
değişkeni
ile
double
değer
alan
static
Bir sınıfın static öğelerine, o sınıfa ait bir nesne yaratılmadan erişilebilir. Sınıfın herhangi bir nesnesiyle
bağlantısı olsun istenmeyen öğelere static nitelemesini vermek ona her yerden erişilebilen bir globallik
niteliği verir. Daha önce, Java dilinde global değişken ve global metot olmadığını söylemiş, public nitelemesiyle
öğelere bir tür global işlevsellik kazandırılabildiğini belirtmiştik. Öğelere globallik kazandırmanın ikinci etkin
bir yolu static nitelemesidir. Statik öğeler, sınıfın bir nesnesi içinde olmadığından, onlara programın her
yerinden erişilebilir. Örneğin, main()metodu daima static nitelemesini alır. O nedenle, ait olduğu sınıfın bir
nesnesi yaratılmadan doğrudan çalışabilir. Sınıfa ait bir ya da daha çok nesne yaratıldığında, bu nesneler içinde
yapılan işler static öğeleri etkilemez. Bunun nedeni, statik bir öğeye, o sınıfa ait bir nesne yaratılmadan ana
bellekte bir ve yalnız bir tane bellek adresi ayrılmasıdır. Nesnelerin yaratılıp yokedilmesi, ya da nesneler içindeki
değişimler onları etkilemez. Dolayısıyla, örneğin, static bir değişkenin belli bir anda yalnız bir tane değeri
olabilir. Daha önemlisi, ana bellekte nesneye bağlı olmadan birer adresleri olduğu için, static öğelere nesne
yaratılmadan erişilebilir.
Statik olmayan öğelere anlık (dinamik) öğeler diyeceğiz. Öndeğer (default) olarak, bildirim sırasında static
nitelemesini almayan her öğe anlık’ tır. Bunlara dinamik denmesinin nedeni, program boyunca ana bellekte
kalmazlar, ait oldukları nesne ile birlikte yaratılır ve onunla birlikte ana bellekten silinirler. Örneğin,
long y;
Bölüm 06: Operatörler
1
float Topla(a,b){ return a+b};
deyimleri, sırasıyla, long tipinden anlık x değişkeni ile float değer alan anlık Topla()metodunun
bildirimidir.
Bir sınıfın anlık öğelerine, o sınıfın yaratılan her bir nesnesi için, ana bellekte birer adres ayrılır. Başka bir
deyişle, bir sınıfın üç ayrı nesnesi yaratılmışsa, sınıftaki anlık bir öğeye her nesnede bir tane olmak üzere üç ayrı
bellek adresi ayrılır. Dolayısıyla, anlık değişkenin yaratılan her nesnede ayrı bir değeri vardır. Ama, onların var
olabilmeleri için, önce sınıfa ait nesnenin yaratılması gerekir.
Neden Nesne Yönelimli Programlama?
Bu soruya yanıt vermeden önce aşağıdaki bir dizi programı inceleyeceğiz. Đncelemeyi bitirince, sorunun yanıtı
kendiliğinden ortaya çıkacaktır. Örnek programların hepsi aynı işi yapmaktadırlar: Konsoldan brüt geliri okur,
hesapladığı gelir vergisini konsola yazar.
Đlk program, sözkonusu işi yapan en basit, en kısa ve en kolay programdır. Ama, yapısal programlama açısından
asla tercih edilemeyecek kadar kötü bir programdır.
Vergi01.java
package statik;
import java.util.Scanner;
public class Vergi01 {
public static void main(String[] args) {
double brüt;
Scanner in = new Scanner(System.in);
System.out.println("Brüt Geliri giriniz :");
brüt = in.nextDouble();
in.close();
System.out.println("Gelir Vergisi :");
System.out.println(brüt * 40 / 100);
}
}
Çıktı
Brüt Geliri giriniz :
12345,678
Gelir Vergisi :
4938.2712
Şimdi programımıza konsoldan veri okuyan, hesap yapan ve konsola veri yazan VeriOku(), Hesapla()
ve VeriYaz() adlı üç ayrı metot ekleyelim. Ama öyle yapalım ki, main()metodu VeriYaz() metodunu
çağırsın. VeriYaz() metodu Hesapla() metodunu çağırsın. Hesapla() metodu VeriOku() metodunu
çağırsın. Böylece arka arkaya dört metot çalışacaktır. Bir metodun başka bir metodu çağırması, matematikteki
bileşik fonksiyon (fonksiyon fonksiyonu) kavramından başka bir şey değildir. Đçteki fonksiyon, dıştaki
fonksiyonun parametresidir (değişken).
Vergi02.java
package statik;
import java.util.Scanner;
public class Vergi02 {
double brüt;
2
t.karaçay : Java
ile Nesne Programlama
double VeriOku() {
Scanner scan = new Scanner(System.in);
System.out.println("Brüt Geliri giriniz :");
brüt = scan.nextDouble();
scan.close();
return brüt;
}
double Hesapla() {
return VeriOku() * 40 / 100;
}
void VeriYaz() {
System.out.println(Hesapla());
}
public static void main(String[] args) {
VeriYaz();
}
}
Bu program derlenince aşağıdaki hata iletisini gönderir.
Cannot make a static reference to the non-static method VeriYaz() …
Bu ileti bize, static olmayan VeriYaz() metoduna static bir referans yapılamadığını; yani ona erişilemediğini
söylemektedir. Bu mesajı alınca, programda hata ayıklama (debug) işine başlayalım. Đlk aklımıza gelen kolay
yol, VeriYaz() metodunu static yapmaktır. 20.satırı şöyle değiştirelim:
static void VeriYaz() {
Bunu yaptığımızda, derleyici VeriYaz() metoduna erişebilir. Ancak onun çağırdığı Hesapla() metoduna
erişemez ve şu hata iletisini yollar:
Cannot make a static reference to the non-static method Hesapla() …
Gene yukarıdaki çözümü uygulayıp, Hesapla() metodunu static yapalım. Bunun için 16.satırı şöyle
değiştirelim:
static double Hesapla() {
Bunu yaptığımızda, derleyici Hesapla() metoduna erişebilir. Ancak onun çağırdığı VeriOku() metoduna
erişemez ve şu hata iletisini yollar:
Cannot make a static reference to the non-static method VeriOku() …
Artık, bu hatayı nasıl ayıklayacağımızı biliyoruz. Gene yukarıdaki çözümü uygulayıp, VeriOku() metodunu
static yapalım. Bunun için 8.satırı şöyle değiştirelim.
static double VeriOku() {
Bu aşamada programdaki bütün hataları ayıkladığımızı düşünüp, tekrar derlemeyi denersek şu hata iletisini
alacağız:
Cannot make a static reference to the non-static field brüt …
Bu hatayı da ayıklamak için 6. Satırda bildirimi yapılan brüt değişkenini static yapalım:
static double brüt;
Bu son düzeltmeden sonra programımızda sözdizimi hatası kalmamıştır. Derleyip koşturabiliriz.
Vergi03.java
package statik;
Bölüm 06: Operatörler
3
import java.util.Scanner;
public class Vergi03 {
static double brüt;
static double VeriOku() {
Scanner scan = new Scanner(System.in);
System.out.println("Brüt Geliri giriniz :");
brüt = scan.nextDouble();
scan.close();
return brüt;
}
static double Hesapla() {
return VeriOku() * 40 / 100;
}
static void VeriYaz() {
System.out.println(Hesapla());
}
public static void main(String[] args) {
VeriYaz();
}
}
Çıktı
Brüt Geliri giriniz :
12345,678
4938.2712
Yaptığımız hata ayıklama (debug) işlemleriyle, Vergi03.java programını çalışır duruma getirdik. Ancak, bu
programda Nesne Yönelimli Programlamanın yeteneklerini hiç kullanmadık. Programdaki bütün değişkenleri ve
bütün metotları static yaptık. Bu yöntem büyük projeler için uygun değildir. Bütün değişkenleri ve bütün
metotları static yapmak yerine, sınıfa ait bir nesne yaratmak daha uygun olacaktır. Çünkü, yaratılan nesneye ait
değişken ve metotların işi bitince, nesne ana bellekten silinir; yeni nesnelere yer açılır. Bu olgu çok sayıda nesne
yaratan büyük programlar için önem taşır.
Şimdi, değişkenleri ve metotları statik yapmadan programı yeniden yazalım. Sonra, sınıfa ait bir nesne yaratalım.
Şimdi de ikinci yöntemi deneyelim. Program03 sınıfından p adlı bir nesne yaratalım. Bunu yapmak için
Sınıf_adı nesne_adı = new Sınıf_adı();
nesne yaratıcı (instantiate) deyimini kullanıyoruz. Örneğimizde, nesne_adı p olacaktır.
Vergi04.java
package statik;
import java.util.Scanner;
public class Vergi04 {
double brüt;
double VeriOku() {
Scanner scan = new Scanner(System.in);
System.out.println("Brüt Geliri giriniz :");
4
t.karaçay : Java
ile Nesne Programlama
brüt = scan.nextDouble();
scan.close();
return brüt;
}
double Hesapla() {
return VeriOku() * 40 / 100;
}
void VeriYaz() {
System.out.println(Hesapla());
}
public static void main(String[] args) {
Vergi04 vergi = new Vergi04();
vergi.VeriYaz();
}
}
Çıktı
Brüt Geliri giriniz :
12345,678
4938.2712
ipucu
Anımsayınız, Java uygulama programlarında main() metodunu içeren sınıf adı ile program adı daima aynı
olmalıdır. Vergi04.java programı tek sınıftan oluşuyor. Ama büyük programlar çok sayıda sınıftan
oluşabilir. Birden çok sınıf olduğunda, onlardan yalnızca birisi main() metodunu içerebilir. main() metodu
programın giriş kapısı gibidir; başka yerden programa girilemez.
26.satırdaki
Vergi04 vergi = new Vergi04();
deyimi, Vergi04 sınıfına ait vergi adlı bir nesne yaratıyor. Bu deyimdeki new bir anahtar sözcüktür ve daima
sınıftan nesne yaratmak için kullanılır. Nesneye vergi yerine istenilen başka bir ad verilebilir. Nesneye verilen ad
aynı zamanda nesneyi işaret eden referansın da adıdır. Tabii, verilen adın, yaratılan nesneyi ima eden bir sözcük
olması, kaynak programın okunabilirliğini artırır; derleyici için verilen adın önemi yoktur.
Vergi04 sınıfındaki VeriOku(), Hesapla(), VeriYaz() metotları ile brüt adlı değişkenin önüne
static nitelemesi konulmadığı için, öntanımlı olarak (default) anlık olurlar; yani static değildirler.
Dolayısıyla, ancak sınıfa ait bir nesne yaratılınca o nesne içinde ana bellekte kendilerine birer yer ayrılır.
Üstlendikleri işlevlerini o nesne içinde yaparlar. Örnekteki nesnenin ana bellekteki adresini işaret eden referansın
(işaretçi, pointer) adı vergi’dir. vergi adı aynı zamanda yaratılan nesnenin de adıdır. vergi nesnesine ait
VeriYaz() metoduna erişmek için
vergi.VeriYaz();
deyimini kullanıyoruz. Nesne adı ile metot arasındaki nokta (.) simgesi, nesnenin öğelerine erişimi sağlayan
operatördür. Nesneye ait değişkenler için de aynı operatör kullanılır.
Böyle olduğunu görmek için, sınıfa ait p ve q adlarıyla iki farklı nesne yaratalım ve o nesnelerdeki
metotlarımızı farklı verilerle çalıştıralım. p ve q nesnelerinden gelen çıktıların farklı olduğunu görebiliriz.
Vergi05.java
import java.util.Scanner;
Bölüm 06: Operatörler
5
public class Vergi05 {
double brut;
double VeriOku() {
Scanner s = new Scanner(System.in);
System.out.println("Brüt Geliri giriniz :");
brut = s.nextDouble();
return (brut);
}
double Hesapla() {
return VeriOku() * 40 / 100;
}
void VeriYaz() {
System.out.println(Hesapla());
}
public static void main(String[] args) {
Demo p = new Demo();
System.out.println("p nesnesi için :");
p.VeriYaz();
System.out.println();
Demo q = new Demo();
System.out.println("q nesnesi için :");
q.VeriYaz();
}
}
Çıktı
p nesnesi için :
Brüt Geliri giriniz :
12345,678
4938.2712
q nesnesi için :
Brüt Geliri giriniz :
98765,4321
39506.17284
Şimdi başka bir durumu deneyelim. Sınıfta bildirimi yapılan metotların hepsini satic yapalım ve sınıftan bir
nesne yaratmayı deneyelim.
Vergi06.java
import java.util.Scanner;
public class Vergi06 {
static double VeriOku() {
Scanner s = new Scanner(System.in);
System.out.println("Brüt Geliri giriniz :");
return (s.nextDouble());
}
6
t.karaçay : Java
ile Nesne Programlama
static double Hesapla() {
return VeriOku() * 40 / 100;
}
static void VeriYaz() {
System.out.println(Hesapla());
}
public static void main(String[] args) {
Vergi06 vrg = new Vergi06();
vrg.VeriYaz();
}
}
Bu programı derlemek istersek şu uyarı iletisini verecektir:
The static method VeriYaz() from the type Vergi06 should be accessed in a static way
Ancak program derlenir ve koşar. Bu demektir ki, static metotlara bir nesnenin referansı ile erişilebilir. Çünkü,
statik öğelere her nesneden erişilebilir. Ancak bu yöntem, erişilen öğenin anlık olduğu yanılgısına yol açabileceği
için, önerilmez. Statik öğelere erişirken, bir nesneye ait referans (pointer) yerine sınıfın adının kullanılması
yeğlenmelidir. Örneğin, yukarıdaki programda 23.satır yerine
Vergi06.VeriYaz();
deyimini yazarsak, programın derlendiğini ve koştuğunu görebiliriz. Statik metotlar için önerilen deyim budur.
Eğer çağrı aynı sınıf içinden yapılıyorsa, sınıf adını yazmaya gerek olmayabilir. Örneğin, yukarıdaki deyim
yerine
VeriYaz();
deyimini yazsak da program derlenir ve koşar. Ancak, program birden çok sınıf içeriyorsa, farklı bir sınıftaki bir
static metodu çağırmak için, öncekinde olduğu gibi sınıf adını belirtmeliyiz. Değilse, derleyici, metodu hangi
sınıftan çağıracağını bilemez. Her durumda, static metotları çağırırken, sınıf adlarını öntakı yapmayı alışkanlık
edinmek, büyük programlarda oluşabilecek yanılgıları önler ve gerektiğinde programın güncellenmesi kolaylaşır.
Statik metotlar için söylediğimiz bu özelikler statik değişkenler için de geçerlidir.
Alıştırmalar
Şimdi aşağıdaki durumları ayrı ayrı deneyelim:
Metotların üçü de anlık ise, 22. ve 23. satırlardaki deyimlerle programın derlenip koşabildiğini gördük.
Metotların üçü de static ise, 23.satırdaki deyim aşağıdakilerden birisi olabilir:
vrg.VeriYaz();
Bu durumda statik metot nesne içinden çağrılıyor.
VeriYaz();
Bu durumda, aynı sınıftaki statik VeriYaz() metoduna adıyla erişiliyor. VeriYaz() metodu
statik Hesapla() metoduna adıyla erişiyor. Sonunda Hesapla() metodu da statik
VeriOku() metoduna adıyla erişiyor.
Vergi06.VeriYaz();
Bu durumda, statik VeriYaz() metoduna, sınıf adı öntakı yapılarak erişiliyor. Karışıklığı önlemek için, büyük
programlarda statik öğelere erişmek için önerilen sözdizimi budur.
VeriYaz() metodu anlık, VeriOku() ve Hesapla() metotları statik ise 22. ve 23. satırlardaki
deyimlerle program derlenir ve koşar. Bunun nedeni şudur: Anlık VeriYaz() metoduna vrg referansı ile
Bölüm 06: Operatörler
7
erişiliyor. Sonra nesne içinden statik Hesapla() metodu çağrılıyor. En sonunda, statik Hesapla() metodu
statik VeriOku() metodunu çağırıyor. Bunlar Java’da geçerli eylemlerdir.
VeriYaz() ile Hesapla() metotları anlık VeriOku() metodu statik ise 22. ve 23. satırlardaki
deyimlerle program derlenir ve koşar. Bunun açıklaması kolaydır. Anlık VeriYaz() metoduna vrg
referansı ile erişiliyor. Sonra aynı nesne içindeki anlık Hesapla() metodu çağrılıyor. En sonunda, nesne
içinden statik VeriOku() metodunu çağrılıyor.
VeriYaz() statik, Hesapla() anlık ise program derlenemez. Çünkü statik metotlar nesne içindeki öğelere
erişemezler. O nedenle statik VeriYaz() metodu nesne içindeki anlık Hesapla() metodunu çağıramaz.
Aşağıdaki program, farklı sınıftaki statik öğelere adlarıyla erişilemeyeceğini gösteriyor.
Demo.java
import java.util.Scanner;
class Vergi {
static double VeriOku() {
Scanner s = new Scanner(System.in);
System.out.println("Brüt Geliri giriniz :");
return (s.nextDouble());
}
static double Hesapla() {
return VeriOku() * 40 / 100;
}
static void VeriYaz() {
System.out.println(Hesapla());
}
}
public class Demo{
public static void main(String[] args) {
VeriYaz();
}
}
Bu program için derleyici şu hata iletisini atar:
The method VeriYaz() is undefined for the type Demo
Bu ileti, Demo sınıfında VeriYaz() metodunun varolmadığını söylüyor. Hatayı ayıklamak (debug) için,
derleyiciye VeriYaz() metodunu hangi sınıfta bulacağını söylemeliyiz. Bunu yapmak için, sınıf adını
metodun öntakısı yapmak yetecektir; yani 24.satırdaki deyimi şöyle yazmalıyız:
Vergi.VeriYaz();
Bunu yapınca programın derlendiğini ve koştuğunu göreceğiz.
Şimdi 15.satırdaki VeriYaz() metodunu anlık yapalım.
Demo.java
import java.util.Scanner;
class Vergi {
8
t.karaçay : Java
ile Nesne Programlama
static double VeriOku() {
Scanner s = new Scanner(System.in);
System.out.println("Brüt Geliri giriniz :");
return (s.nextDouble());
}
static double Hesapla() {
return VeriOku() * 40 / 100;
}
void VeriYaz() {
System.out.println(Hesapla());
}
}
public class Demo {
public static void main(String[] args) {
// Vergi p = new Vergi();
Vergi.VeriYaz();
}
}
Bu program derlenemez ve şu hata iletisini atar:
Cannot make a static reference to the non-static method VeriYaz() from
the type Vergi
Bu ileti, farklı bir sınıftaki anlık metoda erişilemediğini söylüyor. Hatayı ayıklamak için, ya VeriYaz()
metodunu statik yapacağız ya da VeriYaz() metodunu içeren sınıfa ait bir nesne yaratacak ve VeriYaz()
metoduna referans ile erişeceğiz. Birinci yöntemi, yani metodu statik yapmayı önceden denedik ve çalıştığını
gördük. Şimdi VeriYaz() metodunu içeren Vergi sınıfına ait bir nesne yaratalım. Bunun için 23. ve 24.
satırlardaki deyimleri şöyle değiştirelim:
Vergi vergi = new Vergi();
vergi.VeriYaz();
Bu deyimlerle programın derlendiğini ve koştuğunu görebiliriz.
Şimdi de static metotları çağırırken metot adlarının önüne this anahtar sözcüğünün konamayacağını görelim.
Bunun için 23.satırdaki deyim yerine
this.VeriYaz();
deyimini yazalım. Derleyicinin şu hata uyarısını attığını göreceğiz:
Cannot use this in a static context
Özet
Buraya kadar yaptıklarımızı özetlersek şu kurallar ortaya çıkar:
1.
2.
3.
4.
5.
6.
Anlık metot, anlık değişkenlere ve anlık metotlara referanslarıyla erişebilir.
Anlık metot, aynı sınıftaki statik değişkenlere ve statik metotlara adlarıyla erişebilir.
Anlık metot, farklı sınıftaki statik değişkenlere ve statik metotlara, sınıf adlarını öntakı yaparak
erişebilir.
Aynı sınıf içindeki statik değişkenlere ve statik metotlara adlarıyla erişilebilir.
Farklı sınıf içindeki statik değişkenlere ve statik metotlara, ait oldukları sınıf adları öntakı yapılarak
erişebilir. Bu durumda sınıf adı işaretçinin görevini üstlenir.
Nesne içinden, aynı sınıfa ait statik değişkenlere ve statik metotlara adlarıyla erişilebilir.
Bölüm 06: Operatörler
9
7.
Nesne içinden, farklı sınıfa ait statik değişkenlere ve statik metotlara, ait oldukları sınıf adları öntakı
yapılarak erişebilir. Bu durumda sınıf adı işaretçinin görevini üstlenir.
8. Statik metot, aynı sınıftaki statik değişkenlere ve statik metotlara adlarıyla erişebilir.
9. Statik metot, farklı sınıftaki statik değişkenlere ve statik metotlara, sınıf adlarını öntakı yaparak
erişebilir.
10. Statik metot, anlık değişkenlere ve anlık metotlara adlarıyla erişemez; ancak referans (pointer)
kullanarak erişebilir.
11. Statik metot, this anahtar sözcüğünü kullanamaz; çünkü this için işaret edilecek bir nesne yoktur.
10
t.karaçay : Java
ile Nesne Programlama
Download