【问题标题】:How can I simulate pass by reference in Java?如何在 Java 中模拟通过引用传递?
【发布时间】:2011-10-25 04:05:41
【问题描述】:

我是一个完整的 Java 菜鸟。我知道 Java 将所有参数视为按值传递,并且还有其他几个线程可以解释这一点。

例如,在 C++ 中我可以这样做:

void makeAThree(int &n)
{
   n = 3;
}
int main()
{
   int myInt = 4;
   makeAThree(myInt);
   cout << myInt;
}

哪个会输出3。我知道在Java中,所有参数都是按值传递的,因此您不能操纵传入的参数。在Java中模拟按引用传递的标准方法有吗?有没有办法调用一个操作传入的变量的函数?我很难接受没有办法做到这一点的想法。

【问题讨论】:

  • Java 使用按值传递。为什么要模拟它?你可能错过了一点。你能更准确地说你想要什么吗?
  • 附带说明,在 Java 中,这样的代码可能看起来像 n = makeAThree(n)

标签: java pass-by-reference


【解决方案1】:

您可以模拟传递引用的主要方法是传递一个保存该值的容器。

static void makeAThree(Reference<Integer> ref)
{
   ref.set(3);
}

public static void main(String[] args)
{
  Reference<Integer> myInt = new Reference<>(4);
  makeAThree(myInt);
  System.out.println(myInt.get());
}

由于在Java中,它是对按值传递的对象的引用(对象本身根本不会传递),将makeAThree中的ref设置为3更改相同myIntmain() 中引用的对象。

免责声明:Reference 不是一个可以直接用于开箱即用 Java 的类。我在这里使用它作为任何其他对象类型的占位符。这是一个非常简单的实现:

public class Reference<T> {
    private T referent;

    public Reference(T initialValue) {
       referent = initialValue;
    }

    public void set(T newVal) {
       referent = newVal;
    }

    public T get() {
       return referent;
    }
}

编辑

这并不是说修改方法的参数是一种很好的做法。通常这会被认为是副作用。通常最好的做法是将方法的输出限制为返回值和this(如果方法是实例方法)。修改参数是一种非常“C”的方法设计方式,不能很好地映射到面向对象的编程。

【讨论】:

  • “修改参数是一种非常“C”的设计方法的方式,不能很好地映射到面向对象的编程。”我认为它与 OOP 完全正交,在许多情况下,它可能是针对给定问题的最佳设计解决方案。
【解决方案2】:

您可以使用大小为 1 的数组

【讨论】:

    【解决方案3】:

    Java 通过值传递所有内容,如果它是一个对象,那么将传递的是对象的引用值。就像,

    void someMethod()
    {
       int value = 4;
       changeInt(value);
       System.out.printlin(value); 
    }
    
    public void changeInt(int x)
    {
       x = x + 1;
    }
    

    上面的代码会打印 4,因为它是按值传递的

    class SomeClass
        {
           int x;
        }
    
    void someMethod()
        {
           SomeClass value = new SomeClass();
           value.x = 4;
           changeCls(value);
           System.out.printlin(value.x); 
        }
    
        public void changeCls(SomeClass cls)
        {
            cls = new SomeClass();
            cls.x = 5;
        }
    

    上面的代码还是会打印4,因为对象是按值传递的,而对对象的引用是在这里传递的,即使在方法内部改变了也不会反映到方法'someMethod'。

    class SomeClass
    {
       int x;
    }
    
    void someMethod()
        {
           SomeClass value = new SomeClass();
           value.x = 4;
           changeCls(value);
           System.out.printlin(value.x); 
        }
    
        public void changeCls(SomeClass cls)
        {
            cls.x = cls.x + 1;
        }
    

    这里也是按值传递对象,这个值就是对象的引用。因此,当您更改此对象的某些字段时,它将反映到引用该对象的所有位置。因此它会打印 5。所以这可以是你可以用来做你想做的事的方式。将值封装在对象中,并将其传递给要更改它的方法。

    【讨论】:

      【解决方案4】:

      我在上面运行了一些不同的场景。

      是的,如果您想在函数外部更改一个值而不返回相同的原语,则必须将其传递给该原语的单个单位数组。但是,在 Java 中,数组都是内部对象。请注意,如果您按名称将“值”传递给println(),则不会出现编译错误,并且由于toString() 是内部数组类的本机,它会打印散列。您会注意到这些名称在打印时会发生变化(将其放在一个长循环中并观察)。遗憾的是,由于某些原因,Java 还没有想到我们会喜欢一个受保护但物理上静态的地址空间。不过,这会损害 Java 的安全机制。我们不能依赖已知地址这一事实意味着更难破解它。 Java 性能非常出色,因为我们拥有快速的处理器。如果您需要更快或更小,那就是其他语言。我记得早在 1999 年阅读 Dobbs 的一篇关于这个论点的文章时。由于它是一种用于在线运行的网络感知语言,因此这是对安全性的重大设计让步。您 1999 年的 PC 有 64mb 到 256mb 的 RAM,运行速度约为 800mhz 今天,您的移动设备的内存是其 2 到 8 倍,速度提高了 200-700mhz,并且每次执行的操作次数要多得多,而且 Java 是 Android 的首选语言,Android 是按销量计算的主导操作系统(iOS 仍然很糟糕,我必须学习 Objective我想有一天C,讨厌我见过的语法)。

      如果您将 int[] 而不是 int 传递给此代码,您会从 someMethod() 调用它得到 5


      public void changeInt(int x)
      {
         x = x + 1;
      } 
      

      public void changeInt(int[] x)
      {
         x[0] += 1; 
      
      }
      

      这是一个令人困惑的选择。如果作者没有通过声明同名的局部变量来隐藏传递的变量,则代码将起作用。当然,这是行不通的,为了清楚起见,请忽略上面引用的以下示例。


        public void changeCls(SomeClass cls)
         {
             cls = new SomeClass();
             cls.x = 5;
         }
      

      上面的代码仍然会打印4,因为传递的对象被本地声明隐藏在范围之外。另外,这是在方法内部,所以我认为即使调用 this 和 super 也无法正确说明。


      如果它没有在方法中本地隐藏,那么它会更改外部传递的对象的值。

      【讨论】:

        【解决方案5】:

        要完成方法中原始变量的更改,有两个基本选项:

        1) 如果您想以不同的方法更改原语的值,您可以将原语包装在“java bean”对象中,该对象本质上类似于指针。

        或者

        2) 您可以使用用于并发的 AtomicInteger/AtomicLong 类,当许多线程可能需要修改变量时......所以变量必须具有一致的状态。这些类为您包装原语。

        警告:从可维护性的角度来看,您通常最好返回新值,而不是在方法内部设置/编辑它..

        【讨论】:

          【解决方案6】:

          Java 是 pass-by-value,意思是 pass-by-copy。我们不能像在 C++ 中那样对引用变量进行算术运算。简而言之,Java 不是 C/C++。 因此,作为一种解决方法,您可以这样做:

          public static void main (String [] args) {
              int myInt = 4;
              myInt = makeAThree(myInt);
          
          }
          static int makeAThree(int n)
          {
             return n = 3;
          }
          

          附:刚刚制作了方法static,以便在没有类对象的情况下使用它。没有其他意图。 ;)

          【讨论】:

            【解决方案7】:

            实现模拟通过引用传递的一种快速方法是将参数移动到封闭类的成员变量

            尽管有多种方法可以做到这一点,例如使用类或数组包装器或将它们移动到函数返回类型,但代码可能并不干净。如果你和我一样,问这样一个问题的原因是一段 Java 代码已经以 C++ 方式编码(这不起作用),需要快速修复。例如,在深度优先搜索等递归程序中,我们可能需要在 C++ 递归函数的参数列表中保留多个变量,例如搜索路径、是否结束搜索的标志。如果您处于这种情况,最快的解决方法是将这些参数变量变成类成员变量。不过要注意变量的生命周期,并在必要时重置它们的值。

            【讨论】:

              【解决方案8】:

              Java 对一切使用按值传递。

              据我了解,您不确定是否可以修改传入的变量。

              当您将对象传递给方法时,如果您在该方法中使用该对象,您实际上是在修改该对象。但是,您正在修改它的副本上的该对象,该副本仍指向同一对象。所以实际上当你将一个对象传递给一个方法时,你可以修改它。

              再一次,java 中的一切都是通过 value.period 传递的。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2016-02-29
                • 2017-02-19
                • 2011-01-31
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-02-09
                相关资源
                最近更新 更多