简体   繁体   English

如何在 Java 中模拟通过引用传递?

[英]How can I simulate pass by reference in Java?

I'm a complete Java noob.我是一个完整的 Java 菜鸟。 I know that Java treats all parameters as pass by value and there are several other threads where people explain this.我知道 Java 将所有参数视为按值传递,并且还有其他几个线程对此进行了解释。

For example, in C++ I can do:例如,在 C++ 中,我可以这样做:

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

Which will output 3. I know that in Java, all parameters are passed by value and thus you can not manipulate the parameter passed in. Is there a standard way to simulate pass by reference in Java?哪个将输出 3。我知道在 Java 中,所有参数都是按值传递的,因此您无法操纵传入的参数。在 Java 中是否有模拟通过引用传递的标准方法? Is there no way to call a function that manipulates a variable passed in?有没有办法调用一个函数来操作传入的变量? It's tough for me to wrap my head around the idea of there being no way to do this.我很难理解没有办法做到这一点的想法。

The primary way you can simulate passing a reference is to pass a container that holds the value.模拟传递引用的主要方法是传递一个保存值的容器。

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

Since in Java, it is references to objects that are passed by value (the object itself is never passed at all), setting ref to 3 in makeAThree changes the same object referred to by myInt in main() .由于在Java中,它是由值(对象本身从不在全部通过)传递对象的引用,设置ref3makeAThree改变相同的对象称为由myIntmain()

Disclaimer: Reference isn't a class you can just use with out-of-the-box Java.免责声明: Reference不是一个可以与开箱即用的 Java 一起使用的类。 I'm using it here as a placeholder for any other object type.我在这里使用它作为任何其他对象类型的占位符。 Here's a very simple implementation:这是一个非常简单的实现:

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

Edit编辑

That's not to say it's great practice to modify the arguments to your method.这并不是说修改方法的参数是一种很好的做法。 Often this would be considered a side-effect.通常这会被认为是副作用。 Usually it is best practice to limit the outputs of your method to the return value and this (if the method is an instance method).通常最佳做法是将方法的输出限制为返回值和this (如果方法是实例方法)。 Modifying an argument is a very "C" way of designing a method and doesn't map well to object-oriented programming.修改参数是设计方法的一种非常“C”的方式,不能很好地映射到面向对象的编程。

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

Java pass everything by value, if it's an object then what would be passed is the reference value of the object. Java通过值传递一切,如果它是一个对象,那么将传递的是对象的引用值。 It's like,就像是,

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

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

above code will print 4, because it's passed by value上面的代码将打印 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;
    }

Above code will still print 4, because the object is passed by value and the reference to the object is passed here, even it's changed inside the method it won't reflect to the method 'someMethod'.上面的代码仍然会打印 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;
    }

here also it passes the object by value, and this value will be the reference to the object.这里它也按值传递对象,这个值将是对对象的引用。 So when you change some field of this object it will reflect to the all the places where the object is referred.因此,当您更改此对象的某些字段时,它将反映到引用该对象的所有位置。 Hence it would print 5. So this can be the way you can use to do what you want.因此它会打印 5。所以这可以是你可以用来做你想做的事情的方式。 Encapsulate the value in an object and pass it to the method where you want to change it.将值封装在一个对象中,并将其传递给要更改它的方法。

I ran some of the various scenarios above.我运行了上面的一些不同场景。

Yes, if you wanted to change a value outside of the function without returning the same primitive, you'd have to pass it a single unit array of that primitive.是的,如果您想在不返回相同原语的情况下更改函数外部的值,则必须向它传递该原语的单个单元数组。 HOWEVER, in Java, Array's are all internal objects.但是,在 Java 中,Array 都是内部对象。 You please note that if you pass 'value' by name to the println() there is no compile error and it prints hashes because of the toString() native to the internal array class.请注意,如果按名称将“值”传递给println()则不会出现编译错误,并且由于内部数组类的toString()原生,它会打印哈希值。 You will note that those names change as they print (put it in a long loop and watch).您会注意到这些名称在打印时会发生变化(将其放入一个长循环中并观察)。 Sadly, Java hasn't gotten the idea that we WOULD like a protected yet physically static address space available to us for certain reasons.可悲的是,Java 还没有意识到出于某些原因,我们会想要一个受保护但物理上静态的地址空间可供我们使用。 It would hurt Java's security mechanisms though.不过,这会损害 Java 的安全机制。 The fact that we can't depend on known addresses means that it's harder to hack at that.我们不能依赖已知地址的事实意味着更难破解。 Java performance is fantastic because we have fast processors. Java 性能非常棒,因为我们有快速的处理器。 If you need faster or smaller, that's for other languages.如果您需要更快或更小,那就是其他语言。 I remember this from way back when in 1999 reading an article in Dobbs just about this argument.我记得这要追溯到 1999 年在 Dobbs 上读到的一篇关于这个论点的文章。 Since it's a web aware language meant to function online, this was a big design concession to security.由于它是一种旨在在线运行的网络感知语言,因此这是对安全性的重大设计让步。 Your PC in 1999 had 64mb to 256mb of RAM and ran around 800mhz Today, your mobile device has 2 to 8 times that ram and is 200-700mhz faster and does WAY more ops per tick, and Java is the preferred language for Android, the dominant OS by unit sales (iOS still rocks, i gotta learn Objective C someday i guess, hate the syntax i've seen though). 1999 年你的 PC 有 64mb 到 256mb 的内存,运行速度大约为 800mhz 今天,你的移动设备的内存是它的 2 到 8 倍,速度快 200-700mhz,并且每滴答执行更多的操作,Java 是 Android 的首选语言,单位销售额占主导地位的操作系统(iOS 仍然很摇滚,我想有一天我必须学习 Objective C,但我讨厌我见过的语法)。

If you passed int[] instead of int to this code you get 5 back from someMethod() calling it.如果你将int[]而不是 int 传递给这段代码,你会从someMethod()调用它得到5


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

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

}

This is a confusing selection from above.这是一个令人困惑的选择。 The code WOULD work if the author hadn't hidden the passed variable by declaring a local variable of the same name.如果作者没有通过声明同名的局部变量来隐藏传递的变量,则代码将起作用。 OFCOURSE this isn't going to work, ignore the following example cited from above for clarity.当然这不会起作用,为了清楚起见,请忽略上面引用的以下示例。


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

Above code will still print 4 , because the passed object is HIDDEN FROM SCOPE by the local declaration.上面的代码仍然会打印4 ,因为传递的对象是本地声明的 HIDDEN FROM SCOPE 。 Also, this is inside a method, so I think even calling this and super wouldn't clarify it properly.此外,这是在一个方法中,所以我认为即使调用 this 和 super 也不会正确地澄清它。


If it weren't hidden locally in the method, then it would have changed the value of the object passed externally.如果它没有在方法中本地隐藏,那么它就会改变外部传递的对象的值。

One quick way to achieving simulate passing by reference is to move the arguments to member variables of the enclosing class .实现模拟按引用传递的一种快速方法是将参数移动到封闭类的成员变量中

Although there are multiple ways to do it such as using a class or array wrapper or moving them to the function return type, the code may not turn out clean.尽管有多种方法可以做到这一点,例如使用类或数组包装器或将它们移动到函数返回类型,但代码可能不会干净。 If you are like me, the reason to ask such a question is that a piece of Java code has already been coded in a C++ way (which does not work) and a quick fix is needed.如果你和我一样,问这样一个问题的原因是一段 Java 代码已经用 C++ 方式编码(不起作用),需要快速修复。 For example, in a recursion program such as depth-first-search, we may need to keep multiple variables in the C++ recursion function's argument list such as search path, flags whether the search should end.例如,在诸如深度优先搜索的递归程序中,我们可能需要在 C++ 递归函数的参数列表中保留多个变量,例如搜索路径、标志是否应该结束搜索。 If you are in such a situation, the quickest fix is to make these argument variables into class member variables.如果您遇到这种情况,最快的解决方法是将这些参数变量变成类成员变量。 Take care of the variable life cycle though and reset their values when necessary.不过要注意变量生命周期,并在必要时重置它们的值。

To accomplish the changing of a primitive variable in a method there are 2 basic options :要完成方法中原始变量的更改,有两个基本选项:

1) If you want to change values on a primitive in a different method you can wrap the primitive in a "java bean" object, which will be essentially like a pointer. 1)如果您想以不同的方法更改原语上的值,您可以将原语包装在“java bean”对象中,该对象本质上就像一个指针。

Or或者

2) You can use an AtomicInteger/AtomicLong class which are used to concurrency, when many threads might need to modify a variable....so the variables has to have state that is consistent. 2)您可以使用用于并发的 AtomicInteger/AtomicLong 类,当许多线程可能需要修改变量时......因此变量必须具有一致的状态。 Theses classes wrap primitives for you.这些类为您包装了原语。

Warning : you are usually better off returning the new value, rather than setting/editting it internally in a method, from a maintainability standpoint ..警告:从可维护性的角度来看,您通常最好返回新值,而不是在方法内部设置/编辑它。

Java is pass-by-value that mean pass-by-copy . Java 是按值传递,意思是按复制传递 We cannot do arithmetic on a reference variable as in C++.我们不能像在 C++ 中那样对引用变量进行算术运算。 In-short Java is not C/C++.简而言之,Java 不是 C/C++。 So as a workaround you can do this:因此,作为一种解决方法,您可以这样做:

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

}
static int makeAThree(int n)
{
   return n = 3;
}

PS Just made the method static so as to use it without class object. PS只是将方法设为static以便在没有类对象的情况下使用它。 No other intention.没有其他意图。 ;) ;)

这会让你更好地理解。

Java uses pass by value for everything . Java 对一切都使用按值传递。

As far as I understand you are not really sure if you can modify a variable passed in.据我了解,您不确定是否可以修改传入的变量。

When you pass an object to a method, and if you use that object within that method, you are actually modifying that object.当您将一个对象传递给一个方法,并且如果您在该方法中使用该对象时,您实际上是在修改该对象。 However you are modifying that object on a copy of it which still points to the same object.但是,您正在修改该对象的副本,该副本仍然指向同一个对象。 So actually when you pass an object to a method, you can modify it.所以实际上当你将一个对象传递给一个方法时,你可以修改它。

Once again, everything in java is pass by value.period.再一次,java 中的一切都是通过 value.period 传递的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM