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的对象,这个返回来的对象 ...
随机推荐
- switch case 穿透 示例
public class SwitchCase { //判断输入的月份属于第几季度 public static void main(String[] args) { //随机获得 1-12个月份中的一 ...
- 微信小程序隐藏页面滚动条
开发小程序时,经常会碰到页面长度超过屏幕高度,然后下拉时会出现滚动条,对于一些有强迫症的人来说是不可忍受的. 网上看了好多,写的.都评论有起作用或者不起作用的. 我在这分享一个全局隐藏滚动条的方式. ...
- Vue3 element-plus 下拉分页 select分页
由于用 input 实现下拉分页不太理想,转换了一个角度,用 select 实现,以下是具体实现(script-setup TS) script-setup <script lang=" ...
- 解密Elasticsearch:深入探究这款搜索和分析引擎
作者:京东保险 管顺利 开篇 最近使用Elasticsearch实现画像系统,实现的dmp的数据中台能力.同时调研了竞品的架构选型.以及重温了redis原理等.特此做一次es的总结和回顾.网上没看到有 ...
- pg数据库的备份和恢复以及sql脚本错误的解决方法
1.备份单库单表的数据,以insert语句的方式 pg_dump -h IP -p 端口 -U 用户名 -t 表名 --inserts –f dbname.sql 数据库名 pg_dump -h 17 ...
- 【GiraKoo】常用编码的对比(ASCII,GB2312,GBK,GB18030,UCS,Unicode)
常用编码的对比(ASCII,GB2312,GBK,GB18030,UCS,Unicode) 在程序开发中,文字编码一直扮演着人畜无害,却背后捅一刀的角色. 可能在源代码文件中,注释莫名其妙地变成了乱码 ...
- javaer你还在手写分表分库?来看看这个框架怎么做的 干货满满
java orm框架easy-query分库分表之分表 高并发三驾马车:分库分表.MQ.缓存.今天给大家带来的就是分库分表的干货解决方案,哪怕你不用我的框架也可以从中听到不一样的结局方案和实现. 一款 ...
- 图解三代测序(SMRT Sequencing)
目前主流三代测序平台除了Oxford 家的 Nanopore,还有 Pacific Biosciences(简称 PacBio)公司的 Single Molecule Real-Time(SMRT)S ...
- R 语言绘制环状热图
作者:佳名来源:简书 - R 语言文集 1. 读取并处理基因表达数据 这是我的基因表达量数据: 图 Fig 1 > myfiles <- list.files(pattern = &quo ...
- 解决google翻译出错问题
解决google翻译问题 一.为什么失效 因为google把google翻译的API给关闭了,导致翻译不了. 据网上说是服务器耗钱,但盈利不够导致的. 二.可修复的前提 国内还存有服务器可以用API ...