zoukankan      html  css  js  c++  java
  • java源码解析之Object类

    一、Object类概述

      Object类是java中类层次的根,是所有类的基类。在编译时会自动导入。Object中的方法如下: ![](https://img2018.cnblogs.com/blog/1195929/201904/1195929-20190413184508312-1759109771.png)

    二、方法详解

      Object的方法可以分成两类,一类是被关键字`final`修饰的的方法,这类方法能被子类覆盖。另一类是没有`final`修饰,它们可以被子类重写。

    构造方法:Object()

      在Object中没有显式的构造方法,这个只是创建一个Object对象,没有什么可以说的。

    getClass()

      getClass被`final`修饰,不能被子类重写。它返回的是正在运行的对象的Class对象。并且本方法被`native`修饰,其具体实现是在本地C(C++)方法。应为java无法与一些底层系统直接交互,这个时候就可以通过native方法通过非java语言间接交互。想更多的了解`native`可以参考这篇文章[(了解native)](https://xzknet.iteye.com/blog/274122),你也可以自己在java中通过native调用c写的方法[(看这里)](https://blog.csdn.net/zmx729618/article/details/50779924)。

    equals(Object obj)

      表明`obj`是否“equals to”本对象。在Object中的equals返回的是两个对象是否相同(指两个对象是同一个对象)
    public boolean equals(Object obj) {
        return (this == obj);
    }
    

    如果我们要实现本对象上逻辑相等的概念时,可以考虑在子类中重写该方法。但必须遵守一些准则:

    • 自反性(reflexive)。对任务非null的对象x,x.equals(x)必须返回true
    • 对称性(symmetric)。对任何非null的x,y,x.equals(y)返回true,y.equals(x)也必须返回true
    • 传递性(transitive)。对任何非null的x,y,z。x.equals(y)返回truey.equals(z)返回true,则x.equals(z)必须返回true
    • 一致性(consistent)。对任何非null的引用x,y,只要对象内的信息没有被修改,则多次调用的结果一致。
    • 非null的引用x,x.equals(null)返回false

    hashCode()

      该对象放回对象的hash值,这个是为了可以应用在一些使用对象hash的容器中,如`HashMap`等。这也是一个本地方法调用。在每一个覆盖了equals方法的类中都必须覆盖hashCode(这是Object.hashCode的通用约定)。 * 只要equals中所用到的对象的信息没有被修改,那么多次调用hashCode的返回值相同。 * 如果两个对象的equals比较是相同的,那么两个对象调用hashCode返回相同的整数值。 * 如果两个对象的equals比较是不同的,两个对象的hashCode返回值不一定不同。但是如果我们提供equals不同的对象中提供不同的hashCode返回值,可能提高散列表的性能。 你也可以查看一下hashMap或者HashSet的源码中用于对象的比较,就更能体会到为什么要在重写equals的情况下重写hashCode了。

    clone()

      创建并返回本对象的一个副本,是一个native方法。`x.clone()!=x`,`x.clone().getClass()==x.getClass()`,`x.clone().equals(x)==true`。但是如果需要覆盖本方法,上述的要求并不是必须遵守的。如果一个类实现了Cloneable,Object的clone方法就返回该对象的逐域拷贝(返回的对象和原来的对象不易同一个对象),如果没有实现Cloneable接口,将会抛出`CloneNotSupportedException`异常。测试代码下面,你可以将Cloneable接口去掉试试:
    public class Main {
    
    public static void main(String args[]) {
      Student stu = new Student("fanl",18);
      Student classMate = new Student("xiaoming",18);
      stu.setClassMate(classMate);
      Student stuCopy = null;
      try {
         stuCopy = (Student) stu.clone();
      } catch (CloneNotSupportedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      System.out.println(stu);
      System.out.println(stuCopy);
      System.out.println(stu.classMate);
      System.out.println(stuCopy.classMate);
    }
    }
    
    class Student implements Cloneable{
    public String name;
    public int age;
    public Student classMate;
    
    public void setClassMate(Student classMate) {
      this.classMate = classMate;
    }
    public Student(String name,int age) {
      this.name = name;
      this.age = age;
    }
    public Object clone() throws CloneNotSupportedException {
      return super.clone();
    }
    
    }
    

    还有一点需要注意的是Object.clone()实现的是浅拷贝,如果对象的域中含有对象A的引用,那么copy的只是引用(即clone得到的对象和原对象指向同一个对象A)

    toString()

       Object中该方法的实现如下:
    public String toString() {
          return getClass().getName() + "@" + Integer.toHexString(hashCode());
      }
    

    可以看出该方法的返回值就是类名加上hashCode的十六进制表示,所以在子类中最好重写本方法,使其更便于阅读。

    notify(),wait()

      `notify()`唤醒一个在本对象监视器上等待的的线程,`wait()`使本进程在某个对象锁上等待。`Obj.notify()`,`obj.wait()`必须要与`synchronized(Obj)`一起使用,也就是notify和wait是针对已经获取了Obj锁进行的操作,`notify()`与`wait()`是相对的,`wait()`是线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。`wait()`和`notify()`都执行在synchronize语句块中。 下面是一个例子,会按顺序的打印出"ACB"
    public class Main implements Runnable {
    
    private String name;
    private Object prev;
    private Object next;
    
    private Main(String name, Object prev, Object next) {
        this.name = name;
        this.prev = prev;
        this.next = next;
    }
    
    @Override
    public void run() {
        int count = 10;
        while (count > 0) {
            synchronized (prev) {
                synchronized (next) {
                    System.out.print(name);
                    count--;
    
                    next.notify();
                }
                try {
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    
    public static void main(String[] args) throws Exception {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();
        Main pa = new Main("A", c, a);
        Main pb = new Main("B", a, b);
        Main pc = new Main("C", b, c);
    
    
        new Thread(pa).start();
        new Thread(pb).start();
        new Thread(pc).start();
    }
    }
    

    next.notify();会唤醒在next上waiting的进程,之后将自己waiting在pre的对象锁上。这样在一个对象锁上唤醒一个进程,在另一个对象锁上wait本进程,循环往复。另外还有两个重载的wait方法,目的是为等待加上超时设置。

    注意:Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。

    notifyAll()

      唤醒在这个对象上等待的所有进程。

    finalize()

    当垃圾收集确定不再引用对象时,由对象上的垃圾收集器调用。子类覆盖finalize方法来处理系统资源或执行其他清理。在《Effective Java》中作者建议我们避免使用该方法。详细说明可以参考《Effective Java》(经典之作)。

    总结

      在写本博文时,参考了《Effective Java》,其中有些东西本文只是略微提及。读者可以翻阅这本书了解更多具体情况。其中Object中的方法的准确使用在《Effective Java》(第二版)的第三章有详细的说明。
  • 相关阅读:
    Servlet生命周期
    DAO 开发模式的几个类
    Iterator用法
    mysql相似于oracle的to_char() to_date()方法
    Java Web页面跳转
    JSP 连接MySQL 5.1
    采用DOM进行表格的修改操作
    使用css让XML文件按照HTML的风格显示出来
    正则表达式Regular Expression
    什么是“堆”,"栈","堆栈","队列",它们的区别
  • 原文地址:https://www.cnblogs.com/Mrfanl/p/10702379.html
Copyright © 2011-2022 走看看