一、数据类型
数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型。
1、基本数据类型的特点:直接存储在栈(stack)中的数据
2、引用数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
*
二、浅拷贝与深拷贝
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。
深拷贝和浅拷贝的示意图大致如下:
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
三、赋值和浅拷贝的区别
当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。
public class Student {
private String name;
private int age;
private int grade;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", grade=" + grade +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
}
下面用的“=”赋值
public class Studenttest {
public static void main(String[] args) {
int age = 10;
int age1 = 20;
String name = "aaa";
String name1 = "bbb";
Student student = new Student();
student.setAge(age);
student.setName(name);
Student student1 = student;
student.setAge(age1);
student.setName(name1);
System.out.println(student);
System.out.println(student1);
}
}
----Output----
Student{name='bbb', age=20, grade=0}
Student{name='bbb', age=20, grade=0}
上面代码直接使用了 “=” 对对象进行拷贝,发现,无论是基本类型还是引用类型的数据,只要其中一个对象的值被改变,另一个对象的数据也随之改变。
浅拷贝:
public class School implements Cloneable {
String name;
public School(String name) {
this.name = name;
}
...
@Override
public String toString() {
return "School{" +
"name='" + name + '\'' +
'}';
}
}
public class Student implements Cloneable{
private String name;
private int age;
private int grade;
private School school;
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
public Student(String name, int age, School school) {
this.name = name;
this.age = age;
this.school = school;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", grade=" + grade +
", school=" + school +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
@Override
protected Student clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
this.school.clone();
return student;
}
}
public class Tester {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student("李四",10, new School("重大"));
Student student1 = (Student) student.clone();
student.setAge(20);
student.setName("张三");
student.getSchool().setName("城科");
System.out.println(student);
System.out.println(student1);
}
}
}
----Output----
Student{name='张三', age=20, grade=0, school=School{name='城科'}}
Student{name='李四', age=10, grade=0, school=School{name='城科'}}
以上就是一个实现浅拷贝的例子,首先,需要拷贝的类要实现Cloneable接口(此接口是个标识性接口,接口内无任何方法),重写clone方法,在拷贝对象的时候,使用Student student = (Student) student.clone(),这样就实现了浅拷贝。从测试类的输出可以看出来,修改原对象的基本数据类型的成员变量和引用数据name时,拷贝对象并没有收到影响,而修改原对象原数据包含子对象School的属性时,拷贝类型也受到了影响。
深拷贝:
@Override
protected School clone() throws CloneNotSupportedException {
School school= (School) super.clone();
return school;
}
public class Student implements Cloneable{
private String name;
private int age;
private int grade;
private School school;
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
public Student(String name, int age, School school) {
this.name = name;
this.age = age;
this.school = school;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", grade=" + grade +
", school=" + school +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
@Override
protected Student clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
student.school = (School) this.school.clone();
return student;
}
}
public class Tester {
public static void main(String[] args) throws CloneNotSupportedException {
Student student = new Student("李四",10, new School("重大"));
Student student1 = (Student) student.clone();
student.setAge(20);
student.setName("张三");
student.getSchool().setName("城科");
System.out.println(student);
System.out.println(student1);
}
}
----Output----
Student{name='张三', age=20, grade=0, school=School{name='城科'}}
Student{name='李四', age=10, grade=0, school=School{name='重大'}}
深拷贝从测试类的输出可以看出来,修改原对象的基本数据类型的成员变量时,拷贝对象并没有收到影响,而修改原对象引用类型成员变量School的属性时,拷贝类型也不受到了影响。
– | 和原数据是否指向同一对象 | 第一层数据为基本数据类型 | 原数据包含子对象 |
---|---|---|---|
赋值 | 是 | 改变会使原数据一同改变 | 改变会使原数据一同改变 |
浅拷贝 | 否 | 改变会不使原数据一同改变 | 改变会使原数据一同改变 |
深拷贝 | 否 | 改变不会使原数据一同改变 | 改变不会使原数据一同改变 |