我可以在Java中通过引用传递参数吗?


Answers:


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

48
另外,如果您确实想使人感到困惑,则可以使用长度为1的数组来创建引用:)
Christoffer

2
AtomicReference和相应的类(AtomicInteger)最适合此类操作
Naveen

3
AtomicReference是一个过大的杀伤力,您不一定在那里需要内存屏障,否则可能会花费您。由于构造对象时使用了易失性变量,因此JRuby存在性能问题。对我来说,使用数组作为临时引用的选项似乎已经足够好了,而且我已经看到它使用了不止一次。
Elazar Leibovich 2012年

我不认为这会造成混淆,但这是正常现象。值类型存在于堆栈中,引用类型存在于堆中,但对引用类型的引用也存在于堆栈中。因此,您始终使用堆栈中的值进行操作。我认为这里的问题是:是否可以从堆栈传递指向其他也在堆栈上的值的引用。还是传递指向存在于堆上的某些值的其他参考的参考?
eomeroff

这我很好的信息。我一直在使用数组,但这似乎是为工作而构建的。我不知道这个vs.阵列的性能,但是,如果处于性能关键循环中
steveh

65

我可以在Java中通过引用传递参数吗?

没有。

为什么呢 Java 只有一种将参数传递给方法的模式:按值。

注意:

对于基元,这很容易理解:您将获得值的副本。

对于所有其他对象,您将获得引用的副本,这也称为按值传递。

这是所有图片:

在此处输入图片说明


25

在Java中,在语言级别上没有类似于ref的东西。在Java中,只有按值传递语义

为求好奇,您可以在Java中实现类似于ref的语义,只需将对象包装在可变的类中即可:

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"

为什么不改用java.lang.ref.Reference?
硬编码

java.lang.ref.Reference没有set方法,它是不可变的
dfa

为什么不使用AtomicReference(对于非原始)?或AtomicSomething(例如:)AtomicBoolean用于基本体?
Arvin

不幸的是,<T>不接受原始类型。想要这样做:Ref <int>
jk7

1
@ jk7真的必须使用int代替Integerbool代替Boolean等等足够重要吗?也就是说,包装类(如果担心语法会自动解包)不起作用?
Paul Stelian

18

摘自James Gosling的“ Java编程语言”:

“ ...在Java中,只有一种参数传递模式-按值传递-使事情变得简单。。。”


2
语言上帝的发音应该宣布为答案。
ingyhere

3
@ingyhere The pronouncement of the language god should be declared the answer:不是。尽管Java的创建者有什么想法或相信,但绝对的答案将来自语言规范的引文。
paercebal,2015年

1
实际上,它在JLS的8.4.1节中,并且JLS引用了Gosling作为第一作者:“当调用方法或构造函数(第15.12节)时,实际参数表达式的值会初始化新创建的参数变量,每个在执行方法或构造函数主体之前声明的类型。”
ingyhere 2015年

究竟。低声望成员的讽刺无济于事。
duffymo 2015年

11

我认为你不能。最好的选择是将要通过“通过引用”传递的东西封装到另一个类实例上,并(外部)传递类的引用(按值)。如果你明白我的意思...

也就是说,您的方法更改了它传递的对象的内部状态,然后调用者可以看到该状态。


7

Java总是按值传递。

传递基元时,它是值的副本,传递对象时,是引用指针的副本。


4

另一种选择是使用一个数组,例如, void method(SomeClass[] v) { v[0] = ...; } 但是1)必须在调用方法之前初始化该数组; 2)仍然不能以这种方式实现例如swap方法...这种方式在JDK中使用,例如在中java.util.concurrent.atomic.AtomicMarkableReference.get(boolean[])

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.