Java基础教程之Object类是怎么回事?

news2024/10/5 17:26:25

前言

在前面的文章中,壹哥跟大家说过,Java是面向对象的编程语言,而在面向对象中,所有的Java类都有一个共同的祖先类,这就是Object。那么Object都有哪些特性呢?今天壹哥就简单跟大家分析一下。

------------------------------前戏已做完,精彩即开始----------------------------

全文大约【4500】字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考......

一. Object简介

1. 简介

在了解Object中的常用方法之前,我们先来看看Object类的源码,如下所示:

 

从Object类的源码注释可以知道,Object类是Java中所有类的父类,相当于是Java中的”万类之王“,处于最顶层。所以在Java中,所有的类默认都继承自Object类。同时Java中的所有类对象,包括数组,也都要实现这个类中的方法。

所以,Object是Java中所有类的父类、超类、基类,位于继承树的最顶层。可以说,任何一个没有显式地继承别的父类的类,都会直接继承Object,否则就是间接地继承Object,并且任何一个类也都会享有Object提供的方法。又因为Object是所有类的父类,所以基于多态的特性,该类可以用来代表任何一个类,允许把任何类型的对象赋给 Object类型的变量,也可以作为方法的参数、方法的返回值。

 

二. 常用方法

在Object类中,自带了几个常用的方法,这几个方法任意的子类都会继承,如下图所示:

 

根据上图,壹哥 把Object类中的常用方法归纳为这么几种:

1. 构造方法;

2. hashCode()和equals()方法用来判断对象是否相同;

3. wait()、wait(long)、wait(long,int)、notify()、notifyAll();

4. toString()和getClass();

5. clone();

6. finalize()

接下来壹哥就给各位介绍Object类中的几个常用方法,分别说一下这些方法的功能作用。

1. clone()方法

1.1 clone方法作用

Object中有两个protected修饰的方法,其中一个就是clone()方法,并且该方法还是一个native方法。clone()方法用于创建复制出当前类对象的一个副本,得到一个复制对象。所谓的复制对象,首先会分配一个和源对象(调用clone方法的对象)同样大小的内存空间,在这个内存空间中会创建出一个新对象;然后再使用源对象中对应的各个成员,填充新对象的成员,填充完成之后,clone方法会创建返回一个新的相同对象供外部引用。

1.2 clone源码分析

我们再看看clone()方法源码上的注释,如下图所示:

 

从这段注释中,我们可以了解到:

1. 以x为蓝本创建出的副本,与x对象并不相同,这保证了克隆出的对象拥有单独的内存空间;

2. 源对象和克隆的新对象字节码相同,它们具有相同的类类型,但这并不是强制性的;

3. 源对象和克隆的新对象利用equals()方法比较时是相同的,但这也不是强制性的。

1.3 Java的浅克隆与深克隆

因为每个类的直接或间接父类都是Object,因此它们都含有clone()方法,但因该方法是protected修饰的,所以我们不能在类外访问该方法。但如果我们要对一个对象进行复制,可以对clone方法进行复写,而Java中提供了两种不同的克隆方式,浅克隆(ShallowClone)深克隆(DeepClone)。

1.3.1 浅克隆

在浅克隆中,如果源对象的成员变量是值类型,则复制一份给克隆对象;如果源对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说源对象和克隆对象的成员变量指向相同的内存地址。

简单说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。我们可以用下图对浅克隆进行展示:

 

在Java语言中,通过实现Cloneable接口,默认覆盖Object类的clone()方法就可以实现浅克隆。

1.3.2 深克隆

在深克隆中,无论源对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,即深克隆将源对象的所有引用对象也复制一份给克隆对象。

简单来说,在深克隆中,除了对象本身被复制外,对象中包含的所有成员变量也将复制。我们可以用下图对深克隆进行展示:

 

在Java语言中,如果需要实现深克隆,可以通过实现Cloneable接口,自定义覆盖Object类的clone()方法实现,也可以通过序列化(Serialization)等方式来实现。如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。

2. hashCode()方法

2.1 简介

hashCode()是Object中的一个native方法,也是所有类都拥有的一个方法,主要是返回每个对象十进制的hash值。hash值是由hash算法根据对象的地址、对象中的字符串、数字等计算出来的。一般情况下,相同的对象应会返回相同的哈希吗值,不同的对象会返回不同的哈希码值。

2.2 hash值

哈希值是根据地址值换算出来的一个值,并不是实际的地址值,常用于哈希表中,如HashMap、HashTable、HashSet。关于哈希值,不同的JDK算法其实是不一样的:

Java 6、7 中会默认返回一个随机数;

Java 8 中默认通过和当前线程有关的一个随机数 + 三个确定值,运用Marsaglia’s xorshift scheme的随机数算法得到的一个随机数。

2.3 案例

 

以上两个对象的hash值是不同的,表示这是不同的两个对象。

3. equals(obj)方法

3.1 equals简介

Object中的equals方法用于判断this对象和obj本身的值是否相等,即用来判断调用equals方法的对象和形参obj所引用的对象是否是同一对象。所谓同一对象,就是指两个对象是否指向了内存中的同一块存储单元地址。如果this和obj指向的是同一块内存单元地址,则返回true;如果this和obj指向的不是同一块内存单元地址,则返回false。如果没有指向同一内存单元,即便是内容完全相等,也会返回false。

Object类的equals方法,其作用是比较两个对象是否相同,默认比较的是内存地址,其底层是通过==实现的。如果我们不想比较内存地址,那么就需要重写equals方法。默认的实现源码如下:

 

我们知道,Java中还有一个==运算符,也可以对两个对象进行比较。如果是基本数据类型,==比较的是它们的值是否相同;如果是引用数据类型,比较的是它们的内存地址是否相同。而equals()方法则是比较两个对象的内容是否相等。

3.2 使用原则

我们在使用equals()方法时,需注意下面这些原则:

(1).equals()只能处理引用类型变量;

(2).一般情况下,equals()方法比较的是两个引用类型变量的地址值是否相等;

(3).但是String类、基本类型包装类、File类、Date类等,都重写了Object类的equals()方法,比较是两个对象的"具体内容"是否相同。

3.3 基本特性

另外Java语言规范也要求equals方法具有下面的特性:

1. 自反性:对于任何非空引用x,x.equals(x)应该返回true;

2. 对称性:对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true;

3. 传递性:对于任何引用x,y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true;

4. 一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果;

5. 对于任何非空引用x,x.equals(null)应该返回false。

3.4 案例

 

从上面的案例中,我们也可以证明,equals()方法用于处理引用类型的变量,默认比较的是两个引用类型的变量地址是否相等。

4. getClass()方法

4.1 简介

getClass()方法可以用于获取对象运行时的字节码类型,得到该对象的运行时的真实类型。该方法属于Java的反射机制,其返回值是Class类型,例如 Class c = obj.getClass();。通过对象c,我们可以进一步获取该对象的所有成员方法,每个成员方法都是一个Method对象。我们也可以获取该对象的所有成员变量,每个成员变量都是一个Field对象。同样的,我们也可以获取该对象的构造函数,构造函数则是一个Constructor对象。

4.2 案例

 

从上面的代码案例中,我们可以得知,getClass方法用于返回该对象的真实类型(运行时的类型),可以根据对象的字节码来判断两个对象是否是同一个对象。

5. toString()方法

5.1 简介

toString()方法可以说是一个进行“自我描述”的方法,可以返回某个对象的字符串,当要输出某个实例对象的信息时,我们可以通过重写该方法来输出自我描述的信息。该方法通常只是为了方便输出本类的描述信息,比如执行System.out.println("xyz")这样的日志语句。一般情况下,当程序要输出一个对象或者把某个对象和字符串进行连接运算时,系统就会自动调用该对象的toString()方法返回该对象的字符串表示。

Object类的toString()方法会返回“运行时的类名@十六进制哈希码”格式的字符串,但很多类都重写了 Object类的toString()方法,用于返回可以表述该对象信息的字符串。

5.2 案例

 

上述代码执行结果如下图所示:

 

从上面程序的运行结果可以发现,默认情况下,对象带不带toString()方法,其最终的输出结果是一样的,即对象输出时一定会调用 Object类中的 toString()方法打印内容,所以我们可以利用此特性来通过 toString()方法取得一些对象的信息。

6. wait()、wait(long)、wait(long,int)、notify()、notifyAll()方法

这几个函数体现的是Java的多线程机制,一般是结合synchronize语句使用。

wait()用于让当前线程失去操作权限,当前线程进入等待序列;

notify()用于随机通知一个持有对象的锁的线程获取操作权限;

wait(long) 和wait(long,int)用于设定下一次获取锁的距离当前释放锁的时间间隔;

notifyAll()用于通知所有持有对象的锁的线程获取操作权限。

这几个方法我们后面在分析多线程的面试题时再细说,此处先仅做了解。

7. finalize()方法

7.1 简介

finalize()方法在进行垃圾回收的时候会用到,主要是在垃圾回收时,用于作为确认该对象是否确认被回收的一个标记。我们在使用finalize()方法时要注意:

finalize方法不一定会执行,只有在该方法被重写的时候才会执行;

finalize方法只会被执行一次;

对象可以在finalize方法中获得自救,避免自己被垃圾回收,同样的自救也只能进行一次;

不推荐Java程序员手动调用该方法,因为finalize方法代价很大。

7.2 案例

为了测试出finalize()方法的作用,壹哥给大家设计了如下案例:

/**

*/

public class Dog implements Animal{

private String name;

public Dog() {}

public Dog(String name) {

this.name = name;

}

@Override

public void eat() {

System.out.println("小狗"+this.name+"狗爱吃骨头");

}

//复写finalize方法

@Override

protected void finalize() throws Throwable {

super.finalize();//不要删除这行代码

System.out.println("finalize方法执行了");

}

}

然后我们对Dog对象进行回收测试:

public class ObjectTest {

public static void main(String[] args) {

Dog dog=new Dog("乔治");

//手动将对象标记为垃圾对象

dog = null;

//触发垃圾回收器,回收垃圾对象

System.gc();

}

}

要想确保finalize()方法的执行,我们首先需要在相关对象中重新finalize()方法,然后将待回收的对象手动标记为null,最后再手动调用gc()方法,这样才有可能确保finalize()方法一定执行。

------------------------------正片已结束,来根事后烟----------------------------

三. 结语

至此,壹哥就把Object类给大家介绍完毕了,这个类的内容并不是很难,主要是掌握几个常用的方法就可以了,尤其是equals()、hashCode()、toString()、getClass()等方法。

四. 今日作业

在Object类中,定义的finalize方法在______时调用,toString()方法的返回值表示______,equals方法的作用是______,getClass方法的作用是______。

请在评论区给出你的答案哦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/419229.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

刷题记录|Day55● 392.判断子序列 ● 115.不同的子序列

● 392.判断子序列 题目描述 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"…

pure-admin九州权限系统地址简单读

分成页面权限(ex:权限管理page) & 标签节点权限(ex:下载按钮) 【九州地址娜娜手机𝕍找看看kwk3589提供】以下是范例: /*** admin : 管理员角色* common : 普通角色*/const permissionRouter {path: "/permission"…

C++之入门之缺省参数函数重载引用

文章目录前言一、缺省参数1.缺省参数的概念2.缺省函数的分类(1)全缺省参数(2)半缺省参数3.使用注意二、函数重载1.函数重载的概念3.函数重载的原理--名字修饰(name Mangling)三、引用1.引用的概念2.引用特性3.引用的使用前言 重新…

工地人员工装穿戴识别系统 opencv

工地人员工装穿戴识别系统通过pythonopencv网络模型AI视频智能分析技术,工地人员工装穿戴识别算法模型可对施工现场人员是否佩戴合规穿戴进行自动识别预警。OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉处理开源软件库&…

堆来咯!!!

堆是什么? 是土堆吗? 那当然不是啦~ 堆是一种被看作完全二叉树的数组。 那么什么是完全二叉树呢? 如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。 堆的特…

开源自动化测试框架有哪些?怎么选择适合自己的

目录 前言 一、Selenium 二、Appium 三、Robot Framework 四、Cypress 五、TestCafe 六、Nightwatch.js 七、JUnit 八、Pytest 总结: 前言 开源自动化测试框架是现代软件开发和测试领域中不可或缺的一部分。它们使得测试人员能够快速、准确地执行测试用例…

Mysql(十) -- 常见问题处理

1. MySQL数据库cpu飙升的话你会如何分析 重点是定位问题。 使用top观察mysqld的cpu利用率 切换到常用的数据库使用show full processlist;查看会话观察是哪些sql消耗了资源,其中重点观察state指标定位到具体sql pidstat 定位到线程在PERFORMANCE_SCHEMA.THREADS中…

HuggingGPT强势来袭,LLM+专家模型,迈向更通用的AI

出品人:Towhee 技术团队 超级组合:HuggingFace ChatGPT HuggingGPT强势来袭。人类仿佛距离真正的AGI又更近了一步。 HuggingGPT是浙江大学与微软亚洲研究院的联手研究,发布之后迅速引发关注,已经开源。 它的使用非常简单&#x…

计算机时间旅行者:NTP如何帮助计算机在时间上保持同步?

应用场景: NTP(网络时间协议)是一种用于同步计算机时钟的协议,它可以让多台计算机在网络上保持同步的时间。因此,NTP可以应用于各种需要时间同步的应用程序中,例如: 计算机网络:在…

《花雕学AI》20:ChatGPT使用之体验评测AI EDU的网页版+桌面端+Android+App store组合

最近准备出门,要去新疆哈密参加活动,一直在寻找手机上可用的AI移动端。昨天在网上偶然找到了AI EDU(这个不是 MSRA 创立的人工智能开源社区),其链接是:https://ai.aigcfun.com,今天就尝试做个相…

三菱FX2N PLC与昆仑通态(MCGS)之间无线通讯

三菱FX2N PLC与昆仑通态(MCGS)之间建立无线通讯,其实就是昆仑通态使用三菱 PLC FX2N 通信口扩展 485 接口通过专用协议对 PLC 站点相应寄存器进行读写操作, 从而实现对站点工作状态的控制。 这种情况下,可以使用三菱PL…

ChatGPT对我们的影响-ChatGPT能给我们带来什么

ChatGPT日常应用 ChatGPT是一种应用广泛的自然语言处理算法,其可以应用于多种日常场景。以下是一些ChatGPT的日常应用: 聊天机器人:ChatGPT可用于构建聊天机器人,通过与用户进行自然语言交互来提供个性化的服务和支持。 新闻稿和…

配置springboot的静态资源访问地址为本机,并使用docker部署

springboot版本 2.7.4开发工具 IDEA 2021.2.4 1. SpringBoot提供的默认静态资源访问方法 resouces目录下的 /static、 /public、 /resouces、 /META-INF/resouces、这四个目录下放置的静态资源都可以通过直接通过以下链接访问 http://localhost:8080/aaa.png 2. 将SpringBoo…

入门力扣自学笔记256 C++ (题目编号:1019)

1019. 链表中的下一个更大节点 题目: 给定一个长度为 n 的链表 head 对于列表中的每个节点,查找下一个 更大节点 的值。也就是说,对于每个节点,找到它旁边的第一个节点的值,这个节点的值 严格大于 它的值。 返回一…

C++中COM组件管理思想实践

在开发一个产品时,其往往有很多子模块,子业务和子功能,这些都可以抽象成组件(C中本质表现就是一个类)。但是如何更好的管理这些类的对象呢?我们可以借鉴微软的COM组件思想来进行对象的注册,创建…

OpenHarmony标准系统开机时长优化

简介 万物互联时代,产品性能至关重要,而系统启动时间是系统性能的重要组成部分,因为用户必须等待系统启动完成后才能使用设备。对于经常需要进行冷启动的汽车等设备而言,较短的启动时间至关重要(没有人喜欢在等待几十秒…

如何手写一个文件索引工具everything(第一章)

第一章(NTFS格式及USN日志) 背景介绍 Windows平台的Everything文件查找速度非常快,优势在于利用了NTFS的USN日志,以及Windows上的文件监测机制我们也可以仿照类似原理,通过查询USN日志、监测Windows平台文件修改、使…

亚马逊卖家如何用facebook推广?

亚马逊作为全球最大的电商平台之一,吸引了大量的卖家和买家。对于亚马逊的卖家而言,如何进行有效的推广和引流成为了他们最关心的问题之一。其中,利用Facebook广告为亚马逊进行推广是一种较为常见的做法。 但是,亚马逊可以用Face…

springboot实现修改用户信息功能

目录 1、UserEntity层 2、UserMapper层 3、UserService层 4、UserController类 5、Postman测试 要实现修改用户信息的功能,需要编写对应的代码: 如: 在UserEntity中定义用户实体类的属性。 在UserMapper中编写修改用户的SQL语句&#…

【C++】哈希的应用 -- 布隆过滤器

文章目录一、布隆过滤器的引入二、哈希函数个数的选择三、布隆过滤器的实现四、布隆过滤器的应用五、布隆过滤器总结一、布隆过滤器的引入 我们在上一节中学习了 位图,知道了位图可以用来快速判断某个数据是否在一个集合中,但是位图有如下的缺点&#x…