JDK源码阅读计划(Day1) Object类

JDK8源码阅读计划(一) Object类

/**
 * Class {@code Object} is the root of the class hierarchy.
 * Every class has {@code Object} as a superclass. All objects,
 * including arrays, implement the methods of this class.
 *
 * @author  unascribed
 * @see     java.lang.Class
 * @since   JDK1.0
 */

Object类是Java中所有类的父类,每一个类的父类都是Object类,
Object类也是唯一一个没有父类的类.

一些预备知识:

1.类加载的时候,首先会执行静态代码块中的
内容.(先执行父类的静态代码块,再执行子类的静态代码块)

2.native关键字: Java中的本地方法,由C或者C++语言编写

3.final修饰的方法不可被重写,protected修饰的方法只能被子类或者本类调用

  • registerNatives()方法
private static native void registerNatives();
    static {
        registerNatives();
    }

Object类加载的时候,先会执行这个本地方法,具体是进行一些与系统有关的
方法调用.

事实上,不光是Object类,甚至System类、Class类、ClassLoader类、Unsafe类等等
类加载的时候都会执行上述代码块.

那什么是本地方法? 什么是Java方法?

  • Java 方法

由Java语言编写,编译成.class字节码,因此Java方法
是平台无关的.

  • 本地方法

由非Java语言编写,存储在动态链接库中,格式每个平台都会不同.
在调用本地方法的时候,JVM先需要装载包含这个本地方法的动态库.

由于Java语言不能直接与操作系统打交道,只有C语言或者汇编语言才能,
因此Java只能依靠本地方法作为Java程序和底层主机操作系统的连接方法

回到这个方法本身,这个本地方法的作用就是完成对该类中其他本地方法的注册.
那到底注册了哪些本地方法呢?怎么注册?

可以看到Object类除了这个本地方法外,还有hashcode(),clone()等本地方法.
凡是包含registerNatives()本地方法的类,同时也包含了其他本地方法.所以当包含registerNatives()方法的类被加载的时候,
注册的方法就是该类所包含的除了registerNatives()方法以外的所有本地方法

那么一个Java程序如果想要调用一个本地方法,需要执行哪些步骤?

1.通过System.loadLibrary()将包含本地方法实现的动态链接库加载进内存

2.当Java程序需要调用本地方法时,JVM在加载的动态文件中定位并链接该本地方法,从而得以执行本地方法

而registerNatives()方法的作用就是取代第二步,让程序主动将本地方法链接到调用方,当Java程序需要调用本地方法时就可以直接调用,
而不需要虚拟机再去定位并链接.

而且这样做的话,如果本地方法在程序运行中更新了,可以通过调用registerNative方法进行更新.

  • getClass()方法
public final native Class<?> getClass();

JVM中每一个对象在类加载的时候都会对应一个java.lang.class对象,
对象调用该方法可以获取这个runtime class类的信息,
然后再通过反射来访问类的全部信息,包括函数和字段.

  • hashCode()方法
public native int hashCode();

1.主要用于实现HashMap等数据结构

2.在Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,
前提是将对象进行 equals 比较时所用的信息没有被修改。

3.如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。

4.如果根据 equals(Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不要求一定生成不同的整数结果。
但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。

5.值得注意的是,Object类的hashCode()方法具体是怎么生成的呢?
注释里其实写道,一种常用的策略是根据这个object的虚拟地址生成(不同地址object就会
生成一个独一无二的hashcode)但Java并没有使用这种策略.具体实现可参考(https://blog.csdn.net/qq_41264055/article/details/80272299)

  • equals()
    public boolean equals(Object obj) {
        return (this == obj);
    }

1.如果object为null返回false

2.Object类的equals方法在比较对象的时候,只有对象A等于对象B(A==B,内存地址相同)
的时候才返回true

3.通常需要同时重写equals方法和hashcode()方法

因为假设现在有两个对象


Integer I1 = new Integer(4);
Integer I2 = new Integer(4);

当使用Hashmap的时候,这两个对象因为值相同,应该视为
相同的对象,这就需要重写equals方法使得判断相等的逻辑为
值相等.

而重写了equals方法就需要重写hashcode方法,否则就会产生歧义:
两个对象相同,产生的hashcode却不相同.

那怎么重写呢?

举个例子:

class Coder {
    private String name;
    private int age;

    @Override
    public boolean equals(Object o) {
        //自反
        if (this == o) {
            return true;
        }
        //非空且所属的类应该相同
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        //最后才比较成员
        Coder coder = (Coder) o;
        return coder.name.equals(name) && coder.age == age;
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = result * 31 + name.hashCode();
        result = result * 31 + age;

        return result;
    }

}
  • clone 方法
    protected native Object clone() throws CloneNotSupportedException;
  • 所要序列化的类必须要实现Cloneable接口,其对象才能调用该方法

  • 返回的是Object,通常需要强制类型转换

  • (1)x.clone() != x为true (但其成员对象还是指向同一个堆内存)
    (2)x.clone().getClass() == x.getClass()为true
    (3)x.clone().equals(x)一般情况下为true,但这并不是必须要满足的要求

  • clone默认实现的是浅拷贝(只克隆了自身对象和对象内实例变量的地址引用,它内部的实例变量还是指向原先的堆内存区域)

  • 深拷贝则是拷贝包括自身以及包括成员的所有对象

  • 实现深拷贝一个方法是需要该类实现Cloneable接口并且重写clone方法并设置为public,调用super.clone()复制本身对象,然后对象每个成员都需要调用clone来赋值来实现深拷贝:具体可参考该博客介绍: https://blog.csdn.net/zhangjg_blog/article/details/18369201

  • 实现深拷贝的另外一个方法是实现序列化接口,要拷贝对象写入到内存中的字节流中,然后再从这个字节流中读出刚刚存储的信息,作为一个新对象返回.

下面给出一个简单的例子

package ObjectClass;

import java.io.*;

//Shallow Copy
class Person implements Cloneable {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}

//Deep Copy method1
class Animal implements Cloneable{
    Person host;//主人
    int age;//年纪
    Animal(Person person,int age){
        this.host=person;
        this.age=age;
    }

    @Override
    public Object clone() {
        try {
            Animal animal = (Animal)super.clone();
            animal.host = (Person) host.clone();
            return animal;
        } catch (CloneNotSupportedException e){
            e.printStackTrace();
            return null;
        }
    }
}

//Deep copy method 2
public class test {

    public static <T extends Serializable> T clone(T obj) {
        T cloneObj = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close();

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);

            cloneObj = (T)ois.readObject();
            ois.close();

        }catch (Exception E){
            E.printStackTrace();
        }
        return cloneObj;
    }

    static class Person2 implements Serializable{
        String name;
        int age;
        Person2(String name,int age){
            this.name=name;
            this.age=age;
        }
    }

    //测试类2
    static class Animal2 implements Serializable{
        Person2 host;//主人
        int age;//年纪
        Animal2(Person2 person,int age){
            this.host=person;
            this.age=age;
        }
    }

    public static void main(String[] args) {

        Person p1 = new Person("zhk", 22);
        Person p2 = (Person) p1.clone();

        Person2 p3 = new Person2("zhk", 22);
        Person2 p4 = clone(p3);

        System.out.println(p1==p2?"p1==p2":"p1!=p2");
        System.out.println(p3==p4?"p3==p4":"p3!=p4");

    }

}

输出:

p1!=p2
p3!=p4

  • toString 方法
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
  1. 建议每一个子类都重写该方法
  2. 用该对象的类名+该对象的hashcode十六进制来唯一标识字符串形式的对象
  • notify方法
public final native void notify();

1.随机唤醒一个在对象的monitor等待的线程,即调用wait方法后释放锁的线程
2.唤醒的线程从等待队列进入就绪队列,获得重新竞争锁的机会,只有竞争锁成功之后才能获得CPU资源而运行.
3.在以下场景线程会成为该对象的monitor
1)执行synchronized修饰的实例方法
2)执行synchronized修饰的静态方法
3)执行synchronized修饰的包含该对象的同步块代码

  • notifyAll()
   public final native void notifyAll();

与notify的区别在于它是唤醒在等待队列中的所有线程,而notify是随机唤醒一个.

  • wait方法
    public final native void wait(long timeout) throws InterruptedException;

1.使得当前monitor 的owner线程放弃对当前monitor的掌控权,当前线程会释放obj锁,并且进入等待队列中直到调用notify或者notifyall或者线程中断或者超时才能结束等待.
2.这个方法必须要在一个循环内调用

synchronized (obj) {//当前线程获得monitor掌控权,monitor object为obj
              while (condition does not hold)
                  obj.wait(timeout, nanos);
              ... // Perform action appropriate to condition
          }
  • finalize 方法
    protected void finalize() throws Throwable { }

1.当对象没有引用的时候(GC不可达),GC会判断该对象是否重写了finalize方法.如果没有重写可以直接将其回收.否则如果没有执行过的话,会把对象放入F-queue队列,由一个低优先级的线程执行这个队列中对象的finalize()方法.

2.如果重写的finalize让对象重新设置一个引用,那么就可以使得对象复活.因为执行完finalize方法之后,GC会再次判断这个对象是否可达,如果不可达就进行回收.

有些表述可能不太准确或者有错误,请各位指正!

Ref

https://www.jianshu.com/p/08c8890af5a0
https://blog.csdn.net/Saintyyu/article/details/90452826
https://blog.csdn.net/qq_41264055/article/details/80272299
https://blog.csdn.net/zhangjg_blog/article/details/18369201
https://www.cnblogs.com/Smina/p/7189427.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值