Jak mogę używać wskaźników w Javie?

112

Wiem, że Java nie ma wskaźników, ale słyszałem, że programy Java można tworzyć za pomocą wskaźników i że mogą to zrobić nieliczni eksperci w Javie. Czy to prawda?

Peter Lawrey
źródło
1
W każdym razie we wdrożeniu firmy Sun.
Michael Myers
1
czy mogę użyć tego adresu wskaźnika
6
Domyślnym hashCode dla obiektu java NIE jest jego adres wskaźnika. Przeczytaj uważnie kontrakt dla hashCode, a zauważysz, że dwa różne obiekty w pamięci mogą mieć tę samą wartość hashCode.
Amir Afghani
3
W 64-bitowej i 32-bitowej Javie 32-bitowy hashCode nie jest adresem. Nie ma gwarancji, że hashCodes są unikalne. Lokalizację obiektu można przenosić w pamięci podczas przenoszenia między spacjami, a pamięć jest kompaktowana, jednak hashCode się nie zmienia.
Peter Lawrey
2
90% tego, co możesz zrobić ze wskaźnikami C ++, możesz zrobić za pomocą referencji java, pozostałe 10% możesz osiągnąć, pakując odwołanie do innego obiektu (nie żebym kiedykolwiek uważał, że jest to konieczne)
Richard Tingle

Odpowiedzi:

243

Wszystkie obiekty w Javie są odniesieniami i można ich używać jak wskaźników.

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;

Dereferencing a null:

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

Problem z aliasingiem:

third = new Tiger();
first = third;

Utrata komórek:

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

Możesz to zabezpieczyć, upewniając się najpierw, że nie ma już potrzeby stosowania starej wartości sekundy lub przypisując innemu wskaźnikowi wartość sekundy.

first = second;
second = third; //OK

Zauważ, że nadanie drugiemu wartości w inny sposób (NULL, nowy ...) jest równie potencjalnym błędem i może spowodować utratę obiektu, na który wskazuje.

System Java zgłosi wyjątek ( OutOfMemoryError), gdy wywołasz new i alokator nie może przydzielić żądanej komórki. Jest to bardzo rzadkie i zwykle wynika z rekursji ucieczki.

Zauważ, że z punktu widzenia języka, porzucanie obiektów do garbage collectora wcale nie jest błędem. To jest coś, o czym programista musi być świadomy. Ta sama zmienna może wskazywać na różne obiekty w różnym czasie, a stare wartości zostaną odzyskane, gdy żaden wskaźnik nie będzie do nich odwoływał. Ale jeśli logika programu wymaga utrzymywania co najmniej jednego odwołania do obiektu, spowoduje to błąd.

Nowicjusze często popełniają następujący błąd.

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

Prawdopodobnie chciałeś powiedzieć:

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

Niewłaściwe przesyłanie:

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.

Wskaźniki w C:

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
}

Wskaźniki w Javie:

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
    }
} 

AKTUALIZACJA: zgodnie z sugestią w komentarzach należy zauważyć, że C ma arytmetykę wskaźnikową. Jednak nie mamy tego w Javie.

Sajad Bahmani
źródło
16
Dobra odpowiedź, ale nie udało Ci się rozwiązać jednej kluczowej różnicy: C ma arytmetykę wskaźników. (Na szczęście) nie możesz tego zrobić w Javie.
R. Martinho Fernandes
10
Nienawidzę głosować negatywnie na jakąkolwiek odpowiedź, zwłaszcza taką, która jest popularna, ale Java nie ma wskaźników i po prostu nie można używać odniesienia, takiego jak wskaźnik. Są pewne rzeczy, które możesz zrobić tylko z prawdziwymi wskaźnikami - na przykład możesz pobrać lub ustawić dowolny bajt w przestrzeni danych procesu. Po prostu nie możesz tego zrobić w Javie. Naprawdę źle, że tak wielu ludzi tutaj wydaje się nie rozumieć, czym naprawdę jest wskaźnik, imho, a teraz zniosę moje pudełko z mydłem i mam nadzieję, że wybaczysz mi mój mały wybuch.
Niebezpieczeństwo,
5
Myślę, że masz na myśli Niestety. Dlaczego uważasz, że to dobrze, że java nie obsługuje arytmetyki wskaźników?
user3462295
2
@ user3462295 Ponieważ ludzie wydają się myśleć, że jest zbyt trudny do wykorzystania przez ogół społeczeństwa i może być zrozumiany tylko przez kodowanie ninja piszących kompilatory lub sterowniki urządzeń.
iCodeSometime
4
@Danger Pierwsza linia odpowiedzi „Wszystkie obiekty w Javie są referencjami i możesz ich używać jak wskaźników.”. To najlepsza odpowiedź, jakiej można udzielić. Gdyby jedyną odpowiedzią było „Nie, java nie ma wskazówek”. ta odpowiedź byłaby nieprzydatna. Zamiast tego ta odpowiedź określa, co możesz zrobić.
csga5000
46

Java ma wskaźniki. Za każdym razem, gdy tworzysz obiekt w Javie, w rzeczywistości tworzysz wskaźnik do obiektu; ten wskaźnik może być wtedy ustawiony na inny obiekt lub na null, a oryginalny obiekt będzie nadal istniał (w oczekiwaniu na czyszczenie pamięci).

To, czego nie można zrobić w Javie, to arytmetyka wskaźników. Nie można wyłuskać konkretnego adresu pamięci ani zwiększyć wskaźnika.

Jeśli naprawdę chcesz uzyskać niski poziom, jedynym sposobem na to jest użycie natywnego interfejsu Java ; a nawet wtedy część niskopoziomowa musi być wykonana w C lub C ++.

Michael Myers
źródło
9
Java po prostu nie ma wskaźników, jasnych i prostych, i nie mogę do końca życia zrozumieć, dlaczego tak wiele odpowiedzi zawiera nieprawidłowe informacje. To ludzie StackOverlfow! Definicja wskaźnika w informatyce to (możesz to zrobić w Google): „W informatyce wskaźnik to obiekt języka programowania, którego wartość odnosi się (lub„ wskazuje ”) do innej wartości przechowywanej w innym miejscu w pamięci komputera przy użyciu jego adres. " Odwołanie w Javie NIE jest wskaźnikiem. Java musi uruchamiać czyszczenie pamięci i gdybyś miał prawdziwy wskaźnik, byłoby źle!
Niebezpieczeństwo,
3
@Danger Wskaźnik to fragment danych zawierający adres pamięci. Java to dwie rzeczy, o których ludzie zapominają. To język i platforma. Chociaż nie można powiedzieć, że .NET jest językiem, musimy pamiętać, że powiedzenie „Java nie ma wskaźników” może być mylące, ponieważ platforma oczywiście ma i używa wskaźników. Te wskaźniki nie są dostępne dla programisty za pośrednictwem języka Java, ale istnieją w środowisku wykonawczym. Odniesienia w języku istnieją nad rzeczywistymi wskaźnikami w środowisku wykonawczym.
kingfrito_5005
@ kingfrito_5005 Wygląda na to, że zgadzasz się ze mną. Wydaje mi się, że powiedziałeś: „Te wskaźniki nie są dostępne dla programisty za pośrednictwem języka Java”, co jest zgodne z moim stwierdzeniem, że „Java nie ma wskaźników”. Odnośniki znacznie różnią się semantycznie od wskaźników.
Niebezpieczeństwo,
1
@Niebezpieczeństwo, to prawda, ale myślę, że w takich przypadkach ważne jest, aby odróżnić platformę od języka, ponieważ wiem, że kiedy zaczynałem, takie stwierdzenie jak twoje, by mnie zmyliło.
kingfrito_5005
1
Tak, Java z pewnością ma wskaźniki za zmiennymi. Po prostu programiści nie muszą sobie z nimi radzić, ponieważ java robi to za kulisami. Oznacza to, że programiści nie mogą bezpośrednio wskazywać zmiennej na określony adres pamięci i zajmować się przesunięciami danych i innymi rzeczami. Pozwala java na utrzymywanie czystego usuwania pamięci w pamięci, ale ponieważ każda zmienna jest zasadniczo wskaźnikiem, potrzeba również trochę dodatkowej pamięci, aby mieć wszystkie te automatycznie generowane adresy wskaźników, których można było uniknąć w językach niższego poziomu.
VulstaR
38

Ponieważ Java nie ma typów danych wskaźników, nie można używać wskaźników w Javie. Nawet nieliczni eksperci nie będą w stanie używać wskaźników w java.

Zobacz także ostatni punkt w: Środowisko języka Java

Fortega
źródło
3
Jedna z niewielu poprawnych odpowiedzi nie ma prawie żadnych głosów za?
Niebezpieczeństwo,
Zauważ, że istnieją obiekty typu „wskaźnik”, które symulują zachowanie podobne do wskaźnika. Do wielu celów może to być używane w ten sam sposób, bez adresowania na poziomie pamięci. Biorąc pod uwagę naturę pytania, wydaje mi się dziwne, że nikt inny o tym nie wspomniał.
kingfrito_5005
To powinna być odpowiedź u góry. Ponieważ dla początkujących mówienie, że „za kulisami java ma wskaźniki” może prowadzić do bardzo zagmatwanego stanu. Nawet jeśli Java jest nauczana na poziomie początkowym, uczy się, że wskaźniki nie są bezpieczne, dlatego nie są używane w Javie. Należy poinformować programistę, co może zobaczyć i nad czym pracować. Odniesienie i wskaźniki są w rzeczywistości bardzo różne.
Neeraj Yadav
Nie jestem pewien, czy całkowicie zgadzam się z tematem „2.2.3 Brak wyliczeń” w linku. Dane mogą być nieaktualne, ale Java czy teksty stałe wsparcie.
Sometowngeek
1
@Sometowngeek, do którego prowadzi link, to biała księga z 1996 roku opisująca pierwszą wersję java. Wyliczenia zostały wprowadzone w wersji java 1.5 (2004)
Fortega
11

W Javie są wskaźniki, ale nie można nimi manipulować tak, jak w C ++ lub C. Przekazując obiekt, przekazujesz wskaźnik do tego obiektu, ale nie w takim samym sensie, jak w C ++. Nie można usunąć odwołania do tego obiektu. Jeśli ustawisz jego wartości za pomocą natywnych metod dostępu, zmieni się, ponieważ Java zna swoją lokalizację w pamięci za pomocą wskaźnika. Ale wskaźnik jest niezmienny. Kiedy próbujesz ustawić wskaźnik w nowej lokalizacji, zamiast tego otrzymujesz nowy obiekt lokalny o tej samej nazwie co drugi. Oryginalny obiekt pozostaje niezmieniony. Oto krótki program pokazujący różnicę.

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;
    }
}

Można to uruchomić na Ideone.com.

kingfrito_5005
źródło
10

Java nie ma wskaźników takich jak C, ale umożliwia tworzenie nowych obiektów na stercie, do których „odwołują się” zmienne. Brak wskaźników ma uniemożliwić programom Java nielegalne odwoływanie się do lokalizacji pamięci, a także umożliwia automatyczne wykonywanie Garbage Collection przez wirtualną maszynę Java.

appshare.co
źródło
7

Możesz używać adresów i wskaźników za pomocą klasy Unsafe. Jednak, jak sama nazwa wskazuje, metody te są NIEBEZPIECZNE i ogólnie zły pomysł. Nieprawidłowe użycie może spowodować, że Twoja JVM przypadkowo umiera (w rzeczywistości ten sam problem pojawia się przy nieprawidłowym użyciu wskaźników w C / C ++)

Chociaż możesz być przyzwyczajony do wskaźników i myślisz, że ich potrzebujesz (ponieważ nie wiesz, jak kodować w inny sposób), przekonasz się, że tego nie robisz i będziesz na tym lepiej.

Peter Lawrey
źródło
8
Wskaźniki są niezwykle potężne i przydatne. Istnieje wiele przypadków, w których brak wskaźników (Java) sprawia, że ​​kod jest znacznie mniej wydajny. Tylko dlatego, że ludzie są do dupy w używaniu wskaźników, nie oznacza, że ​​języki powinny je wykluczać. Czy nie powinienem mieć karabinu, ponieważ inni (np. Wojsko) używają go do zabijania ludzi?
Ethan Reesor
1
W Javie nie ma zbyt wielu przydatnych ani potężnych funkcji, których nie można zrobić w takim języku. Do wszystkiego innego jest klasa Niebezpieczna, z której bardzo rzadko korzystam.
Peter Lawrey
2
Znaczna część kodu, który napisałem w ciągu ostatnich 40 lat, nie mogłaby nigdy zostać zaimplementowana w Javie, ponieważ w Javie brakuje podstawowych funkcji niskiego poziomu.
Niebezpieczeństwo,
1
@Danger, dlatego tak duża liczba bibliotek używa Unsafe, a pomysł usunięcia go wywołał tak wiele dyskusji w Javie 9. Plan jest taki, aby zapewnić zamienniki, ale nie są one jeszcze gotowe.
Peter Lawrey
3

Technicznie rzecz biorąc, wszystkie obiekty Java są wskaźnikami. Jednak wszystkie typy pierwotne są wartościami. Nie ma możliwości ręcznego przejęcia kontroli nad tymi wskaźnikami. Java tylko wewnętrznie używa przekazywania przez odwołanie.

Poindexter
źródło
1
Nawet przez JNI? Nie ma tu wiedzy o Javie, ale pomyślałem, że słyszałem, że można w ten sposób zrobić karczowanie bitów na niskim poziomie.
David Seiler
Jeśli chodzi o moje ~ 4-letnie doświadczenie w Javie i szukanie odpowiedzi, sam mówi, że nie, wskaźniki Java nie są dostępne z zewnątrz.
Poindexter
Dzięki za odpowiedź. Zapytałem, ponieważ mój wykładowca powiedział, że na poziomie badań, gdzie java jest używana w urządzeniach przenośnych, używają wskaźnika ... dlatego zapytałem
@unknown (google) jeśli jest na poziomie badawczym, w zależności od tego, co jest robione, mogło być, że potrzebowali takiej funkcjonalności, więc naruszyli normalne idiomy i i tak je zaimplementowali. Możesz zaimplementować własną maszynę JVM, jeśli chcesz mieć nadzbiór (podzbiór lub mieszankę) normalnych funkcji Java, ale taki kod może nie działać na innych platformach Java.
San Jacinto
@san: Dzięki za odpowiedź. BTW, nie mogę głosować… mówi, że potrzebuję 15
2

Nie, naprawdę nie.

Java nie ma wskaźników. Gdybyś naprawdę chciał, możesz spróbować je naśladować, budując wokół czegoś takiego jak odbicie, ale miałoby to całą złożoność wskaźników bez żadnych korzyści .

Java nie ma wskaźników, ponieważ ich nie potrzebuje. Jakich odpowiedzi oczekiwałeś w tym pytaniu, tj. Czy w głębi duszy miałeś nadzieję, że możesz ich użyć do czegoś, czy była to tylko ciekawość?

Andrzej Doyle
źródło
Ciekawi mnie to. Dzięki za odpowiedź
Java potrzebuje wskaźników i dlatego Java dodała JNI - aby obejść brak funkcji „wskaźnikowych” lub funkcji niższego poziomu, których po prostu nie można wykonać w Javie, bez względu na to, jaki kod piszesz.
Niebezpieczeństwo
2

Wszystkie obiekty w Javie są przekazywane do funkcji przez kopię odniesienia, z wyjątkiem elementów pierwotnych.

W efekcie oznacza to, że wysyłasz kopię wskaźnika do oryginalnego obiektu, a nie kopię samego obiektu.

Zostaw komentarz, jeśli chcesz, aby przykład to zrozumiał.

coolest_head
źródło
To jest po prostu błędne. Nie możesz napisać swapfunkcji w Javie, która zamieni dwa obiekty.
Michael Tsang
2

Typy odwołań w języku Java nie są tym samym, co wskaźniki w języku C, ponieważ w Javie nie można mieć wskaźnika do wskaźnika.

zezba9000
źródło
1

Jak powiedzieli inni, krótka odpowiedź brzmi „nie”.

Jasne, możesz napisać kod JNI, który bawi się wskaźnikami Java. W zależności od tego, co próbujesz osiągnąć, może to gdzieś cię zaprowadzi, a może nie.

Zawsze możesz symulować punkty, tworząc tablicę i pracując z indeksami w tablicy. Ponownie, w zależności od tego, co próbujesz osiągnąć, może to być przydatne lub nie.

Sójka
źródło
1

z książki o nazwie Decompiling Android autorstwa Godfrey'a Nolana

Bezpieczeństwo mówi, że wskaźniki nie są używane w Javie, więc hakerzy nie mogą przedostać się z aplikacji do systemu operacyjnego. Brak wskaźników oznacza, że ​​coś innego - w tym przypadku JVM - musi zająć się alokowaniem i zwalnianiem pamięci. Wycieki pamięci również powinny przejść do przeszłości, a przynajmniej tak mówi teoria. Niektóre aplikacje napisane w C i C ++ są znane z przecieków pamięci jak sito, ponieważ programiści nie zwracają uwagi na zwalnianie niechcianej pamięci w odpowiednim czasie - nie znaczy to, że ktokolwiek to czyta, byłby winny takiego grzechu. Wyrzucanie elementów bezużytecznych powinno również zwiększyć produktywność programistów, poświęcając mniej czasu na debugowanie problemów z pamięcią.

Mustafa Güven
źródło
0

możesz mieć również wskaźniki do literałów. Musisz je wdrożyć samodzielnie. Dla ekspertów jest to dość proste;). Użyj tablicy int / object / long / byte i voila, masz podstawy do implementowania wskaźników. Teraz dowolna wartość int może być wskaźnikiem do tej tablicy int []. Możesz zwiększyć wskaźnik, możesz zmniejszyć wskaźnik, możesz pomnożyć wskaźnik. Rzeczywiście masz arytmetykę wskaźnika! Tylko w ten sposób można zaimplementować 1000 klas atrybutów int i mieć ogólną metodę, która ma zastosowanie do wszystkich atrybutów. Możesz także użyć tablicy byte [] zamiast int []

Chciałbym jednak, aby Java pozwalała na przekazywanie wartości dosłownych przez odniesienie. Coś w tym stylu

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

Mordan
źródło
-1

Wszystkie obiekty java są wskaźnikami, ponieważ zmienna, która przechowuje adres, nazywa się wskaźnikiem, a adres trzymania obiektu. Więc obiekt jest zmienną wskaźnikową.

Krishna Kant
źródło
1
Nie, obiekty Java nie są wskaźnikami. Są odpowiednikiem odwołań, które mają inny zestaw możliwości i semantyki.
Niebezpieczeństwo,