1. 浅克隆和深克隆
首先要想克隆的类,需要实现Cloneable接口。这个接口是一个标志接口,没有任何内容。需要克隆的类要override clone()方法,默认是浅克隆,即super.clone()。那么会把对象和对象中的基本数据类型的值复制一份,但是引用类型的变量无法复制,即克隆之后,原来的那个引用还是指向同一个。比如:
public class Teacher implements Cloneable {
private String name;
private int age;
Student student;
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;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
", student=" + student +
'}';
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public Teacher clone() throws CloneNotSupportedException {
// return (Teacher)super.clone();浅克隆方法
Teacher tea = (Teacher)super.clone();
Student stu = (Student)tea.getStudent().clone();
tea.setStudent(stu);
return tea;
}
}
public class Student implements Cloneable {
private String name;
private int age;
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试:
public class cloneTest {
public static void main(String[]agrs) throws CloneNotSupportedException {
cloneTest c = new cloneTest();
Student stu1 = new Student();
stu1.setAge(18);
stu1.setName("张三");
Teacher tea1 = new Teacher();
tea1.setAge(30);
tea1.setName("老师");
tea1.setStudent(stu1);
Teacher tea2 = (Teacher)tea1.clone();
tea2.setName("新的老师");
tea2.setAge(25);
Student stu2 = tea2.getStudent();
stu2.setName("李四");
stu2.setAge(25);
System.out.println(tea1);
System.out.println(tea2);
}
}
当使用浅克隆的时候,会发现输出的两个teacher对象的student是同一个对象。使用深克隆的时候,tea2的student对象更改属性并不影响tea1的student,说明引用变量复制成功。
使用克隆的tip:
1:要进行克隆的类必须实现Cloneable接口
2:必须重写clone方法,并抛出异常,clone方法需要用public修饰
3:对于要实现深克隆的类,不能简单使用super.clone(),要重写clone方法,并对要深克隆的引用类型变量进行克隆。
2. 抽象类和接口
从组成来看
抽象类不能被实例化,可以有普通成员变量,可以有抽象方法,可以有普通方法,有构造函数,可以有静态成员变量和方法,但不能是抽象的;接口不能被实例化,不能有普通成员,所有成员默认为public static final,方法只能是Public abstract的,没有构造函数,不能有静态方法(abstract和static不能共存)。所有类继承抽象类如果没有完全实现其中的抽象方法,那么这个子类也是抽象类。
关于抽象类的构造函数
既然抽象类不能被实例化为什么还要有构造函数
子类是可以实例化的,在子类实例化的时候,往往需要对父类中的属性进行初始化,在这个时候会调用super()来使用父类的构造函数。
接口和抽象类的使用
接口更多的是在系统架构上发挥作用,实现模块间的通信规则,抽象类更多的是实现代码的重用。
3:super.getClass()
根据Object类的源码,getClass方法用final修饰,子类不能覆盖这个方法,这个方法实际上是返回当前正在运行的类,如果加上getName就是当前运行调用方法的类的名字,而与父类毫无关系。如果想获取父类的类名可以:getClass().getSuperClass().getName()
4. String s = “Hello”;s = s + " world!";这两行代码执行后,原始的String对象中的内容到底变了没有?
第一行代码:String s = “Hello”;将会在字符串常量池中寻找是否存在内容为Hello的字符串,如果有,s指向它,否则创建一个新的String对象,放入常量池和堆中。
第二行代码:s = s + “world!”;由于String 属于不可变类,因此一开始创建出来内容为Hello的字符串依旧不变,变的只是s的指向,s将会指向一个叫做Hello world!的字符串。
由于每次修改String的内容,实际上都是在创建新的String对象,因此开销会比较大,最好是使用StringBuffer,StringBuffer属于可变类,而且多线程安全。
5.String和StringBuffer
String重写了equals方法,所以new String(“abc”).equals(new String(“abc”))结果为true,而StringBuffer没有重写equals方法。String属于不可变类,当对String进行改变的时候,会创建新的对象,而StringBuffer不会。
6.StringBuffer和StringBuilder
StringBuffer中的所有方法都是用synchronized修饰的,因此是线程安全的,而StringBuilder没有,所以StringBuilder的效率要远大于StringBuffer。StringBuffer的toString方法每次都会获取缓冲区的toStringCache,但是StringBuilder的toString方法每次都会复制一遍字符数组,再构建字符串。
7::内部类访问局部变量的时候,局部变量必须用final修饰
个人理解,局部变量在方法访问结束的时候,生命周期就会消亡,但是内部类和外部类具有相同的生命周期,如果内部类要访问局部变量,局部变量提前消亡了,就会导致错误,因此用final修饰可以让局部变量和内部类具有相同的生命周期。