1.什么是引用?
如下表达式:
Student s1 = new Student();
它代表Student是类,s1是引用,s1不是对象,new Student()才是对象,s1引用指向new Student()这个对象。
在JAVA里,“=”不能被看成是一个赋值语句,它不是在把一个对象赋给另外一个对象,它的执行过程实质上是将右边对象的地址传给了左边的引用,使得左边的引用指向了右边的对象
。Java表面上看起来没有指针,但它的引用其实质就是一个指针,引用里面存放的并不是对象,而是该对象的地址,使得该引用指向了对象。
Student s2;
它代表Student是类,s2是引用,s2不是对象,s2所指向的对象为空null;
s2 = s1;
它代表,s2是引用,s1也是引用,s1所指向的对象的地址传给了s2(传址),使得s2和s1指向了同一对象。
综上所述, 在初始化时,“=”语句左边的是引用,右边new出来的是对象。 在后面的左右都是引用的“=”语句时,左右的引用同时指向了右边引用所指向的对象。
再所谓实例,其实就是对象的同义词。
2.什么是值传递,什么是引用传递。为什么说Java中只有值传递?
错误理解:传递的参数如果是基本类型,那就是值传递,如果是对象,那就是引用传递。
-
值传递
是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响
到实际参数。 -
引用传递
是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响
到实际参数。
举三个例子:
public static void main(String[] args) {
int i = 10;
pass(i);
System.out.println("print in main , i is " + i);
}
public void pass(int j) {
j = 20;//注意这里
System.out.println("print in pass , j is " + j);
}
print in pass , j is 20
print in main , i is 10
有人得到结论:Java的方法传递是值传递。
但是:
public static void main(String[] args) {
User user= new User();
user.setName("User");
user.setGender("W");
pass(user);
System.out.println("print in main , user is " + user);
}
public void pass(User user0) {
user0.setName("Jxy");//注意这里
System.out.println("print in pass , user0 is " + user0);
}
print in pass , user is User{name='Jxy', gender='W'}
print in main , user is User{name='Jxy', gender='W'}
有人得出一个新的结论:Java的方法中,在传递普通类型的时候是值传递,在传递对象类型的时候是引用传递。
但是,这种表述仍然是错误的。
public static void main(String[] args) {
String name = "Jxy";
pass(name);
System.out.println("print in main , name is " + name);
}
public void pass(String name) {
name = "Hello";//注意这里
System.out.println("print in pass , name is " + name);
}
print in pass , name is Hello
print in main , name is Jxy
同样传递了一个对象,但是原始参数的值并没有被修改!
举了三个例子,表现的结果却不一样,其实,上面的概念没有错,只是代码的例子有问题。
为什么说例子错了----->
- 你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
- 你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。
- 但是,不管上面哪种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?而我们在pass方法中,改变user对象的name属性的值的时候,就是在“砸电视”。你改变的不是那把钥匙,而是钥匙打开的房子。
所以第二个例子应该是这样的:
public static void main(String[] args) {
User user= new User();
user.setName("User");
user.setGender("W");
pass(user);
System.out.println("print in main , user is " + user);
}
public void pass(User user) {
user = new User();//注意这里
user.setName("Jxy");
user.setGender("W");
System.out.println("print in pass , user is " + user);
}
print in pass , user is User{name='Jxy', gender='W'}
print in main , user is User{name='User', gender='W'}
这里是把实参引用的地址复制了一份,传递给了形参。所以,上面的参数其实是值传递,把实参的值(引用地址)传递给了形参,二者指向的对象是同一个
- 错误的例子中是把他们指向的
对象里的内容
改了,所以,只要是指向这个对象的引用都受到了影响。 - 正确的例子中是把pass里的引用指向换了对象,当回到main,main里的引用指向并没有变化,仍然是原来那个对象,各指各的,二者无关。
至于传String对象和User对象的表现结果不一样,是因为String的不可变性,我们在pass方法中使用name = “Hello”;直接改变了name的引用地址。因为这段代码,其实是去常量池中寻找"Hello",有–>返回"Hello"的引用,没有–>常量池中创建"Hello",返回"Hello" 的地址。<详情请看String>
所以:
在判断实参内容有没有受影响的时候,要看传的的是什么,如果你传递的是个地址,那么就看这个地址的变化会不会有影响,而不是看地址指向的对象的变化。就像钥匙和房子的关系。
即:Java中是值传递的,对于对象参数,值的内容是对象的引用,即对象的地址;对于基础数据参数,值的内容就是一个值
ref: https://blog.csdn.net/qq_41854911/article/details/122149948