package java.lang; public class Object { public Object() { /* compiled code */ } private static native void registerNatives(); public final native java.lang.Class<?> getClass(); public native int hashCode(); public boolean equals(java.lang.Object o) { /* compiled code */ } protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException; public java.lang.String toString() { /* compiled code */ } public final native void notify(); public final native void notifyAll(); public final native void wait(long l) throws java.lang.InterruptedException; public final void wait(long l, int i) throws java.lang.InterruptedException { /* compiled code */ } public final void wait() throws java.lang.InterruptedException { /* compiled code */ } protected void finalize() throws java.lang.Throwable { /* compiled code */ } } Object是java所有类的终极类,我们常见的自定义class 但是并没有继承Object(Java编译器自动引入,如果手动继承Object,也是没有问题的,java单继承 有一定的局限) public static class User { @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public int hashCode() { return super.hashCode(); } } Object类 约12个方法,下面一一解释这些方法的作用1. getClass() public final Class<?> getClass() { return shadow$_klass_; } 返回此Object的运行时类 实际结果的类型是Class<? extends |X|>其中|X|是静态类型上其表达的擦除getClass被调用。 例如,在此代码片段中不需要转换: Number n = 0; Class<? extends Number> c = n.getClass(); 2. hashCode() /* @return a hash code value for this object. * @see java.lang.Object#equals(java.lang.Object) * @see java.lang.System#identityHashCode */ public int hashCode() { return identityHashCode(this); } 返回对象的hash值,支持这种方法是为了散列表,如hashmap hashcode 的特点是: 1.只要在执行Java应用程序时多次在同一个对象上调用该方法,hashcode()始终返回相同的整数(前提是该对象的信息没有发生改变) 2.相对于两个对象来说,如果使用了equals方法比较返回true,那么这两个对象的hashcode值也是相同的 3. 对于两个对象来说,如果使用equals方法比较为false,那么这两个对象的hash值不一定要求不同,可以相同也可以不同,然而如果不同,则可以提高性能4. 对于Object类来说,不同的Object对象的hashcode是不同的(Object的hashcode 表示对象的存储地址,但是如果重写了hashcode 就不一定表示存储地址了) 3. Clone() protected Object clone() throws CloneNotSupportedException { if (!(this instanceof Cloneable)) { throw new CloneNotSupportedException("Class " + getClass().getName() + " doesn't implement Cloneable"); } return internalClone(); } 克隆方法:创建并返回此对象的副本; 对于任何对象x x.clone()!=x 而且x.clone().getClass()==x.getClass() 成立 虽然对象的基类都支持clone 但是object本身并未实现cloneable,所以自定义的类需要实现cloneable接口,否则将抛出异常 4. toString() 返回对象的字符串表示形式,一般说来,这个方法返回一个固定的模版public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } 这个在实际开发中,并不好体现,所以各大编译器都支持 重新生成toString(),如: public static class User { private String name; private int age; @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } } 5. finalize() /* @throws Throwable the {@code Exception} raised by this method * @see java.lang.ref.WeakReference * @see java.lang.ref.PhantomReference * @jls 12.6 Finalization of Class Instances */ protected void finalize() throws Throwable { } finalize()方法可以被子类对象所覆盖,然后作为一个终结者,当GC被调用的时候完成最后的清理工作(例如释放系统资源之类)。这就是终止。默认的finalize()方法什么也不做,当被调用时直接返回。 对于任何一个对象,它的finalize()方法都不会被JVM执行两次。如果你想让一个对象能够被再次调用的话(例如,分配它的引用给一个静态变量),注意当这个对象已经被GC回收的时候,finalize()方法不会被调用第二次。测试: package asange.javastudy.java.lang; /** * @author youxuan E-mail:xuanyouwu@163.com * @version 2.3.1 * @Description * @date createTime:2018/1/20 */ public class ObjectTest { public static class User { private String name; private int age; @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override protected void finalize() throws Throwable { System.out.println("finalize"); super.finalize(); } } public static void main(String[] args) throws Exception { User o = new User(); o = null; System.gc(); System.gc(); } } 运行结果: asange.javastudy.java.lang.ObjectTest finalize Process finished with exit code 0 可以看到结果:两次gc finalize 只执行了一次 6. wait() notify() notifyAll() 这三个方法是有关线程阻塞与线程唤醒 1. wait(),notify()与notifyAll()方法是本地方法,并且是final类型,无法重写 2. 调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁) 3. 调用某个对象的notify()方法能唤醒一个正在等待这个对象的monitor的线程,如果有多个线程在等待这个monitor,唤醒其中一个线程; 4. 调用notifyAll()方法能唤醒所有正在等待这个对象的monitor为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。 上面已经提到,如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。 调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁); notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。 同样地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。 nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。 这里要注意一点:notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。 举个简单的例子:假如有三个线程Thread1、Thread2和Thread3都在等待对象objectA的monitor,此时Thread4拥有对象objectA的monitor,当在Thread4中调用objectA.notify()方法之后,Thread1、Thread2和Thread3只有一个能被唤醒。注意,被唤醒不等于立刻就获取了objectA的monitor。假若在Thread4中调用objectA.notifyAll()方法,则Thread1、Thread2和Thread3三个线程都会被唤醒,至于哪个线程接下来能够获取到objectA的monitor就具体依赖于操作系统的调度了。 上面尤其要注意一点,一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。 public class ObjectTest { public static Object obj = new Object(); public static void main(String[] args) throws Exception { Object o2 = new Object(); Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); thread2.start(); } static class Thread1 extends Thread { @Override public void run() { super.run(); System.out.println("线程" + Thread.currentThread().getName() + "开始"); synchronized (obj) { try { System.out.println("线程" + Thread.currentThread().getName() + "调用了object.wait()"); obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁"); } } } static class Thread2 extends Thread { @Override public void run() { super.run(); System.out.println("线程" + Thread.currentThread().getName() + "开始"); synchronized (obj) { obj.notify(); System.out.println("线程" + Thread.currentThread().getName() + "调用了object.notify()"); } System.out.println("线程" + Thread.currentThread().getName() + "释放了锁"); } } }运行结果:线程Thread-0开始 线程Thread-1开始 线程Thread-0调用了object.wait() 线程Thread-1调用了object.notify() 线程Thread-1释放了锁 线程Thread-0获取到了锁 Process finished with exit code 0 1、Object类的七大native方法:registerNatives()、getClass()、hashCode()、clone()、notify()、notifyAll()、wait(long)native关键字修饰的方法称为本地方法,这些方法并不是用java实现的,考虑到实现的性能问题,大多是由C/C++编写的程序,编译成dll文件,再由java去加载这些dll文件,就可以通过java来调用dll中的函数了1)**registerNatives()方法:**主要是将用C/C++语言写的一些方法,如hashCode、wait、notify等方法加载到jvm中,感兴趣的可以去OpenJDK中查看相关C的代码如下static JNINativeMethod methods[] = { {“hashCode”, “()I”, (void *)&JVM_IHashCode}, {“wait”, “(J)V”, (void *)&JVM_MonitorWait}, {“notify”, “()V”, (void *)&JVM_MonitorNotify}, {“notifyAll”, “()V”, (void *)&JVM_MonitorNotifyAll}, {“clone”, “()Ljava/lang/Object;”, (void *)&JVM_Clone},};JNIEXPORT void JNICALLJava_java_lang_Object_registerNatives(JNIEnv *env, jclass cls) { (*env)->RegisterNatives(env, cls,methods, sizeof(methods)/sizeof(methods[0])); } 2)**getClass()方法:**我们可以看到这个方法是由final修饰的,因此不能被重写的,对final关键字不太了解的,可以看一下我的另一篇文章:Java源码解析之 final关键字的使用详解//class 是一个类的属性,能获取该类编译时的类对象 System.out.println(Bird.class);//class com.somta.test.commonuseobj.objectobj.Bird//getClass() 是一个类的方法,它是获取该类运行时的类对象 System.out.println(bird.getClass());//class com.somta.test.commonuseobj.objectobj.Bird 3)hashCode()方法:返回一个整数的HashCode值,该值依赖于内部表示堆的对象的指针,一般情况下,该方法都会被重写。关于hashCode与equals的关系,我们只需要记住下面四句话(前提:需要重写equals和hashCode方法,String、Integer、Boolean、Double等都重写了这两个方法,不重写HashCode方法,任何对象的hashCode都是不相等的,并且equals方法也会使用Object中的equals方法,此时equals和外汇返佣http://www.fx61.com/==是等价的,equals比较的是栈中的引用,而非对象的值)1、如果两个对象相等,则它们的hashCode值一定相等 2、如果两个对象不相等,则它们的hashCode值不一定都不相等 3、如果两个对象的hashCode值相等,两个对象不一定相等 4、如果两个对象的hashCode值不相等,两个对象一定不相等4)clone()方法: 将一个对象克隆出一个新对象,要实现clone的对象一般需要先实现Cloneable接口才能达到克隆的目的,克隆其实也分“浅克隆”,“深克隆” ,Object类的克隆属于“浅克隆”,关于克隆的知识,后续在写文章说明,此处就不展开了5)notify()、notifyAll()、wait(long)方法: 这些方法后续在多线程中在具体讲解2、Object类的常用方法:equals()、toString() 1)、equals()方法我们可能在以前的面试中被问到过关于 == 和 equals的区别,可能大多人的回答是:“==运算符通常是用来比较基本类型的值是否相等或者比较对象的引用是否相等;equals比较的是两个对象是否相等”,这种说法其实并不太正确,不妨我们先看Object中关于equals()方法的源码public boolean equals(Object obj) { return (this == obj); } Object类是所有类的子类,如果一个类没有重写equals()方法,那它就会使用父类的的equals()方法,通过上面的代码可以看出其实在Object类中,==运算符和equals()方法是等价的,其实上面的说法只适用于那些重写了Object类equals方法的类而言是正确的,比如String等。注意:重写equals()方法的时候,必须要重写hashCode方法,以维护hashCode的约束,确保equals()方法声明相等的对象具有相同的hashCode值2)、toString()方法public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } getClass().getName()返回了该类的全类名,Integer.toHexString(hashCode())返回了以16进制无符号整数的形式返回了此hashCode的字符串,这个是根类Object的实现,子类中一般都会重写该方法
|