在C/C++语言中,常用到指针,指针不仅会给开发人员带来使用上的不便,而且也是造成程序不稳定的根源。
Java语言取消了指针的概念,但这只是在Java语言中没有明确提供指针的概念与用法,而实质上每个new语句返回的都是一个指针的引用,只不过在大部分情况下开发人员不需要关心如何去操作这个指针而已。
由于Java取消了指针的概念,因此开发人员在编程中往往忽略了对象和引用的区别。
public class UserBean {
private String userName ="张三";
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
public class TestRef {
private UserBean userBean = new UserBean();
private int userAge =22;
public UserBean getUserBean() {
return userBean;
}
public int getUserAge() {
return userAge;
}
public void setUserBean(UserBean userBean) {
userBean.setUserName("李四");
}
public void setUserAge(int userAge1) {
userAge1 = 100;
}
}
测试:
TestRef testRef = new TestRef();
System.out.println("userBean 引用类型是:"+testRef.getUserBean().getUserName());
System.out.println("------调用setUserBean------");
testRef.setUserBean(testRef.getUserBean());
System.out.println("userBean 引用类型是:"+testRef.getUserBean().getUserName());
System.out.println("userAge 基本数据类型是:"+testRef.getUserAge());
System.out.println("------调用setUserAge------");
testRef.setUserAge(testRef.getUserAge());
System.out.println("userAge 基本数据类型是:"+testRef.getUserAge());
输出结果:
userBean 引用类型是:张三
------调用setUserBean------
userBean 引用类型是:李四
userAge 基本数据类型是:22
------调用setUserAge------
userAge 基本数据类型是:22
TestRef 中的 userBean 中的userName值改变了而 userAge的值未改变,原因是Java在处理基本数据类型(例如int、char、double等)时,都是采用按值传递(传递的是输入参数的复制)的方式执行,除此之外的其他类型都是按引用传递(传递的是对象的一个引用)的方式执行。对象除了在函数调用时是引用传递,在使用“=”赋值时也采用引用传递。
在实际开发中,经常会遇到从某个已有的对象A创建出另外一个与A具有相同状态的对象B,并且对B的修改不会影响到A的状态。
例如要实现 clone一个对象实例,在Java语言中,仅仅通过简单的赋值操作显然无法达到这个目的。
Java提供了一个简单有效的clone()方法来满足这个需求。Java中的所有类默认都继承自Object类,而Object类中提供了一个clone()方法。这个方法的作用是返回一个Object对象的复制。这个复制函数返回的是一个新的对象而不是一个引用。实现 clone 的基本实现步骤如下:
- 1、实现clone的类首先需要实现Cloneable接口。Cloneable接口实质上是一个标识接口,没有任何接口方法。
- 2、在类中重写Object类中的clone()方法。
- 3、 在clone方法中调用super.clone()。无论clone类的继承结构是什么,super.clone()都会直接或间接调用java.lang.Object类的clone()方法。
- 4、把浅复制的引用指向原型对象新的克隆体。
实例操作如下:
public class TestCRef implements Cloneable {
private int userAge =22;
public void changeAge(){
userAge =100;
}
public int getUserAge() {
return userAge;
}
@Override
public TestCRef clone() {
try {
TestCRef clone = (TestCRef) super.clone();
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
测试用例如下:
TestCRef testRef = new TestCRef();
TestCRef testRef2 = testRef.clone();
testRef2.changeAge();
System.out.println("testRef userAge :"+testRef.getUserAge());
System.out.println("testRef2 userAge :"+testRef2.getUserAge());
输出结果如下:
testRef userAge :22
testRef2 userAge :100
在C++语言中,当开发人员自定义复制构造函数时,会存在浅复制与深复制之分。Java语言在重载clone()方法时也存在同样的问题,当类中只有一些基本的数据类型时,采用上述方法就可以了,但是当类中包含了一些对象时,就需要用到深复制了,实现方法是在对对象调用clone()方法完成复制后,接着对对象中的非基本类型的属性也调用clone()方法完成深复制。
那么在实际开发中,如何选择使用哪种复制方式呢?首先,检查类有无非基本类型(即对象)的数据成员。若没有,则返回super.clone()即可;若有,确保类中包含的所有非基本类型的成员变量都实现了深复制。
实例操作如下:
public class TestCRef2 implements Cloneable {
private UserBean2 userBean2 = new UserBean2();
private int userAge =22;
public void changeAge(){
userAge =100;
}
public int getUserAge() {
return userAge;
}
public UserBean2 getUserBean2() {
return userBean2;
}
public void setUserBean2(UserBean2 userBean2) {
this.userBean2 = userBean2;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
@Override
public TestCRef2 clone() {
try {
TestCRef2 clone = (TestCRef2) super.clone();
//实现实例对象的深复制
clone.userBean2 = this.getUserBean2().clone();
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
完毕