源码系列(三)----Object类

    java是一门面向对象的编程语言,所有的事物都可以抽象为对象。

    Object类是所有类的超类,位于java.lang包下,可以看做是所有类的父类,因此所有类都具备它的实现方法,或自己重写,或直接使用。

    1.注册本地函数

    在Object中存在这样头一段代码

private static native void registerNatives();
static {
    registerNatives();
}

    在静态代码块中,调用了registerNatives()函数,也就是说在Object类加载过程中,便执行了registerNatives()函数。

    这个函数的作用是向JVM(Java Virtual Machine,我们的java不是直接运行在操作系统之上,而是运行在java虚拟机上)注册Object的本地函数hashCode()、clone()、notify()、notifyAll()、wait()方法(不包含getClass())。

    2.getClass()

public final native Class<?> getClass();

    getClass()用于获取某对象运行时类对象,这是获取运行时类对象的方法之一,另一种方式是Class.forName(包名 + 类名)的方式。

    同一类的实例都是有该类的类对象生成。创建新的对象可以通过new 的方式,通过Class对象,我们可以使用它的newInstance()函数调用其无参构造函数,完成新对象的创建;如果需要使用有参构造函数,需要获取Constructor对象(以后有机会在介绍)。

    运行以下代码:

class MyTest{
    static int count;
    public MyTest(){
        System.out.println("创建的第" + (++count) + "个对象实例...");
    }
}
public class Main {

    public static void main(String[] args) throws Exception {

        // MyTest的对象实例
        MyTest myTest = new MyTest();

        // 获取运行时类对象
        Class clazz1 = myTest.getClass();
        Class clazz2 = Class.forName("com.guaniu.MyTest");

        // 创建新的对象实例
        MyTest instance1 = (MyTest) clazz1.newInstance();
        MyTest instance2 = (MyTest) clazz2.newInstance();
        System.out.println("clazz1 == clazz2:" + (clazz1 == clazz2));
        System.out.println("instance1 == instance2:" + (instance1 == instance2));
    }

}

    得出结果:

    3.hashCode()

public native int hashCode();

    hashCode()方法返回的是32位的int类型值,在底层实现中是将对象地址的转换。这个方法主要是用于支持哈希表的数据结构,如HashCode。

    hashCode需要满足的原则是:

     a.在同一个java程序中对用同一个对象的多次调用,返回的数值必须是一致的,在不同的Java程序中不需要满足此规则;

     b.如果通过equals方法判断两个对象相等,那么两个对象hashCode()必须返回相等的数值;

     c.如果两个对象不相等,不需要要求它们的hashCode()方法返回相同的数值。

    4.equals()

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

    equals()方法用于判断两个对象是否相等,没有要求相同(==),虽然在Object的原有实现中是“==”的方式。

    equals() 方法需要满足一下性质:

     a.自反性(reflexive):对于任何非null引用x,x.equals(x)为true;

     b.对称性(symmetric):对于任何非空引用x和y,如果x.equals(y)为true,y.equals(x)必须也为true;

     c.传递性(transitive):对于任何非空引用x、y和z,如果x.equals(y)为true,y.equals(z)为true,那么x.equals(z)也为true;

     d.一致性(consistent):对于任何非空引用x和y,对于多次调用x.equals(y)始终返回的是true和false,不会改变。

     e.对于任意非空引用x,x.equals(null)返回false。

    5.clone()

protected native Object clone() throws CloneNotSupportedException;

    可以看到这个方法是使用protected关键字修饰的,这方法用于返回当前对象的复制。然而,通过对象调用这个方法需要实现Cloneable接口,然后重写这个方法(如果需要在其他包中调用,还需要将其声明为public方法),否则会抛出CloneNotSupportedException。

    Cloneable接口是一个空接口,并没有包含clone()方法的声明!!!但是仅实现Cloneable接口,而不重写clone()方法是不能使用这个方法的。

public interface Cloneable {}

    案例分析1

class MyTest implements Cloneable{
    int i1;
    Integer i2;
    String str;
    Content content = new Content();

    public MyTest(int i1, int i2, String str){
        this.i1 = i1;
        this.i2 = i2;
        this.str = str;
    }

    @Override
    protected MyTest clone() throws CloneNotSupportedException {
        return (MyTest) super.clone();
    }

    @Override
    public String toString() {
        return "MyTest{" +
                "i1=" + i1 +
                ", i2=" + i2 +
                ", str='" + str + '\'' +
                ", content=" + content +
                '}';
    }
}

class Content{
    String value = "test";

    @Override
    public String toString() {
        return "Content{" +
                "value='" + value + '\'' +
                '}';
    }
}
public class Main {

    public static void main(String[] args) throws Exception {

        MyTest myTest = new MyTest(128, 128, "aaa"); // 因为Integer数值在[-128,127]之间时会从缓存中获取,因此我的测试用例选择大于127的值
        MyTest cloneMyTest = myTest.clone();

        System.out.println("myTest:" + myTest.toString());
        System.out.println("cloneMyTest:" + cloneMyTest.toString());

        System.out.println("myTest.i2 == cloneMyTest.i2:" + (myTest.i2 == cloneMyTest.i2));
        System.out.println("myTest.str == cloneMyTest.str:" + (myTest.str == cloneMyTest.str));
        System.out.println("myTest.content == cloneMyTest.content:" + (myTest.content == cloneMyTest.content));

        cloneMyTest.i1 = 129;
        cloneMyTest.i2 = 130;
        cloneMyTest.str = "bbb";
        cloneMyTest.content.value = "new content";

        System.out.println("myTest:" + myTest.toString());
        System.out.println("cloneMyTest:" + cloneMyTest.toString());
    }

}

     运行结果:

    结论:a.克隆对象的基本类型的成员变量的修改不会影响到原来的对象;

               b.克隆对象的包装类型的成员变量的修改不会影响到原来的对象;

               c.克隆对象的String类型的成员变量的修改不会影响到原来的对象;

               d.克隆对象的自定义类型【引用类型】的成员变量的修改会影响原来的对象;

      解决办法时Content类同样需要实现Cloneable接口,在MyTest类的clone()方法中,在克隆对象返回之前,调用content的clone()方法,如下所示:

class MyTest implements Cloneable{
    int i1;
    Integer i2;
    String str;
    Content content = new Content();

    public MyTest(int i1, int i2, String str){
        this.i1 = i1;
        this.i2 = i2;
        this.str = str;
    }

    @Override
    protected MyTest clone() throws CloneNotSupportedException {
        MyTest myTest = (MyTest) super.clone();
        myTest.content = this.content.clone();
        return myTest;
    }

    @Override
    public String toString() {
        return "MyTest{" +
                "i1=" + i1 +
                ", i2=" + i2 +
                ", str='" + str + '\'' +
                ", content=" + content +
                '}';
    }
}

class Content implements Cloneable{
    String value = "test";

    @Override
    public String toString() {
        return "Content{" +
                "value='" + value + '\'' +
                '}';
    }

    @Override
    protected Content clone() throws CloneNotSupportedException {
        return (Content) super.clone();
    }
}
public class Main {

    public static void main(String[] args) throws Exception {

        MyTest myTest = new MyTest(128, 128, "aaa"); // 因为Integer数值在[-128,127]之间时会从缓存中获取,因此我的测试用例选择大于127的值
        MyTest cloneMyTest = myTest.clone();

        System.out.println("myTest:" + myTest.toString());
        System.out.println("cloneMyTest:" + cloneMyTest.toString());

        System.out.println("myTest.i2 == cloneMyTest.i2:" + (myTest.i2 == cloneMyTest.i2));
        System.out.println("myTest.str == cloneMyTest.str:" + (myTest.str == cloneMyTest.str));
        System.out.println("myTest.content == cloneMyTest.content:" + (myTest.content == cloneMyTest.content));

        cloneMyTest.i1 = 129;
        cloneMyTest.i2 = 130;
        cloneMyTest.str = "bbb";
        cloneMyTest.content.value = "new content";

        System.out.println("myTest:" + myTest.toString());
        System.out.println("cloneMyTest:" + cloneMyTest.toString());
    }

}

    代码运行结果:

    为什么会产生这样的结果呢?

    首先先介绍两个概念:

    浅拷贝:对于拷贝对象内部的引用类型变量,只拷贝了原有对象引用成员变量的引用,两个引用实际指向的还是同一个对象。

    深拷贝:对于拷贝对象内部的引用类型变量,对原有对象引用所指向的对象进行了拷贝,两个引用指向的不是同一个变量。

    java中对于基本类型成员变量来说,拷贝的就是值;对于引用类型来说,默认的拷贝类型就是浅拷贝(不管是包装类、String、还是其他引用类型)。

    在上面的例子中,对Integer和String类型拷贝是引用拷贝,也就是浅拷贝,对于赋新值得过程中,生成了新的对象,使他们指向了新生成的对象。

    6.toString()

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

    toString()方法就是对象的字符串展示方法,默认是【类型 + @ + hash码十六进制表示】

    7.notify()/notifyAll()

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

    这两个方法用于唤醒在当前对象锁等待的线程,notify()随机唤醒一条等待线程,notifyAll()唤醒所有等待线程,这些线程竞争对象的监视器锁权限,实际同一时刻只能有一条线程能够获得锁权限。

    使用notify/notifyAll的线程当前应该通过synchronized持有这对象所,而被唤醒的线程则等待该线程退出同步方法或者同步代码块,才能获取到锁。

   8.wait()

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

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 >= 500000 || (nanos != 0 && timeout == 0)) {
       timeout++;
    }

    wait(timeout);
}

public final void wait() throws InterruptedException {
    wait(0);
}

    可以看到wait/notify方法都是使用final关键字修饰的,属于不能被重写的方法。

    wait()用于使当前线程在其调用位置等待,它会释放锁的占用,中断该线程或者其他线程调用notify/notifyAll可使该线程脱离该状态。

    wait有三种方法可供使用:

     a.无参,一致等待直到其他线程使用notify/notifyAll;

     b.一个参数timeout(毫秒),等待timeout的时间直到其他线程使用notify/notifyAll或者超时;

     c.两个参数timeout(毫秒),nanos(纳秒),可以看到,对于纳秒的计算实际上是类似四舍五入的形式,等待round(timeout + nanos)的时间直到其他线程使用notify/notifyAll或者超时;

    9.finalize()

protected void finalize() throws Throwable { }

    当前对象回收前会被自动调用,个人不要使用!!!。

 

    总结:

    1、Object是Java中所有类的超类;

    2、深拷贝与浅拷贝的认识和使用;

    3、对象锁等待、与唤醒方法的认识。

   wait()和notify()涉及到了多线程的内容,具体内容后面再细讲。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值