我一直以为Java使用pass-by-reference. 但是,我读了一篇博客文章,声称Java使用pass-by-value. 我不认为我明白作者所做的区别。

什么是解释?


当前回答

数据通过参数在函数之间共享,现在有两种方式通过参数:

通过参数 : 呼叫器和呼叫器使用相同的变量为参数. 通过值 : 呼叫器和呼叫器有两个独立的变量与相同的值。

Java 使用 Pass by Value

在传输原始数据时,它复制原始数据类型的值;在传输对象时,它复制对象的地址,并转移到转换方法变量。

Java 在存储变量中遵循以下规则:

原始和对象参考等本地变量在 Stack 记忆中创建,对象在 Heap 记忆中创建。

使用原始数据类型的例子:

public class PassByValuePrimitive {
    public static void main(String[] args) {
        int i=5;
        System.out.println(i);  //prints 5
        change(i);
        System.out.println(i);  //prints 5
    }
    
    
    private static void change(int i) {
        System.out.println(i);  //prints 5
        i=10;
        System.out.println(i); //prints 10
        
    }
}

使用对象的例子:

public class PassByValueObject {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("prem");
        list.add("raj");
        new PassByValueObject().change(list);
        System.out.println(list); // prints [prem, raj, ram]
        
    }
    
    
    private  void change(List list) {
        System.out.println(list.get(0)); // prem
        list.add("ram");
        list=null;
        System.out.println(list.add("bheem")); //gets NullPointerException
    }
}

其他回答

Java 编程语言仅通过值的论点,也就是说,您无法从所称方法中更改呼叫方法中的论点值。


但是,当一个对象例子作为论点转移到一种方法时,论点的价值不是对象本身,而是对象的参考。


对于许多人来说,这似乎是通过参考,行为上,它与通过参考有很多共同点,但是,有两个原因,这是不准确的。

第一,改变已转化为一种方法的能力仅适用于对象,而不是原始价值;第二,与对象类型变量相关的实际价值是对象的参考,而不是对象本身。


The following code example illustrates this point:
1 public class PassTest {
2
3   // Methods to change the current values
4   public static void changeInt(int value) {
5     value = 55;
6  }
7   public static void changeObjectRef(MyDate ref) {
8     ref = new MyDate(1, 1, 2000);
9  }
10   public static void changeObjectAttr(MyDate ref) {
11     ref.setDay(4);
12   }
13
14 public static void main(String args[]) {
15     MyDate date;
16     int val;
17
18     // Assign the int
19     val = 11;
20     // Try to change it
21     changeInt(val);
22     // What is the current value?
23     System.out.println("Int value is: " + val);
24
25 // Assign the date
26     date = new MyDate(22, 7, 1964);
27     // Try to change it
28     changeObjectRef(date);
29     // What is the current value?
30 System.out.println("MyDate: " + date);
31
32 // Now change the day attribute
33     // through the object reference
34     changeObjectAttr(date);
35     // What is the current value?
36 System.out.println("MyDate: " + date);
37   }
38 }

This code outputs the following:
java PassTest
Int value is: 11
MyDate: 22-7-1964
MyDate: 4-7-1964
The MyDate object is not changed by the changeObjectRef method;
however, the changeObjectAttr method changes the day attribute of the
MyDate object.

Java 总是使用 Call by Value. 这意味着该方法获得所有参数值的副本。

考虑下列三种情况:

1、试图改变原始变量

public static void increment(int x) { x++; }

int a = 3;
increment(a);

x 将复制一个值,并将增加 x,一个将保持相同的值

(二)试图改变对象的原始领域

public static void increment(Person p) { p.age++; }

Person pers = new Person(20); // age = 20
increment(pers);

p 将复制个体的参考值,并将增加年龄领域,变量是指相同的对象,所以年龄改变。

(三)试图改变参考变量的参考值

public static void swap(Person p1, Person p2) {
    Person temp = p1;
    p1 = p2;
    p2 = temp;
}

Person pers1 = new Person(10);
Person pers2 = new Person(20);
swap(pers1, pers2);

呼叫交换 p1 后,p2 复制 pers1 和 pers2 的参考值与值交换,因此 pers1 和 pers2 仍然相同。

因此,您只能在将参考值复制到该对象时更改对象的字段。

在我所知道的范围内,Java只知道值的呼叫,这意味着对于原始数据类型,您将使用复印件工作,对于对象,您将使用对象的参考复印件工作。

public static void swap(StringBuffer s1, StringBuffer s2) {
    StringBuffer temp = s1;
    s1 = s2;
    s2 = temp;
}


public static void main(String[] args) {
    StringBuffer s1 = new StringBuffer("Hello");
    StringBuffer s2 = new StringBuffer("World");
    swap(s1, s2);
    System.out.println(s1);
    System.out.println(s2);
}

这将人口Hello World而不是World Hello,因为在交换函数中,您使用复印件,这些复印件不会对主要的参考产生影响。

public static void appendWorld(StringBuffer s1) {
    s1.append(" World");
}

public static void main(String[] args) {
    StringBuffer s = new StringBuffer("Hello");
    appendWorld(s);
    System.out.println(s);
}

如果您将 StringBuffer 更改为 String,它只会产生 Hello 因为 String 是不可变的。

public static void appendWorld(String s){
    s = s+" World";
}

public static void main(String[] args) {
    String s = new String("Hello");
    appendWorld(s);
    System.out.println(s);
}

但是,你可以为 String 做一个插槽,这将使它能够与 Strings 一起使用:

class StringWrapper {
    public String value;

    public StringWrapper(String value) {
        this.value = value;
    }
}

public static void appendWorld(StringWrapper s){
    s.value = s.value +" World";
}

public static void main(String[] args) {
    StringWrapper s = new StringWrapper("Hello");
    appendWorld(s);
    System.out.println(s.value);
}

编辑:我认为这也是使用 StringBuffer 的理由,当涉及到“添加”两个线条时,因为你可以修改原始对象,你不能用像 String 这样的不可变的对象。

Java 只通过值,这是一个很简单的例子来验证这一点。

public void test() {
    MyClass obj = null;
    init(obj);
    //After calling init method, obj still points to null
    //this is because obj is passed as value and not as reference.
}
private void init(MyClass objVar) {
    objVar = new MyClass();
}

Book book = new Book("Effective Java");

在上面的例子中,参考变量是“书”存储在堆积中。 由新运营商创建的例子 ->新书(“有效Java”)存储在Heap中. ref变量“书”有在Heap中分配的对象地址。

此分類上一篇

public class PrimitiveTypeExample { 
    public static void main(string[] args) {
       int num = 10;
       System.out.println("Value before calling method: " + num);
       printNum(num);
       System.out.println("Value after calling method: " + num);
    }
    public static void printNum(int num){
       num = num + 10;
       System.out.println("Value inside printNum method: " + num);
    }
}

考虑,例如通过一个定义阶级的对象作为论点的例子。

此分類上一篇

此分類上一篇

因此,当任何东西作为方法论时,它总是是Stack实体 - 无论是原始还是参考变量. 我们永远不会通过在Heap中存储的东西. 因此,在Java,我们总是通过值在Stack中,它通过值。