Java'da işaretçileri nasıl kullanabilirim?


112

Java'nın işaretçileri olmadığını biliyorum, ancak Java programlarının işaretçilerle oluşturulabileceğini ve bunun java konusunda uzman birkaç kişi tarafından yapılabileceğini duydum. Bu doğru mu?


1
Yine de Sun'ın uygulamasında.
Michael Myers

1
bu işaretçi adresini kullanabilir miyim

6
Bir java nesnesi için varsayılan hashCode, işaretçi adresi DEĞİLDİR, hashCode sözleşmesini dikkatlice yeniden okuyun ve bellekteki iki farklı nesnenin aynı hashCode değerine sahip olabileceğini fark edeceksiniz.
Amir Afghani

3
64 bit ve 32 bit Java'da, 32 bitlik hashCode adres değildir. hashCode'ların benzersiz olduğu garanti edilmez. Bir nesnenin konumu, boşluklar arasında hareket ederken ve bellek sıkıştırılırken bellekte taşınabilir, ancak hashCode değişmez.
Peter Lawrey

2
C ++ işaretçileriyle yapabileceklerinizin% 90'ını java referanslarıyla yapabilirsiniz, kalan% 10'unu başka bir nesnenin içine bir referans paketleyerek elde edebilirsiniz (bunu yapmayı daha önce gerekli bulduğumdan değil)
Richard Tingle

Yanıtlar:


243

Java'daki tüm nesneler referanstır ve bunları işaretçiler gibi kullanabilirsiniz.

abstract class Animal
{...
}

class Lion extends Animal
{...
}

class Tiger extends Animal
{   
public Tiger() {...}
public void growl(){...}
}

Tiger first = null;
Tiger second = new Tiger();
Tiger third;

Bir boşluğun referansının kaldırılması:

first.growl();  // ERROR, first is null.    
third.growl(); // ERROR, third has not been initialized.

Takma Adlandırma Sorunu:

third = new Tiger();
first = third;

Hücreleri Kaybetmek:

second = third; // Possible ERROR. The old value of second is lost.    

Bunu, önce saniyenin eski değerine artık gerek olmadığından emin olarak veya başka bir işaretçiye ikinci değeri atayarak güvenli hale getirebilirsiniz.

first = second;
second = third; //OK

İkinciye başka şekillerde bir değer vermenin (NULL, yeni ...) aynı derecede potansiyel bir hata olduğunu ve işaret ettiği nesneyi kaybetmeyle sonuçlanabileceğini unutmayın.

Java sistemi, OutOfMemoryErroryeniyi aradığınızda bir istisna ( ) atar ve ayırıcı istenen hücreyi ayıramaz. Bu çok nadirdir ve genellikle kaçma özyinelemesinden kaynaklanır.

Unutmayın ki, bir dil bakış açısından, nesneleri çöp toplayıcıya bırakmak hiç de hata değildir. Bu sadece programcının bilmesi gereken bir şeydir. Aynı değişken, farklı zamanlarda farklı nesnelere işaret edebilir ve hiçbir işaretçi onlara başvurmadığında eski değerler geri alınacaktır. Ancak programın mantığı nesneye en az bir referans tutmayı gerektiriyorsa, bu bir hataya neden olacaktır.

Acemiler genellikle şu hatayı yaparlar.

Tiger tony = new Tiger();
tony = third; // Error, the new object allocated above is reclaimed. 

Muhtemelen söylemek istediğin şey:

Tiger tony = null;
tony = third; // OK.

Yanlış Döküm:

Lion leo = new Lion();
Tiger tony = (Tiger)leo; // Always illegal and caught by compiler. 

Animal whatever = new Lion(); // Legal.
Tiger tony = (Tiger)whatever; // Illegal, just as in previous example.
Lion leo = (Lion)whatever; // Legal, object whatever really is a Lion.

C'deki işaretçiler:

void main() {   
    int*    x;  // Allocate the pointers x and y
    int*    y;  // (but not the pointees)

    x = malloc(sizeof(int));    // Allocate an int pointee,
                                // and set x to point to it

    *x = 42;    // Dereference x to store 42 in its pointee

    *y = 13;    // CRASH -- y does not have a pointee yet

    y = x;      // Pointer assignment sets y to point to x's pointee

    *y = 13;    // Dereference y to store 13 in its (shared) pointee
}

Java'daki işaretçiler:

class IntObj {
    public int value;
}

public class Binky() {
    public static void main(String[] args) {
        IntObj  x;  // Allocate the pointers x and y
        IntObj  y;  // (but not the IntObj pointees)

        x = new IntObj();   // Allocate an IntObj pointee
                            // and set x to point to it

        x.value = 42;   // Dereference x to store 42 in its pointee

        y.value = 13;   // CRASH -- y does not have a pointee yet

        y = x;  // Pointer assignment sets y to point to x's pointee

        y.value = 13;   // Deference y to store 13 in its (shared) pointee
    }
} 

GÜNCELLEME: yorumlarda önerildiği gibi, C'nin işaretçi aritmetiğine sahip olduğuna dikkat edilmelidir. Ancak, Java'da buna sahip değiliz.


16
Güzel cevap, ancak bir anahtar farkı ele alamadınız: C işaretçi aritmetiğine sahiptir. (Neyse ki) bunu Java'da yapamazsınız.
R.Martinho Fernandes

10
Herhangi bir cevaba, özellikle de popüler olana oy vermekten nefret ediyorum, ancak Java'nın işaretçileri yoktur ve bir işaretçi gibi bir referansı kullanamazsınız. Yalnızca gerçek işaretçilerle yapabileceğiniz belirli şeyler vardır - örneğin, işlem veri alanınızda herhangi bir bayt alabilir veya ayarlayabilirsiniz. Bunu Java'da yapamazsınız. Buradaki pek çok insanın bir işaretçinin gerçekte ne olduğunu anlamaması gerçekten kötü, imho ve şimdi rant sabun kutumdan çıkacağım ve küçük patlamam için beni affetmeni umuyorum.
Danger

5
Sanırım maalesef demek istiyorsun. Java'nın işaretçi aritmetiğini desteklememesinin neden iyi olduğunu düşünüyorsunuz?
user3462295

2
@ user3462295 İnsanlar bunun genel halk tarafından kullanılmasının çok zor olduğunu düşündükleri için ve yalnızca ninjaların derleyiciler veya aygıt sürücülerini yazarak kodlanmasıyla anlaşılabilir.
iCodeSometime

4
@Danger İlk yanıt satırı "Java'daki tüm nesneler referans niteliğindedir ve bunları işaretçiler gibi kullanabilirsiniz." Verilebilecek en iyi cevap budur. Tek cevap "Hayır, java'nın işaretçisi yok" olsaydı. bu cevap yardımcı olmazdı. Bunun yerine bu cevap, bunun yerine ne yapabileceğinizi belirtir.
csga5000

46

Java'nın işaretçileri var. Java'da bir nesne oluşturduğunuzda, aslında nesneye bir işaretçi oluşturursunuz; bu işaretçi daha sonra farklı bir nesneye veya konumuna ayarlanabilir nullve orijinal nesne hala var olacaktır (çöp toplama beklemede).

Java'da yapamayacağınız şey işaretçi aritmetiğidir. Belirli bir bellek adresine başvuruda bulunamaz veya bir işaretçiyi artıramazsınız.

Gerçekten düşük seviyeye geçmek istiyorsanız, bunu yapmanın tek yolu Java Yerel Arayüzü'nü kullanmaktır ; ve o zaman bile, düşük seviyeli kısım C veya C ++ ile yapılmalıdır.


9
Java basitçe işaretçiler içermiyor, basit ve basit ve hayatım boyunca neden bu kadar çok cevabın yanlış bilgi verdiğini anlayamıyorum. Bu StackOverlfow insanları! Bilgisayar bilimlerinde bir işaretçinin tanımı şu şekildedir (bunun için Google'ı kullanabilirsiniz): "Bilgisayar bilimlerinde, işaretçi, değeri bilgisayar belleğinde başka bir yerde depolanan başka bir değere atıfta bulunan (veya" işaret eden ") bir programlama dili nesnesidir. adresi. " Java'da bir referans bir işaretçi DEĞİLDİR. Java'nın çöp toplama işlemini çalıştırması gerekiyor ve eğer gerçek bir işaretçiniz varsa, bu yanlış olur!
Tehlike

3
@ Tehlike Bir işaretçi, bir hafıza adresi içeren bir veri parçasıdır. Java, insanların unuttuğu iki şeydir. Bu bir dil ve platformdur. NET'in bir dil olduğu söylenemezse de, 'Java'nın işaretçileri yok' demenin yanıltıcı olabileceğini unutmamalıyız çünkü platform elbette işaretçiler içeriyor ve kullanıyor. Bu işaretçiler programcı tarafından Java dili aracılığıyla erişilebilir değildir, ancak çalışma zamanında mevcutturlar. Dildeki referanslar, çalışma zamanında gerçek işaretçilerin üzerinde bulunur.
kingfrito_5005

@ kingfrito_5005 Benimle aynı fikirde olduğunuz anlaşılıyor. "Java'nın işaretçileri yoktur" ifademle tutarlı olan "Bu işaretçiler Java Dili aracılığıyla programcı tarafından erişilebilir değildir" dediğine inanıyorum. Referanslar anlamsal olarak işaretçilerden önemli ölçüde farklıdır.
Tehlike

1
@ Tehlike bu doğru, ancak bu durumlarda platform ile dil arasında ayrım yapmanın önemli olduğunu düşünüyorum çünkü ilk başladığımda seninki gibi bir ifadenin kafamı karıştıracağını biliyorum.
kingfrito_5005

1
Evet, Java'nın kesinlikle değişkenlerin arkasında işaretçileri var. Sadece geliştiricilerin onlarla uğraşmasına gerek yok çünkü java bunun için perde arkasında sihri yapıyor. Bu, geliştiricilerin bir değişkeni belirli bir bellek adresine yönlendirmesine ve veri ofsetleri ve diğer şeylerle doğrudan ilgilenmesine doğrudan izin verilmediği anlamına gelir. Java'nın temiz bir hatıra çöp toplamasını tutmasına izin verir, ancak her değişken bir işaretçi olduğu için, daha düşük seviyeli dillerde önlenebilecek olan tüm bu otomatik olarak oluşturulmuş işaretçi adreslerine sahip olmak biraz fazladan hafıza gerektirir.
VulstaR

38

Java'da işaretçi veri türü bulunmadığından, Java'da işaretçi kullanmak imkansızdır. Birkaç uzman bile javada işaretçileri kullanamayacak.

Ayrıca şu son noktaya da bakın: Java Dil Ortamı


3
Buradaki birkaç doğru cevaptan birinde neredeyse hiç olumlu oy yok mu?
Tehlike

İşaretçi benzeri davranışı simüle eden 'işaretçi' nesnelerin var olduğuna dikkat edin. Pek çok amaç için bu aynı şekilde kullanılabilir, hafıza seviyesi adreslemeye dayanmaz. Sorunun doğası göz önüne alındığında, bundan başka kimsenin bahsetmemesi garip geliyor.
kingfrito_5005

En üstteki cevap bu olmalı. Çünkü yeni başlayanlar için "java'nın perde arkasında işaretçiler var" demesi çok kafa karıştırıcı bir duruma yol açabilir. Java başlangıç ​​seviyesinde öğretilse bile, işaretçilerin güvenli olmadığı öğretilir, bu yüzden Java'da kullanılmazlar. Bir programcıya ne görebileceği ve üzerinde çalışabileceği söylenmelidir. Referans ve işaretçiler aslında çok farklı.
Neeraj Yadav

Bağlantıdaki "2.2.3 Numaralandırma Yok" konusunu tamamen kabul ettiğimden emin değilim. Veri eski olabilir, ama Java yapar destek Çeteleler.
Sometowngeek

1
@Sometowngeek, bağlantılı belge, java'nın ilk versiyonunu açıklayan 1996 tarihli bir teknik incelemedir.
Numaralandırmalar

11

Java'da işaretçiler vardır, ancak bunları C ++ veya C'de yapabildiğiniz şekilde işleyemezsiniz. Bir nesneyi ilettiğinizde, o nesneye bir işaretçi iletirsiniz, ancak C ++ ile aynı anlamda değil. Bu nesneye referansta bulunulamaz. Değerlerini yerel erişimcilerini kullanarak ayarlarsanız, Java işaretçi aracılığıyla bellek konumunu bildiği için değişecektir. Ancak işaretçi değişmez. İşaretçiyi yeni bir konuma ayarlamaya çalıştığınızda, bunun yerine diğeriyle aynı ada sahip yeni bir yerel nesne elde edersiniz. Orijinal nesne değişmez. İşte farkı göstermek için kısa bir program.

import java.util.*;
import java.lang.*;
import java.io.*;

class Ideone {

    public static void main(String[] args) throws java.lang.Exception {
        System.out.println("Expected # = 0 1 2 2 1");
        Cat c = new Cat();
        c.setClaws(0);
        System.out.println("Initial value is " + c.getClaws());
        // prints 0 obviously
        clawsAreOne(c);
        System.out.println("Accessor changes value to " + c.getClaws());
        // prints 1 because the value 'referenced' by the 'pointer' is changed using an accessor.
        makeNewCat(c);
        System.out.println("Final value is " + c.getClaws());
        // prints 1 because the pointer is not changed to 'kitten'; that would be a reference pass.
    }

    public static void clawsAreOne(Cat kitty) {
        kitty.setClaws(1);
    }

    public static void makeNewCat(Cat kitty) {
        Cat kitten = new Cat();
        kitten.setClaws(2);
        kitty = kitten;
        System.out.println("Value in makeNewCat scope of kitten " + kitten.getClaws());
        //Prints 2. the value pointed to by 'kitten' is 2
        System.out.println("Value in makeNewcat scope of kitty " + kitty.getClaws());
        //Prints 2. The local copy is being used within the scope of this method.
    }
}

class Cat {

    private int claws;

    public void setClaws(int i) {
        claws = i;
    }

    public int getClaws() {
        return claws;
    }
}

Bu, Ideone.com'da çalıştırılabilir.


10

Java, C'nin sahip olduğu gibi işaretleyicilere sahip değildir, ancak yığın üzerinde değişkenler tarafından "referans verilen" yeni nesneler oluşturmanıza izin verir. İşaretçilerin eksikliği, Java programlarının bellek konumlarına yasa dışı bir şekilde başvurmasını engellemek ve ayrıca Çöp Toplama işleminin Java Sanal Makinesi tarafından otomatik olarak gerçekleştirilmesini sağlamaktır.


7

Güvenli olmayan sınıfını kullanarak adresleri ve işaretçileri kullanabilirsiniz. Ancak adından da anlaşılacağı gibi, bu yöntemler GÜVENLİ DEĞİLDİR ve genellikle kötü bir fikirdir. Yanlış kullanım JVM'nizin rastgele ölmesine neden olabilir (aslında aynı sorun işaretçileri C / C ++ 'da yanlış kullanır)

İşaretçilere alışkın olsanız ve onlara ihtiyaç duyduğunuzu düşünseniz de (çünkü başka bir şekilde kodlamayı bilmiyorsunuz), olmadığını göreceksiniz ve bunun için daha iyi olacaksınız.


8
İşaretçiler son derece güçlü ve kullanışlıdır. İşaretçilere (Java) sahip olmamanın kodu çok daha az verimli hale getirdiği birçok durum vardır. İnsanların işaretçileri kullanmaktan çekinmesi, dillerin onları dışlaması gerektiği anlamına gelmez. Diğerleri (ordu gibi) insanları öldürmek için kullandığı için tüfeğim olmamalı mı?
Ethan Reesor

1
Java'da dil olduğu gibi yapamayacağınız çok fazla yararlı veya güçlü yoktur. Diğer her şey için, nadiren kullanmam gerektiğini düşündüğüm Güvensiz sınıfı var.
Peter Lawrey

2
Son 40 yılda yazdığım kodun çoğu Java'da asla uygulanamazdı çünkü Java'da çekirdek, düşük seviyeli yetenekler eksik.
Tehlike

1
@ Tehlike, bu nedenle çok sayıda kitaplık Güvensiz'i kullanıyor ve onu kaldırma fikri Java 9'da çok fazla tartışmaya yol açtı. Plan, değiştirmeleri sağlamak, ancak henüz hazır değiller.
Peter Lawrey

3

Teknik olarak, tüm Java nesneleri işaretçilerdir. Yine de tüm ilkel türler değerlerdir. Bu işaretçilerin manuel kontrolünü almanın bir yolu yok. Java sadece dahili olarak referansla geçiş kullanır.


1
JNI aracılığıyla bile mi? Burada bahsedilecek Java bilgisi yok, ama bu şekilde bazı düşük seviyeli bit grubunu yapabileceğini duyduğumu düşündüm.
David Seiler

~ 4 yıllık Java deneyimim ve cevabı kendim aradığım sürece hayır diyor, Java işaretçilerine dışarıdan erişilemiyor.
Poindexter

Cevap için teşekkürler. Sordum çünkü

@unknown (google) eğer araştırma düzeyindeyse, ne yapıldığına bağlı olarak, böyle bir işlevselliğe ihtiyaçları olabilir, bu yüzden normal deyimleri ihlal ettiler ve yine de uyguladılar. Normal Java özelliklerinin bir üst kümesine (veya alt kümesine veya karışımına) sahip olmak istiyorsanız kendi JVM'nizi uygulayabilirsiniz, ancak bu tür kod diğer Java platformlarında çalışmayabilir.
San Jacinto

@san: Cevap için teşekkürler. BTW Oy veremiyorum ... 15 itibara ihtiyacım olduğunu söylüyor

2

Gerçekten değil, hayır.

Java'nın işaretçileri yoktur. Eğer gerçekten istiyorsan, yansıma gibi bir şey etrafında inşa ederek onları taklit etmeye çalışabilirsin, ama hiçbir faydası olmadan işaretçilerin tüm karmaşıklığına sahip olacaktı .

Java'nın işaretçileri yoktur çünkü onlara ihtiyacı yoktur. Bu sorudan ne tür cevaplar bekliyordunuz, yani derinlerde onları bir şey için kullanabileceğinizi mi umuyordunuz yoksa bu sadece merak mıydı?


Bunu bilmek merak ediyorum. Cevap için teşekkürler

Java'nın işaretleyicilere ihtiyacı vardır ve bu yüzden Java, "işaretçi benzeri" veya daha düşük seviyeli işlevlerin eksikliğini gidermek için, hangi kodu yazarsanız yazın, Java'da yapamayacağınız JNI'yi ekledi.
Tehlike

2

Java'daki tüm nesneler, ilkel öğeler hariç işlevlere referans kopyası ile aktarılır.

Aslında bu, nesnenin kendisinin bir kopyası yerine orijinal nesneye işaretçinin bir kopyasını gönderdiğiniz anlamına gelir.

Bunu anlamak için bir örnek istiyorsanız lütfen yorum bırakın.


Bu tamamen yanlış. swapJava'da iki nesneyi değiştirecek bir işlev yazamazsınız .
Michael Tsang

2

Java başvuru türleri, Java'da bir işaretçiye sahip olamayacağınız için C işaretçileriyle aynı değildir.


1

Diğerlerinin de söylediği gibi, kısa cevap "Hayır" dır.

Elbette, Java işaretçileriyle oynayan JNI kodu yazabilirsiniz. Neyi başarmaya çalıştığınıza bağlı olarak, belki bu sizi bir yere götürür ve belki de olmaz.

Bir dizi oluşturarak ve diziye dizinler ile çalışarak her zaman noktaları simüle edebilirsiniz. Yine, neyi başarmaya çalıştığınıza bağlı olarak, bu yararlı olabilir veya olmayabilir.


1

Godfrey Nolan tarafından yazılan Decompiling Android adlı kitaptan

Güvenlik, işaretçilerin Java'da kullanılmadığını, böylece bilgisayar korsanlarının bir uygulamadan çıkıp işletim sistemine giremeyeceğini belirtir . İşaretçi olmaması, başka bir şeyin - bu durumda, JVM'nin - bellek ayırma ve boşaltma ile ilgilenmesi gerektiği anlamına gelir. Bellek sızıntıları da geçmişte kalmalı, yoksa teori öyle devam ediyor. C ve C ++ ile yazılmış bazı uygulamalar, bir elek gibi bellek sızdırmasıyla ünlüdür çünkü programcılar, istenmeyen belleği uygun zamanda boşaltmaya dikkat etmezler - bunu okuyan hiç kimse böyle bir günahtan suçlu sayılmaz. Çöp toplama aynı zamanda programcıları daha verimli hale getirmeli ve bellek sorunlarının giderilmesi için daha az zaman harcanmalıdır.


0

değişmez değerler için de işaretçileriniz olabilir. Bunları kendin uygulamalısın. Uzmanlar için oldukça basit;). Bir int / object / long / byte dizisi kullanın ve işte işaretçileri uygulamak için temel bilgilere sahipsiniz. Artık herhangi bir int değeri o dizinin int [] işaretçisi olabilir. İmleci artırabilir, işaretçiyi küçültebilir, işaretçiyi çarpabilirsiniz. Gerçekten de işaretçi aritmetiğine sahipsiniz! 1000 int öznitelik sınıflarını uygulamanın ve tüm öznitelikler için geçerli olan genel bir yönteme sahip olmanın tek yolu budur. İnt [] yerine bayt [] dizisi de kullanabilirsiniz.

Bununla birlikte, Java'nın referans olarak değişmez değerleri geçirmenize izin vermesini diliyorum. Çizgiler boyunca bir şey

//(* telling you it is a pointer) public void myMethod(int* intValue);


-1

Tüm java nesneleri göstericidir çünkü adresi tutan bir değişken işaretçi ve nesne tutma adresi olarak adlandırılır. Yani nesne işaretçi değişkenidir.


1
Hayır, Java nesneleri işaretçi değildir. Farklı yeteneklere ve anlamsallığa sahip referanslara eşdeğerdirler.
Tehlike
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.