Object 源码:
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
/**
* 获取类的字节码对象
*/
public final native Class<?> getClass();
/**
* 调用对象的notify()方法能够唤醒一个正在等待该对象的线程,
* 如果有多个线程都在等待这个对象,则只能唤醒其中一个线程;
*/
public final native void notify();
/**
* 够唤醒所有正在等待这个对象的线程
*/
public final native void notifyAll();
/**
* 堵塞当前线程,timeout毫秒后自动唤醒当前线程。
* 当timeout为0时,表示不会自动唤醒线程。
*/
public final native void wait(long timeout) throws InterruptedException;
/**
*阻塞当前线程
*/
public final void wait() throws InterruptedException {
wait(0);
}
/**
* 堵塞当前线程,timeout毫秒后自动唤醒当前线程。nanos纳秒
* 当timeout为0时,表示不会自动唤醒线程。
*/
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
/**
* 获取hashCode
*/
public native int hashCode();
/**
* 对象比较是否相等
*/
public boolean equals(Object obj) {
return (this == obj);
}
/**
* 对象转出字符串
*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
/**
*当垃圾回收器确定回收该对象时自动调用
*/
protected void finalize() throws Throwable { }
/**
* 克隆对象
*/
protected native Object clone() throws CloneNotSupportedException;
}
一、getClass方法
getClass() 是本地方法,并且为final方法,无法被重写。
实例1:public class App {
public static void main(String[] args) {
Class clazz1 = new Person().getClass();
Class clazz2 = new Person().getClass();
Class clazz3 = Person.class;
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz1 == clazz3);//true
System.out.println(Object.class == (Object)(new Person()).getClass());//false
System.out.println(Person.class == (Object)(new Person()).getClass());//true
}
}
class Person{
}
结论:
1、 一个类可以有很多实例,但是它的字节对象只有一个。字节对象是类级别的,因此只有一个。
2、 一个对象可以被强转成其他对象但是其字节码保持不变。
二、equals和hashCode方法
在Java规范中,equals()方法的生成要遵循如下规则:x、y、z都非空
1、自反性: x.equals(x) 一定是true;
2、对null: x.equals(null)一定是false;
3、对称性: x.equals(y) ==y.equals(x);
4、传递性: 如果 x.equals(y) 返回 true,并且y.equals(z) 返回 true,那么 x.equals(z) 应返回 true;
5、一致性: 如果 equals 比较中所用的信息没有被修改,那么 x.equals(y) 比较的结果始终不会改变。
hashCode 和equals的关系:
1、 重写equals必须重写hashCode,这里就不再多说了,百度一下就知道;
2、X.equals(Y)==true => X.hashCode()==Y.hashCode();
3、X.hashCode()==Y.hashCode() ≠> X.equals(Y)==true;
4、X.equals(Y)==false ≠>X.hashCode()!=Y.hashCode()。
实例2:
public class App {
public static void main(String[] args) {
Person person = new Person();
Student student = new Student();
System.out.println(person.equals(student));//true
System.out.println(student.equals(person));//false
}
}
class Person {
private String name;
private char firstName;
private int height;
private double weight;
private boolean sex;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;
if (firstName != person.firstName) return false;
if (height != person.height) return false;
if (Double.compare(person.weight, weight) != 0) return false;
if (sex != person.sex) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
@Override
public int hashCode() {
int result;
long temp;
result = name != null ? name.hashCode() : 0;
result = 31 * result + (int) firstName;
result = 31 * result + height;
temp = Double.doubleToLongBits(weight);
result = 31 * result + (int) (temp ^ (temp >>> 32));
result = 31 * result + (sex ? 1 : 0);
return result;
}
}
class Student extends Person {
private String school;
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
if (!super.equals(o)) return false;
Student student = (Student) o;
return school != null ? school.equals(student.school) : student.school == null;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (school != null ? school.hashCode() : 0);
return result;
}
}
这个例子中的equals和hashCode方法是编辑器帮我自动生成的,从中可以学到equals和hashCode的生成规则。main方法中,person.equals(student)==true而student.equals(person))==false,这显然违反了equals规则中的对称性。
实例3:
public class App {
public static void main(String[] args) {
Person person = new Person();
Student student = new Student();
System.out.println(person.equals(student));//false
System.out.println(student.equals(person));//false
}
}
class Person {
private String name;
private char firstName;
private int height;
private double weight;
private boolean sex;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (firstName != person.firstName) return false;
if (height != person.height) return false;
if (Double.compare(person.weight, weight) != 0) return false;
if (sex != person.sex) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
@Override
public int hashCode() {
int result;
long temp;
result = name != null ? name.hashCode() : 0;
result = 31 * result + (int) firstName;
result = 31 * result + height;
temp = Double.doubleToLongBits(weight);
result = 31 * result + (int) (temp ^ (temp >>> 32));
result = 31 * result + (sex ? 1 : 0);
return result;
}
}
class Student extends Person {
private String school;
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
Student student = (Student) o;
return school != null ? school.equals(student.school) : student.school == null;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (school != null ? school.hashCode() : 0);
return result;
}
}
这里的equals和hashCode方法同样也是编辑器自动生成的。这次并没有违反了equals规则中的对称性。实例2和实例3结果的差异,是由equals方法中instanceof和getClass导致的。
实例4:
public class App {
public static void main(String[] args) {
Person person = new Person();
Student student = new Student();
System.out.println(person.equals(student));//true
System.out.println(student.equals(person));//true
}
}
class Person {
private String name;
private char firstName;
private int height;
private double weight;
private boolean sex;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;
if (firstName != person.firstName) return false;
if (height != person.height) return false;
if (Double.compare(person.weight, weight) != 0) return false;
if (sex != person.sex) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
@Override
public int hashCode() {
int result;
long temp;
result = name != null ? name.hashCode() : 0;
result = 31 * result + (int) firstName;
result = 31 * result + height;
temp = Double.doubleToLongBits(weight);
result = 31 * result + (int) (temp ^ (temp >>> 32));
result = 31 * result + (sex ? 1 : 0);
return result;
}
}
class Student extends Person {
private String school;
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
这里的Student类不在重写equals和hashCode方法。运行结果同样没有违反了equals规则中的对称性。
实例2中,因为它违反了equals的对称性,就不用去探究了。
实例3和实例4都是正确的,到底选哪一种方式是由我们的业务场景决定的。
实例3中,equals中使用getClass,这种equsls比较是严格的;创建对象使用的类必须是同一个类,才有可能返回true;如果父类的equals中使用了getClass,子类可以覆盖父类的equals方法,如果子类覆盖父类equals方法,那么equals方法同样要使用getClass。
实例4中,equals中使用instanceof,这种equals比较是宽松的;创建对象使用的类不是同一个类,也有可能返回true。如果非抽象父类的equals中使用了instanceof,子类覆盖父类的equals方法,将会出现实例2中的错误;如果抽象父类的equals中使用了instanceof,一部分子类覆盖父类的equals方法,一部分子类没有覆盖,将会出现实例2中的错误。如果抽象父类的equals中使用了instanceof,全部子类都覆盖父类的equals方法,意味着抽象父类的equals方法将永远不可能被调用,这种方式是可以的,Spring中org.springframework.core.io.AbstractResource 抽象父类就是采用这种方式。
三、wait、notify和notifyAll方法
首先了解一下线程的状态:
线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。
1.新建状态
当用new操作符创建一个线程时。此时程序还没有开始运行线程中的代码。
2.就绪状态
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序来调度的。
3.运行状态(running)
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法。
4.阻塞状态(blocked)
线程运行过程中,可能由于各种原因进入阻塞状态:
① 线程通过调用sleep方法进入睡眠状态;
② 线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
③ 线程试图得到一个锁,而该锁正被其他线程持有;
④ 线程在等待某个触发条件;
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
堵塞状态是前述四种状态中最有趣的,值得我们作进一步的探讨。线程被堵塞可能是由下述五方面的原因造成的:
(1) 调用sleep(毫秒数),使线程进入"睡眠"状态。在规定的时间内,这个线程是不会运行的。
(2) 用suspend()暂停了线程的执行。除非线程收到resume()消息,否则不会返回"可运行"状态。
(3) 用wait()暂停了线程的执行。除非线程收到nofify()或者notifyAll()消息,否则不会变成"可运行"(是的,这看起来同原因2非常相象,但有一个明显的区别是我们马上要揭示的)。
(4) 线程正在等候一些IO(输入输出)操作完成。
(5) 线程试图调用另一个对象的"同步"方法,但那个对象处于锁定状态,暂时无法使用。
5.死亡状态(dead)
有两个原因会导致线程死亡:
①run方法正常退出而自然死亡;
②一个未捕获的异常终止了run方法而使线程猝死;enum Type {
sleep, wait, notify, notifyAll
}
class ThreadTest extends Thread {
private Object lock;
private Type type;
public ThreadTest(Object lock, Type type, String threadName) {
super(threadName);
this.lock = lock;
this.type = type;
}
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "获取了锁");
content();
System.out.println(Thread.currentThread().getName() + "释放了锁");
}
}
private void content() {
try {
if ("sleep".equals(type.name())) {
Thread.sleep(5000L);
} else if ("wait".equals(type.name())) {
lock.wait(10000);
for (int i=1;i<=3;i++){
Thread.sleep(5L*i);
System.out.println(Thread.currentThread().getName() + "running");
}
} else if ("notify".equals(type.name())) {
lock.notify();
} else {
lock.notifyAll();
}
} catch (Exception e) {
}
}
}
实例5:
public class Test {
public static Object object = new Object();
public static void main(String[] args) throws Exception {
Object ziYuan = new Object();//资源
ThreadTest threadSleep = new ThreadTest(ziYuan, Type.sleep, "threadSleep");
ThreadTest threadWait1 = new ThreadTest(ziYuan, Type.wait, "threadWait1");
ThreadTest threadWait2 = new ThreadTest(ziYuan, Type.wait, "threadWait2");
ThreadTest threadNotify = new ThreadTest(ziYuan, Type.notify, "threadNotify");
ThreadTest threadNotifyAll = new ThreadTest(ziYuan, Type.notifyAll, "threadNotifyAll");
threadWait1.start();
threadWait2.start();
Thread.sleep(4000);
threadNotify.start();
}
}
结果:
threadWait1和threadWait2由于调用了wait方法使线程进入堵塞状态,threadNotify调用notify方法将threadWait1,threadWait2其中的一个线程唤醒进入就绪状态。剩下的线程自一直在堵塞状态。
实例6:
public class Test {
public static Object object = new Object();
public static void main(String[] args) throws Exception {
Object ziYuan = new Object();//资源
ThreadTest threadSleep = new ThreadTest(ziYuan, Type.sleep, "threadSleep");
ThreadTest threadWait1 = new ThreadTest(ziYuan, Type.wait, "threadWait1");
ThreadTest threadWait2 = new ThreadTest(ziYuan, Type.wait, "threadWait2");
ThreadTest threadNotify = new ThreadTest(ziYuan, Type.notify, "threadNotify");
ThreadTest threadNotifyAll = new ThreadTest(ziYuan, Type.notifyAll, "threadNotifyAll");
threadWait1.start();
threadWait2.start();
Thread.sleep(4000);
threadNotifyAll.start();
}
}
结果:
threadNotifyAll唤醒所有线程进入就绪状态。
实例7:
public class Test {
public static Object object = new Object();
public static void main(String[] args) throws Exception {
Object ziYuan = new Object();//资源
ThreadTest threadSleep = new ThreadTest(ziYuan, Type.sleep, "threadSleep");
ThreadTest threadWait1 = new ThreadTest(ziYuan, Type.wait, "threadWait1");
ThreadTest threadWait2 = new ThreadTest(ziYuan, Type.wait, "threadWait2");
ThreadTest threadNotify = new ThreadTest(ziYuan, Type.notify, "threadNotify");
ThreadTest threadNotifyAll = new ThreadTest(ziYuan, Type.notifyAll, "threadNotifyAll");
threadSleep.start();
threadNotify.start();
}
}
结果:
线程sleep方法不会释放资源,而wait方法会释放资源。
四、finalize方法
当jvm进行垃圾回收的时候会自动调用finalize方法
实例8:
public class App {
public static void main(String[] args) {
new Person();
System.gc();
while (true);
}
}
class Person {
@Override
protected void finalize() throws Throwable {
System.out.println("Person.finalize()");
}
}
五、clone方法
我们创建的类默认继承了Object,但是clone方法不能直接使用,创建的类还必须要实现Cloneable接口,该接口无任何方法。为了让创建的对象方便调用clone方法,创建的类最好覆盖Object的clone方法,并将方法作用范围改成public。
clone方法生成规则:
①对任何的对象x,都有x.clone() !=x//克隆对象与原对象不是同一个对象;
②对任何的对象x,都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型一样;
③如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。
实例9:
public class App {
public static void main(String[] args) throws Exception{
Person person0 = new Person(new Address("shangHai"),"xiaoHua");
Person person1=(Person) person0.clone();
System.out.println(person0==person1);//false
System.out.println(person1.getName());//xiaoHua
System.out.println(person1.getAddress().getName());//shangHai
System.out.println(person0.getAddress()==person1.getAddress());//true
person0.getAddress().setName("beiJing");
System.out.println(person0.getAddress().getName());//beiJing
System.out.println(person1.getAddress().getName());//beiJing
}
}
class Person implements Cloneable {
private String name;
private Address address;
public Person(Address address, String name) {
this.address = address;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Address{
private String name;
public Address(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
上面的例子中可以看出,我们的确的成功地克隆出了person1,但是当我们修改person0的address时,person1的address也发生了改变。person1和person2的address使用的是同一个对象,这种克隆的方式我们称作 浅克隆。有浅克隆当然就会有深克隆。在Java语言里深克隆一个对象,常常可以先使对象实现Serializable接口(如:Person),对象中的属性也要实现Serializable接口(如:Address),然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。
public class App {
public static void main(String[] args) throws Exception {
Person person0 = new Person(new Address("shangHai"), "xiaoHua");
Person person1 =(Person) deepClone(person0);
System.out.println(person0 == person1);//false
System.out.println(person1.getName());//xiaoHua
System.out.println(person1.getAddress().getName());//shangHai
System.out.println(person0.getAddress() == person1.getAddress());//false
person0.getAddress().setName("beiJing");
System.out.println(person0.getAddress().getName());//beiJing
System.out.println(person1.getAddress().getName());//shangHai
}
public static Object deepClone(Object o) throws Exception {
ByteArrayOutputStream bo = null;
ObjectOutputStream oo = null;
ByteArrayInputStream bi = null;
ObjectInputStream oi = null;
try {
//将对象写到流里
bo = new ByteArrayOutputStream();
oo = new ObjectOutputStream(bo);
oo.writeObject(o);
//从流里读出来
bi = new ByteArrayInputStream(bo.toByteArray());
oi = new ObjectInputStream(bi);
return (oi.readObject());
}finally {
if (oi != null) {
oi.close();
}
if (bi != null) {
oi.close();
}
if (oo != null) {
oo.flush();
oo.close();
}
if (bo != null) {
bo.flush();
bo.close();
}
}
}
}
class Person implements Cloneable, Serializable {
private String name;
private Address address;
public Person(Address address, String name) {
this.address = address;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Address implements Serializable {
private String name;
public Address(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}