Como posso usar ponteiros em Java?

112

Eu sei que Java não tem ponteiros, mas ouvi dizer que programas Java podem ser criados com ponteiros e que isso pode ser feito por poucos que são especialistas em java. É verdade?

Peter Lawrey
fonte
1
Na implementação da Sun, pelo menos.
Michael Myers
1
Posso usar esse endereço de ponteiro
6
O hashCode padrão para um objeto java NÃO é o endereço do ponteiro, releia o contrato para hashCode cuidadosamente e você notará que dois objetos distintos na memória podem ter o mesmo valor hashCode.
Amir Afghani
3
Em Java de 64 e 32 bits, o hashCode de 32 bits não é o endereço. hashCodes não são garantidos como exclusivos. A localização de um objeto pode ser movida na memória conforme ele se move entre os espaços e a memória é compactada, mas o hashCode não muda.
Peter Lawrey
2
90% do que você poderia fazer com ponteiros C ++, você pode fazer com referências java, os 10% restantes você pode obter empacotando uma referência dentro de outro objeto (não que eu já tenha achado necessário fazer isso)
Richard Tingle

Respostas:

243

Todos os objetos em Java são referências e você pode usá-los como ponteiros.

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;

Desreferenciando um nulo:

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

Problema de aliasing:

third = new Tiger();
first = third;

Células perdidas:

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

Você pode tornar isso seguro garantindo primeiro que não há mais necessidade do valor antigo de segundo ou atribuindo a outro ponteiro o valor de segundo.

first = second;
second = third; //OK

Observe que atribuir a segundo um valor de outras maneiras (NULL, novo ...) é um erro potencial e pode resultar na perda do objeto para o qual ele aponta.

O sistema Java lançará uma exceção ( OutOfMemoryError) quando você chamar new e o alocador não puder alocar a célula solicitada. Isso é muito raro e geralmente resulta de uma recursão descontrolada.

Observe que, do ponto de vista da linguagem, abandonar objetos para o coletor de lixo não é um erro. É apenas algo que o programador precisa estar ciente. A mesma variável pode apontar para objetos diferentes em momentos diferentes e valores antigos serão recuperados quando nenhum ponteiro fizer referência a eles. Mas se a lógica do programa requer a manutenção de pelo menos uma referência ao objeto, isso causará um erro.

Os novatos freqüentemente cometem o seguinte erro.

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

O que você provavelmente quis dizer foi:

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

Fundição imprópria:

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.

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

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

ATUALIZAÇÃO: conforme sugerido nos comentários, deve-se observar que C possui aritmética de ponteiros. No entanto, não temos isso em Java.

Sajad Bahmani
fonte
16
Boa resposta, mas você não conseguiu resolver uma das principais diferenças: C tem aritmética de ponteiro. (Felizmente) você não pode fazer isso em Java.
R. Martinho Fernandes
10
Eu odeio votar contra qualquer resposta, especialmente uma que seja popular, mas Java não tem ponteiros e você simplesmente não pode usar uma referência como um ponteiro. Existem certas coisas que você só pode fazer com ponteiros reais - por exemplo, você pode obter ou definir qualquer byte em seu espaço de dados de processo. Você simplesmente não pode fazer isso em Java. É realmente ruim que tantas pessoas aqui pareçam não entender o que um ponteiro realmente é, imho, e agora vou tirar minha caixa de sabão retórica e espero que você me perdoe por minha pequena explosão.
Perigo
5
Eu acho que você quer dizer Infelizmente. Por que você acha que é bom que o java não suporte aritmética de ponteiros?
user3462295 01 de
2
@ user3462295 Porque as pessoas parecem pensar que é muito difícil de ser usado pela população em geral e só pode ser compreendido codificando ninjas escrevendo compiladores ou drivers de dispositivo.
iCodeSometime
4
@Danger Primeira linha de resposta "Todos os objetos em Java são referências e você pode usá-los como ponteiros.". Esta é a melhor resposta que pode ser fornecida. Se a única resposta fosse "Não, java não tem ponteiros." essa resposta teria sido inútil. Em vez disso, esta resposta afirma o que você pode fazer.
csga5000
46

Java tem ponteiros. Sempre que você cria um objeto em Java, na verdade está criando um ponteiro para o objeto; esse ponteiro pode então ser definido para um objeto diferente ou para null, e o objeto original ainda existirá (coleta de lixo pendente).

O que você não pode fazer em Java é aritmética de ponteiro. Você não pode desreferenciar um endereço de memória específico ou incrementar um ponteiro.

Se você realmente deseja obter um nível inferior, a única maneira de fazer isso é com a Java Native Interface ; e mesmo assim, a parte de baixo nível tem que ser feita em C ou C ++.

Michael Myers
fonte
9
Java simplesmente não tem ponteiros, puros e simples, e não consigo entender por que tantas respostas aqui afirmam informações incorretas. Este é o pessoal StackOverlfow! A definição de ponteiro em ciência da computação é (você pode usar o Google para isso): "Em ciência da computação, um ponteiro é um objeto de linguagem de programação, cujo valor se refere a (ou" aponta para ") outro valor armazenado em outro lugar na memória do computador usando seu endereço. " Uma referência em Java NÃO é um ponteiro. Java precisa executar a coleta de lixo e se você tivesse um ponteiro real, ele estaria errado!
Perigo
3
@Danger Um ponteiro é um dado que contém um endereço de memória. Java é duas coisas que as pessoas esquecem. É uma linguagem e uma plataforma. Por mais que não se diga que .NET é uma linguagem, precisamos lembrar que dizer 'Java não tem ponteiros' pode ser enganoso porque a plataforma, é claro, tem e usa ponteiros. Esses ponteiros não são acessíveis ao programador por meio da linguagem Java, mas existem no tempo de execução. Referências na linguagem existem em cima de ponteiros reais no tempo de execução.
kingfrito_5005
@ kingfrito_5005 Parece que você concorda comigo. Eu acredito que você disse "Esses ponteiros não são acessíveis ao programador através da linguagem Java", o que é consistente com minha afirmação de que "Java não tem ponteiros". As referências são semanticamente significativamente diferentes dos ponteiros.
Perigo
1
@ Perigo, isso é verdade, mas acho importante, nesses casos, distinguir entre a plataforma e a linguagem, porque sei que, quando estava começando, uma declaração como a sua teria me confundido.
kingfrito_5005
1
Sim, Java certamente tem ponteiros por trás das variáveis. Só que os desenvolvedores não precisam lidar com eles porque java está fazendo a mágica para isso nos bastidores. Isso significa que os desenvolvedores não têm permissão direta para apontar uma variável para um endereço de memória específico e lidar com deslocamentos de dados e outras coisas. Ele permite que o java mantenha uma coleção de lixo de memória limpa, mas como cada variável é essencialmente um ponteiro, também é necessária alguma memória extra para ter todos os endereços de ponteiro gerados automaticamente, o que em linguagens de nível mais baixo poderia ter sido evitado.
VulstaR
38

Como Java não tem tipos de dados de ponteiro, é impossível usar ponteiros em Java. Mesmo os poucos especialistas não serão capazes de usar ponteiros em java.

Veja também o último ponto em: The Java Language Environment

Fortega
fonte
3
Uma das poucas respostas corretas aqui dificilmente tem votos positivos?
Perigo
Observe que existem objetos 'ponteiro' que simulam o comportamento semelhante a um ponteiro. Para muitos propósitos, isso pode ser usado da mesma maneira, não obstante o endereçamento de nível de memória. Dada a natureza da pergunta, acho estranho que ninguém mais tenha mencionado isso.
kingfrito_5005
Esta deve ser a resposta no topo. Porque para iniciantes dizer que "nos bastidores, java tem ponteiros" pode levar a um estado muito confuso. Mesmo quando Java é ensinado no nível inicial, ensina-se que os ponteiros não são seguros, por isso não são usados ​​em Java. Um programador precisa saber o que ele / ela pode ver e trabalhar. Referência e ponteiros são realmente muito diferentes.
Neeraj Yadav
Não tenho certeza se concordo inteiramente com o tópico "2.2.3 Sem Enums" no link. Os dados poderia ser ultrapassada, mas Java faz enums apoio.
Somegeek
1
@Sometowngeek, o documento vinculado é um white paper de 1996 que descreve a primeira versão do java. Enums são introduzidos no java versão 1.5 (2004)
Fortega 01 de
11

Existem ponteiros em Java, mas você não pode manipulá-los da maneira que faria em C ++ ou C. Quando você passa um objeto, está passando um ponteiro para esse objeto, mas não no mesmo sentido que em C ++. Esse objeto não pode ser referenciado. Se você definir seus valores usando seus acessadores nativos, ele mudará porque o Java conhece sua localização na memória por meio do ponteiro. Mas o ponteiro é imutável. Ao tentar definir o ponteiro para um novo local, em vez disso, você acaba com um novo objeto local com o mesmo nome do outro. O objeto original não foi alterado. Aqui está um breve programa para demonstrar a diferença.

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

Isso pode ser executado em Ideone.com.

kingfrito_5005
fonte
10

Java não tem ponteiros como o C, mas permite que você crie novos objetos no heap que são "referenciados" por variáveis. A falta de ponteiros impede que os programas Java façam referência a localizações de memória ilegalmente e também permite que a Coleta de Lixo seja realizada automaticamente pela Java Virtual Machine.

appshare.co
fonte
7

Você pode usar endereços e ponteiros usando a classe Unsafe. No entanto, como o nome sugere, esses métodos são INSEGUROS e geralmente uma má ideia. O uso incorreto pode resultar na morte aleatória de sua JVM (na verdade, o mesmo problema é o uso de ponteiros incorretamente em C / C ++)

Embora você possa estar acostumado a ponteiros e pense que precisa deles (porque não sabe codificar de outra maneira), você descobrirá que não sabe e ficará melhor com isso.

Peter Lawrey
fonte
8
Os ponteiros são extremamente poderosos e úteis. Existem muitos casos em que não ter ponteiros (Java) torna o código muito menos eficiente. Só porque as pessoas são péssimas em usar ponteiros, não significa que as linguagens devam excluí-las. Não deveria ter um rifle porque outros (como os militares) o usam para matar pessoas?
Ethan Reesor
1
Não há muito útil ou poderoso que você não possa fazer em Java como a linguagem é. Para todo o resto, há a classe Insegura que acho que preciso usar muito raramente.
Peter Lawrey
2
Muito do código que escrevi nos últimos 40 anos nunca poderia ter sido implementado em Java, porque o Java não possui recursos básicos de baixo nível.
Perigo
1
@Danger é por isso que um grande número de bibliotecas usam Unsafe e a ideia de removê-lo gerou tanto debate no Java 9. O plano é fornecer substituições, mas elas ainda não estão prontas.
Peter Lawrey
3

Tecnicamente, todos os objetos Java são ponteiros. Todos os tipos primitivos são valores. Não há como assumir o controle manual desses ponteiros. Java usa apenas passagem por referência internamente.

Poindexter
fonte
1
Nem mesmo através do JNI? Nenhum conhecimento de Java para falar aqui, mas pensei ter ouvido que você poderia obter algum grubbing de baixo nível feito dessa maneira.
David Seiler
No que diz respeito aos meus cerca de 4 anos de experiência em Java e procurando a resposta por conta própria, digo que não, os ponteiros do Java não são acessíveis externamente.
Poindexter
Obrigado pela resposta. Eu perguntei porque, meu palestrante disse que em nível de pesquisa onde o java é usado em dispositivos portáteis, eles usam ponteiro ... é por isso que eu perguntei
@unknown (google) se estiver no nível de pesquisa, dependendo do que está sendo feito, pode ser que eles precisassem dessa funcionalidade, então eles violaram os idiomas normais e os implementaram de qualquer maneira. Você pode implementar sua própria JVM se desejar ter um superconjunto (ou subconjunto ou mistura) de recursos Java normais, mas tal código pode não ser executado em outras plataformas Java.
San Jacinto
@san: Obrigado pela resposta. BTW, não posso votar ... diz que preciso de 15 reputação
2

Não, realmente não.

Java não tem ponteiros. Se você realmente quisesse, poderia tentar emulá-los construindo em torno de algo como o reflexo, mas teria toda a complexidade dos indicadores sem nenhum dos benefícios .

Java não tem ponteiros porque não precisa deles. Que tipo de respostas você esperava com essa pergunta, ou seja, no fundo, você esperava que pudesse usá-las para algo ou foi apenas curiosidade?

Andrzej Doyle
fonte
Estou curioso para saber disso. Obrigado pela resposta
Java precisa de ponteiros, e é por isso que Java adicionou JNI - para contornar a falta de funções "semelhantes a ponteiros" ou de nível inferior que você simplesmente não pode fazer em Java, não importa o código que você escreva.
Perigo
2

Todos os objetos em java são passados ​​para funções por cópia de referência, exceto primitivos.

Na verdade, isso significa que você está enviando uma cópia do ponteiro para o objeto original, em vez de uma cópia do próprio objeto.

Por favor, deixe um comentário se quiser um exemplo para entender isso.

coolest_head
fonte
Isso é totalmente errado. Você não pode escrever uma swapfunção em Java que irá trocar dois objetos.
Michael Tsang
2

Os tipos de referência Java não são iguais aos ponteiros C, pois você não pode ter um ponteiro para um ponteiro em Java.

zezba9000
fonte
1

Como já foi dito, a resposta curta é "Não".

Claro, você poderia escrever código JNI que brinca com ponteiros Java. Dependendo do que você está tentando realizar, talvez isso o levasse a algum lugar, talvez não.

Você sempre pode simular pontos criando uma matriz e trabalhando com índices na matriz. Novamente, dependendo do que você está tentando realizar, isso pode ou não ser útil.

Jay
fonte
1

do livro Decompiling Android de Godfrey Nolan

A segurança determina que os ponteiros não sejam usados ​​em Java, de forma que os hackers não possam sair de um aplicativo e entrar no sistema operacional. Sem ponteiros significa que alguma outra coisa ---- neste caso, a JVM ---- deve cuidar da alocação e liberação de memória. Vazamentos de memória também deveriam se tornar uma coisa do passado, ou assim diz a teoria. Alguns aplicativos escritos em C e C ++ são notórios por vazar memória como uma peneira porque os programadores não prestam atenção em liberar memória indesejada no momento apropriado - não que alguém lendo isso seja culpado de tal pecado. A coleta de lixo também deve tornar os programadores mais produtivos, com menos tempo gasto na depuração de problemas de memória.

Mustafa Güven
fonte
0

você pode ter ponteiros para literais também. Você tem que implementá-los sozinho. É bastante básico para especialistas;). Use um array de int / object / long / byte e voila, você tem o básico para implementar ponteiros. Agora, qualquer valor int pode ser um ponteiro para esse array int []. Você pode incrementar o ponteiro, pode decrementar o ponteiro, pode multiplicar o ponteiro. Você realmente tem aritmética de ponteiro! Essa é a única maneira de implementar 1000 classes de atributos int e ter um método genérico que se aplica a todos os atributos. Você também pode usar uma matriz de bytes [] em vez de um int []

No entanto, eu gostaria que o Java deixasse você passar valores literais por referência. Algo ao longo das linhas

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

Mordan
fonte
-1

Todos os objetos java são apontadores porque uma variável que contém o endereço é chamada de apontador e o objeto contém o endereço.so objeto é uma variável do ponteiro.

Krishna Kant
fonte
1
Não, os objetos Java não são ponteiros. Eles são equivalentes a referências, que têm um conjunto diferente de recursos e semânticas.
Perigo