¿Cómo puedo usar punteros en Java?

112

Sé que Java no tiene punteros, pero escuché que los programas Java se pueden crear con punteros y que esto lo pueden hacer unos pocos expertos en Java. ¿Es verdad?

Peter Lawrey
fuente
1
En la implementación de Sun de todos modos.
Michael Myers
1
¿Puedo usar esa dirección de puntero
6
El hashCode predeterminado para un objeto java NO es su dirección de puntero, vuelva a leer el contrato de hashCode con cuidado y notará que dos objetos distintos en la memoria pueden tener el mismo valor hashCode.
Amir Afghani
3
En Java de 64 y 32 bits, el hashCode de 32 bits no es la dirección. No se garantiza que los hashCodes sean únicos. La ubicación de un objeto se puede mover en la memoria a medida que se mueve entre espacios y la memoria se compacta, sin embargo, el hashCode no cambia.
Peter Lawrey
2
El 90% de lo que podría hacer con los punteros de C ++ lo puede hacer con las referencias de Java, el 10% restante lo puede lograr empaquetando una referencia dentro de otro objeto (no es que haya encontrado necesario hacerlo)
Richard Tingle

Respuestas:

243

Todos los objetos en Java son referencias y puede usarlos como punteros.

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;

Desreferenciar un nulo:

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

Problema de aliasing:

third = new Tiger();
first = third;

Perdiendo células:

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

Puede hacer que esto sea seguro asegurándose primero de que no hay más necesidad del valor anterior de segundo o asignando a otro puntero el valor de segundo.

first = second;
second = third; //OK

Tenga en cuenta que dar a second un valor de otras formas (NULL, new ...) es un error potencial y puede resultar en la pérdida del objeto al que apunta.

El sistema Java generará una excepción ( OutOfMemoryError) cuando llame a new y el asignador no pueda asignar la celda solicitada. Esto es muy raro y generalmente es el resultado de una recursión de fuga.

Tenga en cuenta que, desde el punto de vista del lenguaje, abandonar objetos al recolector de basura no son errores en absoluto. Es algo de lo que el programador debe estar consciente. La misma variable puede apuntar a diferentes objetos en diferentes momentos y los valores antiguos se recuperarán cuando ningún puntero haga referencia a ellos. Pero si la lógica del programa requiere mantener al menos una referencia al objeto, provocará un error.

Los novatos suelen cometer el siguiente error.

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

Lo que probablemente quisiste decir fue:

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

Casting inadecuado:

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.

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

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

ACTUALIZACIÓN: como se sugiere en los comentarios, se debe tener en cuenta que C tiene aritmética de puntero. Sin embargo, no tenemos eso en Java.

Sajad Bahmani
fuente
16
Buena respuesta, pero no pudo abordar una diferencia clave: C tiene aritmética de puntero. (Afortunadamente) no puedes hacer eso en Java.
R. Martinho Fernandes
10
Odio rechazar cualquier respuesta, especialmente una que es popular, pero Java no tiene punteros y simplemente no puedes usar una referencia como un puntero. Hay ciertas cosas que solo puede hacer con punteros reales; por ejemplo, puede obtener o establecer cualquier byte en su espacio de datos de proceso. Simplemente no puedes hacer eso en Java. Es realmente malo que tanta gente aquí parezca no entender lo que realmente es un puntero, en mi humilde opinión, y ahora saldré de mi perorata y espero que me perdonen por mi pequeño arrebato.
Danger
5
Creo que te refieres a Desafortunadamente. ¿Por qué crees que es bueno que Java no admita la aritmética de punteros?
user3462295
2
@ user3462295 Porque la gente parece pensar que es demasiado difícil de usar por la población en general, y solo puede entenderse codificando ninjas que escriban compiladores o controladores de dispositivos.
iCodeSometime
4
@Danger Primera línea de respuesta "Todos los objetos en Java son referencias y puede usarlos como punteros". Ésta es la mejor respuesta que se puede dar. Si la única respuesta hubiera sido "No, java no tiene punteros". esa respuesta habría sido inútil. En cambio, esta respuesta establece lo que puede hacer en su lugar.
csga5000
46

Java tiene punteros. Cada vez que crea un objeto en Java, en realidad está creando un puntero al objeto; este puntero podría establecerse en un objeto diferente o en null, y el objeto original seguirá existiendo (recolección de basura pendiente).

Lo que no puedes hacer en Java es aritmética de punteros. No puede eliminar la referencia a una dirección de memoria específica o incrementar un puntero.

Si realmente desea obtener un nivel bajo, la única forma de hacerlo es con la interfaz nativa de Java ; e incluso entonces, la parte de bajo nivel debe hacerse en C o C ++.

Michael Myers
fuente
9
Java simplemente no tiene punteros, simple y llanamente, y no puedo entender por qué tantas respuestas aquí indican información incorrecta. ¡Esto es gente de StackOverlfow! La definición de puntero en informática es (puede buscar en Google): "En informática, un puntero es un objeto de lenguaje de programación, cuyo valor se refiere a (o" apunta a ") otro valor almacenado en otra parte de la memoria de la computadora usando su dirección ". Una referencia en Java NO es un puntero. Java necesita ejecutar la recolección de basura y si tuviera un puntero real, ¡estaría mal!
Danger
3
@Danger Un puntero es un dato que contiene una dirección de memoria. Java son dos cosas que la gente olvida. Es un lenguaje y una plataforma. Por mucho que uno no diría que .NET es un lenguaje, debemos recordar que decir 'Java no tiene punteros' puede ser engañoso porque la plataforma, por supuesto, tiene y usa punteros. Esos punteros no son accesibles para el programador a través del lenguaje Java, pero existen en el tiempo de ejecución. Las referencias en el idioma existen además de los punteros reales en el tiempo de ejecución.
kingfrito_5005
@ kingfrito_5005 Parece que estás de acuerdo conmigo. Creo que dijiste "Esos punteros no son accesibles para el programador a través del lenguaje Java", lo cual es consistente con mi afirmación de que "Java no tiene punteros". Las referencias son semánticamente significativamente diferentes de los punteros.
Danger
1
@Danger, esto es cierto, pero creo que es importante en estos casos distinguir entre la plataforma y el idioma porque sé que cuando comencé por primera vez, una declaración como la suya me habría confundido.
kingfrito_5005
1
Sí, Java ciertamente tiene indicadores detrás de las variables. Es solo que los desarrolladores no tienen que lidiar con ellos porque Java está haciendo la magia para esto detrás de escena. Esto significa que los desarrolladores no pueden apuntar directamente una variable a una dirección de memoria específica y lidiar con compensaciones de datos y demás. Permite que Java mantenga una colección de basura de memoria limpia, pero dado que cada variable es esencialmente un puntero, también se necesita algo de memoria adicional para tener todas esas direcciones de puntero generadas automáticamente, que en los lenguajes de nivel más bajo podrían haberse evitado.
VulstaR
38

Como Java no tiene tipos de datos de puntero, es imposible utilizar punteros en Java. Incluso los pocos expertos no podrán usar punteros en java.

Consulte también el último punto en: El entorno del lenguaje Java

Fortega
fuente
3
¿Una de las pocas respuestas correctas aquí apenas tiene votos a favor?
Danger
Tenga en cuenta que existen objetos 'punteros' que simulan un comportamiento similar a un puntero. Para muchos propósitos, esto se puede usar de la misma manera, independientemente del direccionamiento del nivel de memoria. Dada la naturaleza de la pregunta, me parece extraño que nadie más haya mencionado esto.
kingfrito_5005
Esta debería ser la respuesta en la parte superior. Porque para los principiantes decir que "detrás de escena Java tiene punteros" puede llevar a un estado muy confuso. Incluso cuando se enseña Java en el nivel inicial, se enseña que los punteros no son seguros, por eso no se utilizan en Java. Un programador necesita que le digan lo que puede ver y trabajar. Las referencias y los punteros son en realidad muy diferentes.
Neeraj Yadav
No estoy seguro de estar completamente de acuerdo con el tema "2.2.3 Sin enumeraciones" en el enlace. Los datos podría ser obsoleta, pero Java hace enumeraciones de apoyo.
Sometowngeek
1
@Sometowngeek, el documento al que se vincula es un documento técnico de 1996 que describe la primera versión de java. Las enumeraciones se introducen en la versión de Java 1.5 (2004)
Fortega
11

Hay punteros en Java, pero no puede manipularlos de la forma en que puede hacerlo en C ++ o C. Cuando pasa un objeto, está pasando un puntero a ese objeto, pero no en el mismo sentido que en C ++. Ese objeto no se puede desreferenciar. Si establece sus valores utilizando sus accesos nativos, cambiará porque Java conoce su ubicación de memoria a través del puntero. Pero el puntero es inmutable. Cuando intenta establecer el puntero en una nueva ubicación, en su lugar termina con un nuevo objeto local con el mismo nombre que el otro. El objeto original no se modifica. Aquí hay un breve programa para demostrar la diferencia.

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

Esto se puede ejecutar en Ideone.com.

kingfrito_5005
fuente
10

Java no tiene punteros como C, pero te permite crear nuevos objetos en el montón a los que "hacen referencia" las variables. La falta de indicadores es para evitar que los programas Java hagan referencia a ubicaciones de memoria ilegalmente y también permite que la máquina virtual Java lleve a cabo la recolección de basura automáticamente.

appshare.co
fuente
7

Puede utilizar direcciones y punteros con la clase Inseguro. Sin embargo, como sugiere el nombre, estos métodos NO SON SEGUROS y generalmente son una mala idea. El uso incorrecto puede provocar que su JVM muera aleatoriamente (en realidad, el mismo problema es el uso incorrecto de punteros en C / C ++)

Si bien puede estar acostumbrado a los punteros y pensar que los necesita (porque no sabe cómo codificar de otra manera), descubrirá que no es así y será mejor para ello.

Peter Lawrey
fuente
8
Los punteros son extremadamente poderosos y útiles. Hay muchos casos en los que no tener punteros (Java) hace que el código sea mucho menos eficiente. El hecho de que la gente no use punteros no significa que los idiomas deban excluirlos. ¿No debería tener un rifle porque otros (como los militares) los usan para matar gente?
Ethan Reesor
1
No hay muchas cosas útiles o poderosas que no puedas hacer en Java como es el lenguaje. Para todo lo demás, existe la clase Inseguro que encuentro que necesito usar muy raramente.
Peter Lawrey
2
Gran parte del código que he escrito durante los últimos 40 años nunca podría haberse implementado en Java, porque Java carece de capacidades básicas de bajo nivel.
Danger
1
@Danger, que es la razón por la que un gran número de bibliotecas usan Unsafe y la idea de eliminarlo ha provocado tanto debate en Java 9. El plan es proporcionar reemplazos, pero aún no están listos.
Peter Lawrey
3

Técnicamente, todos los objetos de Java son punteros. Sin embargo, todos los tipos primitivos son valores. No hay forma de tomar el control manual de esos punteros. Java solo usa internamente paso por referencia.

Poindexter
fuente
1
¿Ni siquiera a través de JNI? No tengo conocimientos de Java del que hablar aquí, pero pensé que había escuchado que de esa manera se podía hacer un poco de arranque de bits de bajo nivel.
David Seiler
En cuanto a mis ~ 4 años de experiencia en Java y buscando la respuesta, yo mismo digo que no, los punteros de Java no son accesibles externamente.
Poindexter
Gracias por responder. Pregunté porque, mi profesor dijo que a nivel de investigación donde Java se usa en dispositivos de mano, usan puntero ... por eso pregunté
@unknown (google) si está en el nivel de investigación, dependiendo de lo que se esté haciendo, es posible que necesitaran dicha funcionalidad, por lo que violaron los modismos normales y los implementaron de todos modos. Puede implementar su propia JVM si desea tener un superconjunto (o subconjunto, o mezcla) de características normales de Java, pero es posible que dicho código no se ejecute en otras plataformas Java.
San Jacinto
@san: Gracias por la respuesta. Por cierto, no puedo votar ... dice que necesito 15 reputación
2

No, realmente no.

Java no tiene punteros. Si realmente lo quisiera, podría intentar emularlos construyendo alrededor de algo como la reflexión, pero tendría toda la complejidad de los punteros sin ninguno de los beneficios .

Java no tiene punteros porque no los necesita. ¿Qué tipo de respuestas esperabas de esta pregunta, es decir, en el fondo, ¿esperabas poder usarlas para algo o era solo curiosidad?

Andrzej Doyle
fuente
Tengo curiosidad por saber eso. Gracias por la respuesta
Java necesita punteros, y es por eso que Java agregó JNI - para evitar la falta de funciones de "tipo puntero" o de nivel inferior que simplemente no puede hacer en Java, sin importar el código que escriba.
Danger
2

Todos los objetos en java se pasan a funciones por copia de referencia excepto primitivas.

En efecto, esto significa que está enviando una copia del puntero al objeto original en lugar de una copia del objeto en sí.

Deje un comentario si desea un ejemplo para entender esto.

cool_head
fuente
Esto es completamente incorrecto. No puede escribir una swapfunción en Java que intercambie dos objetos.
Michael Tsang
2

Los tipos de referencia de Java no son los mismos que los punteros de C, ya que no puede tener un puntero a un puntero en Java.

zezba9000
fuente
1

Como han dicho otros, la respuesta corta es "No".

Claro, podría escribir código JNI que juegue con punteros Java. Dependiendo de lo que esté tratando de lograr, tal vez eso lo lleve a algún lugar y tal vez no.

Siempre puede simular puntos creando una matriz y trabajando con índices en la matriz. Nuevamente, dependiendo de lo que intente lograr, eso podría ser útil o no.

Arrendajo
fuente
1

del libro llamado Decompiling Android de Godfrey Nolan

La seguridad dicta que los punteros no se utilizan en Java para que los piratas informáticos no puedan salir de una aplicación y entrar en el sistema operativo. Sin punteros significa que algo más, en este caso, la JVM, tiene que encargarse de la asignación y liberación de memoria. Las pérdidas de memoria también deberían convertirse en una cosa del pasado, o eso dice la teoría. Algunas aplicaciones escritas en C y C ++ son conocidas por perder memoria como un colador porque los programadores no prestan atención a liberar memoria no deseada en el momento apropiado, no es que cualquiera que lea esto sea culpable de tal pecado. La recolección de basura también debería hacer que los programadores sean más productivos, con menos tiempo dedicado a depurar problemas de memoria.

Mustafa Güven
fuente
0

también puede tener punteros para literales. Tienes que implementarlos tú mismo. Es bastante básico para los expertos;). Use una matriz de int / object / long / byte y listo, tiene los conceptos básicos para implementar punteros. Ahora cualquier valor int puede ser un puntero a esa matriz int []. Puede incrementar el puntero, puede disminuir el puntero, puede multiplicar el puntero. ¡De hecho, tienes aritmética de punteros! Esa es la única forma de implementar 1000 clases de atributos int y tener un método genérico que se aplique a todos los atributos. También puede utilizar una matriz de bytes [] en lugar de un int []

Sin embargo, me gustaría que Java le permitiera pasar valores literales por referencia. Algo a lo largo de las líneas

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

Mordan
fuente
-1

Todos los objetos de Java son punteros porque una variable que contiene la dirección se llama puntero y el objeto retiene la dirección, por lo que el objeto es una variable de puntero.

Krishna Kant
fuente
1
No, los objetos Java no son punteros. Son equivalentes a las referencias, que tienen un conjunto diferente de capacidades y semánticas.
Danger