咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java 知识点啦,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~
🏆本文收录于「Java进阶实战」专栏,数多年Java开发老兵项目累计经验,专业攻坚指数级提升,助你一臂之力,早日实现财富自由🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!!
环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8
前几日,有位女读者遇到了一个很常见却又不好答的棘手面试题,于是特地向我诉苦了一波,于此,我怎么能见女不救,正所谓救人一命胜造七级浮屠。在此,我们就一起来再来温故一波对象的鼻祖Object
类,及它的所有方法及使用场景吧。
解读Object类
在Java中,我们都知道,Object
类是所有类的根类,它位于类层次结构的顶端。Object
类提供了一些基本的方法,这些方法被继承到所有的类中。以下是Object
类中定义的主要方法及其用法:
toString()
toString()
- 返回对象的字符串表示,通常用于打印对象的简要信息。
Object obj = new Object();
System.out.println(obj.toString());
默认实现会返回类名@哈希码,源码实现如下:
如上段代码是Java中用于生成对象的字符串表示形式的常见方法之一。它在默认情况下返回一个由类名和对象的哈希码组成的字符串,格式为类名@哈希码
。
拓展一下:
getClass().getName()
返回对象所属类的名称。Integer.toHexString(hashCode())
将对象的哈希码转换为十六进制字符串表示。
因此,toString()
方法返回的字符串将类名和对象的哈希码连接在一起,以 @
符号分隔。这样的字符串并不是特别有用,因为它不提供对象的实际内容信息,但是它可以用于快速检查对象的标识。
示例演示:
equals(Object obj)
equals(Object obj)
- 用于比较两个对象是否相等。默认实现是比较对象的引用,即是否为同一个实例。
Object obj1 = new Object();
Object obj2 = obj1;
Object obj3 = new Object();
System.out.println(obj1.equals(obj2)); // true,因为obj1和obj2是同一个对象
System.out.println(obj1.equals(obj3)); // false,因为obj1和obj3是不同的对象
示例演示如下:
hashCode()
hashCode()
- 返回对象的哈希码值,该值在equals
方法中用于比较对象的相等性。默认实现返回对象的系统标识符。
例如:
Object obj = new Object();
int hash = obj.hashCode();
示例演示:
getClass()
getClass()
- 获取运行时对象的类信息,返回一个Class
对象。
例如:
Object obj = new Object();
Class<?> clazz = obj.getClass(); // 获取obj的运行时类类型
示例演示:
clone()
clone()
- 创建并返回对象的一个副本。默认实现是浅拷贝,即复制对象的引用值,而不是实际的内容。
例如:
/**
* @Author bug菌
* @Source 公众号:猿圈奇妙屋
* @Date 2024-04-02 19:02
*/
public class testObject implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) {
testObject obj = new testObject();
try {
testObject cloneObj = (testObject) obj.clone(); // 创建obj的一个浅拷贝
System.out.println(cloneObj);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
示例演示:
拓展一下:
在Java中,要使用clone()
方法,首先需要确保被克隆的对象的类实现了Cloneable
接口。如果一个类没有实现Cloneable
接口,调用其clone()
方法会抛出CloneNotSupportedException
异常。
另外,clone()
这个方法使用了protected访问修饰符,因此只能在同一包内的类或者继承了这个类的子类中访问,因此只能在同一个包内或者子类中使用。
如果要克隆一个对象,最好的方式是通过实现Cloneable
接口并重写clone()
方法。示例代码如下:
class MyClass implements Cloneable {
// 类的成员和方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
然后你可以这样使用:
MyClass obj = new MyClass();
MyClass cloneObj = (MyClass) obj.clone();
若你直接对Object
类的实例调用了clone()
方法,但是Object
类并没有实现Cloneable
接口,会抛出CloneNotSupportedException
异常。
wait()
wait()
- 使当前线程等待,直到另一个线程调用此对象的notify()
或notifyAll()
方法。
synchronized (obj) {
try {
obj.wait(); // 当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
示例这里我就先不着急给大家演示,同学们先看下我给大家解读的notify()方法学习后,我再一并给大家演示,毕竟这两是一起连用的,单独用没啥意义。
notify()
notify()
- 唤醒在此对象上等待的单个线程(如果有的话)。
使用例如:
synchronized (obj) {
obj.notify(); // 唤醒一个在此对象上等待的线程
}
示例演示:
这里联合notify(),关于Object类中使用wait()及notify()方法的示例演示:
定义启动类,将演示wait()、notify()方法效果。
/**
* @Author bug菌
* @Source 公众号:猿圈奇妙屋
* @Date 2024-04-02 19:38
*/
public class WaitNotifyExample {
public static void main(String[] args) {
Message message = new Message();
// 创建一个等待线程
Thread waiterThread = new Thread(new Waiter(message));
// 创建一个通知线程
Thread notifierThread = new Thread(new Notifier(message));
// 启动线程
waiterThread.start();
notifierThread.start();
}
}
定义一个Message类,用于在线程之间传递消息
package com.example.javase.bugTest.objectDemo;
/**
* @Author bug菌
* @Source 公众号:猿圈奇妙屋
* @Date 2024-04-02 19:38
*/
public class Message {
private String content;
public synchronized String getContent() {
return content;
}
public synchronized void setContent(String content) {
this.content = content;
}
}
定义一个Waiter类,等待消息并打印。
/**
* @Author bug菌
* @Source 公众号:猿圈奇妙屋
* @Date 2024-04-02 19:38
*/
public class Waiter implements Runnable {
private Message message;
public Waiter(Message message) {
this.message = message;
}
@Override
public void run() {
synchronized (message) {
try {
System.out.println("Waiter 等待中...");
// 调用wait()方法,等待被通知
message.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 收到通知后打印消息
System.out.println("Waiter 收到通知: " + message.getContent());
}
}
}
定义一个Notifier类,负责发送通知。
/**
* @Author bug菌
* @Source 公众号:猿圈奇妙屋
* @Date 2024-04-02 19:38
*/
// Notifier类负责发送通知
public class Notifier implements Runnable {
private Message message;
public Notifier(Message message) {
this.message = message;
}
@Override
public void run() {
synchronized (message) {
// 设置消息内容
message.setContent("你好,这是一条消息!");
// 通知等待线程
message.notify();
System.out.println("Notifier 发送通知完毕");
}
}
}
其中这个示例包含了一个Message
类用于在线程之间传递消息,一个Waiter
类等待消息并打印,一个Notifier
类负责发送通知。在Waiter
类中,使用了wait()
方法来使线程进入等待状态,直到收到Notifier
类发送的通知才继续执行。
实际运行结果展示:
拓展一下:
如上示例演示了Object类中的wait()和notify()方法的使用,用于线程间的通信和同步。第一,我定义了一个Message
类,它包含一个私有的字符串成员变量msg
,以及一个getMessage()
方法和setMessage()
方法用于设置和获取消息。第二,定义了一个Waiter
类和一个Notifier
类,它们都实现了Runnable
接口,用于作为线程执行体。Waiter
类中的run()方法中调用了message.waitMessage()
方法,而Notifier
类中的run()方法中调用了message.setMessage()
方法。第三,在WaitNotifyExample
类的main()
方法中,先创建了一个Message
对象,并分别创建了一个等待线程waiterThread
和一个通知线程notifierThread
,它们都传入了同一个Message
对象。最后,通过start()
方法启动了这两个线程,实现了等待线程和通知线程的交互。
总的来说,为大家演示了如何使用wait()和notify()方法实现线程间的等待和通知机制,从而实现线程间的协作。不知道大家可否能及时掌握,有疑问的及时评论区告知于我。
notifyAll()
notifyAll()
- 唤醒在此对象上等待的所有线程。
例如:
synchronized (obj) {
obj.notifyAll(); // 唤醒所有在此对象上等待的线程
}
示例演示:
1、定义一个MessageProducer类,实现Runnable 接口,定位为一个消息生产者,当执行它的 run() 方法时,它会将一个新消息设置到 message 对象中,并通知所有等待中的线程。
/**
* @Author bug菌
* @Source 公众号:猿圈奇妙屋
* @Date 2024-04-02 19:51
*/
public class MessageProducer implements Runnable {
private Message message;
public MessageProducer(Message message) {
this.message = message;
}
@Override
public void run() {
synchronized (message) {
// 生产消息
message.setContent("New message: Hello, world!");
// 通知所有等待的线程
message.notifyAll();
}
}
}
2、定义一个MessageConsumer类,实现Runnable 接口,定位为一个消息消费者,目的是实现一个等待消息并处理的逻辑,通过线程的等待和唤醒机制,确保消息在到达后能够及时被消费。
/**
* @Author bug菌
* @Source 公众号:猿圈奇妙屋
* @Date 2024-04-02 19:51
*/
public class MessageConsumer implements Runnable {
private Message message;
public MessageConsumer(Message message) {
this.message = message;
}
@Override
public void run() {
synchronized (message) {
// 等待消息的到来
while (message.getContent() == null) {
try {
// 等待消息通知
message.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 收到消息后进行处理
System.out.println("Received message: " + message.getContent());
}
}
}
3、定义一个mian主函数,用于创建生产者和消费者线程,最后通过启动线程,达到生产者线程将消息放入 Message
对象中,而消费者线程则从 Message
对象中获取消息并进行处理的效果。
package com.example.javase.bugTest.objectDemo;
/**
* @Author bug菌
* @Source 公众号:猿圈奇妙屋
* @Date 2024-04-02 19:51
*/
public class NotifyAllTest {
public static void main(String[] args) {
// 创建一个共享的消息对象
Message message = new Message();
// 创建生产者线程和消费者线程
Thread producerThread = new Thread(new MessageProducer(message));
Thread consumerThread1 = new Thread(new MessageConsumer(message));
Thread consumerThread2 = new Thread(new MessageConsumer(message));
// 启动线程
producerThread.start();
consumerThread1.start();
consumerThread2.start();
}
}
4、执行main函数本地测试结果如下:
很明显,结果符合预期,生产者将消息传递后且唤醒在此对象上等待的所有线程(所有消费者线程),消费者拿到消息后并进行了打印。
拓展一下:
如上示例演示了线程间如何通过共享对象进行通信。具体来说,创建了一个名为 NotifyAllTest
的公共类,其中包含一个名为 main
的静态方法。在 main
方法中:
- 创建了一个
Message
对象,这是一个自定义的消息对象,用于在生产者和消费者线程之间传递消息。 - 创建了一个生产者线程 (
producerThread
) 和两个消费者线程 (consumerThread1
和consumerThread2
)。 - 启动了这三个线程。
其中,MessageProducer
类和 MessageConsumer
类实现了 Runnable
接口,用于定义生产者和消费者线程的行为。生产者线程将消息放入 Message
对象中,而消费者线程则从 Message
对象中获取消息并进行处理。
这个示例,我给大家演示了下线程间的基本通信和协作机制,但需要注意的是,它并没有提供线程安全的保障。在实际应用中,可能需要采取额外的措施来确保多线程环境下的数据安全性。
finalize()
finalize()
- 在垃圾收集器决定回收对象之前,由垃圾收集器调用此方法。这个 finalize 机制是不确定的,不保证会被调用,且在Java 9中已经被弃用。
简单使用实例例如:
protected void finalize() throws Throwable {
super.finalize();
// 清理资源的代码
}
示例演示:
好的,下面是一个关于Object类使用finalize()方法的示例:
/**
* @Author bug菌
* @Source 公众号:猿圈奇妙屋
* @Date 2024-04-02 20:08
*/
public class FinalizeExample {
// 定义一个类来演示finalize()方法
static class MyObject {
// 在对象销毁前调用finalize()方法
@Override
protected void finalize() throws Throwable {
try {
// 执行清理资源的操作
System.out.println("对象被销毁前执行finalize()方法");
} finally {
super.finalize();
}
}
}
public static void main(String[] args) {
// 创建一个对象
MyObject obj = new MyObject();
// 将对象设为null,以便触发垃圾回收
obj = null;
// 强制垃圾回收
System.gc();
// 等待一段时间以确保finalize()方法被调用
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
本地执行结果如下:
在这个示例中,我定义了一个包含finalize()方法的内部类MyObject。在该方法中,我们可以执行清理资源的操作。在主函数中,我创建了一个MyObject对象,并将其设为null以便触发垃圾回收。然后,我手动调用了System.gc()方法来强制进行垃圾回收。最后,通过让主线程休眠一段时间,等待finalize()方法被调用。
小结
大多数时候,你可能需要重写这些方法来提供特定的实现。例如,为了正确地比较两个对象是否相等,你可能需要重写equals
和hashCode
方法。同样,为了实现深拷贝,你可能需要重写clone
方法。
请注意,Object
类中的wait()
, notify()
和notifyAll()
方法是同步控制的一部分,它们只能在对象的监视器(由synchronized
关键字实现)被当前线程持有时调用。不当使用这些方法可能导致死锁或其他同步问题。
… …
ok,以上就是我这期的全部内容啦,如果还想学习更多,你可以看看专栏《Java进阶实战》中的其他硬货,每篇都是实打实的项目实战经验所撰。只要你每天学习一个奇淫小知识,日积月累下去,你一定能成为别人眼中的大佬的!功不唐捐,久久为功!
「赠人玫瑰,手留余香」,咱们下期拜拜~~
☀️建议/推荐你
无论你是计算机专业的学生,还是对编程感兴趣的跨专业小白,都建议直接入手「滚雪球学Java」专栏;该专栏不仅免费,bug菌还郑重承诺,只要你学习此专栏,均能入门并理解Java SE,以全网最快速掌握Java语言,每章节源码均同步「Gitee」,你真值得拥有;学习就像滚雪球一样,越滚越大,带你指数级提升。
最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。
同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同Java实战类型技术硬货,还可免费白嫖最新BAT大厂面试真题、4000G Pdf技术书籍、万份简历/PPT模板、技术文章Markdown文档等海量资料,你想要的我都有!
📣关于我
我是bug菌,CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云2023年度十佳博主,掘金多年度人气作者Top40,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 20w+;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿。