学习点1
在学习深拷贝,浅拷贝之前,先要区分java中的数据类型
java中的数据类型分为基础类型和引用类型
基础类型只有八种,固定的:long、int、short、byte 、float、double、char、boolean
引用类型比较多,主要有几种:类、 接口、 数组、 枚举、 字符串等
因为,对于基础类型,拷贝会直接复制值,所以,深拷贝和浅拷贝的问题基本都是针对引用类型的,我们不需要考虑基础类型
学习点2
所谓引用类型,就是需要通过引用访问的数据类型
因此,对于这类数据,浅拷贝与深拷贝的区别是:
浅拷贝:创建新对象,然后复制老对象的引用赋值给它
深拷贝:创建新对象,并将老对象中的所有数据(基础类型和引用类型)递归的复制一份,赋值给它
学习点3
在拷贝的问题中,String类型是特殊的,String 对象是不可变的(immutable),因此即使在浅拷贝里String的表现也更像深拷贝,这个听起来不太好理解,可以看下面的例子
示例
public static void main(String[] args) {
Student s1 = new Student("hj", new Teacher("t1"));
Student s2 = s1.clone();
System.out.println(s1.name);//1输出:hj
System.out.println(s2.name);//2输出:hj
System.out.println(s1.teacher.name);//3输出:Teacher@47f37ef1
System.out.println(s2.teacher.name);//4输出:Teacher@47f37ef1
s2.name = "hj2";
System.out.println(s1.name);//5输出:hj
System.out.println(s2.name);//6输出:hj2
s2.teacher.name = "t2";
System.out.println(s1.teacher.name);//7输出:t2
System.out.println(s2.teacher.name);//8输出:t2
}
分析:
这个clone()直接返回了super.clone(),是一个典型的浅拷贝
因此1输出和2输出结果是一样的都是hj
2输出和3输出也是一样的,是同一个对象,这符合浅拷贝特征
接下来,修改了s2的name,按照浅拷贝的逻辑,引用对象,应该会造成s1.name也发生变化,但是,当执行 s2.name = “hj2” 时,实际上是创建了一个新的 String 对象 “hj2”,并将 s2.name 指向这个新对象
因此5输出hj,6输出hj2,这里表现是不是很像深拷贝,但它确确实实是一个浅拷贝,只是String类型特殊罢了
我们接下来修改s2.teacher.name = “t2”
按照浅拷贝的原理,s1和s2的teacher应该是同一个对象,修改一个,另一个也会发生变化,因此7输出t2,8输出t2
学习点4
clone()方法
clone()本身是Object中的方法,但它是protected的,所以类或者对象不能直接使用
但是类可以通过实现Cloneable接口,重写clone()方法,将它变成public,Cloneable里没有任何方法,它只起到一个标记的作用,相当于告诉java虚拟机,这个类可以被拷贝了。
至于重写的clone()方法,是直接返回一个super.clone()(浅拷贝),还是用户重写方法逻辑(实现深拷贝),这是由程序员自己决定的。
---------------------------以上是关于拷贝---------------------------
学习点5
==是操作符,对于基础类型,只能用==来比较,就是字面的表现,一样返回true,不一样返回false;对于引用类型,它比较的是引用是否相同,本质上来说,就是同一个对象。
hashCode()是Object中的一个方法,计算的是hash值,相当于是对象的hash值,两个对象如果相同,hash值一定相同,但hash值相同,不一定是同一个对象。
equals()也是Object中的方法,在Object,它也是跟==一样,对比的是引用,但是我们常见的一些引用类型都重写了这个方法,通常是比较对象里的具体内容的。
示例
public static void main(String[] args) {
int a = 1;
int b = 1;
System.out.println(a == b);//输出:true
Student s1 = new Student("hj", new Teacher("t1"));
Student s2 = s1.clone();
System.out.println(s1==s2);//输出:false(clone是新建的对象,不是用一个引用)
System.out.println(s1.equals(s2));//输出:false(没有重写equals()方法)
s2 = s1;
System.out.println(s1==s2);//输出:true(=是赋值,直接把s2的引用赋值给s1)
String s3 = "hj";
String s4 = "hj";
System.out.println(s3==s4);//输出:true(String 特殊,相当于“hj”在内存里是同一个引用)
s3 = new String("hj");
s4 = new String("hj");
System.out.println(s3==s4);//输出:false(new了两个对象,==比较的是引用)
System.out.println(s3.equals(s4));//输出:true(String 重写了equals,比较的是值)
}