Могу ли я передать параметры по ссылке в Java?

Ответы:

225

Java сбивает с толку, потому что все передается по значению . Однако для параметра ссылочного типа (т. Е. Не для параметра примитивного типа) это сама ссылка, которая передается по значению, следовательно, она кажется передачей по ссылке (и люди часто утверждают, что это так). Это не тот случай, о чем свидетельствует следующее:

Object o = "Hello";
mutate(o)
System.out.println(o);

private void mutate(Object o) { o = "Goodbye"; } //NOT THE SAME o!

Распечатает Helloна консоль. Опции, если вы хотите, чтобы вышеприведенный код печатался Goodbye, должны использовать явную ссылку следующим образом:

AtomicReference<Object> ref = new AtomicReference<Object>("Hello");
mutate(ref);
System.out.println(ref.get()); //Goodbye!

private void mutate(AtomicReference<Object> ref) { ref.set("Goodbye"); }
oxbow_lakes
источник
48
Кроме того, массив длины 1 может использоваться для создания ссылки, если вы действительно хотите запутать людей :)
Christoffer
2
AtomicReference и соответствующие классы (AtomicInteger) являются лучшими для таких операций
Naveen
3
AtomicReference - это излишество, вам не обязательно иметь барьер памяти, и они могут стоить вам. У JRuby была проблема производительности из-за изменчивой переменной, используемой при создании объекта. Вариант использования массива в качестве специальной ссылки мне кажется достаточно хорошим, и я видел, что он использовался не раз.
Элазар Лейбович
Я не думаю, что это сбивает с толку, однако это нормальное поведение. Типы значений существуют в стеке, а ссылочные типы существуют в куче, но ссылки на ссылочные типы также существуют в стеке. Таким образом, вы всегда работаете со значением, которое находится в стеке. Я полагаю, что вопрос здесь был: возможно ли передать ссылку из стека, которая указывает на какое-то другое значение, которое также находится в стеке. Или передать ссылку на другую ссылку, которая указывает на какое-то значение, которое живет в куче?
eomeroff
это я отличная информация. я использовал массивы, но это похоже на что-то созданное для работы. однако я не знаю, как это работает по сравнению с массивами, если в цикле, критичном к производительности,
steveh
65

Могу ли я передать параметры по ссылке в Java?

Нет.

Зачем ? В Java есть только один способ передачи аргументов в методы: по значению.

Примечание:

Для примитивов это легко понять: вы получаете копию значения.

Для всех остальных вы получаете копию ссылки, и это также называется передачей по значению.

Это все на этой картинке:

введите описание изображения здесь

PeterMmm
источник
25

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

Ради любопытства вы можете реализовать в Java рефлексивную семантику, просто оборачивая ваши объекты в изменяемый класс:

public class Ref<T> {

    private T value;

    public Ref(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }

    public void set(T anotherValue) {
        value = anotherValue;
    }

    @Override
    public String toString() {
        return value.toString();
    }

    @Override
    public boolean equals(Object obj) {
        return value.equals(obj);
    }

    @Override
    public int hashCode() {
        return value.hashCode();
    }
}

прецедент:

public void changeRef(Ref<String> ref) {
    ref.set("bbb");
}

// ...
Ref<String> ref = new Ref<String>("aaa");
changeRef(ref);
System.out.println(ref); // prints "bbb"
DFA
источник
Почему бы не использовать вместо этого java.lang.ref.Reference?
Закодировано
У java.lang.ref.Reference нет метода set, он не изменяем
dfa
почему бы не использовать AtomicReference(для не примитивов)? Или AtomicSomething(например:) AtomicBooleanдля примитивов?
Арвин
К сожалению, <T> не принимает примитивные типы. Хотелось бы сделать это: Ref <int>
jk7
1
@ jk7 Достаточно ли важно использовать intвместо Integer, boolвместо Booleanи так далее? То есть классы-обертки (которые автоматически разворачиваются, если вы беспокоитесь о синтаксисе) не работают?
Пол Стелиан
18

От Джеймса Гослинга из "Языка программирования Java":

«... В Java существует ровно один режим передачи параметров - передача по значению - и это упрощает задачу…»

duffymo
источник
2
Объявление бога языка должно быть объявлено ответом.
ingehere
3
@ingyhere :: The pronouncement of the language god should be declared the answerНе совсем. Несмотря на то, что создатель Java думает или верит, абсолютный ответ будет взят из цитаты из спецификации языка.
paercebal
1
Фактически это в JLS, в разделе 8.4.1 , и JLS цитирует Гослинга как первого автора: «Когда вызывается метод или конструктор (§15.12), значения фактических выражений аргумента инициализируют вновь созданные переменные параметра , каждый объявленного типа, перед выполнением тела метода или конструктора. "
ingehere
Именно. Сарказм от членов с низким числом представителей не помогает.
Duffymo
11

Я не думаю, что ты можешь. Наилучшим вариантом может быть инкапсуляция того, что вы хотите передать «по ссылке», в другой экземпляр класса и передать ссылку на (внешний) класс (по значению). Если вы понимаете, что я имею в виду ...

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

Марк Гравелл
источник
7

Java всегда передается по значению.

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

Ник Холт
источник
4

Другим вариантом является использование массива, например, void method(SomeClass[] v) { v[0] = ...; } но 1) массив должен быть инициализирован перед вызовом метода, 2) тем не менее, никто не может реализовать, например, метод swap таким образом ... Этот способ используется в JDK, например, в java.util.concurrent.atomic.AtomicMarkableReference.get(boolean[]).

Михал Мисиак
источник