50从零开始学Java之万类之王Object是怎么回事?

作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦

千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者

前言

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

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

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

配套开源项目资料

Github:

GitHub - SunLtd/LearnJava

Gitee:

一一哥/从零开始学Java

一. Object简介

1. 简介

在了解Object中的常用方法之前,我们先来看看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
 */
public class Object {
    
    ......

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

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

2. 配套视频

与本节内容配套的视频链接如下:

Bilibili External Player

二. 常用方法

在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 案例

Dog dog01=new Dog("乔治01");
Dog dog02=new Dog("乔治02");
//两个对象的hash值是不同的
System.out.println("dog01的hash值 "+dog01.hashCode());
System.out.println("dog02的hash值 "+dog02.hashCode());

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

2.4 配套视频

与本节内容配套的视频链接如下:

Bilibili External Player

3. equals(obj)方法

3.1 equals简介

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

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

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

我们知道,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 案例

/**
 * @author 一一哥Sun
 */
public class ObjectTest {
	public static void main(String[] args) {
		Dog dog01=new Dog("乔治01");
		Dog dog02=new Dog("乔治02");
		System.out.println("dog01对比dog02 "+(dog01==dog02));//false
        //equals()方法的底层默认还是利用==实现的
		System.out.println("dog01对比dog02 "+(dog01.equals(dog02)));//false
	}
}

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

3.5 配套视频

与本节内容配套的视频链接如下:

Bilibili External Player

4. getClass()方法

4.1 简介

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

4.2 案例

/**
 * @author 一一哥Sun
 */
public class ObjectTest {
	public static void main(String[] args) {
		//判断运行时d对象和c对象是否是同一个类型
		Animal d = new Dog();
		Animal c = new Cat();

		//方式1:通过instanceof关键字判断
		if((d instanceof Dog && c instanceof Dog) ||(d instanceof Cat && c instanceof Cat)) {
		    System.out.println("是同一个类型");
		}else {
		    System.out.println("不是同一个类型");
		}
		
		//方式2:通过getClass方法判断
		if(d.getClass() == c.getClass()) {
		    System.out.println("是同一个类型");
		}else {
		    System.out.println("不是同一个类型");
		}
	}	
}

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

5. toString()方法

5.1 简介

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

5.2 案例

/**
 * @author 一一哥Sun
 */
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+"狗爱吃骨头");
	}
	
    //	@Override
    //	public String toString() {
    //		
    //	    return "Dog name= " + name;
    //	}   
}


public class ObjectTest {
	public static void main(String[] args) {
		Dog dog=new Dog("乔治");
		System.out.println("dog一号="+dog);
		System.out.println("dog二号="+dog.toString());
	}
}

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

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

5.3 配套视频

与本节内容配套的视频链接如下:

Bilibili External Player

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()方法的作用,壹哥给大家设计了如下案例:

/**
 * @author 一一哥Sun
 */
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.mfbz.cn/a/35512.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

langchain调用chatGLM2纪实

一、科学上网要注意: 域名全代和全局代理(网卡),都要打开。这样conda install特别快。 二、安装langchain 1、 conda install langchain 2、 conda install openai 注意: 使用pip install和conda install 是不同…

SpringBoot使用EasyExcel批量导出500万数据

SpringBoot使用EasyExcel批量导出500万数据 说明excel版本比较EasyExcel介绍项目目录mysql对应表建表语句pom.xmlapplication.yml配置类启动类代码OrderInfo 实体类OrderInfoExcel excel模版标题类(EasyExcel需要使用这个)TestController控制层接口层TestServiceTestServiceImp…

十五、docker学习-docker核心docker数据卷

什么是数据卷 当我们在使用docker容器的时候,会产生一系列的数据文件,这些数据文件在我们删除docker容器时是会消失的,但是其中产生的部分内容我们是希望能够把它给保存起来另作用途的,Docker将应用与运行环境打包成容器发布&…

创建启动前端vue与后端python/flask,前后端分离,相互传递参数

创建启动vue 确保你已经安装了Node.js和npm 安装vue npm install -g vue/cli创建vue项目: vue create my-project cd my-project启动vue npm run serve如果安装vue报错:管理员权限模式打开powershell Windows PowerShell 版权所有(C&#…

斐波那契数列

目录 斐波那契数列 斐波那契数列和黄金分割率的关联 解析表达式 练习 斐波那契数列 一个人将一对兔子放到一个封闭的围墙内,并假设每对兔子每个月都繁殖出一对兔子,且新生兔子从第二个月开始有繁殖能力,那么一年以后这个封闭的围墙内有多…

kotlin Flow系列之 - 冷流SafeFlow源码解析之 - Safe在那里?

本文涉及源码基于kotlinx-coroutines-core-jvm:1.7.1 kotlin 协成系列文章: 你真的了解kotlin中协程的suspendCoroutine原理吗? Kotlin Channel系列(一)之读懂Channel每一行源码 kotlin Flow系列之-冷流SafeFlow源码解析之 - Safe在那里? ko…

vue el-table的每行操作el-button添加单独的loading效果实现

vue el-table的每行操作el-button添加单独的loading效果实现 效果图&#xff1a;实现代码&#xff1a;结语 效果图&#xff1a; 实现代码&#xff1a; <tamplate><el-table :data"list" ><el-table-column fixed"right" label"操作&q…

服务负载均衡Ribbon

服务负载均衡Ribbon Ribbon 介绍Ribbon 案例Ribbon 负载均衡策略Ribbon 负载均衡算法设置自定义负载均衡算法 Ribbon 介绍 Ribbon 是一个的客服端负载均衡工具&#xff0c;它是基于 Netflix Ribbon 实现的。它不像 Spring Cloud 服务注册中心、配置中心、API 网关那样独立部署…

启动网站调试提示 HTTP 错误 403.14 – Forbidden Web 服务器被配置为不列出此目录的内容。

启动网站调试提示 HTTP 错误 403.14 – Forbidden Web 服务器被配置为不列出此目录的内容。 解决方案第一种.在网站的配置文件里添加第二种.ISS管理界面修改 解决方案 第一种.在网站的配置文件里添加 <system.webServer><directoryBrowse enabled"true" /&…

STM32+PWM+输入捕获测频

外部时钟&#xff0c;主频64M 定时器1 通道1发出PWM波 频率1K 定时器2 通道1输入捕获&#xff0c;上升沿触发 串口 /* USER CODE BEGIN 0 */ uint32_t time_up_num0;//上升沿计数 float time_frequency;//频率 /* USER CODE END 0 */ 初始换打开定时器 /* USER CODE BEGIN 2 …

跑深度学习nvidia驱动忽然实效的详细解决方法

由于经常跑深度学习&#xff0c;所以对于显卡驱动什么的都还是整的比较明白的不含糊&#xff0c;所以都能跑的起来。但是今天跑pytorch框架时&#xff08;用到cuda&#xff09;忽然给我报了个错&#xff1a; RuntimeError: No CUDA GPUs are available这给我整不会了&#xff…

解决uni-app微信小程序底部输入框,键盘弹起时页面整体上移问题

存在问题 做了一个记录页面&#xff08;类似单方聊天页&#xff09;&#xff0c;输入框在底部&#xff1b;当弹出键盘时&#xff0c;页面整体上移&#xff0c;页面头信息会消失不见 需要实现效果&#xff1a; 比如一个记录页面&#xff0c;需要在键盘弹出时&#xff1a; 底…

【SQL】群辉 NAS 安装 Mysql 远程访问连接

群辉安装MySQL具有高效、安全、可靠、灵活等优势&#xff0c;可以为用户提供一个优秀的数据管理和分析环境。同时具有良好的硬件性能和稳定性&#xff0c;可以保障MySQL数据库的高效运行和数据安全. cpolar 是一款内网穿透工具,通过简单的设置,我们即可实现远程访问群辉中mysq…

微信为什么使用 SQLite 保存聊天记录?

概要 SQLite 是一个被大家低估的数据库&#xff0c;但有些人认为它是一个不适合生产环境使用的玩具数据库。事实上&#xff0c;SQLite 是一个非常可靠的数据库&#xff0c;它可以处理 TB 级的数据&#xff0c;但它没有网络层。接下来&#xff0c;本文将与大家共同探讨 SQLite 在…

Element+Vue+OpenLayers的项目实战

WebGIS ElementVueOpenLayers的项目实战 使用npm配置开发环境Vue的安装npm安装vue-cli脚手架构建工具安装桥接工具运行项目 使用npm配置开发环境 开发是项目级别的&#xff0c;则需要使用npm来配置开发环境&#xff1b; 使用npm配置开发环境主要包括Vue、Element和OpenLayers的…

Linux·图解Linux网络包接收过程

因为要对百万、千万、甚至是过亿的用户提供各种网络服务&#xff0c;所以在一线互联网企业里面试和晋升后端开发同学的其中一个重点要求就是要能支撑高并发&#xff0c;要理解性能开销&#xff0c;会进行性能优化。而很多时候&#xff0c;如果你对Linux底层的理解不深的话&…

ContOS7 Oracle11g 安装配置

挂载镜像和配置本地yum源 mount /dev/cdrom /mnt cat <<EOF>/etc/yum.repos.d/local.repo [local] namelocal baseurlfile:///mnt gpgcheck0 enabled1 EOFSelinux配置 #selinux修改后需要重启主机生效&#xff1a; sed -i s/SELINUXenforcing/SELINUXdisabled/g /et…

python dataframe 时间格式处理

1&#xff0c;20230101 这种处理成 2023-01-01 这种 df1[apply_date] df1[apply_date].astype(str) df1[apply_dt] pd.to_datetime(df1[apply_date]).dt.strftime(%Y-%m-%d) 2、时间&#xff0c;截取年月日 如 &#xff1a;2023-01-01 12:01:01 &#xff0c;只取年月日 df[…

java中地址问题

//第一个大mapMap<String, Object> map new HashMap<>();HashMap<String, String> map2 new HashMap<>();map2.put("358","999");//给小map赋值并将其添加到map中map.put("666",map2);//获取小map并且修改对应的键值对…

基于springboot+vue框架的电影订票系统_wqc3k

随着网络科技的不断发展以及人们经济水平的逐步提高&#xff0c;计算机如今已成为人们生活中不可缺少的一部分&#xff0c;为电影订票方便管理&#xff0c;基于java技术设计与实现了一款简洁、轻便的管理系统。本系统解决了电影订票事务中的主要问题&#xff0c;包括个人中心、…
最新文章