Java Paketleri
(Packages)
Java Kaynak Programları
Java
programları paketlerden (packages) oluşur. Paketlerin
içinde sınıflar ve arayüzler (classes
and interfaces) bulunur.
Ayrıca, paketler başka paketlerin dışalımını (import)
yapabilir. Bütün bunlar paketlerin öğeleri olur. Paket içine konacak sınıflar, arayüzler ve dışalımlar için bir sayı sınırlaması yoktur.
İyi bir programcı, birbirleriyle ilişkili olan sınıfları ve arayüzleri
aynı pakete koyar ve başka uygun paketlerin dışalımını yapar..
Bir paket
içinde tanımlanan sınıflar ve arayüzler her
istenildiğinde tekrar takrar kullanılabilirler. Bu
nedenle, Java dili, “bir yaz bin kullan” deyişine hak verdirir. Bir sınıftan
istenildiği kadar nesne (object) yaratılabilir.
Nesneler kendi başlarına birer varlıktır ve her birisine ana bellekte ayrı bir
yer ayrılır. Bir sınıftan bir nesne yaratılmasına nesnenin doğumu diyelim. İşi
biten nesne, ana bellekten silinir. Silme işini, JVM deki Çöp Toplayıcısı (Garbage Collection ) yapar.
Nesnenin bellekten silinmesine nesnenin
ölümü diyelim.
Büyük
programlar çok sayıda sınıfın ve arayüzün tanımını ve
kullanılmasını gerektirir. Yüzlerce, hatta binlerce sınıfın tanımlandığı bir
programda, programcının sınıf adlarını ve sınıfların içerdiği diğer öğelerin
adlarını anımsaması ve tekrarlara düşmemesi hemen hemen
olanaksızdır. Bu nedenle, büyük programlarda, birbirleriyle ilişkili olan
sınıflar bir araya getirilip bir paket içine konulur. Farklı paketlerde aynı
adlar kullanılabilir. Bu açıdan bakınca, paketler, kayıt ortamındaki dizinler
ve alt-dizinler gibidir. Şimdiye kadar dikkat ettiyseniz, aynı alt dizinde olan
kısıtsız sınıflar birbirlerine erişebiliyordu. Bir paket içindekiler de aynı erişim
kolaylığına sahip olurlar. Zaten, Java, aynı dizin içindeki sınıfları, bir
paketin içindeymiş gibi görür.
Paketin Yapısı:
package paket_adı ;
import paket_listesi;
{
//paket
gövdesi
}
Paket
gövdesine konacak sınıflar ve arayüzler, bilindik
şekilde (şimdiye dek yaptığımız gibi) tanımlanırlar. Paket içindeki bir sınıfın
kendi alt sınıfları, constructor’ları, anlık ve static değişkenleri, metotları, metotlar içinde yerel
değişkenleri var olabilir.
Örnek 1a:
//
Basit bir paket
package paket01;
class Denge {
String ad;
double denklik;
Denge(String n, double b) {
ad = n;
denklik
= b;
}
void show() {
if(denklik<0)
System.out.print("-->>
");
System.out.println(ad + ": YTL"
+ denklik);
}
class DengeHesapla
{
public static void main(String args[]) {
Denge dizim[] = new
Denge[3];
dizim[0] = new
Denge("Ahmet Demir", 123.23);
dizim[1] = new
Denge("Sami Yenice", 157.02);
dizim[2] = new
Denge("Bahar Selvi", -12.33);
for(int i=0; i<3; i++) dizim[i].show();
}
}
Paketin
kaynak programı, paket adına eklenen .java uzantısı ile kaydedilir. Örneğin, paketin adı paket01 ise,
bunu paket01.java adıyla
kaydederiz. Java kaynak programlarımız herhangi bir dizinde olabilir. Derlenen bytecode’lar başka bir dizine konulabilir. Örneğin, derlenen
bytekodları c:\jprog dizinine koymak isteyelim. paket01 paketini derlemek
için,
javac –d c:\jprog
paket01.java
komutunu
yazmak yeterlidir. Buradaki –d seçkisi (option) c:\jprog dizini içinde paket01 adlı
bir alt-dizin yaratır ve paket içindeki sınıfların derlenmiş kodlarını (bytecode, .class uzantılı
dosyalar) o alt dizin içine koyar. Eğer, -d c:\jprog
seçkisi konulmazsa, etkin dizin içinde paket01 alt
dizinini yaratır ve bytecode’ları oraya koyar. Gerçekten, c:\jprog\paket01 dizini içine
bakarsanız, paket01 içindeki iki sınıfın bytecode’larının
oraya konmuş olduğunu görebilirsiniz.
Tabii,
sistemin ortam değişkenleri bu dosyaların bulunduğu yerleri bilmiyorsa,
yukarıdaki komut satırına onların tam adreslerini yazmak gerekir. Java,
platformdan bağımsız çalıştığı için, kendi ortam değişkenlerini kullanır. Bu
nedenle, istenirse, sisteme, bytecode’ların bulunduğu
dizin tanıtılabilir. Java, bunun için CLASSPATH adlı ortam değişkenini
kullanır. Java yorumcusunu her çağırışta, bytecode’ların
bulunduğu adresi yazmaktan kurtulmak için, işletim sisteminizde CLASSPATH ortam
değişkenini yaratmalısınız.
paket01 paketi içinde Denge.class
ve DengeHesapla.class adlı iki sınıf
olduğundan, bu sınıfların derlenmiş kodları Denge.class ve HesapDenge.class olacaktır. paket01
alt dizini içine bakarsak, bu iki bytecode’un orada
olduğunu görebiliriz.
Paket
içindeki bir sınıfa erişmek için önce paket adı yazılır, sonuna nokta (.)
konulduktan sonra sınıf adı yazılır.
Örneğin, paket01 paketi
içindeki Denge
adlı sınıfa erişmek için paket01.Denge yazılmalıdır.
Gerçekten
java DengeHesapla
komutu
yazılırsa,
Exception in thread
“main” java.lang.NoClassDefFoundError: DengeHesapla
mesajı
gelir, yani java yorumcusu (interpreter), HesapDenge.class bytecodunu bulamamıştır.
Şimdi
java paket01.DengeHesapla
komutu
yazılırsa, program çalışır ve şu çıktıyı verir:
Ahmet Demir: $123.23
Sami Yenice: $157.02
-->> Bahar Selvi: $-12.33
Pakete eklemeler
Bir paketi
yaratırken, onun bütün sınıflarını ve arayüzlerini
aynı anda bir arada yazmanız gerekmez. İlk satırına
package paket01;
koyarak
yaratacağınız her sınıf ve arayüz, paket01 paketine eklenir; derlendikten sonra, onun bytekodu paket01 dizini içine
gider. Örneğin, yukarıdaki pakette yer alan iki sınıfı ayrı ayrı
yazalım:
Örnek 1b:
//
pakete bir sınıf ekleme
package paket01;
class Denge {
String ad;
double denklik;
Denge(String n, double b) {
ad = n;
denklik
= b;
}
void show() {
if(denklik<0)
System.out.print("-->>
");
System.out.println(ad + ":
YTL" + denklik);
}
Bu programı Denge.java adıyla kaydedip derleyiniz. Denge.class bytekodunun paket01 dizini içine yerleştiğini görünüz. Sonra,
Örnek 1c:
//
pakete başka bir sınıf ekleme
package paket01;
class DengeHesapla
{
public static void main(String args[]) {
Denge dizim[] = new
Denge[3];
dizim[0] = new
Denge("Ahmet Demir", 123.23);
dizim[1] = new
Denge("Sami Yenice", 157.02);
dizim[2] = new
Denge("Bahar Selvi", -12.33);
for(int i=0; i<3; i++) dizim[i].show();
}
}
programını DengeHesapla.java
adıyla kaydedip derleyiniz. DengeHesapla.class bytekodunun da paket01 dizini içine yerleştiğini göreceksiniz.
Dizin adları ve paketler
Şimdiye dek
yazdığımız programlar tek bir sınıftan oluşan programlardır. Bunlar ya class adıyla
başlıyor ya da import deyimi ile başka bir paketin dışalımını yapıyordu.
Oysa, bu bölümün başında, java programlarının
paketlerden oluştuğunu söyledik. Öyleyse, tek sınıftan oluşan bir program ile
paket arasında bir ilişki kurmamız gerekir.
Java’da tek
başına tanımlanan bir sınıftan oluşan program, yalnız o sınıfı içeren bir paket
gibi düşünülebilir. Tek bir sınıftan oluşan program derlendiğinde, bytecode’u hangi dizinde ise, o dizinin adıyla (sanal) bir
paket var ve o paket sözkonusu sınıfı içeriyor diye algılayacağız.
Anımsarsanız, aynı dizin içinde bulunan farklı sınıflar, kısıt yoksa,
birbirlerine erişebiliyordu. Bu demektir ki, aynı dizinde olan sınıflar, bir
paket içindeymiş gibi rol oynarlar.
Hiyerarşik
paketler
Java
paketleriyle dizin adları arasında bire-bir karşılık olduğunu biliyoruz.
Öyleyse, alt-dizin yaratabildiğimize göre, alt-paketler de
yaratabilmeliyiz. Gerçekten bu
mümkündür. Alt-sınıflar yarattığığımız gibi
alt-paketler de yaratabiliriz. Örneğin, paket01 adlı
bir paket yaratmış olalım. Bunun apaket02 adlı
bir alt paketini yaratmak için
paket01.apaket02
adını
kullanırız. Bunun da aapaket03 adlı bir alt-paketi için
paket01.apaket02.aapaket03
adını
kullanırız.
paket01
apaket02
aapaket03
Tabii, paket
ve alt-paket adlarının, java adlandırma kurallarına
uymak koşuluyla, serbestçe seçilebildiğini söylemeye gerek yoktur. JVM, addaki
hiyerarşiye uyan dizin ve alt dizinleri yukarıdan aşağıya doğru arar.
Örnek
2:
//
alt paket
package paket01.apaket02;
public class
Denge02 {
String ad;
double denklik;
public Denge02(String str, double
d) {
ad = str;
denklik = d;
}
public void show() {
if(denklik<0)
System.out.print("-->>
");
System.out.println(ad + ":
YTL" + denklik);
}
}
Bu programı Denge02.java adıyla kaydedip derleyiniz. Denge02.class bytekodunun paket01\apaket02 dizini içine yerleştiğini görürsünüz.
Örnek
3:
// altaltalt paket
package paket01.apaket02.aapaket03;
public class
Denge03 {
String ad;
double denklik;
public Denge03(String str, double
d) {
ad = str;
denklik = d;
}
public void show() {
if(denklik<0)
System.out.print("-->>
");
System.out.println(ad + ":
YTL" + denklik);
}
}
Bu programı Denge03.java adıyla kaydedip derleyiniz. Denge03.class bytekodunun paket01\apaket02\aapaket03 dizini içine yerleştiğini görürsünüz. En içteki
pakete erişmek için
paket01.apaket02.aapaket03
yazılmalıdır.
Java paketleri
http://java.sun.com web sitesinde daima güncel java
paketlerinin tanımları yer alır. Programcı onlara serbestçe ulaşır. Java açık
kaynak sistemlidir. Kaynak programlara herkes erişebilir. Sözkü
edilen sitede API (Application Programmer’s
Interface) listesinde çok sayıda paket vardır. Her
paketin içindeki sınıfların ve arayüzlerin açık
tanımları görülebilir. Onlar, bir programcının gereksinimini büyük ölçüde
karşılayacak yeterliktedir. Elbette, programcı gerekseme duyduğu sınıfları, arayüzleri ve paketleri serbestçe yaratabilir. Ama, API ‘de
hazır olanları yeniden yaratmanın çok akıllıca olduğu söylenemez. Java API ‘leri denenmiş, hatasız kodlar içerdiği gibi, onları
doğrudan kullanmak programcıya büyük zaman kazandırır. Bu derste çok
kullanacağımız java paketlerinden bazıları şunlardır:
java.lang Her java programına default olarak çağrılır. Onu ayrıca import
deyimi ile çağırmaya gerek yoktur. Sistemle ilgili başlıca işleri yapan
sınıflara sahiptir.
java.io Giriş/çıkış işlemlerini yapar.
java.applet Tarayıcıda applet’in
görünebilmesi için gerekli sınıfları içerir.
java.awt Grafiksel kullanıcı arayüzü (GUI -
Graphical User Interface) yaratmak için gerekli sınıfları içerir.
java.util Liste, takvim, tarih vb. araçları yaratmaya yarayan
sınıfları içerir.
java.net TCP/IP
ağ programlaması için gerekli sınıfları içerir.
Paketin kuruluşu
Paketler
kurulurken şu kurallara uyulur:
1.
Her paketin bir
adı vardır. Paket adından hemen önce package anahtar sözcüğü yer alır.
2.
Paketler erişim
belirtkesi almaz, ancak içerdiği sınıflar erişim belirtkesine sahip olabilir.
3.
Sınıftan ayırmak için, paket adları küçük harfle başlatılır.
4.
Bir paket içinde
aynı adı taşıyan iki sınıf ya da arayüz olamaz. Ama,
ayrı paketlerde aynı adı taşıyan sınıflar ve arayüzler
olabilir.
5.
Paketler başka
paketlerin dışalımını yapabilir. Dışalım varsa, başlıktan sonraki deyim import deyimi olur.
6.
import
deyiminden sonra paketin gövdesi yaratılır, yani onun içine konulacak sınıflar,
arayüzler tanımlanır.
7.
Kurulan bir paket
derlenince, onun içindeki bütün sınıflar ve arayüzler
de derlenmiş olur. Bunların bytecode’ları, paketin adıyla
yaratılan alt dizine yerleşir. Bu alt diziyi derleyici kendiliğinden yaratır.
8.
Derlenen paketin,
herhangi bir java programına dışalımı (import) yapılabilir.
9.
Dışalımı (import) yapılan bir paketteki bütün öğeleri JVM bilir.
Dolayısıyla, erişime izinli olan öğeleri içeren kodlar programda serbestçe
kullanılır.
Paket Adları:
Java
paketlerinin adları, UNIX’te dizin adları gibi hiyerarşik bir yapıdadır. Örneğin java.lang.String uzun adı String sınıfının hiyerarşisini gösteren uzun adıdır. String ise, aynı sınıfın basit (kısa) adıdır. Hiyerarşik ad,
sınıfın ait olduğu üst yapıları (sınıf, paket, interface)
gösterir. Sun şirketinin hazırladığı
paketler daima java
ile başlar. Benzer olarak, ibm
şirketinin hazırladığı paketler de ibm ile başlar: ibm.awt.Button uzun adının
önündeki ibm adı, bu sınıfın ibm
şirketi tarafından yaratıldığını gösterir. java.lang.String hiyerarşik adını çözümlersek, şunlar ortaya çıkar. String sınıfı sun şirketinin
hazırladığı java.lang paketi içindedir. İsterseniz, hazırlayacağınız
paketlerin hepsini bir üst dizine koyup, onun adını hiyerarşik
adların en başına getirebilirsiniz. İsterseniz, yalnızca paket01 gibi basit bir ad verebilirsiniz.
javac derleyicisi, bir paketi derlerken, o paket adıyla bir alt dizin
yaratır ve paketin içindeki sınıfların ve arayüzlerin
bytecode’larını bu dizin içine koyar. Başka bir
deyişle, java’da paket adlarıyla dizin adları
bire-bir eşleşirler.
Java Arşivleri
JDK ‘yı kaydettiğiniz dizinin içine bakarsanız, orada src.zip ya da src.jar adlı bir dosya göreceksiniz. Ayrıca lib alt dizini içinde .jar uzantılı dosyalar vardır. Bunlar arşivlenmiş kaynak
programlardır. JDK ‘nın farklı sürümlerine bağlı
olarak arşivlenmiş kaynak kodları değişiklik
gösterebilir. Ama hepsinin temel işlevi, o sürüm için java’nın
temellerini oluşturan paket, sınıf ve arayüzlerin
açık kaynak kodlarını sunmaktır. Arşivlenmiş dosyaları açabilirsiniz. Örneğin, java.awt.Button sınıfını src.jar arşivinden açmak için
jar xf src.jar src/java/awt/Button.java
komutunu
vermeniz yeterlidir. Bu komut src/java/Button alt-dizinini
yaratır ve oraya Button.java kaynak programını koyar. Windows’ ta src.zip dosyasının içeriğini görmek için, örneğin, winzip gibi bir arşivleme programını kullanabilirsiniz.
Jdk/bin alt
dizini içindeki javap.exe dosyasını
kullanarak derlenmiş java programlarının (bytecode) yapısını görebilirsiniz. Örneğin, Denge.class bytecode’unun yapısını görmek için
javap –c Denge
komutunu
yazınız. Tabii, sistemizin PATH’ı
içinde değilse, komutta geçen dosyaların tam adreslerini yazmalısınız.
import bildirimi
(dışalım deyimi)
Bir
programda kullanılacak java paketleri
import paket_adı;
deyimi ile
çağrılır. Birden çok paket çağrılacaksa, aynı deyim içine, paket adları (,) ile
birbirlerinden ayrılarak yazılabilir. import deyimi paket kurulurken, paket adından sonra yazılan
ikinci deyim olmalıdır. Sınıf kurulurken ise, yazılacak ilk deyim olmalıdır. import deyimiyle
dışalımı yapılan sınıflar iki türlü çağrılabilir:
Örnekler:
import java.io.DataInputStream ;
import java.awt.* ;
Birincide, derleyici programın kodları arasında DataInputStream sınıfının tanımını bulamazsa, dışalımını yaptığı pakete bakar ve onu hemen bulur. İkincide ise, programda adı geçen bir öğeyi bulamadığı zaman, dışalımını yaptığı java.awt paketine gider ve orada arar.
Uyarı: import
deyiminde joker kullananılması,
bütün paketin programla birlikte derleneceği anlamına gelmez. Pakette gerekli
olan yerler derlemeye katılır.
JVM, kaynak
programda adı geçen bir öğeyi, yakından uzağa doğru arar. Programın bulunduğu
çevreye göre, arama sırası şöyledir:
1.
Kodun bulunduğu
kaynak blok ya da sınıf
2.
Kaynak programı
içeren paket
3.
Hiyerarşik adıyla dışalımı yapılan sınıf,
4.
Dışalımı yapılan
paket (joker simgeli)
Her java programı java.lang paketini otomatik olarak çağırır. Onun dışalımını (import) yapmaya gerek yoktur. Başka bir deyişle, her java programı
import java.lang.* ;
dışalımının
yapıldığını varsayar. Bu paket içinde tanımlanmış olan her sınıf ve o sınıfların
içerdiği öğeler serbestçe kullanılabilir. Bu nedenle, java.lang paketi bir programın çalışması için gerekli olan
temel öğeleri içerir.
Erişime Koruma
Sınıflar ve
paketler, java’da erişim kısıtlamalarına farklı
düzeylerde olanak sağlar. Daha önce, bir sınıfta public, private,
protected
erişim belirtkesiyle nitelenmiş öğeleri görmüştük. Doğal olarak erişim
kısıtlaması paketler için de geçerlidir. Java’da bir sınıftaki öğelere erişim,
yakından uzağa doğru düşünülmelidir:
Aynı paketteki
öteki sınıflar (alt-sınıf olmayan),
Aynı paketteki
sınıfların alt-sınıfları,
Farklı
paketteki sınıflar,
Farklı
paketteki sınıfların alt-sınıfları
Aşağıda pkt1 ve pkt2 adlarıyla iki paket yaratılmıştır. pkt1
içinde Koru, AltKoru ve Akraba adlı üç sınıf tanımlıdır. Koru içinde, sırasıyla, private, protected ve public damgalı üç değişken bildirimi yapılmıştır: n_pri,
n_pro, n_pub. Erişemeyen kodların önüne //
(açıklama) konmuştur. AltKoru adlı sınıf Koru sınıfının bir alt-sınıfı olduğundan, onun kodları Koru’nun public ve protected damgalı öğelerine erişebilir, ama private öğelerine erişemez. Aynı paketteki alt sınıfların,
üst-sınıftaki private öğelere erişemez olduğunu
anımsayınız. Akraba sınıfı aynı pakettedir. Bunun kodları da Koru’nun public ve protected öğelerine
erişebilir, ama private öğelerine erişemez.
Aşağıdaki üç
programı önce çözümleyiniz, sonra derleyiniz.
package pkt1;
public class Koru {
int n = 1;
private int n_pri = 2;
protected int n_pro = 3;
public int n_pub = 4;
public Koru() {
System.out.println("temel constructor");
System.out.println("n = " +
n);
System.out.println("n_pri = " + n_pri);
System.out.println("n_pro = " + n_pro);
System.out.println("n_pub = " + n_pub);
}
}
---------------------------------------------------
package pkt1;
class AltKoru extends Koru {
AltKoru() {
System.out.println("alt-sınıf için constructor");
System.out.println("n = " +
n);
// yalnızca class için geçerlidir
// System.out.println("n_pri = " + n_pri);
System.out.println("n_pro = " + n_pro);
System.out.println("n_pub = " + n_pub);
}
}
---------------------------------------------------
package pkt1;
class Akraba {
Akraba() {
Koru p = new
Koru();
System.out.println("Aynı pakette constructor");
System.out.println("n = " +
p.n);
// yalnızca class için geçerlidir
// System.out.println("n_pri = " + n_pri);
System.out.println("n_pro = " + p.n_pro);
System.out.println("n_pub = " + p.n_pub);
}
}
Aşağıdaki sınıflar pkt2 paketi içindedirler. Koru2 sınıfı
pkt1 paketi içindeki bir sınıftan türetilmiştir; yani farklı bir paketteki constructoru kullanmaktadır. Hiyerarşik
adı Pkt1.Koru2 olan bu su sınıf, pkt1.Koru
sınıfının public ve protected damgalı öğelerine erişebilir, ama private damgalı öğeleri ile erişim damgası taşımayan (default protection) öğelerine
erişemez. Erişim damgasız öğelere, başka paketlerin sınıflarının
erişemeyeceğini anımsayınız. Komşu sınıfı, farklı
pakette olduğundan, o yalnızca public damgalı öğelere erişebilir.
Aşağıdaki üç
programı önce çözümleyiniz, sonra derleyiniz.
package pkt2;
class Koru2 extends pkt1.Koru {
Koru2() {
System.out.println("Farklı paketten
constructor");
// yalnızca class ya da paket için geçerlidir
// System.out.println("n = " +
n);
// yalnızca class için geçerlidir
// System.out.println("n_pri = " + n_pri);
System.out.println("n_pro = " + n_pro);
System.out.println("n_pub = " + n_pub);
}
}
-----------------------------------------------------
package pkt2;
class Komşu {
Komşu() {
pkt1.Koru p = new
pkt1.Koru();
System.out.println("Farklı
paketteki sınıfın constructor'u");
// yalnızca class ya da paket için geçerlidir
// System.out.println("n = " +
p.n);
// yalnızca class için
geçerlidir
// System.out.println("n_pri = " + p.n_pri);
// class, subclass ve package için
geçerlidir
// System.out.println("n_pro = " + p.n_pro);
System.out.println("n_pub = " + p.n_pub);
}
}
-----------------------------------------------------
//
Demo package pkt1.
package pkt1;
// pkt1
içindeki sınıflar için nesne yaratıyor
public class Demo {
public static void main(String args[]) {
Koru ob1
= new Koru();
AltKoru ob2 = new AltKoru();
Akraba ob3
= new Akraba();
}
}
-----------------------------------------------------
// Demo package pkt2.
package pkt2;
// pkt2 içindeki sınıflar için nesne
yaratıyor
public class Demo2 {
public static void main(String args[]) {
Koru2 ob1 = new
Koru2();
Komşu ob2 = new
Komşu();
}
}