Day3 | Java基础 | 4 常见类
- 基础版
- Object类
- equals
- hashCode(散列码)
- hashCode和equals
- clone方法
- String类
- 问题回答版
- Object类
- Object类的常见方法有哪些?
- ==和equals()的区别是什么?
- 为什么要有hashCode?
- hashCode和equals的区别是什么?
- 为什么重写equals()时必须重写hashCode()方法?
- 深拷贝和浅拷贝有什么区别?
- 深拷贝有几种实现方式?
- String类
- String、StringBuffer、StringBuilder的区别?
- Java的String类为什么不可变?
基础版
Object类
equals
源码:
public boolean equals(Object obj) {
return (this == obj);
}
若使用源码equals比较,不论两个自定义的对象是否相等,结果都是false。所以比较对象前一定要重写equals方法。
hashCode(散列码)
是由对象推导出的一个整型值,这个值为任意整数,包括整数或负数。
散列码没有规律。
- 如果x和y是两个不同的对象,x.hashCode()和y.hashCode()基本不会相同
- 如果a和b相等,则a.hashCode()一定等于b.hashCode()
源码:
public native int hashCode(); // native本地方法
hashCode和equals
规范(不是强制规定)
- hashCode和equals返回值应该是稳定的,不应有随机性
- 俩对象==返回true,则这两个对象的equals也应该返回true
- 俩对象euqals,则这两个对象的hashCode应该相等
扩展
- 默认对象的hashCode方法返回值永远 ≥ 0
- 默认对象的hashCode方法返回值不是对象的地址
- 关于==,有些有“池子”的对象会有特殊效果
clone方法
深拷贝和浅拷贝(没太明白)
一个例子:
//浅拷贝
public class Person {
private Name name;
private Address address;
public Person(Person originalPerson) {
this.name = originalPerson.name;
this.address = originalPerson.address;
// name、address属性与原对象共享
}
}
//深拷贝
public class Person {
private Name name;
private Address address;
public Person(Person otherPerson) {
this.name = new Name(otherPerson.name);
this.address = new Address(otherPerson.address);
// 创建新的实例
}
}
String类
问题回答版
Object类
Object类的常见方法有哪些?
方法 | 描述 |
---|---|
protected Object clone() | 创建并返回一个对象的拷贝 |
boolean equals(Object obj) | 比较两个对象是否相等 |
protected void finalize() | 当GC(垃圾回收器)确定不存在对该对象有更多引用时,由对象的垃圾回收器调用此方法 |
Class<?> getClass() | 获取对象运行时对象的类 |
int hashCode() | 获取对象的hash值 |
void notify() | 唤醒在该对象上等待的某个线程 |
void notifyAll() | 唤醒在该对象上等待的所有线程 |
String toString() | 返回对象的字符串表示形式 |
void wait() | 让当前线程进入等待状态。直到其他线程调用此对象的notify()方法或notifyAll()方法 |
void wait(long timeout) | 让当前线程处于等待(阻塞)状态,直到其他线程调用此对象的notify方法或notifyAll()方法,或者超过参数设置的timeout超时时间 |
void wait(long timeout, int nanos) | 与void wait(long timeout)方法类似,多了一个nanos参数,这个参数表示额外时间(以纳秒为单位,范围是0-999999)。因此超时时间还要加上nanos纳秒 |
==和equals()的区别是什么?
- ==操作符比较对象的引用,判断是否为同一对象
- equals()方法比较对象的内容,但默认情况下与==的行为相同(比较内容)
- 一些类会覆盖equals()方法以便在内容上进行比较,例如String、Integer、Double等
当需要比较对象的内容时,务必使用正确的方法。同时注意处理参数为null的情况,以及在自定义类中正确地覆盖equals()方法。
为什么要有hashCode?
参考链接
1、用于查询某个集合是否在集合中
- 不通过hash方式定位元素(的存储位置):只能按照集合的前后顺序,依次访问比对。效率低下。
- 通过hash计算,可以直接定位出某个值存储的位置。
2、用于比较两个对象是否相等(与equals协同)
- 若hashCode相等,再使用equals再次比较,如果前后比较结果均为true,则认定两个对象相等
- 其他情况均认为两个对象不相等。(提高比较的效率)
为什么不直接使用hashCode就确定俩对象是否相等?
因为不同对象的hashCode可能相同;但hashCode不同的对象一定不相等。所以用hashCode进行快速初次判断。
hashCode和equals的区别是什么?
- | equals | hashCode |
---|---|---|
用途 | 判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定相等。 |
为什么重写equals()时必须重写hashCode()方法?
如果在重写equals时,不重写hashCode,就会导致在某些场景下,例如将两个相等的自定义对象存储在Set集合时(默认情况下Set进行去重操作时,会先盘算两个对象的hashCode是否相同,如果相同再调用equals方法确认是否对象相等,如果确实相等才进行去重操作),就会出现程序执行的异常。为了保证程序的正常执行,我们需要在重写equals时,也一并重写hashCode方法才行。
深拷贝和浅拷贝有什么区别?
数据位置 | 深拷贝 | 浅拷贝 |
---|---|---|
栈内存 存储基本数据类型 | 拷贝 | 拷贝 |
堆内存 存储数组、引用数据类型等 | 拷贝 | 不拷贝 |
实现深拷贝,修改拷贝对象的属性和内容,不会影响到原对象。
深拷贝有几种实现方式?
- 实现Cloneable接口,并重写clone方法(最常见):缺点是比较麻烦,需要所有实体类都实现Cloneable接口,并重写clone方法。如果实体类中新增了一个引用对象类型的属性,还需要添加到clone方法中。如果继任者忘了修改clone方法,相当于一个隐患。
- 使用JSON字符串转换:先把user对象转换成json字符串,再把json字符串转换成user对象。
- 集合:初始化新对象时,把原对象传入到新对象的构造方法中。
String类
参考链接
String、StringBuffer、StringBuilder的区别?
- String:提供了构造和管理字符串的各种基本逻辑。是典型的
Immutable
类,被声明为final class
,所有属性也都是final
的。因其不可变性,类似拼接、裁剪字符串等动作,都会产生新的String对象。由于字符串操作的普遍性,所以相关操作的效率往往对应用性能有明显影响。 - StringBuffer:为解决上面提到的产生太多中间对象的问题而提供的一个类。我们可以用append或add方法,把字符串添加到已有序列的末尾或者指定位置。是一个线程安全的可修改字符序列。它的线程安全是通过在各种修改数据的方法上用
synchronized
关键字修饰实现的。 - StringBuilder:Java1.5中新增的,能力与StringBuffer没有本质区别,但是去掉了线程安全的部分,有效减小了开销,是绝大部分情况下进行字符串拼接的首选。
Java的String类为什么不可变?
String的定义:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
被final
关键字修饰。表示不可继承String类。该类的数据存储与char[]
数组,这个数组被final
修饰,表示String对象不可被更改。
这样设计的目的是:
- 保证String对象的安全性。避免String被篡改。
- 保证hash值不会频繁变更。
- 可以实现字符串常量池。
通常有两种创建字符串对象的方式:
- 通过字符串常量的方式:
String str = "abc"
。
- JVM首先会检查该对象是否在字符串常量池中,如果在,就返回该对象引用,否则在常量池中创建新的字符串。这种方式可以减少同一个值的字符串对象的重复创建,节约内存。
- 字符串变量通过new的形式:
String str = new String("abc")
- 首先,在编译类文件时,“abc”常量字符串会被放入到常量结构中。类加载时,“abc”会在常量池中创建。
- 其次,在调用new时,JVM命令将会调用String的构造函数,同时引用常量池中的“abc”字符串,在堆内存中创建一个String对象。
- 最后,str 将引用String对象。