Java da primitives variable ların call by value ile fonksiyonlara geçtiğini biliyoruz. Peki java da objeler de call by value ile fonksiyonlara geçmiyor mu? benim bildiğim java call by reference desteklemiyor. Objenin bir referansı kopyalanıyor ve bu referans aynı objeye referans ettiği için değişiklik original objeye de yansıyor. cevap verirseniz sevinirim.

soruldu: 06 Haz '12, 07:45

bonurcuk's gravatar image

bonurcuk
41225
cevap kabul oranı: 0%

değiştirildi: 06 Haz '12, 07:59

CemIkta's gravatar image

CemIkta ♦
19.9k29125190

Lütfen basliklari soru seklinde yazalim!

http://www.btsoru.com/questions/1339/btsorucom-kullanm-klavuzu

(06 Haz '12, 08:00) CemIkta ♦ CemIkta's gravatar image

Java'da metodlara geçilen parametreler kopyalanır (metodun stack frame'ine) ve metod parametrelere bu kopya üzerinden erişir, bu hem reference hem de primitive için geçerlidir. Burada karışıklığa neden olan olay primitive'lerde value'nun geçildiği (aslında kopyalandığı) kolayca anlaşılabilirken reference söz konusu olunca reference'in de kopyalandığının yeterince anlaşılmaması ya da anlatılmaması. Şimdi konuyu bir örnekle biraz daha açalım ve bunu bir kod örneği üzerinden yapalım, diyelim ki elimizde şöyle bir kod var

    class Foo 
    {
        private String _someValue = "oldValue";

        public Foo()
        {       
        }

        public String getSomeValue() 
        {
            return _someValue;
        }

        public void setSomeValue(String someValue) 
        {
            _someValue = someValue;
        }   
    }

1   class TestFoo 
2   {
3      public static void main(String[] args)
4      {
5       Foo f = new Foo();
6       System.out.println("Foo value -> "+f.getSomeValue());
7       System.out.println("In main --> "+f);
8       changeFoo(f);
9       System.out.println("Foo value -> "+f.getSomeValue());
10     }
11  
12     private static void changeFoo(Foo foo)
13     {
14      System.out.println("In method [1] --> "+foo);
15      foo.setSomeValue("newValue");
16      foo = new Foo();
17      System.out.println("In method [2]--> "+foo);
18      foo.setSomeValue("dummyValue");
19      System.out.println("Foo value -> "+foo.getSomeValue());
20     }
21   }

Şimdi run edelim ve çıktılar üzerinden değerlendirelim

Foo value -> oldValue
In main --> Foo@3fbefab0
In method [1] --> Foo@3fbefab0
In method [2]--> Foo@133c5982
Foo value -> dummyValue
Foo value -> newValue

Satır satır durumu incelemeye çalışırsak, şimdi en başta şunu söylemiştik reference da olsa primitive de olsa metoda kopya geçiliyordu.

 5. satırda bellekteki temsili durum şöyledir

 [main metodu] [f referansı] = 3fbefab0 adresini tutmakta

 bellekte(heap) 3fbefab0 adresinde
 [3fbefab0] --> Foo'nun instance'ı _someValue field'ı "oldValue" set edilmiş bulunmakta

     f reference                  Foo instance @3fbefab0
  ____________________          ___________________________
  |  3fbefab0        |          |  _someValue="oldValue"  |  
  |__________________|          |_________________________|

     6. satırda [main metodu] içerisinden print edersek 
        someValue field'ını "oldValue" görüyoruz
     7. satırda [main metodu] içerisinden print edersek 
        f nin tuttuğu adresi görürüz yani 3fbefab0
     8. satırda changeValue() metodu call edildi, yani metoda 
        f referansının bir kopyası geçilecek, bunu şöyle düşünelim,
        metodun içerisindeki işlemlerde kopya olarak geçilen referans 
        kullanılırken, metod dışında dışarıdaki referans kullanılacak, 
        ancak her iki referans da aynı nesneye işaret ediyor şu anda,
        bunu da çizelim daha net anlaşılma açısından
                                                    ________________________ 
        main metodundaki referans            ----> |Foo instance @3fbefab0  |
        method içerisindeki (kopya) referans ----> |________________________|

        yani instance'a şu anda 2 referans mevcut, biri main içerisinden, 
        bir de bunun bir kopyası olarak parametre geçilen metod içerisinden.

         14. satırda print edersek referans değerini daha önce 
             main içerisinde print ettiğimiz değerin aynısını alıyoruz,
             bu son derece normal, çünkü kopya olan referans da
             aynı nesneyi işaret etmekte.
         15. satırda nesnenin _someValue alanına "newValue" değerini
             set ettik, metod içerisinde kopyalanmış referans çalışır demiştik,
             kopyalanmış referans da nesnemize işaret ettiğine göre 
             ilgili alanı "newValue" olarak set etti,
             yani nesnemizin ilgili alanı değişti, "newValue" oldu
         16. satırda yeni bir nesne oluşturuldu, referans ismi de 
             metoda geçilen ile aynı, bu ne demek oluyor, öncelikle
             yeni bir nesne oluştuğuna göre bu heap üzerinde yer alacak
             ve yeni bir adresi olacak, bu yeni adres de kendisini
             tutan referansa set edilecek, burada "foo" isimli referans
             aynı zamanda metod içerisinde kullanılan kopya referanstı,
             artık bu referans bellekte başka bir nesneye (yine Foo instance)
             işaret ediyor, şu andaki bellek durumuna bakarsak hemen
                                                    __________________________
         main metodundaki referans ---------------> |Foo instance @3fbefab0  |
                                                    |________________________|
                                                    __________________________
                                                    | Foo instance @133c5982 |
         method içerisindeki (kopya) referans ----> |________________________|

         şimdi artık method içerisindeki referans main'den geçilen referans tan 
         ayrıldı ve farklı bir nesneyi işaret etmeye başladı, benzer şekilde 
         foo = null demiş olsaydık sadece metod içerisindeki referansımızı null 
         etmiş olurduk yani kopya olan metod içerisindeki referans herhangi bir 
         nesneye işaret etmezdi, main metodundaki referans tabiki kalmaya devam ederdi.

        17. satırda foo referansını ekrana yazdırdığımızda "Foo@133c5982" çıktısını 
            veriyor, artık farklı bir nesneye işaret ettiği ortada.
        18. satırda foo referansının işaret ettiği nesnenin _someValue alanına 
            "dummyValue" yazdık, bu @133c5982 adresindeki nesnenin "_someValue" 
            alanına "dummyValue" yazdı.
        19. satırda _someValue alanını bastırdık ve foo nun refere ettiği nesnenin
            ilgili alanında "dummyValue" yazdığını gördük.

        Metod burada bitti ve main metodundaki
        System.out.println("Foo value -> "+f.getSomeValue()); satırına döndük
        artık burada f referansı kimi gösteriyorsa ki o da @3fbefab0 adresindeki 
        Foo nesnesidir, onun _someValue alanı ekrana yazdırılır, onu da metoda 
        ilk girdiğimiz satırlarda "newValue" olarak 
        set etmiştik, dolayısıyla "newValue" değerini görürüz.

Mümkün olduğunca olayı açık anlatmaya çalıştım, umarım anlaşılır olmuştur. Bu arada metod ierisinde oluşturulan nesne metod bittiğinde kendisine herhangi bir referans kalmayacağı için garbage collector için collect edilebilir nesnelerden biri haline gelmiştir.

permanent link

cevaplandı: 06 Haz '12, 10:32

nht's gravatar image

nht
95651720
cevap kabul oranı: 33%

değiştirildi: 06 Haz '12, 15:34

Java spec'e göz attigimda Java'da tüm islemlerin call by value tarzinda oldugunu görüyorum. "Java'da nesneler sadece call by reference mantigi ile metotlara gönderilir" ifadem dogru degildi. Asagidaki örnekte myCounter isimli referans incCounter() metodunun stackine kopyalaniyor, yani referanslar icin call by value gecerli, primitiv veri tipleri icin de.

public class CallByReference
{
    public static void main(String[] args)
    {
        MyCounter myCounter= new MyCounter();
        incCounter(myCounter);
        System.out.println(myCounter.getCounter());

    }

    public static void incCounter(MyCounter counter)
    {
        counter.inc();

    }

    private static class MyCounter
    {
        private int counter;

        void inc()
        {
            this.counter++;
        }

        int getCounter()
        {
            return this.counter;
        }
    }
}

Not: @mceliksoy verdigi Test2 örneginde görüldügü gibi bir referans bir metoda parametre olarak gönderildiginde, bu referans yeni metodun stackine kopyalaniyor, yani referanslar icin call by value mantigi gecerli.

EOF (End Of Fun) Özcan Acar

permanent link

cevaplandı: 06 Haz '12, 08:10

%C3%B6zcanacar's gravatar image

özcanacar ♦♦
17.2k59183183
cevap kabul oranı: 52%

değiştirildi: 06 Haz '12, 09:24

Java spec: http://docs.oracle.com/javase/specs/jls/se5.0/html/classes.html#8.4.1 Asagidaki cümle Java'nin call by value oldugunu gösteriyor:

When the method or constructor is invoked (�15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared Type, before execution of the body of the method or constructor. The Identifier that appears in the DeclaratorId may be used as a simple name in the body of the method or constructor to refer to the formal parameter.

(06 Haz '12, 09:41) özcanacar ♦♦ %C3%B6zcanacar's gravatar image

Özcan hocanin illaki bir bildigi vardir ancak su konuyu danismak iserim : call by value de degiskenin degeri funktiona gönderilir ve yapilan degisikliklerden cagriyi yapan kisim haberdar olmaz. Örnek :

public class Test {
public static void main(String[] args){
    int i = 5;
    System.out.println("i - önce = " + i);
    callValue(i);
    System.out.println("i - sonra = " + i);

}

public static void callValue(int i){
    i = i + 5;
}

}

Burda ekrana verilen cikti acik bir sekilde

  • i - önce = 5
  • i - sonra = 5

Ayni seyi nesnelerle yaparsak

public class Test2 {
public static void main(String[] args){
    Kitap kitap = new Kitap("Mehmet");
    System.out.println("reference - önce = " + kitap);
    System.out.println("yazar - önce = " + kitap.getYazar());
    callValue(kitap);
    System.out.println("reference - sonra = " + kitap);
    System.out.println("yazar - sonra = " + kitap.getYazar());        
}

public static void callValue(Kitap kitap){
    kitap.setYazar("Ahmet");
    kitap = null;

// kitap.getYazar() NullPointerException } }

class Kitap { String yazar;

public Kitap(String yazar){
    this.yazar = yazar;
}

public String getYazar(){
    return this.yazar;
}

public void setYazar(String yazar){
    this.yazar = yazar;
}

}

Burda ekrana verilan cikti ise :

  • reference - önce = Kitap@7c6768
  • yazar - önce = Mehmet
  • reference - sonra = Kitap@7c6768
  • yazar - sonra = Ahmet

Burda callValue funktion nunda reference kitap = null; olsa bile onu cagiran main methodundaki reference bundan etkilenmedi. Ellbette iki reference de ayni Nesneyi gösteriyor ve o bakimdan yazarin ismi degisti. Bu bakimdan da reference acisindan düsünürsek davranisi call by value ye benziyor.

bu konuda nedersiniz ?

permanent link

cevaplandı: 06 Haz '12, 08:32

mceliksoy's gravatar image

mceliksoy ♦
6.5k84988
cevap kabul oranı: 25%

alt text

siz orda kitap=null assignmentı yaparken aslında "metoda pass edilen kitap referansı" nın değerini null yapıyorsunuz. (önceden kitap objesinin bulunduğu adress değeri vardı) haliyle de kitap objesinin değeri bundan etkilenmiyor.

permanent link

cevaplandı: 06 Haz '12, 16:13

jit's gravatar image

jit
86126
cevap kabul oranı: 16%

1

Bende onu diyorum ya, eger reference acisindan düsünürsek, bu call by value mantiginin davranis sekli.

Bu konu biraz karmasik ama Özcan hoca ninda belirttigi gibi bir cok kaynak da java nin call by value oldugunu söylüyor.

(06 Haz '12, 17:30) mceliksoy ♦ mceliksoy's gravatar image

Galiba sorun Java'da pointer yerine reference kelimesinin kullanilmasi. Aslina bakacak olursak Java'da C++'da oldugu gibi pointer kullaniliyor. Reference kelimesi kafa karistirici.

(07 Haz '12, 02:57) özcanacar ♦♦ %C3%B6zcanacar's gravatar image

Yorumlar ve açıklayıcı bilgiler için teşekkür ederim.

permanent link

cevaplandı: 07 Haz '12, 03:12

bonurcuk's gravatar image

bonurcuk
41225
cevap kabul oranı: 0%

Cevabınız
toggle preview

Bu soruyu takip et

E-Posta üzerinden:

Üyelik girişi yaptıktan sonra abonelik işlemlerini yapabilirsiniz

RSS üzerinden:

Cevaplar

Cevaplar ve Yorumlar

Yazı Formatlama

  • *italic* ya da _italic_
  • **bold** ya da __bold__
  • link:[text](http://url.com/ "başlık")
  • resim?![alt text](/path/img.jpg "başlık")
  • liste: 1. Foo 2. Bar
  • temel HTML etiketleri de kullanılabilir

Bu sorunun etiketleri:

×1,077
×1

Soruldu: 06 Haz '12, 07:45

Görüntüleme: 1,716 kez

Son güncelleme: 07 Haz '12, 03:12

powered by BitNami OSQA