如何为 Java 中的原语执行类似于引用传递的操作

这个 Java 代码:

public class XYZ {
public static void main(){
int toyNumber = 5;
XYZ temp = new XYZ();
temp.play(toyNumber);
System.out.println("Toy number in main " + toyNumber);
}


void play(int toyNumber){
System.out.println("Toy number in play " + toyNumber);
toyNumber++;
System.out.println("Toy number in play after increement " + toyNumber);
}
}

将输出以下内容:

Toy number in play 5
Toy number in play after increement 6
Toy number in main 5

在 C + + 中,我可以通过引用传递 toyNumber变量来避免隐藏,例如创建一个与下面相同的变量的副本:

void main(){
int toyNumber = 5;
play(toyNumber);
cout << "Toy number in main " << toyNumber << endl;
}


void play(int &toyNumber){
cout << "Toy number in play " << toyNumber << endl;
toyNumber++;
cout << "Toy number in play after increement " << toyNumber << endl;
}

C + + 的输出如下:

Toy number in play 5
Toy number in play after increement 6
Toy number in main 6

我的问题是——如果使用 Java 是通过值传递的,而不是通过引用传递的,在 Java 中获得与 C + + 代码相同的输出的等价代码是什么?

222307 次浏览

在 Java 中不能通过引用传递原语。当然,所有对象类型的变量实际上都是指针,但我们称之为“引用”,它们也总是通过值传递。

在确实需要通过引用传递原语的情况下,人们有时会将参数声明为原语类型的数组,然后将单元素数组作为参数传递。因此,可以传递一个引用 int [1] ,在该方法中,可以更改数组的内容。

做一个

class PassMeByRef { public int theValue; }

然后传递对其实例的引用。注意,最好避免使用通过其参数变化状态的方法,尤其是在并行代码中。

你有几个选择,其中最有意义的一个取决于你想要做什么。

选项1: 使 toyNumber 成为类中的公共成员变量

class MyToy {
public int toyNumber;
}

然后将对 MyToy 的引用传递给您的方法。

void play(MyToy toy){
System.out.println("Toy number in play " + toy.toyNumber);
toy.toyNumber++;
System.out.println("Toy number in play after increement " + toy.toyNumber);
}

选项2: 返回值而不是通过引用传递

int play(int toyNumber){
System.out.println("Toy number in play " + toyNumber);
toyNumber++;
System.out.println("Toy number in play after increement " + toyNumber);
return toyNumber
}

这个选择需要对 main 中的 callsite 进行一个小小的更改,以便它读取 toyNumber = temp.play(toyNumber);

选择3: 使它成为类或静态变量

如果这两个函数是同一个类或类实例上的方法,则可以将 toyNumber 转换为类成员变量。

选项4: 创建一个 int 类型的单个元素数组并传递它

This is considered a hack, but is sometimes employed to return values from inline class invocations.

void play(int [] toyNumber){
System.out.println("Toy number in play " + toyNumber[0]);
toyNumber[0]++;
System.out.println("Toy number in play after increement " + toyNumber[0]);
}
public static void main(String[] args) {
int[] toyNumber = new int[] {5};
NewClass temp = new NewClass();
temp.play(toyNumber);
System.out.println("Toy number in main " + toyNumber[0]);
}


void play(int[] toyNumber){
System.out.println("Toy number in play " + toyNumber[0]);
toyNumber[0]++;
System.out.println("Toy number in play after increement " + toyNumber[0]);
}

Java 不是 参考电话而是 只按值调用

但是所有对象类型的变量实际上都是指针。

因此,如果您使用一个可变对象,您将看到您想要的行为

public class XYZ {


public static void main(String[] arg) {
StringBuilder toyNumber = new StringBuilder("5");
play(toyNumber);
System.out.println("Toy number in main " + toyNumber);
}


private static void play(StringBuilder toyNumber) {
System.out.println("Toy number in play " + toyNumber);
toyNumber.append(" + 1");
System.out.println("Toy number in play after increement " + toyNumber);
}
}

此代码的输出:

run:
Toy number in play 5
Toy number in play after increement 5 + 1
Toy number in main 5 + 1
BUILD SUCCESSFUL (total time: 0 seconds)

您也可以在标准库中看到这种行为。 例如 Collections.sort () ; Collections.shuffle () ; 这些方法不返回新列表,而是修改其参数对象。

    List<Integer> mutableList = new ArrayList<Integer>();


mutableList.add(1);
mutableList.add(2);
mutableList.add(3);
mutableList.add(4);
mutableList.add(5);


System.out.println(mutableList);


Collections.shuffle(mutableList);


System.out.println(mutableList);


Collections.sort(mutableList);


System.out.println(mutableList);

此代码的输出:

run:
[1, 2, 3, 4, 5]
[3, 4, 1, 5, 2]
[1, 2, 3, 4, 5]
BUILD SUCCESSFUL (total time: 0 seconds)

要获得快速解决方案,可以使用 AtomicInteger 或任何原子变量,这些变量允许您使用内置方法更改方法内部的值。下面是示例代码:

import java.util.concurrent.atomic.AtomicInteger;




public class PrimitivePassByReferenceSample {


/**
* @param args
*/
public static void main(String[] args) {


AtomicInteger myNumber = new AtomicInteger(0);
System.out.println("MyNumber before method Call:" + myNumber.get());
PrimitivePassByReferenceSample temp = new PrimitivePassByReferenceSample() ;
temp.changeMyNumber(myNumber);
System.out.println("MyNumber After method Call:" + myNumber.get());




}


void changeMyNumber(AtomicInteger myNumber) {
myNumber.getAndSet(100);


}


}

产出:

MyNumber before method Call:0


MyNumber After method Call:100

我们在 checkPassByValue 中设置 a = 5,但是它没有反映在。 类似地,当我们传递一个人时,我们可以通过使用 Person.setName 来更新这个人所指向的数据,但是如果更改这个人并使其引用一个新的 Person () ,那么这个更新将不会反映在 main 函数中

class Person {
public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


private String name;


public Person(String name) {
this.name = name;
}


}


public class PassByValueVsReference {
public static void main(String[] args) {
int a = 3;
Person person = new Person("Foo");
checkPassByValue(a, person);
System.out.println(a);
System.out.println(person.getName());
}


public static void checkPassByValue(int number, Person person) {
number = 5;
person.setName("Foo-updated");
person = new Person("Foo_new reference");
}
}


//C++ equivalent of checkPassByValue(int ,Person *);


“传递...”在 JavaC中保留。除此之外,如果您打算更改作为引用给出的原语的包装实例,则可以通过反射来实现。例如 Integer

public class WrapperTest
{
static void mute(Integer a)
{
try
{
Field fValue = a.getClass().getDeclaredField("value");
fValue.setAccessible(true);
fValue.set(a, 6);
}
catch (Exception e)
{
e.printStackTrace();
}
}


public static void main(String[] args)
{
Integer z = 5;
mute(z);
System.out.println(z);
}


}

输出为 6,如 C + + 示例所示。反射是必需的,因为 Java 设计认为原语包装器是不可变的。否则,每个其他类都可以作为包装器,甚至像长度为1的 int[]这样的数组。