Java底层基础-Object底层源码分析

本文详细探讨了Java中Object类的基础知识,包括其作为所有类的超类、继承方式、native和final关键字的使用。重点分析了Object类的构造方法、常用成员方法如getClass()、hashCode()、equals()、clone()和toString(),以及finalize()方法的工作原理。同时,文章还涵盖了面试中常见的深拷贝与浅拷贝概念、==与equals的区别以及hashCode和equals方法的运用。最后,强调了覆盖equals时需同时覆盖hashCode的重要性,并提供了相关参考资料链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Object概述

Object是java所有类中的超类,所有的类都继承object类,是java继承体系中真正的根节点;
Object是每个java类的基类,如果没有明确指出基类,Object就被认为是当前定义的类的基类;(Object 类可以显示继承,也可以隐式继承)

基类:在面向对象设计中,被定义为包含所有实体共性的class类型,被称为“基类”;
超类/父类:被继承的称为父类或者超类。基类父类超类其实是同种意思

所以对于Object中声明的方法,在我们所见到的类(包括我们自己写的)都具有这些方法,我们一般不会直接使用Object的对象,而是重写继承自Object类的方法;

二、Object源码分析

1. 继承方法

显示继承
	public class Student extends Object{
		// ......
	}
隐式继承
	public class Student{
		// ......
	}

2. native,final关键词介绍

  • native
    被native修饰的方法是用C/C++在动态库中实现的,然后通过JNI(java Native Inteface)调用。
    Java语言本身不能对操作系统底层进行访问与操作,但可以通过JNI接口来调用其他语言来实现对底层的访问,JNI已加入Java标准
  • final
    被final修饰的方法表示为最终类,不可修改不可被子类重写

3. 构造方法

Object类中没有显示的提供构造方法,由编译器默认提供的

4. 常用成员方法介绍

4.1 全部成员方法

线程相关的几个方法此处暂不进行介绍

	private static native void registerNatives();
    static {
    	   registerNatives();
    }
    
    public final native Class<?> getClass();
    
    public native int hashCode();
    
    public boolean equals(Object obj) {
       	return (this == obj);
    }
    
    protected native Object clone() throws CloneNotSupportedException;
    
    public String toString() {
       	return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    
    protected void finalize() throws Throwable { }
4.2 registerNatives() 方法
	private static native void registerNatives();
    static {
        registerNatives();
    }

其主要作用是将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦。函数的执行是在静态代码块中执行的,在类首次进行加载的时候执行。

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

方法用于获取对象的运行时对象的类;一般在反射时,我们想得到某个类的class对象,可以通过对象.getClass()获得;

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

返回对象的哈希码(一个整数值),表示在java内存中存储的哈希表中的位置。

4.5 equal() 方法
	public boolean equals(Object obj) {
        return (this == obj);
    }

方法是比较俩个对象是否相等的主要方法,通过源码我们可以看出如果某个类不重写equals(Object obj)方法,那么equals(Object obj)就是==,即表示比较两对象的内存地址(引用地址)是否相等。
如果重写的话,则比较的是值是否相等,类似String类就重写了此方法,即String类在进行equal比较的时候就是比较两个对象的值内容是否相等。

4.6 clone() 方法
	protected native Object clone() throws CloneNotSupportedException;

方法是返回该对象的副本,副本的定义取决于该方法,即是深拷贝还是浅拷贝;
方法有protected修饰,所以子类实现需要覆盖此方法并实现Cloneable接口,才能在外部实现clone功能。

被复制的类需要实现Cloneable接口(否则抛异常),该接口为标记接口,不含任何方法;
覆盖clone(),访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象 )

在这里插入图片描述
浅拷贝: 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝: 深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大

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

Object的该方法返回了类的名称加上’@’,再加上此类哈希码的16进制表示,如:I[@1a46e30,其中I[表示一个整型数组。
一个好的习惯,应该是为每个类,特别是模型类重写一个toString()方法,以便用户能够通过这个方法获得对象状态的必要信息。

4.8 finalize() 方法
	protected void finalize() throws Throwable { }

这是GC清理对象之前所调用的清理方法,是回调方法,我们可以覆盖这个方法写一些清理的代码。(GC清理时会自动扫描没有引用的对象,即对象赋值为null)。可以通过调用System.runFinalization()或System.runFinalizersOnExit()强制GC清理该对象前调用finalize()方法,GC有时不会调用对象的finalize()方法(由JVM决定)
: 此方法与C++中的析构函数相同? 不同,析构函数一定会执行,但finalize函数不一定会执行。
总结
垃圾回收内存是JVM的一个不确定操作,通常会在系统濒临内存溢出可能会回收,所以你不要指望垃圾回收来控制finalize方法的调用。
finalize方法通常是用于清理一些非本地方法(native),和一些对象安全释放校验的操作

三、常见面试问题

3.1 深拷贝和浅拷贝的概念

浅拷贝核心是拷贝了内存地址;两个对象指向同一个地址引用,修改一个对象另一个对象也会受到影响
深拷贝核心是拷贝了分内存地址空间,java内存重新开辟了一个空间用来存放对象;两个对象指向不同的地址引用,修改一个对象不会对一个对象造成影响

3.2 ==和equal的区别

==: 基本类型比较的是值是否相同;引用类型比较的是内存地址是否相同
equal: Object类比较的是地址值是否相同,其他类重写了equal方法,具体看重写的逻辑。(String重写了,比较的是值是否相同)

3.3 hashCode方法和equal方法的区别

hashCode方法和equal方法的应用场景是一样的,一般都是用来对比两个对象是否相等;

  • 为什么hashCode方法的效率比equal方法效率高
    因为重写的equal里的逻辑比较全面和负责,效率就比较低;而利用hashCode方法进行对比,调用底层方法只需要生成一个hash值就可以,效率很高;
  • 为什么不直接使用hashCode方法单独比较两个对象,为什么要两个合起来用
    首先是因为hashCode方法无法判断两个对象一定相等,因为hashCode方法是通过hash函数来获取hash值,两个不同的对象可能出现hash碰撞。也就是两个不同对象可能存在相同的hash值,这也就是为什么不能使用hashCode单独判断两个对象相等。
    其次为什么要合用,为什么不直接使用equal方法进行判断?equal方法进行判断可以确定两个对象是否是相等的(此处的相等取决于程序员重写的equal逻辑,可以是值相等也可以是地址相等)但是上面说到,equal方法的逻辑比较全面和复杂,效率较慢。hashCode方法效率较快,先使用此方法可以先排除掉一大部分hash值不一样的对象(因为其实好的hash函数出现hash碰撞概率会很小)。剩下的小概率部分也就是hash值相同但是可能是不同对象的就由equal方法进行判断。
    这样两个合用起来且hashCode优先判断,对一些hash值不一样的对象可以直接排除在外,对一些特殊的对象数据则在进行第二个equal方法判断。大大提高了判断效率。

拓展: 关于equal和hashCode的约定(主要是在使用HashSet进行存储时需要有一些约定)
当equals()方法被覆盖时,hashCode()方法也必须被覆盖。
如果两个对象相等,则它们的哈希码也必须相等。
如果两个对象不相等,则它们的哈希码没有限制(它们的哈希码可以相等或不相等)。
如果两个对象具有相同的哈希码,则它们的相等性不受限制(它们可以相等或不相等)。
如果两个对象具有不同的哈希码,则它们不能相等。

参考资料:
https://blog.youkuaiyun.com/sinat_30973431/article/details/81784527
https://juejin.cn/post/6844903760179773454#heading-7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值