摘要/Abstract
参数传递有两种形式:值传递(Pass By Value)和引用传递(Pass By Reference)。一般而言,基本数据类型是值传递,对象数据类型是引用传递。而有的时候,我们需要对象的“值传递”,实现方法之一是重写Object的clone()方法。
1.值传递
值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。[1]
代码示例1-1:
public static void main(String[] args) {
int n = 1;
change(n);
System.out.println(n);
}
public static void change(int n) {
n = 123;
}
// output:
// 1
2.引用传递
引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。[2]
代码示例2-1:
public static void main(String[] args) {
A a = new A(1);
change(a);
System.out.println(a.getN());
}
public static void change(A a) {
a.setN(123);
}
static class A {
private int n;
A() {
}
A(int n) {
this.n = n;
}
public int getN() {
return this.n;
}
public void setN(int newN) {
this.n = newN;
}
}
// output:
// 123
另外,我们写代码时经常会用到赋值,即把一个变量用等号(“=”)赋值给另一个变量。对于基本数据类型,赋值后的两个变量除了数值上相等,其他毫无关联,而对于对象变量,等号的赋值仅仅是把一个对象指针指向另一个变量名,实际只存在一个对象,如果你修改其中的一个变量,也会“影响”另外一个变量。
将代码示例2-1中main函数修改成如下,其他不变。
代码示例2-2:
public static void main(String[] args) {
A a = new A(1);
A newa = a;
change(newa);
System.out.println(a.getN());
System.out.println(newa.getN());
}
// output:
// 123
// 123
3.实现对象的值传递
重写Object的clone()方法。需要注意的是,重写方法后,类还需要实现Cloneable接口,不然会报错。
代码示例3-1:
public static void main(String[] args) throws CloneNotSupportedException {
A a = new A(1);
A newa = (A) a.clone();
change(newa);
System.out.println(a.getN());
System.out.println(newa.getN());
}
public static void change(A a) {
a.setN(123);
}
static class A implements Cloneable{
private int n;
A() {
}
A(int n) {
this.n = n;
}
public int getN() {
return this.n;
}
public void setN(int newN) {
this.n = newN;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// output:
// 1
// 123
看到这里,你可能觉得上述例子已经实现了对象的值传递,但实际上还没有,当对象的属性是另外一种对象时,又回到了“起点”。
代码示例3-2:
public static void main(String[] args) throws CloneNotSupportedException {
B b = new B(1);
A a = new A(b);
A newa = (A) a.clone();
change(newa);
System.out.println(a.getN().getBN());
System.out.println(newa.getN().getBN());
}
public static void change(A a) {
a.getN().setBN(123);
}
static class A implements Cloneable{
private B n;
A() {
}
A(B n) {
this.n = n;
}
public B getN() {
return this.n;
}
public void setN(B newN) {
this.n = newN;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
static class B {
private int n;
B() {
}
B(int n) {
this.n = n;
}
public int getBN() {
return this.n;
}
public void setBN(int newN) {
this.n = newN;
}
}
// output:
// 123
// 123
对象的对象也需要重写clone()方法,并且在对象中显式地调用属性的clone()。如果仅仅是上述例子,只有一层,改写也比较方便。但请试想如果出现“套娃”,那么,每有一层就需要重写一次clone()方法,这代码相当麻烦。更多关于clone()方法的内容可以查看[3]。
参考文献/References
[1] https://baike.baidu.com/item/%E5%80%BC%E4%BC%A0%E9%80%92/13009399?fr=aladdin
[2] https://baike.baidu.com/item/%E5%BC%95%E7%94%A8%E4%BC%A0%E9%80%92/2880658?fr=aladdin
[3] https://blog.csdn.net/qq_33314107/article/details/80271963