Diyelim ki bir başka eve taşınacaksınız. Yani kolileri hazırlama vakti geldi çattı... İlk iş gidip koli bulmak olur heralde, sonra da eşyaları bu kollerin içine düzgün ve anlamlı bir şekilde yerleştirmek. Nasıl yerleştiririz ki acaba? Kırılabilecek mutfak eşyalarını bir koliye, giyecekleri bir koliye, temizlik malzemelerini bir ya da birden çok koliye koyarsak hem daha düzenli olurlar hem de aradığımızı daha kolay bulabiliriz heralde.
Buraya kadar olan kısım, olaya bir girişti aslında.
Fakat bu koli paketlerinin mantığının java'da kullanılan paket kavramından hiç bir farklılığı yok. Temel olarak özetlemek gerekirse; Java'da paketler, aynı amaç üzerinde çalışan kod bloklarını bir araya toparlayabilmemiz için kullanılmaktadır. Java'da oluşturulan paketler 'package paket_ismi;' notasyonu ile belirtilmektedir. Bununla ilgili örnek vereceğim ilerleyen satırlarda. Aslında C# kullananların yakından tanıdığı namespace kavramından hiçbir farkı yoktur Java'daki paket kavramının. Oluşan paketlerin kullanılabilmesi için ise import anahtar sözcüğü kullanılmaktadır. Bu da C#'daki using anahtar sözcüğüne karşılık düşmektedir.
Az önce aynı amaç için kullanılan kod parçacıklarının bir araya toparlanmasından bahsetmiştik. Örneğin IO ve math birer paket örneğidirler. IO paketi java'da kullanılan dosya girdi/çıktı işlemleri için yapılabilecek gerekli sınıfları bulundurmaktadır. Yani temel işlevi dosya girdi/çıktı işlemlerin yapılabilmesidir. Math paketi ise matematik işlemlerinin yapılabilmesinden sorumludur ve kullanılabilecek neredeyse tüm matematik işlemlerini bulundurmaktadır. Bu örnekler paketlerinin içeriğinin ne kadar güzel hazırlandığı ve tüm bu aynı amaca hizmet eden kodların nasıl bir araya toplandığına güzel bir örnektir. Paketlerin isimlerinin de güzelce seçilmesi çok önemlidir. Çünkü isimleri içeriklerinin ne olduğunu ve hangi özelleşmiş yapıları barındırdığını güzel bir şekilde anımsatmalıdır.
Aşağda verdiğim örnekte bir paketin uygulamamız tarafından nasıl kullanılabileceğini göstermekteyim.
import java.io.*;
import java.math.*;
/**
*
* @author Cem KEFELİ
* http://www.cemkefeli.com
*/
public class Main
{
public static void main(String[] args)
{
// Matematik işlemleri
double Sayi1 = 5.0;
double Sayi2 = 3.0;
double Sayi3;
Sayi3 = Math.pow(Sayi1, Sayi2);
System.out.println("Sayi1= "+Sayi1+", Sayi2= "+Sayi2+", Sayi1^Sayi2= "+Sayi3+"");
//
// Dosya İşlemleri
try
{
// Dosyayı oluştur
FileWriter fstream = new FileWriter("out.txt");
BufferedWriter out = new BufferedWriter(fstream);
out.write("Merhaba dosya");
//Output stream'i kapat
out.close();
}
catch (Exception e) //Catch exception if any
{
System.err.println("Error: " + e.getMessage());
}
}
}
Program kodlarına bakacak olursak 1. ve 2. satırda yaptığımız ilk iş ilgili paketleri import etmek oldu. Çünkü bu paketleri import etmezsek main yordamı içerisinde çağırdığımız fonksiyonları kullanamayız. İlk önce basit bir matemetik işlemi ile başladık. Sayı1 üssü sayı2 değerini hesaplamak istiyoruz. Bunun için math paketi içerisinde yer alan pow yordamını çağırıyoruz. Bir diğeri ise harddiskte bir dosya oluşturma işlemi bunun için de FileWriter, BufferedWriter sınıflarını kullandık IO paketi içerisindeki. Eğer programı koşturup proje dizininin içine bakarsanız out.txt diye bir dosya oluştuğunu ve içinde de 'Merhaba Dosya' yazdığını göreceksiniz. Tabiki bu sınıflar içerisindeki yordamları kullanırken kod bloğumuzu try-catch içerisine aldık. Çünkü dosya yazma/okuma gibi işlemler hata üretebilecek türden olabilirler kimi zaman. Paketleri kullanmak bu kadar kolay. Şimdiki örnekte ise herhangi bir paketi nasıl oluşturup nasıl kullanacağımızı gösteren bir örnek vereceğim.
/**
*
* @author Cem KEFELI
* http://www.cemkefeli.com
*/
import paket1.*;
public class Main
{
public static void main(String[] args)
{
paket1.yaziyaz Pack1 = new paket1.yaziyaz();
paket2.yaziyaz Pack2 = new paket2.yaziyaz();
Pack1.yaz();
Pack2.yaz();
//
matematik.cikarmasinifi cikarici = new matematik().new cikarmasinifi();
matematik.toplamasinifi toplayici = new matematik().new toplamasinifi();
System.out.println("50 + 20 = "+toplayici.topla(50,20));
System.out.println("50 - 20 = "+cikarici.cikar(50,20));
}
}
/**
*
* @author Cem KEFELI
* http://www.cemkefeli.com
*/
package paket1;
public class yaziyaz
{
public void yaz()
{
System.out.println("paket1 icerisinden yaziyaz sinifidaki yaz() fonksiyonu.");
}
}
/**
*
* @author Cem KEFELI
* http://www.cemkefeli.com
*/
package paket1;
public class matematik
{
public class toplamasinifi
{
public int topla(int sayi1, int sayi2)
{
return( sayi1 + sayi2 );
}
}
public class cikarmasinifi
{
public int cikar(int sayi1, int sayi2)
{
return( sayi1 - sayi2 );
}
}
}
/**
*
* @author Cem KEFELI
* http://www.cemkefeli.com
*/
package paket2;
public class yaziyaz
{
public void yaz()
{
System.out.println("paket2 icerisinden yaziyaz sinifidaki yaz() fonksiyonu.");
}
}
run:
paket1 icerisinden yaziyaz sinifidaki yaz() fonksiyonu.
paket2 icerisinden yaziyaz sinifidaki yaz() fonksiyonu.
50 + 20 = 70
50 - 20 = 30
BUILD SUCCESSFUL (total time: 1 second)
Programın akışını açıklamaya main.java dosyasından başlayalım ilk önce. 7. satıra bakacak olursanız paket1'i o satırda import ettiğimizi göreceksiniz. Peki ya paket2 nerede? Onu neden import etmedik? Bu iki sorunun cevabını biraz ilerleyen bölümlere bırakalım şimdilik. 13 ve 14. satırlarda pack1 ve pack2 iki isimli iki farklı referans oluşturuyoruz. İlki paket1 içerisinden yaziyaz isimli sınıfa, ikincisi ise paket2 içerisindeki yaziyaz isimli sinifa ait birer nesne örneğidirler. Şimdi burada karşımıza güzel birşey çıktığını görüyoruz. Biz bu sınıfların ikisine de ait nesne örneği tanımladık. Fakat sınıfların isimleri aynı. Aynı isimde sınıfı nasıl oluyor da bir başka sınıf içerisinden çağırabiliyoruz? işte burası çok güzel bir nokta. Çünkü bu sınıflar farklı paketlerdeler. Java dilinde aynı isimlerdeki sınıfları farklı paketler altında tanımlayabilir ve full path vererek istediğiniz gibi kullanabilirsiniz. Full path demekle şunu demek istiyorum. Nesne örneği içerisinde sınıfın hangi paketin hangi sınıfından türetileceğini açık açık yazmalısınız. paket1.yaziyaz notasyonu paket1 içerisindeki yaziyaz isimli siniftan bahsedildiğini belirtiyor. paket2 paketini neden import etmediğimize burada yer verebiliriz artık. Eğer full path belirtiyorsanız ilgili paketi import etmenize gerek yoktur. Çünkü en baştan başlayarak teker teker hangi paketin hangi alt sınıfının hangi dahili sınıfının kullanılacağını adım adım belirtmişsinizdir zaten. hemen alt satırlarda ise bu iki sınıfa ait yaz isimli yordamları çağırıyoruz ve her iki sınıf da ait oldukları paketlerin ismilerini yazıyorlar. 18 ve 19. satırlarda ise bir paket1 içerisindeki matemetik sınıfının dahili sınıfınlarına ait nesne örnekleri oluşturuyoruz. Dikkat edecek olursanız burada full path vermedik. Çünkü ihtiyacımız da yok. O paketi zaten import etmişiz uygulamamızdan ve çağırılabileceği tek yer import ettiğimiz o paket. Eğer bu sınıfın aynı isminde bir başka sınıfımızın olduğu paketi de import etmiş olsaydık ozaman full path vermemiz zorunlu olurdu. 20 ve 21. satırlarda ise toplama ve çıkarma işlemlerini yapıyoruz. Böylece bir paket içerisinde yer alan sınıfın dahili sıflarını da başka bir sınıf içerisinden kullanabilidğimizi görmüş oluyoruz.
Yukarıdaki örneklerde sınıfların birer dosya içerisinde tanımlanıp ayrı ayrı olarak paketlere eklendiğini anlamış olduk. Peki aynı dosya içerisinde iki farklı sınıf tanımlayıp ikisini birden pakete ekleyemez miyiz? Bu sorunun cevabı malesef hayır. Çünkü bir sınıfa dışarıdan erişilebilmek isteniyorsa erişim belirteci public olmalıdır. Public erişim belirteci ile belirlenmiş bir sınıftan ise bir java dosyası içerisinde yalnızca bir tane olabilir. Ayrıca public erişim belirtecine sahip sınıfın ismi de dosya ismi ile aynı olmalıdır. Yani siz başka bir sınıf elbette tanımalayabilirsiniz aynı dosya içerisinde fakat onu public yapıp dışarıdan kullanılmasını sağlayamazsınız. Dolayısı ile yalnıza aynı dosya içerisinde bu sınıfı kullanabilirsiniz, eğer onu da erişim belirteci ile sınırlandırmazsanız.
Paketlerin kullanılmasının güzel bir nedeni de ilgili sınıfları bir araya toparlayarak taşınabilirliğinin sağlanmasıdır. Yanidaha önce yazdığımız sınıfları tekrar başka proje içerisinden kullanmak istiyorsak, teker teker sınıfları taşımamıza gerek yoktur. Paketi taşımamız durumunda tüm sınıfları da kullanılabilir kılmış oluruz. Paketlerin kullanılmasının doğal sonucu JAR arşivleri ortaya çıkmıştır. JAR arşivleri içerisinde farklı paketler bulunduran daha geniş bir arşiv yapısıdır. JAR arşivleri konusundan farklı bir yazı ile ileride bahsetmeyi düşünüyorum.