目录
7、notify()、notifyAll() 和 wait()
1、介绍Object类
Object是所有类的顶级父类,属于java.lang包
2、native关键字
看源码的时候发现,Object类的很多方法,被native关键字修饰了
native关键字主要用于方法上
- 一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。
- 在定义一个native方法时,并不提供实现体(比较像定义一个Java Interface),因为其实现体是由非Java语言在外面实现的
- 主要是因为JAVA无法对操作系统底层进行操作,但是可以通过jni(java native interface)调用其他语言来实现底层的访问
3、hashCode()方法
返回对象的hashcode,主要是为了一些哈希表的数据结构服务的,比如 HashMap
如果两个对象相等,则 hashcode 一定相等,但是 hashcode 相等,两个对象不一定相等。如果 hashcode 不相等,那么这两个对象一定不相等
4、equals()方法
看Object类的源码可知,equals方法用的是==来比较两个对象的大小,因此比较的是两个对象的地址:
但是在实际应用中,我们通常在比较两个对象是否相等的时候,想要比较的不是地址,而是字段属性值等,所以需要重写equals方法。
想要达到的比较效果是:如果两个对象指向内存地址相同或者两个对象各个字段值相同,那么就是同一个对象
重写equals方法时,一定要同时重写hashCode方法。一是因为相等的两个对象,哈希值一定得相同,不能违反规则,得保证两者之间的配合关系;二是对于对象集合的判重,如果一个集合含有100个对象实例,仅仅使用equals()方法的话,那么对于一个对象判重就需要调用equals方法比较上千次,随着集合规模的增大,时间开销是很大的,但是同时使用哈希表的话,就能快速定位到对象的大概存储位置,并且在定位到大概存储位置后,后续比较过程中,如果两个对象的hashCode不相同,也不再需要调用equals()方法,从而大大减少了equals()比较次数。所以从程序实现原理上来讲的话,既需要equals()方法,也需要hashCode()方法
重写后的hashCode方法返回的新的哈希值与对象的属性也要有关
重写equals方法需要注意
- 自反性:对于任意的引用值x,x.equals(x)一定为true
- 对称性:对于任意的引用值x 和 y,当x.equals(y)返回true,y.equals(x)也一定返回true
- 传递性:对于任意的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也一定返 回 true
- 一致性:对于任意的引用值x 和 y,如果用于equals比较的对象信息没有被修改,多次调用x.equals(y)要么一致地返回true,要么一致地返回false
- 非空性:对于任意的非空引用值x,x.equals(null)一定返回false
5、clone()方法、深克隆浅克隆
这里的clone()方法是浅克隆
- 浅克隆是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
- 深克隆不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。
有一个Student类
public class Student implements Cloneable{
String name;
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
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
再定义一个浅克隆的Teacher1类
public class Teacher1 implements Cloneable{
String name;
Student student;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public String toString() {
return "Teacher [name=" + name + ", student=" + student + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
和一个深客隆的Teacher2
public class Teacher2 implements Cloneable{
String name;
Student student;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public String toString() {
return "Teacher [name=" + name + ", student=" + student + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
Teacher2 teacher = (Teacher2)super.clone();
teacher.setStudent((Student)teacher.getStudent().clone());
return teacher;
}
}
new出一个teacher1对象和一个teacher2对象,再分别克隆出一个teacher1_clone和teacher2_clone对象,改变teacher1_clone和teacher2_clone的student对象属性
public class TestClone {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student();
student1.setName("学生1号");
student1.setAge(1);
Teacher1 teacher1 = new Teacher1();
teacher1.setName("老师1号");
teacher1.setStudent(student1);
System.out.println("克隆前的老师1:"+teacher1);
Teacher1 teacher1_clone = (Teacher1)teacher1.clone();
Student student1_clone = teacher1_clone.getStudent();
student1_clone.setName("学生1号——克隆");
student1_clone.setAge(11);
System.out.println("浅克隆后修改了学生信息的老师1:"+teacher1);
Student student2 = new Student();
student2.setName("学生2号");
student2.setAge(2);
Teacher2 teacher2 = new Teacher2();
teacher2.setName("老师2号");
teacher2.setStudent(student2);
System.out.println("克隆前的老师2:"+teacher2);
Teacher2 teacher2_clone = (Teacher2)teacher2.clone();
Student student2_clone = teacher2_clone.getStudent();
student2_clone.setName("学生2号——克隆");
student2_clone.setAge(22);
System.out.println("深克隆后修改了学生信息的老师2:"+teacher2);
}
}
打印结果
克隆前的老师1:Teacher [name=老师1号, student=Student [name=学生1号, age=1]]
浅克隆后修改了学生信息的老师1:Teacher [name=老师1号, student=Student [name=学生1号——克隆, age=11]]
克隆前的老师2:Teacher [name=老师2号, student=Student [name=学生2号, age=2]]
深克隆后修改了学生信息的老师2:Teacher [name=老师2号, student=Student [name=学生2号, age=2]]
从打印结果可以看出来,浅克隆由于不拷贝对象包含的引用指向的对象,即Teacher1没有拷贝包含的Student对象,只拷贝了这个对象的引用,所以改变拷贝的Teacher1中的Student对象的属性,也会影响原Teacher1对象中的Student,而Teacher2拷贝的不是Student的引用,而是把这个Student本身复制了一份过去,所以修改这个Student不会影响原Teacher2种的Student。有点绕,需要结合代码来理解。
6、toString()方法
如果没有重写toString方法,那么输出来的默认的字符串内容是“类名+哈希编码”
因为使用这个继续下来toString()方法将引用对象转换成字符串输出时输出的是一连串令人看不懂的哈希编码。为了使打印出来的信息使得正常人都能看得懂,因此要在类里面把这个继承下来的toString()方法重写
7、notify()、notifyAll() 和 wait()
作用:
- wait() 方法让当前线程进入等待状态
- notify() 随机唤醒一个等待线程
- notifyAll() 唤醒全部的等待线程
这个以后专门写线程的时候再详细地讲
8、finalize()方法
finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的
当垃圾回收器确定不再有对该对象的引用时,由垃圾回收器在对象上调用该方法,该方法只会被调用一次
但finalize并不保证执行以后就会把内存释放掉,而是会到执行后的下一次垃圾回收才有机会被回收掉
9、getClass()方法
获取当前对象的类
String s = new String("abc");
System.out.println(s.getClass());
System.out.println(s.getClass().getName());
System.out.println(s.getClass().getSimpleName());
输出结果
class java.lang.String
java.lang.String
String