基本类型和引用类型:
基本类型是按值访问的,引用类型是按引用访问
- [基本类型] int、long、short、byte、char、boolean、float、double这8种
- [引用类型] 以上基本类型的包装类、String类、对象等
浅拷贝、深拷贝:
obj2是对obj1的浅拷贝,obj2新建了一个对象,但是obj2对象复制的是obj1的指针,也就是obj1在堆中的内存地址,而不是复制对象本身。obj1和obj2是共用了内存地址的。
obj3是对obj1的深拷贝,obj3和obj1不共享引用对象在堆中的内存地址
赋值与浅拷贝:
当我们把一个对象赋值给一个变量的时候,赋值的其实是该对象的栈内存地址而不是堆内存数据(引用类型的对象的值分为栈内存的地址和堆内存中的数据)。也就是赋值前的对象和赋值后的对象共用一个存储空间(栈内存地址),而该栈内存地址指向了同一个堆内存空间),对于引用类型,无论哪个对象发生改变,改变的都是同一个堆堆内存空间,对另一个对象都是有影响的。
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原对象属性值的一份精准拷贝,如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址。
深拷贝:
Java 中 Object 中的 clone()
方法默认是浅拷贝,针对引用类型执行的是同一个地址;
实现深拷贝的三种方式:
- 直接通过 new 对象创建新的对象,通过 new 出来的对象肯定是在内存中重新开辟一块空间存储所以可以实现深拷贝;
- 通过调用父类clone再进行重新复制,虽然调用父类 Native 修饰的 clone 方法比第一种方式速度快,此步骤如果继承类中有多个引用类型克隆相对麻烦;
- 通过序列化和反序列化使用流进行深拷贝(注意类都要实现 Serializable 接口),因为保存在流中的数据相当于新的,若要实现对象深拷贝,推荐使用此方式
总结:
Java中“==”和equals()的区别:
-
“==”对比的是栈中的值,对于基本数据类型就是变量值,对于引用类型是堆中内存对象的地址,比较的是二者是否是同一个引用
-
“==”是运算符号,而equals则属于方法,在objcet类中,就存在equals()方法,不重写于就是“= =”。
-
equals()的两边对象只能属于引用数据类型,equals()是属于 object类,默认是判断内存地址是否相同,在object的子类中可以重写equals方法,如在String类中不再是比较两个对象是否是同一个引用,而是比较对象内容是否一致。
public class Student{
public String name;
public int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
}
public class Test implements Cloneable{
public Student st;
public int k=5;
@Override
protected Test clone() throws CloneNotSupportedException {
return (Test) super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student("TOM",10);
Student s2 = new Student("Jack",20);
Test t1 = new Test();
t1.st = s1;
Test t2 = (Test) t1.clone();//浅拷贝,t1.k的值赋给t2.k,t1.st的值(s1在堆中的地址)给到t2.st
Test t3 = t1;//赋值:t1.k和t3.k指向栈中同一个地址;t1.st和t2.st指向栈中同一个地址
/* t1:5/{TOM/10}
t2:5/{TOM/10}
t3:5/{TOM/10} */
System.out.println("------------------------------------------------");
System.out.println(t1==t2);//F
System.out.println(t1==t3);//T
System.out.println(t1.st==s1);//T
System.out.println(t1.equals(t2));//F
System.out.println(t1.st == t2.st);//T
System.out.println(t1.st.equals(t2.st));//T
System.out.println("------------------------------------------------");
s1.age=30;
s1.name="John";
t1.k=6;//改变对象的基本类型变量
/*
t1:6/{John/30}
t2:5/{John/30}浅拷贝:t2的基本类型变量没有改变
t3:6/{John/30}
*/
System.out.println("------------------------------------------------");
t1.st=s2;//改变对象的引用类型变量
t1.k=7;//改变对象的基本类型变量
/*
t1:7/{Jack/20}
t2:5/{John/30}t2.st仍然指向是s1在堆中的地址
t3:7/{Jack/20}赋值:Test的基本类型变量改变;Test的引用类型变量改变,t1.st和t2.st都指向栈中同一个地址,
这个地址现在指向s2在堆中的地址
*/
}
}