7 Mart 2013 Perşembe

Multithreading - Ufak bir not

Şimdi şöyle bir şey var; İnternette multithreading ile ilgili birçok örnek ve gerek detaylı gerek kısa birçok da ders var.
Bunların bize bir şeyler öğrettiği kesin. Thread nasıl başlatılır, yararları nedir, ne zaman kullanılır vs.
Ama geçenlerde multithreading olayını anlamak için yapmak istediğim bir deneme projesinde takılı kaldım.

Deneme projem iki sınıftan oluşuyordu. Her birinin içinde de birden fazla metot bulunuyordu. Bildiğiniz gibi, bir thread, ilk başta run() metodunu arar.
public class ThreadDeneme implements Runnable{
 public void run(){
//çalışacak kod 
}
}
Ama benim istediğim bundan biraz farklıydı. Sadece run içindeki kodu çalıştırmak istemiyordum.  Ama thread de sadece bunu çalıştıracaktı.
İstersem, bu run() metodunun içinden farklı metotları çağırabiliyorum tabi ki. Burada bir sorun yok. Şu şekilde mesela;

public class ThreadDeneme implements Runnable{
 public void run(){
//bu sınıftaki başka bir metot
yeniMetot(); 
}
}
Bu, run metodunun içinden yeniMetot'u çağıracak ve böylece yeni metot, farklı bir thread içinden çalışıyor olacak.  Ama eğer ben tüm metotların farklı threadler içerisinde çalışmasını istersem bunu bu şekilde yapamıyorum. Tabi ki run() metodu içerisinde istediğim kadar metot çağırabilirim. Hatta başka bir sınıf nesnesi oluşturup o sınıftaki bir yöntemi de çağırabiliyorum. Ama ya birden fazla yöntemi multithreading olarak çağırmak istersem ve bu metotların birbirinden farklı bir şekilde çalışmasını istersem?
Yani, metotBir ve metotIki'yi farklı zamanlarda da çağırmak istersem ne olacak? Bu durumda run() metodunun içini program çalışırken değiştiremediğim için, istediğim gibi bir şey mümkün değil. İkisini birden çalıştırmaya razı olacaktım mesela. Ki, bu ideal değil.
Bir thread'i nasıl başlatıyoruz? Bildiğim kadarıyla şöyle başlatabiliyorduk:
Thread t1 = new Thread(SınıfAdı);
İşte burada sadece tun() metodu içindeki yeri çalıştıracak. Ben de bu durumda ya metotBir ya da metotIki'yi seçecektim. Ama biraz araştırdıktan sonra bir thread oluşturmanın tek yolunun bu olmadığını fark ettim. Yukarıdaki satırda Thread'in istediği aslında şu;
Thread t1 = new Thread(Bir Runnable sınıf);
O halde, anonim bir Runnable sınıfı yaratabilir ve içine de istediğim bir metodu koyabilirim:
Thread t1 = new Thread(new Runnable(){ 
public void run(){
sinifNesnem.metotBir(parametrelerim); 
}
});
t1.start();
Böylece istediğim sınıfın istediğim metodunu, istediğim anda başka bir thread içinden çağırabiliyor olacağım. Projemi şöyle yaptım:
SınıfBir sBir = new SınıfBir();
Sınıfİki sİki = new SınıfIki();
Thread t1 = new Thread(sBir);
 Thread t2 = new Thread(new Runnable(){
public void run() {
System.out.println("t2 başlıyor... "+ System.currentTimeMillis());
sİki.metotBir();
}
});
static Thread t3 = new Thread (new Runnable(){
public void run(){
                        System.out.println("t2 başlıyor... "+ System.currentTimeMillis()); 
sİki.metotİki();
}
});

Sonra da geriye bunları başlatmak kalıyor. Onu da t1.start(); diyerek başlatabiliyorum.
 
 










Devamını oku...

30 Ağustos 2012 Perşembe

Döngüler

Döngüler iki türlüdür: while ve for döngüleri. Bunların da farklı şekillerde kullanımı vardır. While'ı inceleyerek başlayalım.

While

Bir ifade doğru olduğu sürece döngü çalışmaya devam eder. Kod bloğunun sonuna kadar gider ve ardından tekrar kontrol eder. Eğer doğru ise devam eder, değil ise döngü dışına çıkar. Şu şekilde kullanılır;


1
2
3
while(kosul){
     yapilacaklar
}

Aynen if deyiminde olduğu gibi, burada da koşul true veya false değeri döndüren bir ifade olmalıdır. Yukarıdaki yapıda, önce koşul kontrol edilir, ardından eğer koşul doğru ise yapılacaklar kısmı çalıştırılır. Yani, bu döngüdeki kodlarımızın hiç çalışmama ihtimali vardır.

Eğer kodlarımızın en az bir defa çalışmasını istiyorsak, bir do-while döngüsü yaratmamız gerekir. Bu şekilde, koşulun durumu ne olursa olsun, kod en az bir defa çalışır.


1
2
3
4
5
do
{
 yapılacaklar
}
while(kosul);

Gördüğünüz gibi, do'dan sonra noktalı virgül filan yok. While'dan sonra normalde yok iken, bu durumda var. Zaten yukarıdaki yapıyı şu şekilde de yazabilirdik, çünkü Java'da boşlukların önemi yok:


1
2
3
do{
 yapılacaklar
}while(kosul);

Bu döngüden de istediğimiz zaman çıkabilmek için break ifadesini kullanabiliriz. Kullanımı yaratıcılığımıza bağlı ama şu şekilde yapabiliriz:



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int a = 0;
while(true){
 System.out.println("Döngüdeyiz: "+a);
 a++;
 if(a>5){
  break;
 }
 
}
System.out.println("Döngüde değiliz.");

Tabi ki buradaki örneği break kullanmak için yaptık. Aslında aynı şeyi yapan daha kısa bir kod yazabilirdik.


1
2
3
4
5
6
int a = 0;
while(a<=5){
 System.out.println("Döngüdeyiz: "+a);
 a++;
}
System.out.println("Döngüde değiliz.");

a<=5 dedik çünkü yukarıdaki kod, a 5 olana kadar çalışıyor. Aynı şeyi bu şekilde de elde edebiliyoruz.

For

For döngüsü genelde bir dizinin elemanlarını sıralarken vs. kullanılır. Ama bu tek kullanım alanı değildir tabi ki. Kısaca kullanımı şu şekildedir;


1
2
3
for(int i = 0;i<10;i++){
   System.out.println(i);
}

for ve yanındaki parantezi açıklayacak olursak;
int i = 0 dediğimiz yerde, döngüyü sürdürmek için gerekli değişkeni tanımlıyoruz. Burada tanımladığımız değişkenin etki alanı sadece döngü içidir. Yani, döngünün dışında bir i değişkenimiz yok.
i<10 verdiğimiz yerde de, döngünün bitmesi için bir koşul belirtiyoruz. Döngüyü kontrol için i değişkeni tanımladığımız için, i ile ilgili bir şey olması gerekiyor.
i++ ise i değişkenini her döngü sonunda bir sayı arttırmayı sağlar. Eğer bu şekilde bir şey yazmazsak, döngüden çıkamayız (Tabi ki döngünün içindeki yerde i'yi arttırmadığımızı varsayıyoruz).

Tabi ki yukarıdaki sadece bir örnektir. Mesela i'yi 100 olarak tanımlayıp i>0 deyip, i-- da diyebilirdik. Aynı şekilde, şunu da yapabilirdik;


1
2
3
for(int i = 0; i<10;i+=2){
 System.out.println(i);
}

Burada da i'yi ikişer arttırıyoruz.

For döngüsünün özel bir tipi vardır. Kimi yerlerde buna for-each döngüsü de denir.  Bu döngünün tek amacı, dizilerin değerlerini sıralamaktır. Öyle ya da böyle, ne kadar ertelersek erteleyelim, dizilerle ilgilenmemiz gerekeceği için, işimizi kolaylaştıracak şu yapıyı bilmekte yarar var;


1
2
3
4
int[] dizi = {1,6,7,9,5,3,8,0,3465,2};
for(int i:dizi){
 System.out.println(i);
}

İlk satırda bir dizi tanımladık. Dizi tanımlarken [ ve ] kullanılır. Farklı şekillerde dizi tanımlanabilir. Şu anda konumuz değil :)
İkinci satırda, gördüğünüz üzere daha kısa şeyler yazarak bir üst örnekle de kurabileceğimiz döngüyü kurduk.
Burada sürekli i kullanmam o harfin zorunlu olduğu manasına gelmesin tabi ki. Yani aslında ikinci satır şudur;


1
2
for(degiskenTip degeri:diziAdi){
}

For ve while ile şöyle bir güzellik yapabiliyoruz kendimize; Konsol programlarımızın sürekli çalışmasını sağlayabiliyoruz. Yazdığımız (veya yazmaya çalıştığımız) tüm programlar, sadece bir defa istediğimiz işlemi yapıp kapanıyordu. Ama istediğimiz şey bunun biraz daha ötesiydi, biz kapatana kadar çalışsın.

Bunun için sonsuz bir döngü yaratmak gerekir. Bu da gayet kolaydır. Programınızı yazın ve hatasız çalıştığından emin olun. Daha sonra da kodlarınızı sonsuz bir döngünün (yani aşağıdaki örneklerden birinin) içine yazın.


for(;;){
 kodlarınız  
}

veya

while(true){
 kodlarınız 
}

Şu anda sürekli devam eden bir programımız var. Peki, eğer köşedeki çarpıya basmadan programın biz istediğimizde kapanmasını istersek ne olacak?

Böyle bir durumda, sınırsız döngünün içinde bir yerde (bu kodlarınızın arasında da olabilir) kullanıcıdan bir girdi alırsınız ve girdiye göre çıkış işlemini başlatırsınız.

Mesela kullanıcı K yazıp ENTER tuşuna basınca program döngüyü terk etsin ve çıkış işlemini yapsın:


String kullaniciGirdisi="";
if(kullaniciGirdisi.equals("K")){
 System.exit(0);
}

Burada kullanıcıdan nasıl girdi edeceğimiz yazmıyor. Sadece kullaniciGirdisi değişkenine, kullanıcı girdisinin atandığını varsayıyoruz.
if içindeki kodlar, değişkenimizi K harfi ile karşılaştırıyor. Karşılaştırmak için normalde == kullanırdık ama Stringler için stringDegisken.equals("string" veya stringNesne) kullanırız.

System.exit(0) ifadesi, sondaki 0 dışında kendini açıklıyor zaten. Sıfır ise, çıkış kodudur. İstediğimiz değeri oraya koyabiliriz ve sıfır dışındaki her biri, bir hatadan dolayı çıkışı simgeler. Ama şu anda, kullanıcı istediği için (hata olmadan) bir çıkış gerçekleştirmeye çalıştığımız için, 0 kullanıyoruz.

Her zamanki gibi, sorular veya yorumlar için açığım :)
Devamını oku...

28 Ağustos 2012 Salı

Koşul İfadeleri

Merhaba.

Bu yazıda, programcılığın en temel konularından birini inceleyeceğiz. İşe yarar programlar yaratabilmemiz için gerekli en temel şey, programın akışını kontrol edebilmektir. Çünkü bir kullanıcının programımızla ne yapacağını bilemeyiz asla. Örneğin, basit bir hesap makinesinde bile, bölme işleminde mesela, kullanıcının sıfır girmeyeceğini bilemeyeceğimiz için, önlem almak zorundayız. Önlem almanın bir yolu, koşul ifadesi kullanmaktır. Yani, şöyle deriz: "Eğer bölen sayı sıfır değilse, devam et ve bölme işlemini yap. Aksi halde kullanıcıyı uyar ve tekrar değer girmesini iste." Yada kullanıcıya seçim yapma hakkı da verebiliriz. Yazdığı kelimeye göre işlem yapan bir programı da seçim ifadelerini kullanarak yapabiliriz.

Koşul ifadeleri, if ve switch'tir. Her ikisinin de farklı durumlarda birbirinden üstün olduğu yerler vardır, ama birbirlerinin yerine de kullanılabilirler.

If ve If-Else

If ifadesi İngilizce'de "eğer" manasına gelir. "Eğer sayı 3'ten büyükse sayıyı 5'e böl" ifadesinin 'Javaca'sı ise şudur;


if(sayi>3)
sayi/5;

Dikkatinizi çekmiş olmalı, if ifadesinin bitiminde diğer ifadelerde yaptığımız gibi noktalı virgül koymuyoruz.
Her ne kadar yukarıdaki kullanım yanlış değilse de, karmaşayı engellemek için if'ten sonraki kısmı süslü parantezlere almak daha uygun ve sorunsuz bir yöntem olacaktır.



if(sayi>3){
sayi/5;
}

Yukarıdaki kodda, program koşul doğru ise süslü parantezler içindeki ifadeyi çalıştıracak, değilse çalıştırmadan devam edecektir. Bu haliyle biraz eksiktir, çünkü koşul yanlışsa bunu kullanıcıya bildirmek isteyebilir veya tamamen farklı bir kod çalıştırmak isteyebiliriz. Bunun için de, else ifadesini kullanmamız gerekir:

int sayi = 1;
if(sayi>3){
System.out.println("Sayı büyük.");
}else{
System.out.println("Sayı küçük");
}

if kod bloklarını iç içe kullanarak daha karmaşık koşulları devreye sokabiliriz. Burada dikkat edilmesi gereken çok önemli bir durum vardır. Hangi if'in hangi else'e ait olduğunu bilmek. Süslü parantezleri kullanmanın avantajını burada da görürüz. Özellikle de if'lerden bir veya birden fazlası bir else ifadesi içermiyorsa.
if-else-if-else şeklinde devam eden kodlar da yazmak mümkündür. Bu durumda program doğru koşulu bulduğu yere kadar ilerler ve sadece doğru olan kodu çalıştırır (Beklendiği gibi). Her ne kadar böyle bir merdiven oluşturmak yanlış değilse de, switch ifadesi varken bunu yapmak işleri karmaşıklaştırmaktan başka bir şey değildir.

Switch

Çok yollu dallanma ifadesidir. Bir ifadenin değerine göre programı dallandırır. Bunu if-else-if merdivenine alternatif olarak düşünebiliriz. Hatta bana kalırsa daha mantıklı bir yol. Basit bir örnek verelim:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
int i = 3;
switch(i){
case 0:
 System.out.println("Sayı 0");
 break;
case 1:
 System.out.println("Sayı 1");
 break;
case 2:
 System.out.println("Sayı 2");
 break;
case 3:
 System.out.println("Sayı 3");
 break;
default:
 System.out.println("Sayı 3'ten büyük.);
}

i değişkeninin değerini değiştirerek sonuçları görebilirsiniz.
Bana göre burada dikkat çekebilecek noktalar;

  1. case 0 ifadelerinden sonra noktalı virgül değil, iki nokta (:) kullanılmış.
  2. switch ifadesinden sonra parantez içinde değerlendirmek istediğimiz değişken (veya duruma göre ifade) yazılmış.
  3. Her case ifadesi break; ile bitiyor.
  4. default: isimli bir satırımız var.
  5. default ifadesinde break; yok.
Kodu çalıştırın. Burada uzun uzun anlatacağım birçok şeyi aslında kodda değişiklikler yaparak görebiliriz. Mesela, break; ifadesini yazmak zorunlu mu? Kodu case ifadesinin alt satırına değil de karşısına yazmak bir şey değiştirir mi? default ifadesinin bitişinde break; yazmak ne değiştirir? 

Bunlardan birkaçını yanıtlayayım. 
Öncelikle, break; ifadesi zorunlu değildir. Ama break ifadesini eklemezsek diğer case'ler de değerlendirilecektir. Yukarıdaki örnekte i'i 1 yapıp case 1'deki break ifadesini kaldırırsak, diğerleri doğru olmadığı için, case 1'i ve default'u çalıştıracaktır.

default ifadesinin ne işe yaradığını i değişkenini 4 yaparak görebiliriz. Bu ifade, aslında her koşulda çalışacak bir ifadedir. Bunu, break ifadesini kaldırdığımızda gördük zaten. Yani break; ifadesini kullanmazsak, default da çalıştırılacaktır. Böyle bir durum gerektirecek program yazdığınızda, default ifadesini boş bırakabilirsiniz veya hiç yazmayabilirsiniz de.

Switch ifadelerinde dikkat edilecek diğer bir konu, case'lerin aynı değeri alamayacak olmasıdır. Yani iki tane case 0 olamaz. 

Switch ifadelerinde switch'in yanındaki parantezlere verdiğimiz değişken, byte, short, int veya char tipinde olmalıdır. Ama eğer JDK 7 ve sonraki sürümleri kullanıyorsanız, String tipini de kullanabilirsiniz.
String tipini kullanırken dikkat edilecek şey ise, case'den sonraki ifadeleri çift tırnak içinde yazmak gerektiğidir. Yukarıdaki örneği String tipi ile yapalım:



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
String i = "üç";
switch(i){
case "sıfır":
 System.out.println("Sayı 0");
 break;
case "bir":
 System.out.println("Sayı 1");
 break;
case "iki":
 System.out.println("Sayı 2");
 break;
case "üç":
 System.out.println("Sayı 3");
 break;
default:
 System.out.println("Sayı üçten büyük.");
}

Şimdi operatörleri ve koşul ifadelerini öğrendiğimize göre kendimize bir problem bulup onu çözecek bir program yazabiliriz.

Problem - 1:
Bir n sayımız olsun (int tipinde). Bunun 34 ile olan mutlak farkını bulalım. Eğer n, 34'ten büyük ise, mutlak değeri iki ile çarpalım.

Problem - 2:
Gün içerisinde uyuyoruz, çalışıyoruz ve dinleniyoruz. Saat 0-8 arası uyuyalım, 8-19 arası çalışalım ve geri kalan vakitlerde de dinlenelim. Bir de çocuğumuz olsun. Eğer o ağlıyorsa, uyumamız veya dinlenmemiz mümkün olmasıın. Tabi ki burada saat integer (int) tipinde bir değişken ve 0-23 arası değer alıyor. Çocuk ise, boolean tipinde bir değişken oluyor. Çalışma, uyuma ve dinlenmeyi veya bunlardan birini yapamamayı (Uyuyamıyoruz) da ekrana yazdırıyoruz :)

Problemlerin çözümü, while ve for döngülerinin anlatılacağı yazıdan sonra :)
Devamını oku...

27 Ağustos 2012 Pazartesi

Operatörler - 2

Merhaba, bu yazıda operatörlere devam edelim ve sonrakinde eğlenceli işlere bir giriş yapalım artık.

Mantıksal Operatörler

Burada anlatacağım operatörler, ilişkisel operatörler gibi, karşılaştırma işini yapacak ve ardından bize bir boolean değeri verecek (true veya false). Biz de, programımızın akışını buna göre değiştirmeye veya devam ettirmeye karar vereceğiz. Bize boolean değeri döndürecek olmalarının anlamı nedir? Bu sorunun cevabı için mantıksal operatörlerin bir listesini verip ardından boolean değeri döndürmelerinin bize ne kattığını görmek için bir kaç satır kod yazalım.

&    Mantıksal AND (VE) - İki koşul doğru ise true, biri bile yanlış ise false döndürür.
|      Mantıksal OR (VEYA) - İki koşul yanlış ise false, diğer türlü true döndürür.
^     Mantıksal XOR - İki ifade doğru veya ikisi de yanlış ise false, en az biri yanlış ise true döndürür.
!      Mantıksal NOT (DEĞİL) - boolean değerinin tersini verir.

Bunları bu şekilde ezberlemeye vs. gerek yok tabi ki. 2-3 satırlık kodla hepsini deneyebiliriz:


  boolean deger;
  deger = false|false;
  System.out.println(deger);


Yukarıdaki kodda deger = false|false; ifadesini istediğiniz şekilde değiştirip çıkan sonucu görebilirsiniz.

Bir de koşullu VE(&&) ve koşullu VEYA(||) vardır. Bunları kullanırsak, Java sonucu soldaki değerden belirleyebilirse, sağdakini incelemez. Bu ne demek oluyor?

& ve | operatörlerini düşünelim: A&B ve A|B ifadelerimiz olduğunu varsayalım. & operatörü eğer A false ise, sonucun false olacağını bilir. Ama buna rağmen diğerini de hesaplar. Aynı şekilde, | operatörü, eğer A true ise, false'u hesaplamasına gerek olmadan değeri true döndürebilir (çünkü en az birinin true olması sonucun true olması için yeterlidir). Buna rağmen ikinci ifadeyi de inceler. Bu durum, && veya || operatörlerinde yoktur.

Belki de şu anda bunun ne gibi yararlar veya hatalar getirebileceğini bilmiyoruz, ama şunu bir kenara not edelim: Programımız kodlar düzgün olduğu halde istediğimiz şekilde çalışmıyorsa, daha derin çözümler aramadan ve çıkmaza gelip programdan bıkmadan önce, mantıksal ve ve mantıksal veya ifadelerimizi koşullu ve ve koşullu veya ile değiştirmeyi (aynı şekilde tam tersi durum da geçerli) düşünebiliriz.

&=, ^=, |= ifadeleri de atama yapıyor. Bunları denemek için ise, şu kodları yazıp sonucuna bakalım:


boolean d = false;
boolean m = true;
d &= m;   
System.out.println(d);


Yukarıda m ve d değerlerini ve alttaki işlemi değiştirerek değişik durumlar için sonuçları görebiliriz.

? Operatörü

Bütün operatörler iki operand alırken bu 3 tane alır. Daha sonra bahsedeceğim if döngüsünün kısa ve basit halidir. Kullanımı şu şekildedir;
Koşul?ifade1:ifade2;
Eğer koşul doğru ise ifade1, değil ise ifade2 çalıştırılır. Burada çok önemli bir ayrıntı vardır, ifade1 ve ifade2 void dışında bir tipe sahip bir değer döndürmelidir ve ikisi de aynı tip döndürmelidir. Örneğe bakalım;


 int a = 10, b = 12, c;
 c = a > b ? a+b:a-b;
 System.out.println(c); 


a, b'den küçük olduğu için ikinci ifade çalışır. a'nın değerini 20 yapıp tekrar denediğimizde ikinci ifade çağrılır.


Bit operatörleri hakkında şimdilik bir şey paylaşmamaya karar verdim. Çünkü uzun bir süre (yapacağımız programlara göre belki de hiç) kullanmamıza gerek yok. Zamanımızı daha temel ve önemli şeyler üzerinde değerlendirirsek, kullanmamız gerektiği zaman öğrenmemiz belki de yarım saatimizi bile almayacaktır.
Bit operatörleri, adlarından da anlaşıldığı gibi, bitler üzerinden işlem yapıyor. Yani, String değişkenimize uygulamaya çalışsak bile, String değil onun bitlerine göre işlem yapacak.

Devamını oku...

26 Ağustos 2012 Pazar

Operatörler - 1

Merhaba.
Bu yazıda biraz operatörlerden bahsedelim. Bunlar aritmetik, bit tabanlı, mantıksal ve ilişkisel operatörler olarak dörde ayrılır.
Aslında oldukça basit bir konudur ve vakit geçtikçe o kadar aşina olacağız ki, belki siz bu yazıyı okuduğunuz ben de yazdığım süreye acıyacağız.
İşin aslına bakarsak, bunları bir liste halinde verip, üzerinde konuşulmaya değer olanları konuşmak yeterli olacak gibi duruyor. O zaman aritmatik operatörlerden başlayalım;

Aritmetik Operatörler

+, -, / ve * operatörlerinin anlamları gayet açık. Bu sebeple bunlardan özellikle bahsetmeye gerek yok diye düşünüyorum.
% işareti, mod için kullanılır. Mod, bir sayının diğerine bölümünden elde edilen kalandır. Yani, 10%3 işleminin sonucunu bir değişkene atarsak, değişkenin değeri 1 olacaktır.
++ ve -- işaretleri ise, değişkenin değerine 1 ekleyen veya çıkartan operatörlerdir. Bunları, değişkenin önünde veya sonunda kullanmak arasında büyük bir fark vardır. Bu farkı anlamak için kısa bir kod yazıp çalıştıralım.


int sayi = 10;
int sayi2 = 5;
System.out.println("İlk sayı: "+ sayi +"Operatör değişkenin sonuna eklenince: "+ sayi++); System.out.println("Şimdi sadece kendisini yazalım: "+sayi);
System.out.println("İlk sayı: "+ sayi2 +"Operatör değişkenin sonuna eklenince: "+ ++sayi2);
System.out.println("Şimdi sadece kendisini yazalım: "+sayi2);

Gördüğünüz gibi, ++ operatörünü değişkenden sonra koyarsak, önce değişkeni kullanıyor, sonra değerini arttırıyor. Böylece bir sonraki kullanımda değerine 1 eklenmiş halde oluyor. Eğer önce koyarsak, bu sefer önce değerini arttırıp sonra kullanıyor. Böylece ++sayi yazdıktan sonra orijinal değeri hiç kullanmamış oluyoruz.
Başka bir grup aritmetik operatörler ise, basit dört işlemin işareti ile eşittir işaretini bir arada kullanan operatörlerdir (+=, -=, /=, *= ve %=). Bunlar ise, sol taraftaki değer ile sağ taraftaki değeri işler, ardından sonucu sol taraftaki değere atar. Dediğimi anlamak için yukarıdaki koda şunları ekleyelim;


  System.out.println("+= operatörünü kullanarak sayi ile sayi2'yi toplayıp değeri sayi'ya atamaya çalışalım:");
  sayi += sayi2;
  System.out.println(sayi);

Burada bu kodların çıktılarını vermeme gerek yok, çünkü zaten siz de deniyorsunuz şu anda :) Yalnız sizden bir ricam var, %= operatörünü kendiniz deneyin. Benim açıklamamı düşününce oradan çıkan sonucun sizi şaşırtma ihtimali var çünkü.

İlişkisel Operatörler

Bunlar bizim karşılaştırma yapacağımız operatörler. Döngülerde vs. en çok bunları kullanacağız. Liste şeklinde yazalım;
==  Eşittir
!=   Eşit değil
<    Küçük
>    Büyük
<=  Küçük eşit
>=  Büyük eşit

== operatörüne dikkat çekmek istiyorum çünkü döngülerde bir kısmımız bunun yerine = operatörünü kullanacak ve neden hata verdiğini merak edeceğiz :)
== operatörü, iki değeri birbiriyle karşılaştırırken, = operatörü atama yapar. Yukarıdaki kodumuza şunu ekleyerek durumu görelim isterseniz;


  System.out.println("= kullanarak sayi = sayi2 yazalım ve iki sayının değerlerini görelim.");
  System.out.println("Şu anda sayi: " + sayi + " ve sayi2: " + sayi2);
  sayi = sayi2;
  System.out.println("İşlemden sonra ise durum şu;");
  System.out.println("sayi: " + sayi + " sayi2: " + sayi2);

Mantıksal operatörleri biraz uzun olduğu için yeni bir yazıda ele alalım.

Şu anda bu operatörlerle yapacak pek bir şeyimiz yok. Hatta, şimdiye kadar öğrendiklerimizle ilgili yapacak bir şey yok. Ama hepsi döngüleri ve koşul ifadelerini öğrendiğimiz zaman işimize yaramaya başlayacak. İşe yarar daha kaliteli programlar için ise, sınıflar, kalıtım ve polimorfizm konularını öğrenmemiz gerekecek.
Devamını oku...

25 Ağustos 2012 Cumartesi

İlkel Veri Tipleri



Merhaba.

Önceki yazıda println, print ve printf kullanımlarını açıklayacağımı söylemiştim. Bir paragrafta açıklamaya çalışayım.

println() ve print() arasında tek fark var; println satır atlıyor, print ise imlecin aynı satırda kalmasını sağlıyor. printf() ise özel bir yöntem. Formatlı bir şekilde çıktı yazabiliyorsunuz. Bir örnek vereyim; diyelim ki kullanıcıdan ismini ve yaşını alıp ona bir selam vermek istiyorsunuz. Yaş için integer tipinde bir yas değişkeni ve isim için de String tipinde bir ad değişkeni oluşturdunuz ve isim ve yaşı kullanıcıdan aldınız. Bunu yazdırmak için aşağıdaki kodu yazabilirsiniz. Burada, %s bir string, %d ise bir integer arıyor.
System.out.printf("Selam %s, yaşın %d.",ad,yas);
İsterseniz bununla ilgili ayrıntıları konuda ilerledikçe verelim ve asıl konumuza dönelim.

Java'da birazdan bahsedeceğimiz ilkel veri tipleri dışında neredeyse her şey sınıf ve nesnelerden oluşuyor. Bu bahsedeceğimiz tiplerin bu şekilde bırakılmalarının sebebi ise performansmış. Bu arada, ilkel veri tiplerinden primitif tipler olarak bahsedildiğini de göreceksiniz. (Primitive = İlkel)

Normalde bir tablo halinde bunların maksimum ve minimum değerlerini vermem gerekirdi ama daha pratik bir yol buldum. Bu şekilde, kendimize bu tipler için bir referans programı yazabileceğiz. Birer cümle ile hepsine göz atıp ardından referans programımızın kodlarını yazalım.

int
İlkel veri tipleri arasında belki de en çok kullanacağımız tiptir. 32 bitlik bir tiptir. İşaretlidir, yani artı ve eksi değerleri vardır.

short
16 bitlik, işaretli bir tiptir. Hafızayı korumak adına kullanılabilir aslında ama günümüz bilgisayarları için bu pek de gerekli değil diye düşünüyorum.

byte
8 bitlik, işaretli bir tiptir.

long
64 bitlik işaretli bir tiptir.

float
32 bit, işaretli bir tiptir. Virgüllü sayılar için kullanabiliriz. Ama bu onun kesinlik gerektiren verileri tanımlamak için kullanılması gerektiği anlamına gelmiyor. Onun için daha sonra göreceğimiz math sınıfının BigDecimal öğesini kullanmamız gerekecek.

double
Bu da 64 bit, işaretli bir tiptir ve virgüllü sayılar için kullanılır.

Burada tanımlara kısa bir ara verip bir noktaya değinmek istiyorum. float ve double tipinde değişken tanımlarken, double için değerin sonuna d, float için de f koymamız gerekir. Yani,
float sayi = 21.4f;
şeklinde tanımlamalıyız.

boolean
Programcılıkla az buçuk ilgilendiyseniz, bunun sadece true ve false değeri döndüren ve çok yararlı bir tip olduğunu bilirsiniz.

char
Karakter için bir ilkel tip. 16 bit Unicode bir tiptir. Karakter değişkeni tanımlarken de, tırnak işareti kullanmalıyız. Yani,
char karakter = 'a';
şeklinde bir tanımlama yapmalıyız.

Yukarıdakilerin dışında bir de String vardır. Bu, Java'daki özel durumlardan bir tanesidir. İlkel veri tipi gibi kullanılabilen ama aslında bir sınıf (ve dolayısıyla nesne) olan değişkendir. char dizilerini String'e çevirebiliriz.
String biraz geniş bir konudur. Şimdilik, Stringleri bir defa tanımladığımızda bir daha değişmediklerini bilmemiz yeterlidir. Bu, tanımladığımız String'i değiştiremeyeceğimiz anlamına gelmiyor. Sadece JVM'nin String değiştirmek için fazla hafıza kullandığı anlamına geliyor.

String ve char arasındaki farklar belli, ama yine de belirtmekte fayda var;

  • String ile istediğimiz uzunlukta karakteri atayabiliyoruz. Ama char ile sadece bir karakter atanıyor. char karakter = 'ui'; yazıp kendiniz görün.
  • String tanımlarken değerinin etrafını çift tırnak ile, char'da ise tek tırnak ile tanımlarız.


Şimdiye kadar şunu da fark etmiş olmamız gerekiyor: Bir değişken tanımlarken söz dizimimiz şu şekilde;
degiskenTipi degiskenAdi = deger;
Yani, int tipinde bir değişken tanımlamak için int sayi = 45; deriz.

Gelelim bunların maksimum ve minimum değerlerini bize daha sonra zahmetsizce verecek olan programımızın kodlarına.

Tabi ki basit bir program olacak. Yeni bir proje açın ve main metot içeren bir sınıf yaratın:


 public static void main(String[] args) {
  char c = '\u00ED';
  System.out.println("Tip: " + Integer.TYPE+ " Boyut: " + Integer.SIZE + " bit " + " Maksimum değer: " + Integer.MAX_VALUE +" Minimum değer: "+ Integer.MIN_VALUE);
  System.out.println("Tip: " + Byte.TYPE+ " Boyut: "+  Byte.SIZE + " bit " + " Maksimum değer: " + Byte.MAX_VALUE +" Minimum değer: "+ Byte.MIN_VALUE);
  System.out.println("Tip: " + Short.TYPE+ " Boyut: "+ Short.SIZE + " bit " +  " Maksimum değer: " + Short.MAX_VALUE +" Minimum değer: "+ Short.MIN_VALUE);
  System.out.println("Tip: " + Long.TYPE+ " Boyut: "+ Long.SIZE + " bit " + " Maksimum değer: " + Long.MAX_VALUE +" Minimum değer: "+ Long.MIN_VALUE);
  System.out.println("Tip: " + Float.TYPE+ " Boyut: "+ Float.SIZE + " bit " + " Maksimum değer: " + Float.MAX_VALUE +" Minimum değer: "+ Float.MIN_VALUE);
  System.out.println("Tip: " + Double.TYPE+ " Boyut: "+ Double.SIZE + " bit " + " Maksimum değer: " + Double.MAX_VALUE +" Minimum değer: "+ Double.MIN_VALUE);
  System.out.println("Tip: " + Character.TYPE+ " Boyut: "+ Character.SIZE + " bit " + " Maksimum değer: " + Character.MAX_VALUE +" Minimum değer: "+ Character.MIN_VALUE);
  System.out.println("Char tipinin maksimum ve minimumu önemli değil tabi ki. Yalnız, Unicode ile değer verebileceğimizi unutmayalım. Yani; \n char c = '\\u00ED'; ile verdiğimiz değer aslında şudur: "+ c);
  System.out.println("Tip: " + Boolean.TYPE+ " " +" Yanlış: " + Boolean.FALSE +" Doğru: "+ Boolean.TRUE);
   }
 
Bu programda dikkat çekecek birkaç şeyi de yeri gelmişken anlamaya çalışalım. İşte örnekler yapmanın güzel tarafı da bu, bazen bildiklerimizle bir şeyler yapmaya çalışırken yeni şeyler de öğrenebiliyoruz. Şimdi benim aklıma gelen yerleri buraya yazayım:

  1. int ve diğer primitif tiplerin değerlerini vs. döndürürken int veya short değil de, Integer ve Short gibi, büyük harfle başlayan ifadeler kullandık. Bunlar, Java'daki ilkel tiplerin nesne gibi kullanılmak istendiği anda dönüştürüleceği nesneleri yaratan sınıflardır. Wrapping Classes yani sarmalayıcı sınıflardır.
  2. Sarmalayıcı sınıfların yanında büyük harflerle yazılan ifadeler kullandık. Bunlar, o sınıfların içindeki sabitlerdir. Java'daki isimlendirme geleneğine göre sabitler büyük harflerle yazılırlar. Tanıdık başka bir sabit için, Math.PI değerini yazdırmayı deneyin.
  3. char tipinde bir değişken tanımlarken değer olark  '\u00ED' verdik. Bu değer karşılığında çıkan karakterin Unicode değeridir. \u, Unicode girildiğini derleyiciye bildirir.
  4. '\u00ED' girmemize rağmen System.out.prinln() içinde '\\u00ED' yani iki tane \ işareti kullandık. Çünkü \u bir anlam ifade eder ve ekrana normal bir şekilde yazdıramazsınız. Bunun için kaçış karakteri olan \ karakterini başına eklersiniz. Derleyici için anlam ifade eden karakterlerin başına \ eklemezsek sorun çıkar. Mesela dosya yolu belirtirken de C:\Program Files değil, C:\\Program Files veya C:/Program Files şeklinde belirtmemiz gerekir.
  5. Kodda char ile ilgili açıklama olan yerde \n ifadesini kullandık ama programı çalıştırınca bunu göremedik. Çünkü \n ifadesi bir alt satıra geçmek için kullanılır. 
Son maddeyi okurken aklınıza print() ve println() gelmiş olabilir. Hatırlarsanız, print() çıktıyı verdikten sonra aynı satırda kalırken, println() bir alt satıra atlıyordu. Demek ki, print() ifadesinde, içindeki yazının sonuna \n eklersek, elimizde bir nevi println() olmuş olacak.

Şimdilik bu kadar. Sormak istediğiniz soru olursa yorumlarınızı bekliyorum. Sonraki yazıda Java'daki operatörleri anlamaya çalışacağız. Böylece biraz da olsa eğlenceli şeyler yazabilecek hale geleceğiz.
Devamını oku...

24 Ağustos 2012 Cuma

Eclipse ile Çalışmak


Bu yazıda yine bir klasik olarak "Merhaba dünya" uygulaması yazacağız.

İşin aslı, eğer Eclipse ile çalışıyorsanız, bir Merhaba Dünya uygulamasını yazmazsınız, Eclipse sizin için yazar.

Normalde, birazdan 1 dakika içinde yapacağımız programcığı notepad ile yaparlar. Böylece Java ile programlama yaparken aslında herhangi bir IDE'ye veya başka bir programa ihtiyacımız olmadığını anlatırlar. Ama az önce hepimizin bu gerçeği öğrenmesini sağlayan cümleyi yazdığıma göre, biz biraz da Eclipse'i tanıma amacı olarak kullanalım bu yazıyı.

Az önce Eclipse'te yapmış olduğum tüm ayarları sildim ve programı son sürümüne güncelledim. Programı da E:\Develop\Eclipse klasörünün içine taşıdım. Programı ilk olarak açtığımızda karşımıza Workspace'i nereye kurmak istediğimizi soran bir dialog çıkıyor. Buna hem kolaylık olsun, hem de format filan atarken yedek almayı unutmayayım diye, E:\Develop\workspace yazdım. Kutucuğu da işaretleyerek bir daha bu soruyu sormamasını istedim. Zaten eğer istersek, daha sonra Window>Preferences yoluyla açacağımız ayarlarda General>Startup and Shutdown altında Workspaces'a tıkladığımızda, yanda açılan yerde "Prompt for workspace on startup" seçeneğini işaretleyerek bunun sonraki açılışta tekrar açılmasını sağlayabiliriz.

Açılışta çıkan yeri geçmek için, aşağıdaki ekran alıntısında göreceğimiz gibi, sağ üstteki Workbench'e tıklamamız yeterlidir.
Eclipse İlk Açılışı

Burayı geçtikten sonra çalışacağımız alan çıkacak. File menüsü altında New'in üzerine gelip Project'i seçtiğimiz zaman bir dialog çıkacak karşımıza. Burada en üstte hemen Java Project'i göreceğiz zaten. Onu seçip Next'e bastığımızda çıkan yerde projemizi isimlendirdikten sonra, projemiz hazır olacak. Buradaki proje ismi bizim programımızın adı olmayacak. O yüzden istediğimiz her şeyi yazabiliriz. Buraya projedeki amacı açıklayan bir ifade yazalım. Mesela İlk Program yazabiliriz.

Ben, hem gelişimimizi görmek, hem de bir düzen oturtmak adına projeleri numaralandırma taraftarıyım. O yüzden bunu 01 - İlk Proje olarak adlandırıyorum. Tabi ki dediğim gibi bu size kalmış bir şey. Burada bir şeyler anlatırken her seferinde yeni bir proje açıp onu adlandırmayacağımıza göre, kafamıza göre takılabiliriz.

İsimlendirdikten sonra eğer Finish'e basarsanız, sınıfları vs. eklemek size kalacak. Eğer Next'e tıklarsanız da bunları eklemeniz için sihirbaz size yardımcı olacak. İşin açıkçası sihirbazın öyle sihirli güçleri yok. O yüzden Finish'e basıp projeyi oluşturalım. Bu daha kolay bir şekilde işleri halletmemizi sağlayacak.
Java ile ilgili öğreneceğimiz ilk şey, Applet'ler dışında tüm programların main metoduna sahip bir sınıfı olması gerektiğidir.Projenizin ismine sağ tıklayıp New>Class'ı seçerek yeni bir sınıf oluşturacak dialogu açabiliriz. Burada sınıfımıza bir isim veriyoruz ve istersek bir paketin içine dahil ediyoruz. Paketin içine dahil etmek zorunlu bir olay değil ama büyük ihtimalle, programımızı dağıtacağımız zaman bize yardımcı olacaktır. İsimlendirme geleneğine göre "sahip olduğumuz web sitesinin tersten yazılmış halini" paket olarak yazabiliriz. Yani, benim şu anda "com.wordpress.javaogreniyoruz" isimli bir paket yapmam tavsiye ediliyor. Ama zorunlu değil. Dediğim gibi, paket belirlemek bile zorunlu değil, çünkü Eclipse otomatik olarak onu varsayılan paket içine ekleyecektir.

Eclipse ile ilgili de bir şey öğreneceğiz burada. Yukarıda dediğimiz gibi, main metodu (yöntemi) olan bir sınıf oluşturmalıyız. Eclipse'de bunu yapmak kolaydır. Yeni sınıf oluştururken açılan pencerede "Which method stubs would you like to create?" altında "public static void main(String[] args)" seçeneğini işaretleyebiliriz. Alternatif olarak, bunu işaretlemeden sınıfı oluşturup, sonra iki süslü parantez arasına "main" yazıp CTRL+Space'e basarak bize seçenekler sunmasını sağlayabiliriz. Bu durumda tek bir seçenek vardır, o da main method yaratmaktır. Bu şekilde yarattığımızda Eclipse yorum satırı filan eklemiyor, o yüzden biraz daha temiz bir yöntem gibi duruyor.

Şimdi konsol ekranına çıktı verecek olan kodu yazalım:
System.out.println("Buraya istediğimizi yazabiliriz.");
Bu kodu, main metodunun içine yazmamız gerekiyor. Yani, main metodunun sonundaki süslü parantez ({) işareti ile ondan sonraki süslü parantez (}) arasına. Şu anda tam olarak şu satırlara sahibiz sanıyorum:


package deneme;
public class AnaSinif {
    public static void main(String[] args) {
       System.out.println("İstediğimizi yazdığımız yer");
    }
}

Burada package'ın işlevini söylemiştim. Eğer programı dağıtmayı düşünmüyorsak bizim için önemli değil.
Sınıf tanımlanan yer public class AnaSinif satırıdır. Gördüğünüz gibi, sınıftan sonra parantez yok. public ifadesi, onun erişim belirleyicisidir. class ise bir sınıf olduğunu gösteriyor. Bu iki anahtar kelimenin (keyword) de farklı şekilleri var. Dedikleri gibi Java sınıflardan ve nesnelerden oluşur ama bundan daha fazlası da vardır.

public static void main(String[] args) ifadesi ise yöntem belirlemiştir. Burada static ifadesi onun bir nesne olmadan da erişilebileceğini gösteriyor. Statik olmayan yöntemler ve içlerindeki değişken ve sabitlere, o sınıftan türemiş bir nesne yoluyla ulaşabiliriz. Programımızın başlaması için neden bir main metoda ihtiyaç duyduğumuz anlaşılıyor burada. Nesne üretmek için program başlamalı ama programı başlatacak sınıfa nesne olmadan erişemiyoruz. Bu mümkün değil tabi ki.

void ifadesi ise, o yöntemin döndüreceği değişkenin tipini tanımlar. void, metodun (yöntemin) bir değişken döndürmediğini anlatmak için kullanılır. void ifadesi yerine, ilkel veri tipleri (int, double vs.) ve sınıflardan türeyecek nesneleri yazabiliriz. Eğer void dışında bir şey yazarsak, yöntemimizde return birseyler şeklinde bir komutumuz olmalıdır.

main, adından da anlaşılacağı gibi, yöntemimizin, ana yöntemimizin adıdır. Java'da isimlendirme geleneğine göre yöntemleri isimlendirirken küçük harfle başlarız, kolaylık açısından da yeni kelimelere büyük harfle başlarız. Yani, ikinci yöntem adında bir yöntemin adının "ikinciYontem" olarak konulması bir gelenektir.

Parantezler içindeki ifadeler ise, argümanlardır. Bu konuya daha sonra gelelim isterseniz :)

Bir yöntem tanımlanırken, public ve static ifadelerinin alternatiflerini veya kendilerini kullanmak zorunda değiliz. Ama void ifadesini veya yöntemin döndüreceği değeri JVM'nin bilmesi için, yazmak zorundayız. Yazmazsak zaten derlenmez.

System.out.println("İstediğimizi yazdığımız yer"); ifadesine gelecek olursak... Burada System paket ismini (hmm, paketlerin kullanılma sebeplerini yavaş yavaş kavrayacağız sanırım), out sınıf ismini ifade ediyor. println() ise, yöntemi temsil ediyor.

Eclipse ile tanıştık.

Eclipse ile ilgili ufak bir ipucu; System.out.println(); yazmak için sysout yazıp CTRL+Space'e basın.

Burada anlatılanlar, teorik kısmı dışında (yani sadece kod kısmı) size kolay gelmiş olmalı. Eğer böyle düşünüyorsanız, basit ama daha sonra birçok konuda biraz daha farklı yerlere dikkat etmenizi sağlayacak bir şeye göz gezdirebilirsiniz; System.out içinde, ekrana yazı yazdıran tek metot println() değil. print() ve printf() de var. Bunlar arasındaki farklara ve nasıl kullanıldıklarına göz gezdirebilirsiniz. Eğer kullanımını veya aradaki farkı çözemezseniz dert etmeyin, sonraki yazının başında bu konudan kısaca bahsederim :)
Devamını oku...