Як я можу використовувати вказівники на Java?


112

Я знаю, що у Java немає вказівників, але я чув, що програми Java можна створювати за допомогою покажчиків і що це можуть зробити нечисленні, хто є фахівцями в галузі Java. Це правда?


1
У впровадженні Sun все одно.
Майкл Майерс

1
Чи можу я використовувати цю адресу вказівника

6
Хеш-код за замовчуванням для об’єкта java НЕ - це адреса вказівника, уважно перечитайте договір на hashCode, і ви помітите, що два різних об’єкти в пам'яті можуть мати однакове значення хеш-коду.
Амір Афгані

3
У 64-розрядному та 32-розрядному Java 32-розрядний хеш-код не є адресою. Не гарантується, що хеш-коди будуть унікальними. Місце розташування об'єкта можна переміщувати в пам'яті під час переміщення між пробілами, а пам'ять ущільнюється, але хеш-код не змінюється.
Пітер Лоурі

2
90% того, що ви могли зробити з покажчиками на C ++, ви можете робити з рефлексіями Java, решта 10% ви можете отримати, упакувавши посилання всередині іншого об'єкта (не те, що я коли-небудь вважав це необхідним зробити)
Річард Тінгл,

Відповіді:


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.    

Ви можете зробити це безпечним, спочатку переконавшись, що більше немає потреби в старому значенні секунди або призначив іншому вказівнику значення секунди.

first = second;
second = third; //OK

Зауважте, що надання другого значення іншими способами (NULL, new ...) - це стільки ж потенційна помилка і може призвести до втрати об'єкта, на який він вказує.

Система Java викине виняток ( OutOfMemoryError), коли ви зателефонуєте за новим, і розподільник не може виділити запитувану комірку. Це дуже рідко і, як правило, є наслідком рекурсії, що біжить.

Зауважте, що з мовної точки зору, відмова від об'єктів до сміттєзбірника зовсім не є помилками. Це просто те, що потрібно усвідомлювати програмісту. Одна і та ж змінна може вказувати на різні об'єкти в різний час, і старі значення будуть повернені, коли жоден покажчик не посилається на них. Але якщо логіка програми вимагає збереження хоча б однієї посилання на об’єкт, це призведе до помилки.

Новачки часто роблять таку помилку.

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.

Покажчики на С:

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. Це дуже погано, що так багато людей тут, здається, не розуміють, що насправді є вказівником, Імхо, і тепер я вийду зі своєї мильної скриньки і сподіваюся, що ви пробачте мене за моє маленьке спалаху.
Небезпека

5
Я думаю, ти маєш на увазі На жаль. Чому ви вважаєте, що добре, що java не підтримує арифметику вказівника?
користувач3462295

2
@ user3462295 Оскільки люди, здається, вважають, що це занадто важко використовувати загальне населення, і це можна зрозуміти лише за допомогою кодування компіляторів для написання ніндзя або драйверів пристроїв.
iCodeSometime

4
@Danger Перший рядок відповіді "Усі об'єкти в Java є посиланнями, і ви можете використовувати їх як покажчики." Це найкраща відповідь, яку можна надати. Якщо єдиною відповіддю було: «Ні, java не має покажчиків». ця відповідь була б корисною. Ця відповідь натомість вказує, що ви можете зробити замість цього.
csga5000

46

У Java є вказівники. Кожен раз, коли ви створюєте об’єкт на Java, ви фактично створюєте вказівник на об’єкт; тоді цей покажчик може бути встановлений на інший об'єкт або на null, і початковий об’єкт все ще буде існувати (очікується збір сміття).

Те, що ви не можете зробити на Java, є арифметикою вказівника. Ви не можете знецінити конкретну адресу пам'яті або збільшити покажчик.

Якщо ви дійсно хочете отримати низький рівень, єдиний спосіб зробити це за допомогою інтерфейсу Java Native ; і навіть тоді частина низького рівня повинна бути виконана в C або C ++.


9
У Java просто немає покажчиків, простих і простих, і я не можу за все життя зрозуміти, чому так багато відповідей наводять неправильну інформацію. Це люди StackOverlfow! Визначення вказівника в інформатиці - це (ви можете Google для цього): "В інформатиці вказівник - це об'єкт мови програмування, значення якого посилається на (або" вказує на ") інше значення, збережене в іншому місці в пам'яті комп'ютера за допомогою його адреса ". Посилання на Java - НЕ вказівник. Java повинна запустити збір сміття, і якби у вас був реальний покажчик, то це було б неправильно!
Небезпека

3
@Danger Покажчик - це частина даних, що містить адресу пам'яті. Java - це дві речі, про які люди забувають. Це мова і платформа. Як би не сказали, що .NET - це мова, ми повинні пам’ятати, що вимова «Java не має покажчиків» може вводити в оману, оскільки платформа, звичайно, має та використовує покажчики. Ці вказівники недоступні програмісту через мову Java, але вони існують під час виконання. Посилання на мові існують поверх фактичних покажчиків під час виконання.
kingfrito_5005

@ kingfrito_5005 Здається, ви погоджуєтесь зі мною. Я вважаю, ви сказали: "Ці покажчики не доступні програмісту через мову Java", що відповідає моєму твердженню, що "Java не має покажчиків". Посилання семантично суттєво відрізняються від покажчиків.
Небезпека

1
@ Небезпека це правда, але я думаю, що в цих випадках важливо розрізняти платформу та мову, тому що я знаю, що коли я вперше розпочав заяву, як ваша, мене це збентежило.
kingfrito_5005

1
Так, Java, безумовно, має вказівники на змінні. Просто розробникам не доведеться з ними боротися, тому що java робить магію для цього за кадром. Це означає, що розробникам заборонено безпосередньо вказувати змінну на певну адресу пам’яті, а також мати справу з компенсацією даних та ін. Це дозволяє java зберігати чисту меморіальну колекцію сміття, але оскільки кожна змінна є істотним покажчиком, вона також потребує додаткової пам’яті, щоб мати всі ці автоматичні генеровані адреси покажчиків, яких на мовах нижчого рівня можна було уникнути.
VulstaR

38

Оскільки у Java немає типів даних вказівника, використовувати вказівники в Java неможливо. Навіть нечисленні експерти не зможуть використовувати покажчики в java.

Дивіться також останній пункт у: Мовне середовище Java


3
Один з небагатьох правильних відповідей навряд чи має оновлення?
Небезпека

Зауважте, що існують об'єкти «вказівника», що імітують поведінку, що вказує на вказівник. Для багатьох цілей це можна використати однаково, не підтримуючи адресації рівня пам'яті. Враховуючи характер питання, мені здається дивним, що ніхто про це не згадував.
kingfrito_5005

Це має бути відповідь вгорі. Тому що для початківців, які говорять про те, що «поза кулісом Java мають покажчики», це може призвести до дуже заплутаного стану. Навіть коли Java навчається на початковому рівні, вона вчить, що покажчики не є безпечними, тому вони не використовуються в Java. Програмісту потрібно сказати, що він / вона може бачити і над чим працювати. Посилання та вказівники насправді дуже різні.
Неєрай Ядав

Не впевнений, що я цілком згоден з темою "2.2.3 Не Enums" за посиланням. Дані можуть бути застарілими, але Java робить підтримку перерахувань.
Sometowngeek

1
@Sometowngeek, на який посилається документ, - це книга з 1996 року, що описує першу версію Java. Енуми представлені у версії java 1.5 (2004)
Fortega

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.


10

У Java немає вказівників, як на C, але вона дозволяє створювати нові об'єкти на купі, на які "посилаються" змінні. Відсутність покажчиків полягає в тому, що вони не дозволяють програмам Java незаконно посилатися на місця пам'яті, а також дозволяє автоматично збирати сміття віртуальною машиною Java.


7

Ви можете використовувати адреси та покажчики за допомогою класу Unsafe. Однак, як випливає з назви, ці методи є UNSAFE і взагалі погана ідея. Неправильне використання може призвести до випадкового вмирання вашого JVM (фактично така ж проблема отримати неправильне використання покажчиків у C / C ++)

Хоча ви можете звикнути до покажчиків і вважаєте, що вони вам потрібні (оскільки ви не знаєте, як кодувати інший спосіб), ви виявите, що цього не зробите, і вам буде краще для цього.


8
Покажчики надзвичайно потужні та корисні. Є багато випадків, коли відсутність покажчиків (Java) робить код набагато менш ефективним. Тільки тому, що люди смоктають за допомогою покажчиків, це не означає, що мови не повинні їх виключати. Чи не маю я гвинтівки, бо інші (як військові) використовують їх для вбивства людей?
Етан Різор

1
На Яві не так багато корисного чи потужного, як це є мова. Для всього іншого є клас "Небезпечний", який я вважаю, що мені потрібно користуватися дуже рідко.
Пітер Лорі

2
Значна частина коду, який я написав за останні 40 років, ніколи не міг бути реалізований на Java, тому що Java не має ядра, можливостей низького рівня.
Небезпека

1
@ Небезпека, через що велика кількість бібліотек використовує Unsafe, і ідея її видалити викликала стільки дискусій у Java 9. План полягає в наданні заміни, але вони ще не готові.
Пітер Лорі

3

Технічно всі об’єкти Java є покажчиками. Усі примітивні типи є значеннями. Немає можливості вручну керувати цими вказівниками. Java просто внутрішньо використовує пропускний посилання.


1
Навіть через JNI? Тут немає жодних знань на Java, але я думав, що чув, що ти можеш зробити так, щоб це було зроблено на низькому рівні.
Девід Сейлер

Що стосується мого ~ 4-річного досвіду роботи Java та, шукаючи відповідь, я кажу, що ні, вказівники Java не є доступними зовні.
Poindexter

Дякуємо за відповідь Я запитав, тому що мій лектор сказав, що на рівні досліджень, де Java використовується в портативних пристроях, вони використовують вказівник ... ось чому я запитав

@unknown (google), якщо це на дослідницькому рівні, залежно від того, що робиться, можливо, вони потребували такої функціональності, тому вони порушували нормальні ідіоми та все одно їх реалізовували. Ви можете реалізувати свій власний JVM, якщо ви хочете мати суперсет (або підмножину, або суміш) звичайних функцій Java, але такий код може не працювати на інших платформах Java.
San Jacinto

@san: Дякую за відповідь. До речі, я не можу проголосувати. Скаже, що мені потрібно 15 репутації

2

Не дуже, ні.

У Java немає покажчиків. Якби ви насправді хотіли, можете спробувати наслідувати їх, будуючи навколо себе щось на зразок відображення, але це матиме всю складність покажчиків, не маючи жодної переваги .

У Java немає покажчиків, оскільки вони не потребують. Які відповіді ви сподівались на це питання, тобто глибоко в глибині душі ви сподівалися, що зможете їх використати для чогось чи це просто цікавість?


Мені цікаво це знати. Дякуємо за відповідь

Java потрібні покажчики, і саме тому Java додала JNI - щоб подолати відсутність функцій, що нагадують вказівник, або функцій нижчого рівня, які ви просто не можете робити на Java, незалежно від того, який код ви пишете.
Небезпека

2

Усі об'єкти в Java передаються функціям шляхом копії, крім примітивів.

Фактично це означає, що ви надсилаєте копію вказівника на оригінальний об'єкт, а не копію самого об'єкта.

Залиште коментар, якщо ви хочете, щоб приклад зрозумів це.


Це явно неправильно. Ви не можете записати на swapJava функцію, яка замінить два об'єкти.
Майкл Цанг

2

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


1

Як казали інші, коротка відповідь - «Ні».

Звичайно, ви можете написати код JNI, який грає з покажчиками Java. Залежно від того, що ви намагаєтеся досягти, можливо, це десь потрапить у вас, а може, і не буде.

Ви завжди можете імітувати пункти, створюючи масив та працюючи з індексами в масиві. Знову ж таки, залежно від того, що ви намагаєтеся досягти, це може бути чи не корисно.


1

з книги під назвою Decompiling Android від Годфрі Нолана

Безпека диктує, що покажчики не використовуються на Java, тому хакери не можуть вийти з програми та в операційну систему. Жодні покажчики не означають, що щось інше ---- у цьому випадку JVM ---- повинен подбати про розподілення та звільнення пам'яті. Витоки пам’яті також повинні стати минулим, або так іде теорія. Деякі програми, написані на C і C ++, відомі тим, що протікає пам'ять, як сито, оскільки програмісти не звертають уваги на звільнення небажаної пам’яті у відповідний час — не те, що хтось, хто читає це, був би винним у такому гріху. Збір сміття також повинен зробити програмістів більш продуктивними, з тим менше часу витрачається на налагодження проблем з пам'яттю.


0

Ви можете мати вказівники і для літералів. Ви повинні їх реалізувати самостійно. Це досить базово для експертів;). Використовуйте масив int / object / long / byte та voila, який ви маєте основи для реалізації покажчиків. Тепер будь-яке значення int може бути вказівником на цей масив int []. Ви можете збільшити покажчик, ви можете декрементувати вказівник, ви можете помножити покажчик. У вас дійсно є вказівна арифметика! Це єдиний спосіб реалізувати 1000 класів атрибутів int та створити загальний метод, який застосовується до всіх атрибутів. Ви також можете використовувати байт [] масив замість int []

Однак я хочу, щоб Java дозволила вам передавати буквальні значення за посиланням. Щось уздовж рядків

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


-1

Усі об'єкти java є вказівниками, тому що змінна, яка містить адресу, називається покажчиком, а об'єкт утримує address.so об'єктом є змінною вказівника.


1
Ні, об'єкти Java не є покажчиками. Вони еквівалентні посиланням, які мають різний набір можливостей і семантики.
Небезпека
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.