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

这篇具有很好参考价值的文章主要介绍了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类型的变量,也可以作为方法的参数、方法的返回值。

java为什么要有object类,从零开始学Java,java,开发语言

2. 配套视频

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

Bilibili External Player

二. 常用方法

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

java为什么要有object类,从零开始学Java,java,开发语言

根据上图,壹哥 把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()方法源码上的注释,如下图所示:

java为什么要有object类,从零开始学Java,java,开发语言

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

  1. 以x为蓝本创建出的副本,与x对象并不相同,这保证了克隆出的对象拥有单独的内存空间;
  2. 源对象和克隆的新对象字节码相同,它们具有相同的类类型,但这并不是强制性的;
  3. 源对象和克隆的新对象利用equals()方法比较时是相同的,但这也不是强制性的。

1.3 Java的浅克隆与深克隆

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

1.3.1 浅克隆

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

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

java为什么要有object类,从零开始学Java,java,开发语言

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

1.3.2 深克隆

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

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

java为什么要有object类,从零开始学Java,java,开发语言

在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());
	}
}

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

java为什么要有object类,从零开始学Java,java,开发语言

从上面程序的运行结果可以发现,默认情况下,对象带不带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方法的作用是______。

请在评论区给出你的答案哦。文章来源地址https://www.toymoban.com/news/detail-575449.html

到了这里,关于50从零开始学Java之万类之王Object是怎么回事?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【从零开始学习JAVA | 第十八篇】接口介绍

    目录 前言: 接口: 如何定义一个接口: 如何使用一个接口: 接口中成员的特点: 接口与类的区别:   接口的应用: 总结:         接口其实是为了弥补继承的缺点: 我们无法让父类完美的适应每一个子类的要求 。 例如我们有一个动物类的父类,有一个蝌蚪类,鱼

    2024年02月10日
    浏览(47)
  • 【从零开始学习JAVA | 第四十五篇】反射

    目录 前言: ​反射:  使用反射的步骤: 1.获取阶段: 2.使用阶段: 反射的应用场景: 使用反射的优缺点: 总结: Java中的反射是一项强大而灵活的功能,它允许程序在运行时 动态地获取、操作和利用类的信息 。通过反射,我们可以在运行时检查和修改类的属性、调用类

    2024年02月13日
    浏览(59)
  • 【从零开始学习JAVA | 第二十五篇】泛型

    目录 前言: 泛型: 额外拓展: 总结:         本文将详细介绍之前我们在JAVA 中一直在讲的泛型,各位感兴趣的同学可以点击进来观看。         泛型是一种编程概念, 它允许在定义类、接口或方法时使用类型参数,这样可以在使用时指定实际的类型。 通过使用泛

    2024年02月16日
    浏览(41)
  • 【从零开始学习JAVA | 三十四篇】IO流

    目录 前言: IO流介绍: IO流的常见方法: 1.字节流类: 2.字符流类: 总结:                 IO流就是存入和读取数据的解决方案,并且他是一个知识点很多的章节,因此我们关于IO流的介绍会分为多篇来进行详解,而掌握好IO流可以大大提高我们的效率,简化我们的代码执

    2024年02月13日
    浏览(90)
  • 【从零开始学习JAVA | 三十九篇】深入多线程

    目录 前言:         ​1.线程的寿命周期​ 2.线程的安全问题 3.锁 同步代码块: 同步方法: 死锁: 4.生产者和消费者模式(等待唤醒机制) 总结:         当今软件开发领域中,多线程编程已成为一项至关重要的技能。然而,要编写出高效、可靠的多线程程序并不容

    2024年02月13日
    浏览(57)
  • 【从零开始学习JAVA | 第十九篇】初识内部类

    内部类是类的五大成员。一个类的其他的成员分别是属性,方法,构造方法,代码块。本文将详细介绍什么是内部类,以及内部类的意义。 内部类(Inner Class)是 Java 中的一个特殊概念,它指的是 一个类定义在另一个类内部的类。 内部类可以访问外部类的成员,包括私有的

    2024年02月15日
    浏览(40)
  • java 从零开始手写 RPC (00) 概览 overview

    rpc 是基于 netty 实现的 java rpc 框架,类似于 dubbo。 主要用于个人学习,由渐入深,理解 rpc 的底层实现原理。 基于 netty4 的客户端调用服务端 p2p 调用 serial 序列化支持 timeout 超时处理 register center 注册中心 load balance 负载均衡 callType 支持 oneway sync 等调用方式 fail 支持 failOve

    2024年03月08日
    浏览(55)
  • 【从零开始学习JAVA | 第四十篇】了解线程池

    目录 前言: 线程池: 线程池的工作流程: 代码实现线程池: 任务拒绝策略:  线程池多大才算合适? 总结:         在Java编程中,线程池是一个强大的工具,它能够管理和复用线程,提供高效的并发处理能力。通过线程池,我们可以有效地控制并发线程的数量,并降

    2024年02月13日
    浏览(52)
  • 【从零开始学习JAVA | 第二十二篇】BigInteger

    目录  前言:  BigInterger: BigInteger常见的方法: 总结:         本篇我们将介绍BigInteger这个比较实用一点的API,这个API在我们实际写项目中都是很实用的API,因此大家应该对这个API有更加熟练的掌握。 在Java中,整数类型int和long的取值范围是有限的,超出该范围的整数无法

    2024年02月15日
    浏览(65)
  • 【从零开始学习JAVA | 第二十七篇】JAVA期末练习(PTA)

    目录 前言: R7-5 Count the letters in a string (统计字符串中的字符) R7-1 找素数 R7-3 电话号码同步(Java) 总结:         临近期末,我也更新一下PTA上的JAVA大题,希望各位都可以考出一个好的成绩。 (Count the letters in a string) (统计字符串中的字符) Write a method that counts th

    2024年02月16日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包