当前位置 : 主页 > 编程语言 > java >

【Java -- 基础】Object 类的公有方法

来源:互联网 收集:自由互联 发布时间:2022-06-22
Java 中所有的类都有一个共同的基类,这个基类就是 Object 。 方法说明 Object() 构造函数。 clone() 1. cloneable clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它

Java 中所有的类都有一个共同的基类,这个基类就是 Object 。

【Java -- 基础】Object 类的公有方法_java

方法说明

Object()

构造函数。

clone()

1. cloneable
clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。

public class CloneExample {
private int a;
private int b;
}CloneExample e1 = new CloneExample();
// CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object'

重写 clone() 得到以下实现:

public class CloneExample {
private int a;
private int b;

@Override
public CloneExample clone() throws CloneNotSupportedException {
return (CloneExample)super.clone();
}
}CloneExample e1 = new CloneExample();
try {
CloneExample e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}java.lang.CloneNotSupportedException: CloneExample

以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。

应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。

public class CloneExample implements Cloneable {
private int a;
private int b;

@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

2. 浅拷贝
拷贝对象和原始对象的引用类型引用同一个对象。

public class ShallowCloneExample implements Cloneable {

private int[] arr;

public ShallowCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}

public void set(int index, int value) {
arr[index] = value;
}

public int get(int index) {
return arr[index];
}

@Override
protected ShallowCloneExample clone() throws CloneNotSupportedException {
return (ShallowCloneExample) super.clone();
}
}ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 222

3. 深拷贝
拷贝对象和原始对象的引用类型引用不同对象。

public class DeepCloneExample implements Cloneable {

private int[] arr;

public DeepCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}

public void set(int index, int value) {
arr[index] = value;
}

public int get(int index) {
return arr[index];
}

@Override
protected DeepCloneExample clone() throws CloneNotSupportedException {
DeepCloneExample result = (DeepCloneExample) super.clone();
result.arr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result.arr[i] = arr[i];
}
return result;
}
}DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

4. clone() 的替代方案
使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。

public class CloneConstructorExample {

private int[] arr;

public CloneConstructorExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}

public CloneConstructorExample(CloneConstructorExample original) {
arr = new int[original.arr.length];
for (int i = 0; i < original.arr.length; i++) {
arr[i] = original.arr[i];
}
}

public void set(int index, int value) {
arr[index] = value;
}

public int get(int index) {
return arr[index];
}
}CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

equals(Object)

1. 等价关系

两个对象具有等价关系,需要满足以下五个条件:

Ⅰ 自反性

x.equals(x); // true

Ⅱ 对称性

x.equals(y) == y.equals(x); // true

Ⅲ 传递性

if (x.equals(y) && y.equals(z))
x.equals(z); // true;

Ⅳ 一致性

多次调用 equals() 方法结果不变

x.equals(y) == x.equals(y); // true

Ⅴ 与 null 的比较

对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false

x.equals(null); // false;

2. 等价与相等
对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。

Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y); // false

3. 实现

  • 检查是否为同一个对象的引用,如果是直接返回 true;
  • 检查是否是同一个类型,如果不是,直接返回 false;
  • 将 Object 对象进行转型;
  • 判断每个关键域是否相等。
public class EqualExample {

private int x;
private int y;
private int z;

public EqualExample(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

EqualExample that = (EqualExample) o;

if (x != that.x) return false;
if (y != that.y) return false;
return z == that.z;
}
}

finalize()

这个函数在进行垃圾回收的时候会用到,匿名对象回收之前会调用到。

getClass()

返回一个Class对象,如果打印出来会发现结果是如下格式
​​​class package.name.xxx ​​

hashCode()

hashCode() 返回哈希值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价,这是因为计算哈希值具有随机性,两个值不同的对象可能计算出相同的哈希值。

在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象哈希值也相等。

HashSet 和 HashMap 等集合类使用了 hashCode() 方法来计算对象应该存储的位置,因此要将对象添加到这些集合类中,需要让对应的类实现 hashCode() 方法。

下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。我们希望将这两个对象当成一样的,只在集合中添加一个对象。但是 EqualExample 没有实现 hashCode() 方法,因此这两个对象的哈希值是不同的,最终导致集合添加了两个等价的对象。

EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size()); // 2

理想的哈希函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的哈希值上。这就要求了哈希函数要把所有域的值都考虑进来。可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。

R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位,最左边的位丢失。并且一个数与 31 相乘可以转换成移位和减法:31*x == (x<<5)-x,编译器会自动进行这个优化。

@Override
public int hashCode() {
int result = 17;
result = 31 * result + x;
result = 31 * result + y;
result = 31 * result + z;
return result;
}

notify()

用于随机通知一个持有对象的锁的线程获取操作权限。

notifyAll()

用于通知所有持有对象的锁的线程获取操作权限。

toString()

默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。

public class ToStringExample {

private int number;

public ToStringExample(int number) {
this.number = number;
}
}ToStringExample example = new ToStringExample(123);
System.out.println(example.toString());ToStringExample@4554617c

wait()

用于让当前线程失去操作权限,当前线程进入等待序列。

wait(long)

用于设定下一次获取锁的距离当前释放锁的时间间隔。

wait(long,int)

用于设定下一次获取锁的距离当前释放锁的时间间隔。


上一篇:【Java -- 基础】多态的实现原理
下一篇:没有了
网友评论