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?


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.


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 ++.


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


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.


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.


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.


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.


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ść?


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ł.


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.


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.


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ą.


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


-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ą.


1
Nie, obiekty Java nie są wskaźnikami. Są odpowiednikiem odwołań, które mają inny zestaw możliwości i semantyki.
Niebezpieczeństwo,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.