java中Object 类
一. Object类简介
Object类是Java.java.lang包下的核心类,Object类是所有类的父类,任何一个类如果没有明确的继承一个父类的话,那么它就是Object的子类;
(使用无需导包,它所属JDK -> SRC.ZIP -> java -> lang 包下)
二. Object的方法
Object 提供9种方法(Clone、equals、 hashcode、wait、notify、notifyall、finalize、toString、getClass)
1 public final native Class<?> getClass(); 2 public native int hashCode(); 3 public boolean equals(Object obj); 4 protected native Object clone() throws CloneNotSupportedException; 5 public String toString(); 6 public final native void notify(); 7 public final native void notifyAll(); 8 public final native void wait(long timeout) throws InterruptedException; 9 public final void wait(long timeout, int nanos) throws InterruptedException; 10 public final void wait() throws InterruptedException ; 11 protected void finalize() throws Throwable;
三.方法的作用
1. getclass() public final native Class<?> getClass(); final修饰,用于反射机制中获取对象,返回Class类型。
1 User user = new User();
2 Class<? extends User> userClass = user.getClass();
3 System.out.println(userClass);
打印结果:
class org.xinhua.entity.User
2. hashCode() public native int hashCode(); hash code是一种编码方式,在Java中,每个对象都会有一个hashcode,Java可以通过这个hashcode来识别一个对象。hashtable等结构,就是通过这个哈希实现快速查找键对象。返回值是int类型的散列码。
1 User user = new User();
2 int userHashCode = user.hashCode();
3 System.out.println(userHashCode);
打印结果:
549374472
--------------------------------------------
List<String> strList = new ArrayList<>();
strList.add("个人分类")
每次往List中添加元素,都会得到不同的hashCode值。
即:对于可变对象,hashCode
应该在它们以某种方式改变时改变,使它们不等于它们以前的状态。
3. equals(Object obj) public boolean equals(Object obj); 用于比较对象是否相等,可通过重写equals()获取不同效果。返回布尔类型的值。
1 String str1 = "aaa", str2 = "bbb";
2 boolean equals = str1.equals(str2);
3 System.out.println(equals);
4 打印结果: false
注意:如果需要重写equals方法,应该重写hashcode方法.原生equals是比较对象是否相同,hashcode内存地址换算出来的一个值,如果只重写equals方法,而不重写hashCode方法的话,就回出现因为equals()两个对象相同,但是原先的hashCode()对应的值不同
4. clone() protected native Object clone() throws CloneNotSupportedException; 用于创建并返回一个对象的拷贝。clone 方法是浅拷贝,对象内属性引用的对象只会拷贝引用地址,而不会将引用的对象重新分配内存,相对应的深拷贝则会连引用的对象也重新创建。建议使用深拷贝.
① 类实现Cloneable接口,
②重写Object中clone() 方法
③有异常CloneNotSupportedException 抛出
④对象.clone()
ClassName类
// 实现Cloneable接口
public class User implements Cloneable {
private String name;// 略去构造方法、getter()、setter()、toString();
// 重写clone方法
@Override
protected User clone() throws CloneNotSupportedException {
return (User) super.clone(); // 这里做了强转
}
} main方法
public static void main(String[] args) throws CloneNotSupportedException {
User user= new User("张三");
User u2= user.clone();
System.out.println(user);
System.out.println(u2);
}
5. toString() public String toString(); 其返回值是String类型,返回类名和它 的引用地址。可以通过重写toString(),在打印对象时获得理想的格式。
User user = new User();
user.setRealName("张三");
user.setEmail("zhangsan@163.com");
System.out.println(user); 重写toString前:
打印输入:
org.xinhua.entity.User@bdf5e5 // 输入的是类名+地址
重写toString:
@Override
public String toString() {return "获取到的姓名是:" + this.getRealName() + ",邮箱是:" + this.getEmail(); }
打印输出:
获取到的姓名是:张三,邮箱是:zhangsan@163.com
6. notify() public final native void notify(); 多线程编程中比较常见,从当前对象锁的等待队列中获取一个线程(如果有多个线程则获取优先级高的,优先级都一样则随机),移入到同步队列中,参与锁的竞争。
①搭配 synchronized 来使用,脱离synchronized使用notify会抛出IllegalMonitorStateException异常,目的是防止死锁或永久等待发生
②调用notify方法后,当前线程不会马上释放该对象锁,要等到同步块或者同步方法执行完后,当前线程才会释放锁。
public static void main(String[] args) throws InterruptedException { Object o = new Object(); new Thread(() -> {
System.out.println("第一步:wait执行,");
synchronized (o) {
try {
o.wait(); // 使当前执行代码的线程进行等待,把线程放在等待队列中,若没有唤醒,则持续等待。
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("第四步:wait执行后");
}).start(); Thread.sleep(1000); new Thread(() -> {
System.out.println("第二步:notify执行前");
synchronized (o) {
o.notify(); // 用于唤醒 o.wait的线程
}
System.out.println("第三步:notify执行后");
}).start();
}
7. notifyAll() public final native void notifyAll(); 和notify一样,都是用来唤醒处于等待状态的线程参与锁的竞争,但也有一些区别。notifyAll会将当前对象锁的等待队列的所有线程,都移入到同步队列中,但与锁的竞争。
①搭配 synchronized 来使用,脱离synchronized使用notify会抛出IllegalMonitorStateException异常,目的是防止死锁或永久等待发生。
②调用notifyAll方法后,当前线程不会马上释放该对象锁,要等到同步块或者同步方法执行完后,当前线程才会释放锁。
public static void main(String[] args) throws InterruptedException { Object o = new Object(); new Thread(() -> {
System.out.println("wait1执行前.");
synchronized (o) {
try {
o.wait(); // 使当前执行代码的线程进行等待,把线程放在等待队列中,若没有唤醒,则持续等待。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("notifyAll执行后,wait1");
}).start(); Thread.sleep(1000); new Thread(() -> {
System.out.println("wait2执行前..");
synchronized (o) {
try {
Thread.sleep(1000);
o.wait(); // 使当前执行代码的线程进行等待,把线程放在等待队列中,若没有唤醒,则持续等待。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("notifyAll执行后,wait2");
}).start(); Thread.sleep(1000); new Thread(() -> {
System.out.println("notify线程执行前");
synchronized (o) {
o.notifyAll(); // 用于唤醒所有当前o锁的等待线程,
}
System.out.println("notify线程执行后");
}).start(); }
8. wait() public final void wait() throws InterruptedException ; 让当前线程进入等待(阻塞)WAITTING 状态,将线程放入到等待队列中,并释放对象锁。
①搭配 synchronized 来使用,脱离synchronized使用notify会抛出IllegalMonitorStateException异常,目的是防止死锁或永久等待发生。
②调用wait方法后,会立即释放当前线程锁占有的对象锁。会暂停执行wait后面的代码块,需要等待,其它拿到该锁对象的线程执行notify或notifyAll方法唤醒该线程后,才会继续执行wait后面的代码块。
代码如上所示。
9. wait(long timeout) public final native void wait(long timeout) throws InterruptedException; timeout(毫秒)时间后会被自动唤醒 wait(0)等同于wait()
1 public static void main(String[] args) {
2 Object o = new Object();
3
4 new Thread(() -> {
5 long start = System.currentTimeMillis();
6 System.out.println("wait开始执行");
7 synchronized (o) {
8 try {
9 o.wait(1000);
10 } catch (InterruptedException e) {
11 e.printStackTrace();
12 }
13 }
14 long end = System.currentTimeMillis();
15 System.out.println("wait执行结束");
16 System.out.println("执行时间:" + (end - start) + "毫秒");
17 }).start();
18 }
19
20 打印输出:
21 wait开始执行
22 wait执行结束
23 执行时间:1010毫秒
10. wait(long timeout, int nanos) public final void wait(long timeout, int nanos) throws InterruptedException; nanos 这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 毫微秒。 wait(0, 0) 与 wait(0) 相同。 wait(500, 50000),先执行5000毫微秒,执行结束后调用timeout执行。
1 public static void main(String[] args) {
2 Object o = new Object();
3
4 new Thread(() -> {
5 long start = System.currentTimeMillis();
6 System.out.println("wait开始执行");
7 synchronized (o) {
8 try {
9 o.wait(5000,50000); // 先执行5000毫微秒,执行结束后调用timeout执行。
10 } catch (InterruptedException e) {
11 e.printStackTrace();
12 }
13 }
14 long end = System.currentTimeMillis();
15 System.out.println("wait执行结束");
16 System.out.println("执行时间:" + (end - start) + "毫秒");
17 }).start();
18 }
19
20 打印输出:
21 wait开始执行
22 wait执行结束
23 执行时间:513毫秒
11. finalize() protected void finalize() throws Throwable; finalize()垃圾回收机器,方法负责回收Java对象所占用的内存,该方法一般是在对象被垃圾收集器回收之前调用。通常我们会在finalize()方法中,指定对象销毁时要执行的操作,比如关闭对象打开的文件、IO流、释放内存资源等清理垃圾碎片的工作。
垃圾回收机器有以下的特点:
①当对象不再被程序所使用的时候,垃圾回收器将会将其回收;
②垃圾回收是在后台运行的,我们无法命令垃圾回收器马上回收资源,但可以通过System.gc()或Runtime.getRuntime().gc()方法提示垃圾回收器执行回收操作。
③垃圾回收器在回收某个对象的时候,首先会调用该对象的finalize()方法
④GC主要针对堆内存
⑤垃圾回收的时机具有不确定性,finallize()也可能自始自终都不被调用。
1 public class User {
2
3 private Integer count;
4
5 public User(Integer count) {
6 this.count = count;
7 }
8
9 // 重写finalize()方法,用于标记count计数。
10 @Override
11 protected void finalize() throws Throwable {
12 System.out.println("gc标记,销毁。" + count++);
13 super.finalize();
14 }
15 }
16
17 public class GCTest{
18 public static void main(String[] args) throws InterruptedException {
19 User user = null;
20 for (int i = 1; i <= 100; i++) {
21 Thread.sleep(100);
22 user = new User(i);
23 System.gc(); // 调用GC方法配合finalize()使用。
24 }
25
26 }
27 }
28
29 打印输出:
30 .........
31 gc标记,销毁。91
32 gc标记,销毁。92
33 gc标记,销毁。93
34 gc标记,销毁。94
35 gc标记,销毁。95
36 gc标记,销毁。96
37 gc标记,销毁。97
38 gc标记,销毁。98
39 gc标记,销毁。99
// 从打印角度没有看到gc标记,销毁。100 有两种可能,第一种是java进程走完了,导致还没有执行回收就被停止了;第二种可能,已标记,暂时未回收。
// 项目中若有其他线程引用了该对象的引用值,会出现标记不清理的情况,长时间不适用,就会自动释放。
三. 拓展
1. 为什么需要重写equals和hashCode方法?
首先我们看下源码中的equals方法;
public boolean equals(Object obj) {
return (this == obj); // 比较地址值是否相同
}
Java中规定:
(1)如果两个对象通过equals方法比较是相等的,那么它们的hashCode方法结果值也是相等的。
(2)如果两个对象通过equals方法比较是不相等的,那么它们的hashCode方法结果值不一定不相等。
如果只重写equals,而不重写hashcode,会导致地址值相同,而hashcode不同。在使用hashMap、hashSet中,通过计算hashcode来筛重,会出现错误。
public class User { private String name; public User(String name) {
this.name = name;
}
// 重写equals
@Override
public boolean equals(Object o) {
if (o instanceof User) {
User u = (User) o;
if (this.name.equals(u.name)) return true;
else return false;
} else {
return false;
}
}
} public class Main {
public static void main(String[] args) {
User u1 = new User("张三");
User u2 = new User("张三");
System.out.println(u1.equals(u2));
System.out.println(u1.hashCode());
System.out.println(u2.hashCode());
}
}
打印输出:
true
23934342
22307196
2. clone() 浅拷贝和深拷贝
浅拷贝:只拷贝栈内存中的数据,不拷贝堆内存的数据。(只拷贝基本类型数据,使用引用类型数据原地址);
深拷贝:既拷贝栈内存中的数据,又拷贝堆内存中的数据。(拷贝基本数据类型,同时在堆内存中开辟一块空间,用于拷贝引用类型的数据);
特殊类型:String类型。clone() 并不会克隆Sting类;String是基于堆内存、常量池,这种比较特殊,本身没有实现CloneAble,自身是由final修饰,每次赋值都是一个新的引用地址,原对象的引用和副本的引用互不影响。
浅拷贝实现:
User类,没有实现Cloneable接口
public class User { private int age; private String name; // 略:构造方法、getter、setter、toString...
// 没有重写clone()方法 } UserDto类,实现Cloneable接口
public class UserDto implements Cloneable { private String phone; //引用user类
private User user; // 略:构造方法、getter、setter、toString...
@Override
protected UserDto clone() throws CloneNotSupportedException {
return (UserDto) super.clone();
} } Main方法
public static void main(String[] args) throws CloneNotSupportedException { User u1 = new User(18, "张三");
UserDto d1 = new UserDto("110",u1); UserDto d2 = d1.clone(); // clone() 克隆
System.out.println(d1.getUser()); //打印d1的引用类型地址:
System.out.println(d2.getUser());// 打印d2的引用类型地址
System.out.println(System.identityHashCode(d1.getPhone())); // String类型的地址值
System.out.println(System.identityHashCode(d2.getPhone()));
} 打印输出: clone.User@16d3586 // 类名+地址值
clone.User@16d3586
22307196
22307196
可以看到引用类型地址值没有变化。
深拷贝实现:
User类,实现Cloneable接口
public class User implements Cloneable { private int age; private String name; // 略:构造方法、getter、setter、toString
// 引用类中重写clone()。
@Override
protected User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
} UserDto类中,实现CloneAble接口
public class UserDto implements Cloneable { private String phone; private User user; // 略:构造方法、getter、setter、toString
// 重写clone()
@Override
protected UserDto clone() throws CloneNotSupportedException {
UserDto userDto = (UserDto) super.clone();
userDto.user = user.clone();
return userDto;
}
} Main类:未做改动
public static void main(String[] args) throws CloneNotSupportedException { User u1 = new User(18, "张三");
UserDto d1 = new UserDto("110",u1); UserDto d2 = d1.clone(); // clone() 克隆
System.out.println(d1.getUser()); //打印d1的引用类型地址:
System.out.println(d2.getUser());// 打印d2的引用类型地址
System.out.println(System.identityHashCode(d1.getPhone())); // String类型的地址值
System.out.println(System.identityHashCode(d2.getPhone()));
} 打印输出:
clone.User@16d3586 // 类名+地址值
clone.User@154617c
10568834
10568834 引用类型地址值发生变化
clone() 过去繁琐,推荐使用 BeanUtils.copyProperties() 方法。 包:import org.springframework.beans.BeanUtils;
3. notify()和 notifyAll()有什么区别?
① 唤醒数量不同,notify()方法只会随机唤醒等待队列中的一个线程,而notifyAll()方法则会唤醒等待队列中的所有线程。
② 调用方式不同,notify()和notifyAll()方法都必须在同步代码块中调用,并且必须包含在synchronized块中,且必须是该对象的监视器对象才能够调用。而且只有在获取了锁之后才能调用,否则会抛出IllegalMonitorStateException异常。
③ 竞争情况不同,notify()方法只会唤醒等待队列中的一个线程,并使其与其他线程竞争获取锁,这可能会导致某些线程无法被唤醒或者一直处于等待状态。 notifyAll()方法则会唤醒等待队列中的所有线程,并使它们竞争获取锁,这样可以使所有线程都有机会获取锁并进入运行状态,从而避免了一些线程一直处于等待状态。
因此,在多线程编程中,如果需要唤醒所有等待该对象的线程,则可以使用notifyAll()方法;如果只需要随机唤醒一个等待该对象的线程,则可以使用notify()方法。
完!
java中Object 类的更多相关文章
- Java中Object类hashCode的底层实现
Java中Object类hashCode的底层实现 openjdk\jdk\src\share\native\java\lang\Object.c 42 static JNINativeMethod ...
- 【Java基础之Object类(一)】Java中Object类中的所有方法(toString、equals、hashCode、clone、finalize、wait和notify等)详解(转载)
java中的hashcode.equals和toString方法都是基类Object的方法. 首先说说toString方法,简单的总结了下API说明就是:返回该对象的字符串表示,信息应该是简明但易于读 ...
- Java中Object类的公有方法
HashCode();wait();notify();equals();getClass();toString();clone();finalize(); 这里只是简单介绍一下其中的几个函数: Has ...
- Java中Object类常用的12个方法,你用过几个?
前言 Java 中的 Object 方法在面试中是一个非常高频的点,毕竟 Object 是所有类的“老祖宗”.Java 中所有的类都有一个共同的祖先 Object 类,子类都会继承所有 Object ...
- java中Object类是怎么回事,干嘛使的?举例说明!
Object类的作用:m a r k - t o- w i n: 在java中,因为所有的类都有共性,所以java的缔造者们把java设计成这样:所有的类都是Object类的直接或间接子 ...
- Java中Object类
Object类是所有类的父类,如果一个类没有使用extends关键字明确标识继承另一个类,那么这个类默认继承Object类. Object类中的方法,适合所有子类. Object中的几个重要方法: 1 ...
- Java中Object类的方法笔记
今天看了下Object类的源码,以下是我看源码的一些笔记,欢迎有小伙伴来补充~ 首先列举下几个主要方法(面试被问到过的): equals:这个主要是用于比较对象的,Object中比较的是比较原始的,直 ...
- java 中Object类中toString()的使用
1. 当我们输出一个对象的引用时,实际上就是调用当前对象的toString() 2. Object类中toString()的定义: public String toString() { return ...
- java中Object类的finalize的用法
Object类的finalize的用法: 马克-to-win:java当中有个垃圾回收机制,具体说,就是当一些对象被创建使用之后若不再使用的话{比如(i)对象被置成null.(ii)局部对象(无需置成 ...
- java中Object类的getClass方法有什么用以及怎么使用?
Object类的getClass的用法: Object类中有一个getClass方法,m a r k- t o- w i n:它会返回一个你的对象所对应的一个Class的对象,这个返回来的对象 ...
随机推荐
- ChatCLM部署随笔
ChatCLM 博客 ChatGLM Github ChatGLM-webui 介绍 ChatGLM-6B 是一个开源的.支持中英双语的对话语言模型,基于 General Language Model ...
- 手把手逐步解析Javaweb登录实例
一.编写前端界面 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- RTSP Server(LIVE555)源码分析(五)-PLAY信令
主要分析RTSPServer::RTSPClientSession针对客户端PLAY事件处理 一. PLAY信令,handleCmd_withinSession源码解析 1)步骤1.03,当RTSP客 ...
- 笔记八:linux系统编程之IO
笔记:linux系统编程之IO 应用层 内核层 硬件层 应用层:数据结构 .java.android.C.C++,C#: l inux高级编程:涉及内核为应用层提供接口函数: 内核五大 ...
- php获取未解码之前的原始接口请求参数
前言 目前的几个项目,业务方基本都使用POST方式请求接口,我们本机磁盘会保留一份请求的原始参数用于请求分析和问题排查使用,一般有问题,也会基于seqid(请求唯一id)捞到日志,copy参数模拟请求 ...
- 带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性 1.什么是语言模型? 大家或多或少都听过 ChatGPT 是一个 LLMs,那 LLMs 是什么? ...
- 富文本编辑器 VUE-QUILL-EDITOR 使用教程 (最全)
VUE-QUILL-EDITOR 基于 QUILL.适用于 VUE 的富文本编辑器,支持服务端渲染和单页应用,非常高效简洁. 一.基础用法 1.NPM 导入 VUE-QUILL-EDITOR npm ...
- 2022-11-22:小美将要期中考试,有n道题,对于第i道题, 小美有pi的几率做对,获得ai的分值,还有(1-pi)的概率做错,得0分。 小美总分是每道题获得的分数。 小美不甘于此,决定突击复习,
2022-11-22:小美将要期中考试,有n道题,对于第i道题, 小美有pi的几率做对,获得ai的分值,还有(1-pi)的概率做错,得0分. 小美总分是每道题获得的分数. 小美不甘于此,决定突击复习, ...
- 2022-09-29:在第 1 天,有一个人发现了一个秘密。 给你一个整数 delay ,表示每个人会在发现秘密后的 delay 天之后, 每天 给一个新的人 分享 秘密。 同时给你一个整数 forg
2022-09-29:在第 1 天,有一个人发现了一个秘密. 给你一个整数 delay ,表示每个人会在发现秘密后的 delay 天之后, 每天 给一个新的人 分享 秘密. 同时给你一个整数 forg ...
- 2022-04-02:你只有1*1、1*2、1*3、1*4,四种规格的砖块。 你想铺满n行m列的区域,规则如下: 1)不管那种规格的砖,都只能横着摆, 比如1*3这种规格的砖,3长度是水平
2022-04-02:你只有11.12.13.14,四种规格的砖块. 你想铺满n行m列的区域,规则如下: 1)不管那种规格的砖,都只能横着摆, 比如1*3这种规格的砖,3长度是水平方向,1长度是竖直方 ...