前言
之前花了一些时间获取多线程的基础知识,很难,很难,很难...如果我打算写一个线程池,我会暂时停止多线程系列…
今天中午发现有些大厂也会问Object对象里有哪些方法(也算是一个知识点)。没认真复习过对象,所以这篇文章主要看一下关于对象对象有什么要注意的~
那我们开始吧。文章如有错误请见谅,也请在评论区不吝指正~
一、对象简介对象
声明:本文使用JDK1.8。
我们学Java的都知道,Java是面向对象的语言。Java里不管发生什么,除了
八种基本数据类型,都可以看成一个对象(
)。当然,这八种基本数据类型也可以
装箱
到对象中:
而Object就是这些对象的最高级别的,所有的Java对象都
隐式
地继承了Object对象(不用显示写extends继承)所有的Java对象都
拥有Object默认的方法
。
所以让我们看看对象有哪些方法:
其实可以归纳为几条:
registerNatives()【底层实现、不研究】hashCode()equals(Object obj)clone()toString()notify()notifyAll()wait(long timeout)【还有重载了两个】finalize()
对象共有
11个
方法,其中一个是registerNatives()的底层实现,另外两个是wait()和wait(long timeout,int nanos)重载方法。
所以我们真正需要看的就是
8个
方法
还有
一个属性
:
二。equals和hashCode方法
Equals和hashCode方法可以说是面试中的重点问题。结合String,可以说在面试题中无处不在
。
首先,我们来看看Object中equals和hashCode
native
的实现:
哈希码:
public native int hashCode();
等于:
public boolean equals(Object obj) { return (this == obj); }
这一切看起来非常简单:
hashCode()由native方法底层实现了。equals()就直接==判断是否相等了。
为了更清楚地了解他们到底在做什么,让我们来看看它的注释:
根据注释我们可以
总结以下的要点
:根据笔记,我们可以
总结出以下几点
:
重写equals()方法,就必须重写hashCode()的方法equals()方法默认是比较对象的地址,使用的是==等值运算符hashCode()方法对底层是散列表的对象有提升性能的功能同一个对象(如果该对象没有被修改):那么重复调用hashCode()那么返回的int是相同的!hashCode()方法默认是由对象的地址转换而来的equals()方法还有5个默认的原则:自反性—>调用equals()返回的是true,无论这两个对象谁调用equals()都好,返回的都是true一致性—>只要对象没有被修改,那么多次调用还是返回对应的结果!传递性—>x.equals(y)和y.equals(z)都返回true,那么可以得出:x.equals(z)返回true对称性—>x.equals(y)和y.equals(x)结果应该是相等的。传入的参数为null,返回的是false
很容易理解为什么hashCode()使用哈希表作为底层来提高性能。让我们再次回顾一下
HashMap的插入:
如果哈希值不相等,可以直接判断密钥不相等!
2.1重写equals和hashCode方法
equals()方法默认使用比较对象的地址,并使用= =等价运算符。但是按照我们正常的开发,比较对象地址是没有意义的。
一般地,如果我们有两个Address对象,只要这两个对象的
省号、城市号、街道号相等
,我们就认为这两个对象相等了!
2.2String实现了equals和hashCode方法
我们初学的时候可能听过:String已经实现了equals和hashCode方法。
这也就是为什么,我们可以
直接
使用String.equals()来
判断两个字符串
是否相等!
让我们来看看它的实现:
三、toString方法三。toString方法
接下来,让我们看看toString方法,它也非常简单:
ToString方法主要用于标识该对象:
从上面的结果我们都可以看出:从结果中我们什么也看不出来~
所以我们一般会重写toString(),那么的打印结果方便我们调试!
@Override public String toString() { return "Address{" + "provinceNo=" + provinceNo + ", cityNo=" + cityNo + ", streetNo=" + streetNo + '}'; }
以下结果看起来要好得多:
四。克隆方法
让我们也来看看它的顶部注释:
看了上面的评论,我们可以总结出以下几点:
clone方法用于对象的克隆,一般想要克隆出的对象是
独立
的(与原有的对象是分开的)深拷贝指的是该对象的成员变量(如果是可变引用)都应该克隆一份,浅拷贝指的是成员变量没有被克隆一份
我们来看一下浅拷贝:拷贝了Employee对象,但是它的成员变量hireday没有被克隆,所以指向同一个Date对象!
4.1克隆用法
那么我们如何克隆对象呢?无论是轻拷贝还是深拷贝,这两个步骤都是一样的:
克隆的对象要
实现Cloneable接口
重写clone方法
,最好修饰成public
light copy:只***人物对象,不***日期!
public class Person implements Cloneable { // 可变的成员变量 private Date date; @Override public Object clone() throws CloneNotSupportedException { return super.clone(); }}
深层***:不仅***了Person对象,还***了date成员变量。
public class Person implements Cloneable { // 可变的成员变量 public Date date; @Override public Object clone() throws CloneNotSupportedException { // 拷贝Person对象 Person person = (Person) super.clone(); // 将可变的成员变量也拷贝 person.date = (Date) date.clone(); // 返回拷贝的对象 return person; }}
4.2单题继续学习受保护
不知道有没有人跟我一样的疑问:
我只想要
浅拷贝
,能不能
直接调用该对象.clone()来实现
?
例如,我现在有一个地址对象:
public class Address { private int provinceNo; private int cityNo; private int streetNo; public Address() { } public Address(int provinceNo, int cityNo, int streetNo) { this.provinceNo = provinceNo; this.cityNo = cityNo; this.streetNo = streetNo; }}
你怎么看下面这段代码?
Address address = new Address(1, 2, 3); address.clone();
我们都知道:
protected修饰的类和属性,对于自己、本包和其子类可见
可能会想:clone()方法是在Object类上定义的(用protected修饰),我们的自定义地址对象隐式继承Object(所有对象都是Object的子类),所以子类调用Object用protected修饰clone()完全没问题。
但是,IDE现实告诉我,这
编译就不通过了
!
我立刻想到了错误的原因:我是否偏离了受保护的修饰符?
受保护的修改类和属性对自己、这个包和它的子类都是可见的这种说法没有错。但是还是要加上:对于受保护的成员或方法,分子类和超类是否在同一个包中很重要。与基类不在同一个包中的子类只能访问从基类继承的自己的受保护成员,而不能访问基类实例本身的受保护成员。
上面的代码就错在:Address与Object
不是在同一个包下
的,而Address直接访问了Object的clone方法。这是不行的。
下面我拍两张图给你看(看完图再看上面的描述,你就明白了):
五、wait和notify方法V .等待和通知方法
wait和notify方法实际上是Java提供的API,用于让线程进行通信。
按照惯例,我们来看看笔记是怎么说的:
等待方法:
通知方法:
NotifyAll()方法:
看了上面的评论,我们可以总结出以下几点:
无论是wait、notify还是notifyAll()都需要
由**器对象(锁对象)来进行调用
简单来说:
他们都是在同步代码块中调用的
,否则会抛出异常!notify()唤醒的是在等待队列的
某个
线程(不确定会唤醒哪个),notifyAll()唤醒的是等待队列
所有
线程导致wait()的线程被唤醒可以有4种情况该线程被中断wait()时间到了被notify()唤醒被notifyAll()唤醒调用wait()的线程会
释放掉锁
其实总结以上你就不会有太深的印象了。可以尝试回答几个问题,加深对wait()和notify()的理解。
5.1为什么对象方法上有wait和notify?
一开始我们就说了:wait()和notify()是Java提供给我们线程间通信的API。既然是线程,那么在对象类而不是线程类上定义了什么?
锁对象是
任意
的,所以这些方法必须定义在Object类中
5.2调用notify方法后会发生什么?
如上所述,notify将唤醒等待队列中的一个线程。
但是注意的是:
notify方法调用后,被唤醒的线程
不会立马获得到锁对象
。而是等待notify的synchronized代码块
执行完之后
才会获得锁对象
睡眠和等待有什么区别?
Thread.sleep()和Object.wait()都可以暂停当前线程并释放CPU控制。
主要的区别在于Object.wait()在释放CPU同时,
释放了对象锁的控制
。而Thread.sleep()没有对锁释放
本文来自又何必自找失落╮投稿,不代表舒华文档立场,如若转载,请注明出处:https://www.chinashuhua.cn/24/599089.html