Как я могу использовать указатели в Java?

112

Я знаю, что в Java нет указателей, но я слышал, что программы на Java можно создавать с помощью указателей, и что это могут делать немногие специалисты в java. Это правда?

Питер Лоури
источник
1
Во всяком случае, в реализации Sun.
Майкл Майерс
1
могу ли я использовать этот адрес указателя
6
Хэш-код по умолчанию для java-объекта НЕ является адресом указателя, внимательно перечитайте контракт для хэш-кода, и вы заметите, что два разных объекта в памяти могут иметь одинаковое значение хэш-кода.
Амир Афгани
3
В 64-битной и 32-битной Java 32-битный хэш-код не является адресом. Не гарантируется, что хэш-коды будут уникальными. Местоположение объекта можно перемещать в памяти, когда он перемещается между пробелами и память сжимается, однако хэш-код не изменяется.
Питер Лоури
2
90% того, что вы могли бы сделать с указателями C ++, вы можете сделать с помощью java-ссылок, оставшиеся 10% вы можете получить, упаковав ссылку внутри объекта annother (не то, чтобы я когда-либо находил это необходимым),
Ричард Тингл,

Ответы:

243

Все объекты в Java являются ссылками, и вы можете использовать их как указатели.

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;

Разыменование нулевого значения:

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

Проблема сглаживания:

third = new Tiger();
first = third;

Теряющие клетки:

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

Вы можете сделать это безопасным, сначала убедившись, что больше нет необходимости в старом значении second или присвоив другому указателю значение second.

first = second;
second = third; //OK

Обратите внимание, что присвоение второму значению другими способами (NULL, new ...) является такой же потенциальной ошибкой и может привести к потере объекта, на который он указывает.

Система Java выдаст исключение (OutOfMemoryError ), когда вы вызываете new, а распределитель не может выделить запрошенную ячейку. Это очень редко и обычно является результатом рекурсии бегства.

Обратите внимание, что с языковой точки зрения передача объектов сборщику мусора вовсе не является ошибкой. Это просто то, о чем программист должен знать. Одна и та же переменная может указывать на разные объекты в разное время, и старые значения будут восстановлены, если на них нет указателя. Но если логика программы требует сохранения хотя бы одной ссылки на объект, это вызовет ошибку.

Новички часто допускают следующую ошибку.

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

Вы, вероятно, хотели сказать:

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

Неправильный кастинг:

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.

Указатели в 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
}

Указатели в 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
    }
} 

ОБНОВЛЕНИЕ: как предлагается в комментариях, следует отметить, что C имеет арифметику указателей. Однако в Java этого нет.

Саджад Бахмани
источник
16
Хороший ответ, но вы не смогли устранить одно ключевое отличие: C имеет арифметику указателей. (К счастью) вы не можете сделать это на Java.
Р. Мартиньо Фернандес,
10
Я ненавижу голосовать против любого ответа, особенно популярного, но в Java нет указателей, и вы просто не можете использовать ссылку как указатель. Есть определенные вещи, которые вы можете делать только с настоящими указателями - например, вы можете получить или установить любой байт в пространстве данных вашего процесса. Вы просто не можете этого сделать на Java. Очень плохо, что так много людей здесь, кажется, не понимают, что такое указатель на самом деле, imho, и теперь я выйду из своей мыльницы и надеюсь, что вы простите меня за мою маленькую вспышку.
Danger
5
Я думаю, вы имеете в виду «К сожалению». Почему вы думаете, что это хорошо, что Java не поддерживает арифметику указателей?
user3462295 01
2
@ user3462295 Потому что люди, кажется, думают, что это слишком сложно для использования широкими массами, и его можно понять только ниндзя-программистами, пишущими компиляторы или драйверы устройств.
iCodeSometime
4
@Danger Первая строка ответа «Все объекты в Java являются ссылками, и вы можете использовать их как указатели.». Это лучший ответ, который можно дать. Если бы единственным ответом было «Нет, в java нет указателей». этот ответ был бы бесполезным. Вместо этого в этом ответе указано, что вы можете сделать вместо этого.
csga5000
46

В Java есть указатели. Каждый раз, когда вы создаете объект в Java, вы фактически создаете указатель на объект; этот указатель может быть установлен на другой объект или наnull , а исходный объект все еще будет существовать (ожидающая сборка мусора).

Что вы не можете сделать в Java, так это арифметику указателей. Вы не можете разыменовать конкретный адрес памяти или увеличить указатель.

Если вы действительно хотите получить низкоуровневую работу, единственный способ сделать это - использовать собственный интерфейс Java ; и даже тогда низкоуровневая часть должна быть выполнена на C или C ++.

Майкл Майерс
источник
9
В Java просто нет указателей, простых и понятных, и я не могу понять, почему так много ответов здесь содержат неверную информацию. Это люди StackOverlfow! Определение указателя в информатике (для этого можно использовать Google): «В информатике указатель - это объект языка программирования, значение которого относится (или« указывает ») на другое значение, хранящееся в другом месте в памяти компьютера, используя его адрес ". Ссылка в Java НЕ является указателем. Java должна запускать сборку мусора, и если бы у вас был реальный указатель, это было бы неправильно!
Danger
3
@Danger Указатель - это часть данных, содержащая адрес памяти. Java - это две вещи, о которых люди забывают. Это язык и платформа. Несмотря на то, что нельзя сказать, что .NET - это язык, мы должны помнить, что утверждение «Java не имеет указателей» может ввести в заблуждение, потому что платформа, конечно же, имеет и использует указатели. Эти указатели недоступны для программиста через язык Java, но они существуют во время выполнения. Ссылки на языке существуют поверх фактических указателей во время выполнения.
kingfrito_5005
@ kingfrito_5005 Похоже, вы со мной согласны. Я полагаю, вы сказали: «Эти указатели недоступны для программиста через язык Java», что согласуется с моим утверждением, что «Java не имеет указателей». Ссылки семантически существенно отличаются от указателей.
Danger
1
@Danger, это правда, но я думаю, что в этих случаях важно различать платформу и язык, потому что я знаю, что когда я только начинал, утверждение, подобное вашему, сбило бы меня с толку.
kingfrito_5005 05
1
Ага, в Java определенно есть указатели за переменными. Просто разработчикам не нужно иметь с ними дело, потому что java творит чудеса за кулисами. Это означает, что разработчикам не разрешается напрямую указывать переменную на конкретный адрес памяти и иметь дело со смещениями данных и прочим. Это позволяет java сохранять чистую сборку мусора памяти, но поскольку каждая переменная по существу является указателем, также требуется некоторая дополнительная память, чтобы иметь все эти автоматически сгенерированные адреса указателей, чего на более низкоуровневых языках можно было бы избежать.
VulstaR 07
38

Поскольку в Java нет типов данных указателя, в Java невозможно использовать указатели. Даже немногие специалисты не смогут использовать указатели в java.

См. Также последний пункт в: Среда языка Java

Fortega
источник
3
Один из немногих правильных ответов здесь вряд ли получит положительные голоса?
Danger
Обратите внимание, что существуют объекты-указатели, которые имитируют поведение, подобное указателю. Для многих целей это можно использовать одинаково, независимо от адресации на уровне памяти. Учитывая характер вопроса, мне кажется странным, что никто об этом не упомянул.
kingfrito_5005
Это должен быть ответ вверху. Потому что для новичков сообщение о том, что «за кулисами в Java есть указатели», может привести к очень запутанному состоянию. Даже когда Java изучают на начальном уровне, его учат, что указатели небезопасны, поэтому они не используются в Java. Программисту нужно сказать, что он может видеть и над чем работать. Ссылка и указатели на самом деле очень разные.
Neeraj Yadav
Не уверен, что полностью согласен с темой "2.2.3 No Enums" в ссылке. Данные могут быть устаревшими, но Java делает поддержку перечислений.
Sometowngeek
1
@Sometowngeek, на который ссылается документ, представляет собой официальный документ 1996 года, описывающий самую первую версию java. Перечисления представлены в java версии 1.5 (2004)
Fortega 01
11

В Java есть указатели, но вы не можете манипулировать ими так, как в C ++ или C. Когда вы передаете объект, вы передаете указатель на этот объект, но не в том же смысле, как в C ++. Этот объект нельзя разыменовать. Если вы установите его значения с помощью его собственных средств доступа, он изменится, потому что Java знает свое местоположение в памяти через указатель. Но указатель неизменен. Когда вы пытаетесь установить указатель на новое место, вместо этого вы получаете новый локальный объект с тем же именем, что и другой. Исходный объект не изменился. Вот краткая программа, демонстрирующая разницу.

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

Это можно запустить на Ideone.com.

kingfrito_5005
источник
10

В Java нет указателей, как в C, но она позволяет вам создавать новые объекты в куче, на которые «ссылаются» переменные. Отсутствие указателей препятствует тому, чтобы программы Java незаконно ссылались на ячейки памяти, а также позволяет виртуальной машине Java автоматически выполнять сборку мусора.

appshare.co
источник
7

Вы можете использовать адреса и указатели, используя класс Unsafe. Однако, как следует из названия, эти методы НЕ БЕЗОПАСНЫ и вообще плохая идея. Неправильное использование может привести к случайной смерти вашей JVM (на самом деле та же проблема возникает при неправильном использовании указателей в C / C ++)

Хотя вы, возможно, привыкли к указателям и думаете, что они вам нужны (потому что вы не умеете кодировать по-другому), вы обнаружите, что это не так, и вам от этого будет лучше.

Питер Лоури
источник
8
Указатели чрезвычайно эффективны и полезны. Во многих случаях отсутствие указателей (Java) делает код намного менее эффективным. Тот факт, что люди не умеют использовать указатели, не означает, что языки должны их исключать. Разве у меня не должно быть винтовки, потому что другие (например, военные) используют ее для убийства людей?
Итан Рисор
1
В Java нет ничего полезного или мощного, чего бы вы не сделали. Для всего остального есть класс Unsafe, который мне приходится использовать очень редко.
Питер Лоури
2
Большая часть кода, который я написал за последние 40 лет, никогда не могла быть реализована на Java, потому что в Java отсутствуют основные низкоуровневые возможности.
Danger
1
@Danger, поэтому большое количество библиотек используют Unsafe, и идея его удаления вызвала столько споров в Java 9. Планируется предоставить замены, но они еще не готовы.
Питер Лоури
3

Технически все объекты Java являются указателями. Однако все примитивные типы являются значениями. Невозможно вручную контролировать эти указатели. Java просто внутренне использует передачу по ссылке.

Пойндекстер
источник
1
Даже через JNI? Здесь нет никаких знаний о Java, но мне показалось, что я слышал, что таким образом можно выполнить низкоуровневую обработку битов.
Дэвид Зайлер
Что касается моего ~ 4-летнего опыта работы с Java, и я сам, ища ответ, говорит нет, указатели Java недоступны извне.
Poindexter
Спасибо за ответ. Я спросил, потому что мой лектор сказал, что на исследовательском уровне, где java используется в портативных устройствах, они используют указатель ... вот почему я спросил
@unknown (google), если это на уровне исследования, в зависимости от того, что делается, возможно, им была нужна такая функциональность, поэтому они нарушили обычные идиомы и все равно реализовали их. Вы можете реализовать свою собственную JVM, если хотите иметь расширенный набор (или подмножество, или смесь) обычных функций Java, но такой код может не работать на других платформах Java.
Сан-Хасинто
@san: Спасибо за ответ. Кстати, я не могу голосовать .. говорит, что мне нужно 15 репутации
2

Не на самом деле нет.

В Java нет указателей. Если бы вы действительно хотели, вы могли бы попытаться имитировать их, построив что-то вроде отражения, но это будет иметь всю сложность указателей без каких-либо преимуществ .

В Java нет указателей, потому что они ей не нужны. Какие ответы вы надеялись получить от этого вопроса, т.е. в глубине души вы надеялись, что сможете использовать их для чего-то, или это было просто любопытством?

Анджей Дойл
источник
Мне любопытно это знать. Спасибо за ответ
Java действительно нуждаются в указателях, и именно поэтому Java добавила JNI - чтобы обойти отсутствие «указателей» или функций более низкого уровня, которые вы просто не можете выполнять в Java, независимо от того, какой код вы пишете.
Danger
2

Все объекты в java передаются функциям посредством ссылочной копии, кроме примитивов.

Фактически это означает, что вы отправляете копию указателя на исходный объект, а не копию самого объекта.

Пожалуйста, оставьте комментарий, если вы хотите, чтобы пример понял это.

cool_head
источник
Это совершенно неправильно. Вы не можете написать swapфункцию на Java, которая меняет местами два объекта.
Майкл Цанг
2

Типы ссылок Java - это не то же самое, что указатели C, поскольку у вас не может быть указателя на указатель в Java.

zezba9000
источник
1

Как говорили другие, короткий ответ - «Нет».

Конечно, вы могли бы написать код JNI, который работает с указателями Java. В зависимости от того, чего вы пытаетесь достичь, возможно, это приведет вас к чему-то, а может и нет.

Вы всегда можете имитировать указатели, создав массив и работая с индексами в массиве. Опять же, в зависимости от того, чего вы пытаетесь достичь, это может быть полезно, а может и нет.

Джей
источник
1

из книги Годфри Нолана "Декомпиляция Android"

Безопасность диктует, что указатели не используются в Java, поэтому хакеры не могут взломать приложение и проникнуть в операционную систему. Отсутствие указателей означает, что что-то еще - в данном случае JVM ---- должно позаботиться о распределении и освобождении памяти. Утечки памяти тоже должны уйти в прошлое, по крайней мере, согласно теории. Некоторые приложения, написанные на C и C ++, печально известны утечкой памяти как решето, потому что программисты не обращают внимания на освобождение нежелательной памяти в нужное время - не то чтобы кто-то, читающий это, был бы виновен в таком грехе. Сборка мусора также должна сделать программистов более продуктивными, с меньшими затратами времени на отладку проблем с памятью.

Мустафа Гювен
источник
0

у вас также могут быть указатели на литералы. Вы должны сами их реализовать. Для знатоков это довольно просто;). Используйте массив int / object / long / byte и вуаля, у вас есть основы для реализации указателей. Теперь любое значение int может быть указателем на этот массив int []. Вы можете увеличить указатель, вы можете уменьшить указатель, вы можете умножить указатель. У вас действительно есть арифметика указателей! Это единственный способ реализовать 1000 классов атрибутов int и иметь общий метод, применимый ко всем атрибутам. Вы также можете использовать массив byte [] вместо int []

Однако я бы хотел, чтобы Java позволяла передавать буквальные значения по ссылке. Что-то в этом роде

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

Мордан
источник
-1

Все объекты java являются указателями, потому что переменная, которая содержит адрес, называется указателем, а объект удерживает адрес. Таким образом, объект является переменной указателя.

Кришна Кант
источник
1
Нет, объекты Java не являются указателями. Они эквивалентны ссылкам, которые имеют другой набор возможностей и семантику.
Danger