【问题标题】:Can I pass parameters by reference in Java?我可以在Java中通过引用传递参数吗?
【发布时间】:2020-09-08 19:28:53
【问题描述】:

我想要类似于 C#ref 关键字的语义。

【问题讨论】:

标签: c# java reference


【解决方案1】:

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

【讨论】:

  • 另外,如果你真的想迷惑别人,可以使用长度为 1 的数组来创建引用 :)
  • AtomicReference 和相应的类(AtomicInteger)最适合此类操作
  • AtomicReference 太过分了,你不一定想要那里的内存屏障,它们可能会让你付出代价。 JRuby 存在性能问题,因为在构造 Object 时使用了 volatile 变量。使用数组作为临时参考的选项对我来说似乎已经足够了,而且我已经看到它不止一次使用了。
  • 我不认为这令人困惑,但这是正常行为。值类型存在于堆栈上,引用类型存在于堆上,但对引用类型的引用也存在于堆栈上。因此,您始终使用堆栈上的值进行操作。我相信这里的问题是:是否可以从堆栈中传递指向也位于堆栈上的其他值的引用。或者传递其他引用的引用,这些引用指向堆上的某个值?
  • 这是我很棒的信息。我一直在使用数组,但这似乎是为这项工作而构建的。但是,如果在性能关键循环中,我不知道这个与数组的性能
【解决方案2】:

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

即你的方法改变了它传递的对象的内部状态,然后调用者可以看到。

【讨论】:

    【解决方案3】:

    在 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 方法,它不可变
    • 为什么不使用AtomicReference(对于非原始人)?还是AtomicSomething(例如:AtomicBoolean)用于基元?
    • 不幸的是, 不接受原始类型。想要这样做:Ref
    • @jk7 必须使用int 而不是Integerbool 而不是Boolean 等真的足够重要吗?也就是说,包装类(如果您担心语法,它们会自动解包)不起作用?
    【解决方案4】:

    我可以通过引用传递参数吗 爪哇?

    没有。

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

    注意:

    对于原语来说,这很容易理解:你会得到一个值的副本。

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

    这一切都在这张照片中:

    【讨论】:

      【解决方案5】:

      Java 总是按值传递。

      当你传递一个原语时,它是值的副本,当你传递一个对象时,它是引用指针的副本。

      【讨论】:

        【解决方案6】:

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

        "...Java 中只有一种参数传递模式——按值传递——这让事情变得简单。 ..”

        【讨论】:

        • 语言大神的宣告应该是答案。
        • @ingyhere :The pronouncement of the language god should be declared the answer :不是真的。不管 Java 的创造者怎么想或相信什么,绝对的答案将来自语言规范的引用。
        • 事实上它在JLS, in section 8.4.1 中,JLS 引用 Gosling 作为第一作者:“当调用方法或构造函数时(第 15.12 节),实际参数表达式的值在方法或构造函数的主体执行之前初始化新创建的参数变量,每个声明的类型。"
        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

          查看我的回复:http://stackoverflow.com/a/9324155/1676736

          在那里,我使用了一个更简单的包装类想法。 我不喜欢将 setter/getter 作为标准。当没有理由掩埋一块田地时,我会将其“公开”。尤其是在这种情况下。

          但是,这将适用于除原始或多参数/类型返回之外的所有返回:

          public class Ref<T> {
              public T val;
          }
          

          不过,我想您可以添加更多类型参数。但我认为创建一个适合用途的内部静态类会更容易:

          public static class MyReturn {
              public String name;
              public int age;
              public double salary;
          }
          

          这将在您因其他原因不需要它时使用。

          MyReturn mRtn = new MyReturn();
          
          public void myMethod(final MyReturn mRtn){
              mRtn.name = "Fred Smith";
              mRtn.age = 32;
              mRtn.salary = 100000.00;
          }
          
          System.out.println(mRtn.name + " " +mRtn.age + ": $" + mRtn.salary);
          

          【讨论】:

            猜你喜欢
            • 2014-08-22
            • 2011-04-09
            • 2020-02-11
            • 2015-05-29
            • 2015-09-15
            • 1970-01-01
            • 2017-09-25
            • 1970-01-01
            相关资源
            最近更新 更多