📋目录
- 📚引入
- 📚浅拷贝
- 📖定义
- 📖实现方式
- 📖特点
- 📚深拷贝
- 📖 定义
- 📖实现方式
- 📖特点
- 📚拓展
- 📖Object类
- ✈️toString()方法
- ✈️equals()方法
- ✈️hashcode方法
- 📖通过方法实现实参的交换
📚引入
public class Person implements Cloneable{
public int age;
public String name;
public Person(String name,int age) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test1 {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1=new Person("zhangsan",10);
Person person2=(Person)person1.clone();
}
}
以上代码完善步骤
运行过程分析
📚浅拷贝
📖定义
创建一个新对象,新对象与原对象共享内部的引用对象,但基本数据类型的值是独立的
📖实现方式
示例代码如下
public class Money {
public double money=9.9;
}
public class Person implements Cloneable{
public int age;
public String name;
public Money m=new Money();
public Person(String name,int age) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test1 {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1=new Person("zhangsan",10);
Person person2=(Person)person1.clone();
System.out.println("修改前:"+person1.m.money);
System.out.println("修改前:"+person2.m.money);
person2.m.money=99.9;
System.out.println("修改后:"+person1.m.money);
System.out.println("修改后:"+person2.m.money);
}
}
//执行结果
修改前:9.9
修改前:9.9
修改后:99.9
修改后:99.9
运行过程分析
📖特点
- 效率相对较高,因为只复制了基本数据类型和引用地址,而不是复制整个引用对象。
- 如果原对象中的引用对象发生变化,浅拷贝得到的新对象中的相应引用对象也会随之改变
📚深拷贝
📖 定义
创建一个新对象,新对象完全独立于原对象,包括内部的所有引用对象也都是独立复制的
📖实现方式
示例代码如下
若要在以上浅拷贝代码的基础上实现深拷贝则需要以下两步:
1.Money类实现Cloneable接口中的clone()方法
2.将Student类中clone()方法中的return super.clone()条件改为
Person tmp=(Person)super.clone();
tmp.m=(Money)this.m.clone();
return tmp;
public class Money implements Cloneable{
public double money=9.9;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Person implements Cloneable{
public int age;
public String name;
public Money m=new Money();
public Person(String name,int age) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person tmp=(Person)super.clone();
tmp.m=(Money)this.m.clone();
return tmp;
}
}
public class Test1 {
public static void main(String[] args) throws CloneNotSupportedException{
Person person1=new Person("zhangsan",10);
Person person2=(Person)person1.clone();
System.out.println("修改前:"+person1.m.money);
System.out.println("修改前:"+person2.m.money);
person2.m.money=99.9;
System.out.println("修改后:"+person1.m.money);
System.out.println("修改后:"+person2.m.money);
}
}
//运行结果
修改前:9.9
修改前:9.9
修改后:9.9
修改后:99.9
运行过程分析
📖特点
- 可以确保新对象与原对象完全独立,不会因为原对象的变化而受到影响。
- 实现相对复杂,效率可能比浅拷贝低
📚拓展
📖Object类
Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父 类。即所有类的对象都可以使用Object的引用进行接收
范例:使用Object接收所有类的对象
class Person{}
class Student{}
public class Test {
public static void main(String[] args) {
function(new Person());
function(new Student());
}
public static void function(Object obj) {
System.out.println(obj);
}
}
//执行结果:
Person@1b6d3586
Student@4554617c
Object类是参数的最高统一类型。但是Object类也存在有定义好的一些方法。如下:
✈️toString()方法
如果要打印对象中的内容,可以直接重写Object类中的toString()方法
// Object类中的toString()方法实现:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
//getClass():获取一个类的字节码对象
//getClass().getName():获取一个类的全限定名(包名+类名)
//hashCode():用于计算一个具体的对象位置(内存地址)
//然后调用Integer.toHexString()方法,将这个地址以16进制输出
}
✈️equals()方法
在Java中,==进行比较时:
a.如果==左右两侧是基本类型变量,比较的是变量中值是否相同
b.如果==左右两侧是引用类型变量,比较的是引用变量地址是否相同
c.如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的:
// Object类中的equals方法
public boolean equals(Object obj) {
return (this == obj); // 使用引用中的地址直接来进行比较
}
class Person{
private String name ;
private int age ;
public Person(String name, int age) {
this.age = age ;
this.name = name ;
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person("lisi", 20) ;
Person p2 = new Person("lisi", 20) ;
int a = 10;
int b = 10;
System.out.println(a == b); // 输出true
System.out.println(p1 == p2); // 输出false
System.out.println(p1.equals(p2)); // 输出false
}
}
Person类重写equals方法后,然后比较:
class Person{
...
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false ;
}
if(this == obj) {
return true ;
}
// 不是Person类对象
if (!(obj instanceof Person)) {
return false ;
}
Person person = (Person) obj ; // 向下转型,比较属性值
return this.name.equals(person.name) && this.age==person.age ;
}
}
结论:比较对象中内容是否相同的时候,一定要重写equals方法
✈️hashcode方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
源码中的hashCode()这个方法,用于计算一个具体的对象位置(内存地址),然后调用Integer.toHexString()方法,将这个地址以16进制输出
hashcode方法源码:
public native int hashCode();
该方法是一个native方法,底层是由C/C++代码写的,我们无法看到
一般认为两个名字相同,年龄相同的对象,将存储在同一个位置,在不重写hashcode()方法的情况下,观察以下示例代码的运行结果:
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class TestDemo4 {
public static void main(String[] args) {
Person per1 = new Person("lisi", 20) ;
Person per2 = new Person("lisi", 20) ;
System.out.println(per1.hashCode());
System.out.println(per2.hashCode());
}
}
//执行结果
460141958
1163157884
注意事项:两个对象的hash值不一样
像重写equals方法一样,我们也可以重写hashcode()方法,代码实现如下:
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class TestDemo4 {
public static void main(String[] args) {
Person per1 = new Person("lisi", 20) ;
Person per2 = new Person("lisi", 20) ;
System.out.println(per1.hashCode());
System.out.println(per2.hashCode());
}
}
//执行结果
460141958
460141958
注意事项:哈希值一样
结论:
1、hashcode方法用来确定对象在内存中存储的位置是否相同
2、事实上hashCode()在散列表中才有用,在其它情况下没用。在散列表中hashCode()的作用是获取对象的散列码,进而确定该对象在散列表中的位置
📖通过方法实现实参的交换
public class Test1 {
public static void swap(int x,int y){
int tmp=x;
x=y;
y=tmp;
}
public static void main(String[] args) {
int a=10;
int b=20;
System.out.println("交换前:"+a+" "+b);
swap(a,b);//调用swap()函数只是交换了x和y的值,并未实现a和b的交换
System.out.println("交换后:"+a+" "+b);
}
}
//运行结果
交换前:10 20
交换后:10 20
交换过程
class MyValue{
public int val;
}
public class Test1 {
public static void swap(MyValue myV1,
MyValue myV2){
int tem=myV1.val;
myV1.val=myV2.val;
myV2.val=tem;
}
public static void main(String[] args) {
MyValue myValue1=new MyValue();
MyValue myValue2=new MyValue();
myValue1.val=10;
myValue2.val=20;
System.out.println("交换前:"+myValue1.val+" "+myValue2.val);
swap(myValue1,myValue2);
System.out.println("交换后:"+myValue1.val+" "+myValue2.val);
}
}
//运行结果
交换前:10 20
交换后:20 10
交换过程