我一直认为Java使用的是按引用传递。然而,我看了一篇博客文章,声称Java使用的是按值传递。我不认为我理解作者所做的区分。
这是什么意思?
我一直认为Java使用的是按引用传递。然而,我看了一篇博客文章,声称Java使用的是按值传递。我不认为我理解作者所做的区分。
这是什么意思?
Java采用的是按照常量引用传递参数的方式,也就是说传递的是引用的一个拷贝,实际上是传递的按值传递。如果类是可变的,你可以改变引用的内容,但是你不能改变这个引用本身,也就是说地址不能被改变,因为是按值传递的,但是被地址指向的内容是可以被改变的。对于不可变类来说,引用的内容也无法被改变。
final
。 - user207421毫无疑问,Java是“传值”的,这个观点毋庸置疑。由于Java大部分是面向对象的,并且对象使用引用来工作,很容易让人混淆并认为它是“按引用传递”。
按值传递意味着您将值传递给方法,如果方法更改了传递的值,则真实实体不会改变。另一方面,“按引用传递”意味着将引用传递给方法,如果方法对其进行更改,则传递的对象也会更改。
在Java中,通常当我们将对象传递给方法时,我们实际上是传递对象作为值的引用,因为Java使用引用和地址来处理堆中的对象。
但是,为了测试它是否真正是按值传递或按引用传递,您可以使用基本类型和引用:
@Test
public void sampleTest(){
int i = 5;
incrementBy100(i);
System.out.println("passed ==> "+ i);
Integer j = new Integer(5);
incrementBy100(j);
System.out.println("passed ==> "+ j);
}
/**
* @param i
*/
private void incrementBy100(int i) {
i += 100;
System.out.println("incremented = "+ i);
}
输出结果为:
incremented = 105
passed ==> 5
incremented = 105
passed ==> 5
所以在这两种情况下,方法内部发生了什么并不会改变真正的对象,因为传递的是该对象的值,而不是对象本身的引用。
但是当您将自定义对象传递给方法并且方法对其进行更改时,它将同时更改真正的对象,因为即使您传递了对象,也将其引用作为值传递给方法。让我们尝试另一个例子:
@Test
public void sampleTest2(){
Person person = new Person(24, "John");
System.out.println(person);
alterPerson(person);
System.out.println(person);
}
/**
* @param person
*/
private void alterPerson(Person person) {
person.setAge(45);
Person altered = person;
altered.setName("Tom");
}
private static class Person{
private int age;
private String name;
public Person(int age, String name) {
this.age=age;
this.name =name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Person [age=");
builder.append(age);
builder.append(", name=");
builder.append(name);
builder.append("]");
return builder.toString();
}
}
在这种情况下,输出为:
Person [age=24, name=John]
Person [age=45, name=Tom]
通过传递参数来共享数据的方法有两种:
按引用传递:调用者和被调用者使用同一个变量作为参数。
按值传递:调用者和被调用者具有相同值的两个独立变量。
Java使用按值传递
Java在存储变量时遵循以下规则:
使用基本数据类型的示例:
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
}
}
关键的核心知识必须是引用所指向的对象,
当将一个对象引用传递给一个方法时,该引用本身会通过按值调用(call-by-value)进行传递。然而,由于被传递的值是指向对象的,因此该值的副本仍将指向相应参数所引用的同一对象。
《Java入门指南》第六版,Herbert Schildt
看一下这段代码。这段代码不会抛出NullPointerException
,它会打印出"Vinay"。
public class Main {
public static void main(String[] args) {
String temp = "Vinay";
print(temp);
System.err.println(temp);
}
private static void print(String temp) {
temp = null;
}
}
如果 Java 是按引用传递的,那么当引用被设置为Null时,它应该抛出NullPointerException异常。长话短说:
结束了。
(2) 太容易了。如果你想了解 (1) 意味着什么,想象一下你有一个类名为 Apple:
class Apple {
private double weight;
public Apple(double weight) {
this.weight = weight;
}
// getters and setters ...
}
然后,当您将此类的实例传递给主方法时:
class Main {
public static void main(String[] args) {
Apple apple = new Apple(3.14);
transmogrify(apple);
System.out.println(apple.getWeight()+ " the goose drank wine...";
}
private static void transmogrify(Apple apple) {
// does something with apple ...
apple.setWeight(apple.getWeight()+0.55);
}
}
哦,但是你可能已经知道了,你对这样做会发生什么很感兴趣:
class Main {
public static void main(String[] args) {
Apple apple = new Apple(3.14);
transmogrify(apple);
System.out.println("Who ate my: "+apple.getWeight()); // will it still be 3.14?
}
private static void transmogrify(Apple apple) {
// assign a new apple to the reference passed...
apple = new Apple(2.71);
}
}
与其他一些语言不同,Java 不允许你在传递参数时选择值传递或者引用传递。
所有的参数都是按值传递的。
方法调用可以传递两种类型的值给一个方法:
对象本身不能被传递给方法
。当一个方法修改一个基本类型的参数时,对参数的修改不会影响调用方法中原始实际参数的值。
对于引用类型参数也是如此。如果你修改了一个引用类型参数,让它引用另一个对象,那么只有该参数会引用新的对象——在调用方法中存储的引用仍然指向原始对象。
一个简单的测试可以检查一个语言是否支持按引用传递,只需编写一个传统的交换函数来测试。 您能否在Java中编写一个传统的swap(a,b)方法/函数?
传统的swap方法或函数接受两个参数并将它们交换,以便在函数外部更改传递到函数中的变量。其基本结构如下:
(非Java) 基本交换函数结构
swap(Type arg1, Type arg2) {
Type temp = arg1;
arg1 = arg2;
arg2 = temp;
}
如果您能够在您的语言中编写这样的方法/函数,以便调用
Type var1 = ...;
Type var2 = ...;
swap(var1,var2);
实际上,如果要交换变量var1和var2的值,该语言支持传递引用。但是,由于Java只支持传递值而不支持指针或引用,因此不允许这样做。
现在,人们喜欢无休止地争论“按引用传递”是否是正确描述Java等编程语言实际操作的方式。重点是:
在我的书中,这被称为按引用传递。