Come posso usare i puntatori in Java?


112

So che Java non ha puntatori, ma ho sentito che i programmi Java possono essere creati con puntatori e che questo può essere fatto dai pochi esperti in Java. È vero?


1
Comunque nell'implementazione di Sun.
Michael Myers

1
posso usare quell'indirizzo del puntatore

6
L'hashCode predefinito per un oggetto java NON è il suo indirizzo del puntatore, rileggi attentamente il contratto per hashCode e noterai che due oggetti distinti in memoria possono avere lo stesso valore hashCode.
Amir Afghani,

3
In Java a 64 e 32 bit, hashCode a 32 bit non è l'indirizzo. hashCodes non è garantito per essere univoco. La posizione di un oggetto può essere spostata in memoria mentre si sposta tra gli spazi e la memoria viene compressa, l'hashCode tuttavia non cambia.
Peter Lawrey

2
Il 90% di ciò che potresti fare con i puntatori C ++ puoi fare con le refrences java, il restante 10% puoi ottenere impacchettando un riferimento all'interno di un altro oggetto (non che io abbia mai trovato necessario farlo)
Richard Tingle

Risposte:


243

Tutti gli oggetti in Java sono riferimenti e puoi usarli come puntatori.

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;

Dereferenziare un null:

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

Problema di aliasing:

third = new Tiger();
first = third;

Perdere cellule:

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

Puoi renderlo sicuro assicurandoti prima che non ci sia più bisogno del vecchio valore di second o assegnando a un altro puntatore il valore di second.

first = second;
second = third; //OK

Si noti che dare a secondo un valore in altri modi (NULL, nuovo ...) è altrettanto un potenziale errore e può comportare la perdita dell'oggetto a cui punta.

Il sistema Java genererà un'eccezione ( OutOfMemoryError) quando si chiama new e l'allocatore non può allocare la cella richiesta. Questo è molto raro e di solito è il risultato di una ricorsione di fuga.

Si noti che, dal punto di vista del linguaggio, abbandonare oggetti al garbage collector non sono affatto errori. È solo qualcosa di cui il programmatore deve essere consapevole. La stessa variabile può puntare a oggetti diversi in momenti diversi e i vecchi valori verranno recuperati quando nessun puntatore vi fa riferimento. Ma se la logica del programma richiede il mantenimento di almeno un riferimento all'oggetto, causerà un errore.

I novizi spesso commettono il seguente errore.

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

Quello che probabilmente intendevi dire era:

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

Casting improprio:

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.

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

Puntatori in Java:

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

AGGIORNAMENTO: come suggerito nei commenti si deve notare che il C ha aritmetica dei puntatori. Tuttavia, non lo abbiamo in Java.


16
Bella risposta, ma non sei riuscito a risolvere una differenza fondamentale: C ha l'aritmetica dei puntatori. (Fortunatamente) non puoi farlo in Java.
R. Martinho Fernandes

10
Odio votare per difetto qualsiasi risposta, specialmente quella popolare, ma Java non ha puntatori e semplicemente non puoi usare un riferimento come un puntatore. Ci sono alcune cose che puoi fare solo con puntatori reali, ad esempio puoi ottenere o impostare qualsiasi byte nello spazio dei dati di processo. Semplicemente non puoi farlo in Java. È davvero brutto che così tante persone qui sembrano non capire cosa sia veramente un puntatore, imho, e ora mi alzerò dalla mia scatoletta di sapone e spero che tu mi perdoni per il mio piccolo sfogo.
Pericolo

5
Penso che intendi Sfortunatamente. Perché pensi che sia positivo che java non supporti l'aritmetica dei puntatori?
user3462295

2
@ user3462295 Perché le persone sembrano pensare che sia troppo difficile per essere utilizzato dalla popolazione generale e può essere compreso solo codificando ninja che scrivono compilatori o driver di dispositivo.
iCodeSometime

4
@ Pericolo Prima riga di risposta "Tutti gli oggetti in Java sono riferimenti e puoi usarli come puntatori.". Questa è la migliore risposta che può essere fornita. Se l'unica risposta fosse stata "No, java non ha puntatori". quella risposta sarebbe stata inutile. Questa risposta invece afferma cosa puoi fare invece.
csga5000

46

Java ha dei puntatori. Ogni volta che crei un oggetto in Java, stai effettivamente creando un puntatore all'oggetto; questo puntatore potrebbe quindi essere impostato su un oggetto diverso o su null, e l'oggetto originale esisterà ancora (in attesa di garbage collection).

Quello che non puoi fare in Java è l'aritmetica dei puntatori. Non è possibile dereferenziare un indirizzo di memoria specifico o incrementare un puntatore.

Se vuoi davvero ottenere un livello basso, l'unico modo per farlo è con Java Native Interface ; e anche allora, la parte di basso livello deve essere eseguita in C o C ++.


9
Java semplicemente non ha puntatori, chiari e semplici, e non riesco a capire perché così tante risposte qui riportano informazioni errate. Questo è StackOverlfow persone! La definizione di un puntatore in informatica è (puoi farlo su Google): "In informatica, un puntatore è un oggetto del linguaggio di programmazione, il cui valore si riferisce a (o" punta a ") un altro valore archiviato altrove nella memoria del computer utilizzando il suo indirizzo. " Un riferimento in Java NON è un puntatore. Java ha bisogno di eseguire la garbage collection e se avessi un vero puntatore sarebbe sbagliato!
Pericolo

3
@ Pericolo Un puntatore è un pezzo di dati che contiene un indirizzo di memoria. Java è due cose che le persone dimenticano. È una lingua e una piattaforma. Per quanto non si possa dire che .NET è un linguaggio, dobbiamo ricordare che dire "Java non ha puntatori" può essere fuorviante perché la piattaforma ovviamente ha e usa puntatori. Questi puntatori non sono accessibili al programmatore tramite il linguaggio Java, ma esistono nel runtime. I riferimenti nella lingua esistono sopra i puntatori effettivi nel runtime.
kingfrito_5005

@ kingfrito_5005 Sembra che tu sia d'accordo con me. Credo che tu abbia detto "Questi puntatori non sono accessibili al programmatore tramite il linguaggio Java", il che è coerente con la mia affermazione che "Java non ha puntatori". I riferimenti sono semanticamente significativamente diversi dai puntatori.
Pericolo

1
@ Pericolo questo è vero, ma penso che sia importante in questi casi distinguere tra la piattaforma e il linguaggio perché so che quando ho iniziato una dichiarazione come la tua mi avrebbe confuso.
kingfrito_5005

1
Sì, Java ha certamente dei puntatori dietro le variabili. È solo che gli sviluppatori non hanno a che fare con loro perché Java sta facendo la magia per questo dietro le quinte. Ciò significa che gli sviluppatori non sono autorizzati a puntare direttamente una variabile a uno specifico indirizzo di memoria e gestire gli offset di dati e cose del genere. Permette a java di mantenere una memoria pulita garbage collection, ma poiché ogni variabile è essenzialmente un puntatore, ci vuole anche un po 'di memoria extra per avere tutti quegli indirizzi di puntatore generati automaticamente, che nei linguaggi di livello più basso avrebbero potuto essere evitati.
VulstaR

38

Poiché Java non ha tipi di dati del puntatore, è impossibile utilizzare i puntatori in Java. Anche i pochi esperti non saranno in grado di utilizzare i puntatori in java.

Vedi anche l'ultimo punto in: The Java Language Environment


3
Una delle poche risposte corrette qui ha a malapena voti positivi?
Pericolo

Notare che esistono oggetti "puntatore" che simulano un comportamento simile a un puntatore. Per molti scopi questo può essere utilizzato allo stesso modo, indipendentemente dall'indirizzamento a livello di memoria. Data la natura della domanda, mi sembra strano che nessun altro lo abbia menzionato.
kingfrito_5005

Questa dovrebbe essere la risposta in alto. Perché per i principianti dire che "dietro le quinte Java ha puntatori" può portare a uno stato molto confuso. Anche quando Java viene insegnato al livello iniziale, viene insegnato che i puntatori non sono sicuri, ecco perché non vengono utilizzati in Java. A un programmatore deve essere detto ciò che può vedere e su cui lavorare. Riferimenti e puntatori sono in realtà molto diversi.
Neeraj Yadav

Non sono sicuro di essere completamente d'accordo con l'argomento "2.2.3 Nessun enum" nel collegamento. I dati potrebbero essere superata, ma Java fa enumerazioni di supporto.
Sometowngeek

1
@Sometowngeek il documento a cui è collegato è un white paper del 1996 che descrive la primissima versione di java. Gli enum sono stati introdotti nella versione java 1.5 (2004)
Fortega

11

Ci sono puntatori in Java, ma non puoi manipolarli nel modo in cui puoi farlo in C ++ o C. Quando passi un oggetto, stai passando un puntatore a quell'oggetto, ma non nello stesso senso come in C ++. Quell'oggetto non può essere dereferenziato. Se imposti i suoi valori utilizzando le sue funzioni di accesso native, cambierà perché Java conosce la sua posizione di memoria tramite il puntatore. Ma il puntatore è immutabile. Quando si tenta di impostare il puntatore in una nuova posizione, si finisce invece con un nuovo oggetto locale con lo stesso nome dell'altro. L'oggetto originale rimane invariato. Ecco un breve programma per dimostrare la differenza.

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

Questo può essere eseguito su Ideone.com.


10

Java non ha puntatori come quelli del C, ma consente di creare nuovi oggetti sull'heap a cui fanno riferimento le variabili. La mancanza di puntatori impedisce ai programmi Java di fare riferimento illegalmente a posizioni di memoria e consente inoltre di eseguire automaticamente la Garbage Collection da parte della Java Virtual Machine.


7

È possibile utilizzare indirizzi e puntatori utilizzando la classe Unsafe. Tuttavia, come suggerisce il nome, questi metodi sono NON SICURI e generalmente una cattiva idea. Un utilizzo errato può causare la morte casuale della tua JVM (in realtà lo stesso problema si ottiene utilizzando i puntatori in modo errato in C / C ++)

Sebbene tu possa essere abituato ai puntatori e pensare di averne bisogno (perché non sai come codificare in nessun altro modo), scoprirai che non lo fai e starai meglio per questo.


8
I puntatori sono estremamente potenti e utili. Ci sono molti casi in cui non avere puntatori (Java) rende il codice molto meno efficiente. Solo perché le persone fanno schifo nell'usare i puntatori non significa che le lingue dovrebbero escluderli. Non dovrei avere un fucile perché altri (come i militari) li usano per uccidere le persone?
Ethan Reesor

1
Non c'è molto di utile o potente che non puoi fare in Java come il linguaggio. Per tutto il resto c'è la classe Unsafe che trovo di dover usare molto raramente.
Peter Lawrey

2
Gran parte del codice che ho scritto negli ultimi 40 anni non avrebbe mai potuto essere implementato in Java, perché Java è privo di funzionalità di base di basso livello.
Pericolo

1
@Danger, ecco perché un gran numero di librerie utilizza Unsafe e l'idea di rimuoverlo ha acceso così tanto dibattito in Java 9. Il piano è di fornire sostituzioni, ma non sono ancora pronte.
Peter Lawrey

3

Tecnicamente, tutti gli oggetti Java sono puntatori. Tuttavia, tutti i tipi primitivi sono valori. Non è possibile assumere il controllo manuale di tali puntatori. Java utilizza solo internamente il passaggio per riferimento.


1
Nemmeno attraverso JNI? Nessuna conoscenza di Java di cui parlare qui, ma pensavo di aver sentito che potresti ottenere un po 'di estirpazione di bit di basso livello in questo modo.
David Seiler,

Per quanto riguarda i miei ~ 4 anni di esperienza Java e cercando io stesso la risposta, dico no, i puntatori Java non sono accessibili esternamente.
Poindexter

Grazie per la risposta. Ho chiesto perché, il mio docente ha detto che a livello di ricerca in cui Java è utilizzato nei dispositivi portatili, usano il puntatore ... ecco perché ho chiesto

@unknown (google) se è a livello di ricerca, a seconda di ciò che viene fatto, potrebbe essere stato necessario tale funzionalità, quindi hanno violato i normali idiomi e li hanno comunque implementati. È possibile implementare la propria JVM se si desidera avere un superset (o un sottoinsieme o una combinazione) di normali funzionalità Java, ma tale codice potrebbe non essere eseguito su altre piattaforme Java.
San Jacinto

@san: Grazie per la risposta. A proposito, non posso votare ... dice che ho bisogno di 15 punti reputazione

2

Non proprio no.

Java non ha puntatori. Se davvero lo volessi, potresti provare a emularli costruendo attorno a qualcosa come il riflesso, ma avrebbe tutta la complessità dei puntatori senza nessuno dei vantaggi .

Java non ha puntatori perché non ne ha bisogno. Che tipo di risposte speravi da questa domanda, cioè in fondo speravi di poterle usare per qualcosa o era solo curiosità?


Sono curioso di saperlo. Grazie per la risposta

Java ha bisogno di puntatori, ed è per questo che Java ha aggiunto JNI - per aggirare la mancanza di funzioni "simili a puntatori" o di livello inferiore che semplicemente non puoi fare in Java, indipendentemente dal codice che scrivi.
Pericolo

2

Tutti gli oggetti in java vengono passati alle funzioni tramite copia di riferimento tranne le primitive.

In effetti, questo significa che stai inviando una copia del puntatore all'oggetto originale piuttosto che una copia dell'oggetto stesso.

Per favore lascia un commento se vuoi un esempio per capirlo.


Questo è semplicemente sbagliato. Non è possibile scrivere una swapfunzione in Java che scambierà due oggetti.
Michael Tsang

2

I tipi di riferimento Java non sono gli stessi dei puntatori C in quanto non è possibile avere un puntatore a un puntatore in Java.


1

Come altri hanno già detto, la risposta breve è "No".

Certo, potresti scrivere codice JNI che gioca con i puntatori Java. A seconda di ciò che stai cercando di realizzare, forse questo ti porterebbe da qualche parte e forse no.

È sempre possibile simulare i punti creando un array e lavorando con gli indici nell'array. Di nuovo, a seconda di ciò che stai cercando di realizzare, potrebbe o non potrebbe essere utile.


1

dal libro Decompiling Android di Godfrey Nolan

La sicurezza impone che i puntatori non vengano utilizzati in Java, quindi gli hacker non possono uscire da un'applicazione e entrare nel sistema operativo. Nessun puntatore significa che qualcos'altro, in questo caso, la JVM, deve occuparsi dell'allocazione e della liberazione della memoria. Anche le perdite di memoria dovrebbero diventare un ricordo del passato, o almeno così dice la teoria. Alcune applicazioni scritte in C e C ++ sono note per perdere memoria come un setaccio perché i programmatori non prestano attenzione a liberare memoria indesiderata al momento opportuno - non che chiunque legga questo sarebbe colpevole di un tale peccato. La raccolta dei rifiuti dovrebbe anche rendere i programmatori più produttivi, con meno tempo speso per il debug dei problemi di memoria.


0

puoi anche avere puntatori per letterali. Devi implementarli da solo. È piuttosto semplice per gli esperti;). Usa un array di int / object / long / byte e voilà hai le basi per l'implementazione dei puntatori. Ora qualsiasi valore int può essere un puntatore a quell'array int []. Puoi aumentare il puntatore, puoi decrementare il puntatore, puoi moltiplicare il puntatore. Hai davvero aritmetica dei puntatori! Questo è l'unico modo per implementare 1000 classi di attributi int e avere un metodo generico che si applica a tutti gli attributi. Puoi anche utilizzare un array byte [] invece di un int []

Tuttavia, vorrei che Java ti consentisse di passare valori letterali per riferimento. Qualcosa sulla falsariga

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


-1

Tutti gli oggetti Java sono puntatori perché una variabile che contiene l'indirizzo è chiamata puntatore e l'oggetto contiene indirizzo.so l'oggetto è una variabile puntatore.


1
No, gli oggetti Java non sono puntatori. Sono equivalenti ai riferimenti, che hanno un diverso insieme di capacità e semantica.
Pericolo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.