1、问题发生场景
定义函数A,此时在A中调用函数function(a),在function中,通过查找数据库,对a进行了重新赋值,此时A中的对象a值并未修改,如下场景模拟所示:
public class test {
public static void main(String[] args) {
Student student = new Student("小王", 18);
function(student);
System.out.println("此时学生对象为:" + student.toString());
}
private static void function(Student student){
student = new Student("小明", 16);
}
}
此时结果输出:此时学生对象为:Student(name=小王, age=18)
尽管在function()中对student对象进行了重新赋值,但是在main函数中的student对象并未修改。
2、未修改值分析
Java是具有值传递的,在传递过程中对不会对原有值产生影响
值传递:在传递过程中,将实际参数复制一份到调用函数中,在函数中对值进行修改,不会影响到实际参数。
引用传递:在传递过程中,将实际参数的地址传递给调用函数,在函数中对值进行修改,会影响到原有参数。
所以在上述代码中,function()函数尽管对st进行了重新赋值,但不会影响main函数中的st对象,也就造成了实际开发中,查数据库进行重新赋值后,未修改对象的情况。
3、值传递测试
其中,student类包含name和age属性。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private Integer age;
}
3.1基本数据类型传递
public class test {
public static void main(String[] args) {
int i = 10;
function(i);
System.out.println("此时的对象i为:" + i);
}
private static int function(int i){
i = 20;
return i;
}
}
此时结果输出:此时的对象i为:10
在传递过程中,对参数j进行了修改,但不影响main中的i值,与Java值传递原理相符合。
3.2引用类型传递
3.2.1传递对象值没有修改
在以下中,传递student对象,但是main函数中的值并未修改,与Java值传递原理相符合
public class test {
public static void main(String[] args) {
Student student = new Student("小王", 18);
function(student);
System.out.println("此时学生对象为:" + student.toString());
}
private static Student function(Student student){
student = new Student("小明", 16);
return student;
}
}
此时结果输出:此时学生对象为:Student(name=小王, age=18)
3.2.2传递对象值被修改
在以下中,传递student对象,并调用set函数修改名字,后续值被修改。
public class test {
public static void main(String[] args) {
Student student = new Student("小王", 18);
function(student);
System.out.println("此时学生对象为:" + student.toString());
}
private static Student function(Student student){
student.setName("小明");
return student;
}
}
此时结果输出:此时学生对象为:Student(name=小明, age=18)
这是否与Java的值传递原理相违背呢?下面继续分析。
main函数中,传递的student对象值(此时值是对象的地址)。
拿到地址后,在function函数中,对该地址下的student对象中的name属性进行了修改,所以function中的setName会影响main中的student,他们指向堆中的地址是一致的。
这也可以分析,在“传递对象值不修改”中,为什么不会对main中的student对象产生影响。
private static Student function(Student student){
student = new Student("小明", 16);
return student;
}
该函数中,直接把function中的student对象指向了new Sthdent。类似为好友复制了一把钥匙,他把这把钥匙锻造成了一把新的钥匙,但是不改变你身上的原有钥匙。
4、解决未赋值方法
如果需要对main中的整个student对象进行同步更改,需要将function()函数中的st对象进行return,并在main函数中对st对象进行重新赋值。
public class test {
public static void main(String[] args) {
Student st = new Student("小王", 18);
st = function(st);
System.out.println("此时学生对象为:" + st.toString());
}
private static Student function(Student st){
st = new Student("小明", 16);
return st;
}
}
此时结果输出:此时学生对象为:Student(name=小明, age=16)
这也是在代码设计中,我们调用方法都是以return方式得到该方法的返回结果,并进行重新赋值。
在以下博客中有一个比喻非常清晰:java对象之间怎么实现值传递,“类似于好友复制了一把你家的钥匙,而他没有对钥匙进行刻字或修改,而是进入到家里,把家里电视给砸了”。这也就对应了传递对象值被修改的场景。