Java学习笔记(七)——Object类详解

Object类详解

equals方法

==与equals对比

==是一个比较运算符,并不是一个方法。

  • ==:既可以判断基本类型,也可以判断引用类型
  • ==:如果判断基本类型,判断的是值是否相等。例:int i =10;double d = 10.0
  • ==:如果判断引用类型,判断的是地址是否相等,即判断是否为同一个对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.learn.object_;

public class Equals01 {
public static void main(String[] args) {
B b = new B();
B c = b;
B d = c;
System.out.println(b == c);//true
System.out.println(d == c);//true
A aObj = b;
System.out.println(aObj == d);//true,因为地址还是相同的
}
}

class A{}
class B extends A{}A a = new A();
  • equals:是Object类中的方法,只能判断引用类型 。查看源码
  • 默认判断的是地址是否相等,子类中往往重写该方法,用于判断是否值相等。比如Integer、String【查看两个类中的equals方法源码】

子类重写equals方法

默认判断的是地址是否相等,子类中往往重写该方法,用于判断是否值相等。比如Integer、String

下面例子jdk版本:18.01

Object类中的equals方法:

1
2
3
public boolean equals(Object obj) {
return (this == obj);//只有一个判断引用(地址)是否相等
}

String里面equals源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public boolean equals(Object anObject) {
//把Object的equals方法重写了,变成了比较两个字符串值是否相等
if (this == anObject) {//如果是同一个对象
return true;//直接返回true
}
return (anObject instanceof String aString)//前置判断:必须是String类型,否则肯定不相等
&& (!COMPACT_STRINGS || this.coder == aString.coder)
//coder方法其实就是获取字符串采用的编码方式,如果编码方式都不一样,肯定结果为false
//根据数据是否是压缩数据,采用不同的比较方式
//数据压缩弃了高八位,一个八位就只占据一个byte数组位
//如果是非压缩版,一个字符对应两个byte数组位
&& StringLatin1.equals(value, aString.value);
//三个条件都相等(是String、编码方式一样、内容一样)则返回true
}

Integer里面equals源码:

1
2
3
4
5
6
public boolean equals(Object obj) {
if (obj instanceof Integer) {//判断是否为integer
return value == ((Integer)obj).intValue();
}
return false;
}

equals和==比较例子

1
2
3
4
5
6
7
8
9
10
Integer integer = new Integer(1000);
Integer integer1 = new Integer(1000);
System.out.println(integer == integer1);//false,两个对象地址不同
System.out.println(integer.equals(integer1));//true,值相同
System.out.println(integer.intValue() == integer1.intValue());//true,值相同

String str1 = new String("test");
String str2 = new String("test");
System.out.println(str1 == str2);//false,地址不同
System.out.println(str1.equals(str2));//true,值相同

自己重写equals方法

应用实例:判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回true,反之false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.learn.object_;

public class EqualsExercise01 {
public static void main(String[] args) {
Person person = new Person("jack", 10, '男');
Person person1 = new Person("jack", 10, '男');

System.out.println(person.equals(person1));//重写前:返回假,因为用的是Object类里面的equals方法,只判断是否为同一个对象,即地址是否相同
System.out.println(person.equals(person1));//重写后:true
}
}

class Person{
private String name;
private int age;
private char gender;

public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}

//重写Object的equals方法
public boolean equals(Object obj){
//判断如果比较的两个对象是同一个对象则直接返回true
if(this == obj){
return true;
}
//类型判断
if(obj instanceof Person){//如果类是Person我们才比较
//进行 向下转型,因为我需要得到obj的各个属性,如果不向下转型,对象无法使用其属性,编译类型为Object
Person obj1 =(Person)obj;
return (this.age == obj1.age)&&(this.name == obj1.name)&&(this.gender == obj1.gender);
}
return false;
}
}

hashcode方法

hashcode():返回该对象的哈希码值。支持此方法是为了提高哈希表(例如java.util.Hashtable提供的哈希表)的功能。

实际上,有Object类定义的hashcode方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是Java编程语言不需要这种实现技巧。因为Java本身是跑在虚拟机上的,是无法拿到对象的真正地址的,像更底层的语言C、C++是可以的。)

五个小结

  • (1)提高具有哈希结构的容器的效率
  • (2)两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
  • (3)两个引用,如果指向的是不同对象,则哈希值是不一样的!(不是绝对的,因为可能会产生哈希碰撞)
  • (4)哈希值主要根据地址号来的!不能完全将哈希值等价于地址。
  • (5)后面在集合中,hashcode如果需要的话,也会重写,如何重写可以看后面笔记。

toString方法

基本介绍

  • 返回对象的字符串表示。默认返回:全类名(包名+类名)+@+哈希值的十六进制(通过hashcode得到的一个整数转换成16进制)。
1
2
3
4
5
6
//Object类中toString方法源码
public String toString() {
//getClass().getName():类的全类名(包名+类名)
//nteger.toHexString(hashCode()):将对象的hashcode值转成一个16进制字符串
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • 子类往往重写toString方法,用于返回对象的属性信息。

  • 当直接输出一个对象时,toString方法会被默认的调用。比如System.out.println(monster),就会默认调用monster.toString()

重写toString方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.learn.object_;

public class toString_ {
public static void main(String[] args) {
Monster monster = new Monster("jk", "巡山", 1000);
System.out.println(monster.toString());
// 当直接输出一个对象时,toString方法会被默认的调用
System.out.println(monster);
}
}

class Monster{
private String name;
private String job;
private double sal;

@Override
public String toString() {//重写后,一般是把对象的属性值输出
return "Monster{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
", sal=" + sal +
'}';
}

public Monster(String name, String job, double sal) {
this.name = name;
this.job = job;
this.sal = sal;
}
}

finalize方法

基本介绍

当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

  • 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作。

  • 什么时候被回收:当某个对象没有任何引用时,则jvm就认为该对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法。

  • 垃圾回收机制的调用,是由系统来决定,也可以通过System.gc()主动出发垃圾回收机制。

  • 实际开发中,几乎不会运用finalize,所以更多是为了面试= =

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.learn.object_;

public class Finalize_ {
public static void main(String[] args) {
car audi = new car("audi");
audi = null;//这时car对象就是一个垃圾,垃圾回收器就会回收对象
//在销毁对象前,会调用该对象的finalize方法,程序员就可以在finalize中,写自己的业户逻辑代码(比如释放资源:数据库连接,或者打开文件...)
//注意:这里car对象的回收不是实时的,不是一变成垃圾就回收,有自己的算法。我们可以通过主动调用垃圾回收器来执行
System.gc();//运行垃圾回收器
System.out.println("程序结束");
//不过这里会不会出发finalize方法也是看运气的= =,至少jdk18一直输出不了finalize中的语句
}
}

class car{
private String name;

@Override
protected void finalize() throws Throwable {
super.finalize();//从9开始已经过时了
System.out.println("销毁汽车"+name);
}

public car(String name) {
this.name = name;
}
}