Object类详解

之前我们了解到Object类是所有类的基类,其中包含了很多方法,现在我们去一一了解一下。

在JDK文档中找到Object类,看看它有哪些方法

 这里我们主要看一些常用的就行。比如:equals()、finalize()、hashcode()、toString()等。

1. equals方法

 1.1 == 和equals的对比 [面试题]

== 是一个比较运算符

  1. ==:既可以判断基本类型,也可以判断引用类型
  2. ==:如果判断基本类型,判断的是值是否相等。
    例如:int i = 10; double d = 10.0 System.out.println( i == d);
  3. ==:如果判断的是引用类型,判断的是地址是否相同,即判定是不是同一个对象。(如下代码)注意区分对象引用和对象实例的概念!
public class Equals01 {
    public static void main(String[] args) {
        A a = new A();
        A b = a;
        A c = b;
        System.out.println(a == c);  // ture
        System.out.println(b == c);  // ture
        // 判断不同类型的引用
        B obj = a; //A类向B类的向上转型
        System.out.println(obj == c); //true
    }
}
class A extends B{}
class B{}

4. equals:是Object类中的方法,只能判断引用类型。

5. 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如Integer,String,这两个类已经默认重写了equals方法。

 1.2 equals方法在JDK中的源码

为了更清楚的明白equals的本质,看下equals方法是怎么写的

    public boolean equals(Object anObject) {
        if (this == anObject) {  // 如果是同一个对象
            return true;   // 返回true
        }
        if (anObject instanceof String) {  // 判断运行类型是不是相同或者其子类
            String anotherString = (String)anObject;  // 向下转型
            int n = value.length;
            if (n == anotherString.value.length) {  // 传入的是字符串,如果字符串长度相同
                char v1[] = value;  //这里是把String 转成了char 数组
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {   // 然后一个一个的比较字符
                    if (v1[i] != v2[i])  // 如果有一个不同
                        return false;   // 返回flase
                    i++;
                }
                return true;  // 如果两个字符串的所有字符都相同,返回true
            }
        }
        return false; // 如果比较的不是字符串,直接返回flase
    }

再看一下equals在Object类中的源码是怎么写的(对应1.1中的第4条)

public boolean equals(Object obj) {
 return (this == obj);
 }

如上述代码所示,Obejct类的equals方法默认就是比较对象是否相同,也就是判断两个对象是不是同一个对象。

在源码中也能看到Integer重写了Object的equals方法,变成了判断两个值是否相同。如下:

public boolean equals(Object obj) {
 if (obj instanceof Integer) {
   return value == ((Integer)obj).intValue();
   }
   return false;
}

1.3 如何重写equals方法

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

        System.out.println(person1.equals(person2));
    }
}

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类型,才进行比较
            Person p = (Person)obj; //进行向下转型,因为需要得到obj的各个属性
            return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
        }
        // 如果不是Person,则直接返回false
        return false;
    }
}

2. hashCode 方法

2.1 hashCode概念

hashCode在API文档中的介绍如下图:

2.2  hsdCode的要点

  1. 提高具有哈希结构的容器的效率
  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的
  4. 哈希值主要是根据地址号来的。不能完全将哈希值等价于地址
  5. 在集合中,hashCode如果需要的话,也会进行重写操作

看一下两个对象和同一个对象的hash值是什么样的

public class HashCode {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
        A a3 = a1;

        System.out.println("a1.hashCode()="+ a1.hashCode());
        System.out.println("a2.hashCode()="+ a2.hashCode());
        System.out.println("a3.hashCode()="+ a3.hashCode());
    }
}

class A{}

输出的结果如下:

需要注意的是,哈希值可以当作地址来看,但它并不是真实的地址,因为java是跑在java虚拟机上的,没有办法得到底层真正的地址。

3. toString方法

3.1 toString概念

toString在API文档中的介绍如下图:

 toString在Object类中的源码

 public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
 // 其中getClass().getName()为全类名(就是包名+类名)
 // @ 为间隔符
 // Integer.toHexString(hashCode())是把哈希值的整数调用Integer.toHexString转成16进制的字符串

3.2 toString方法重写

3.2.1 没重写之前的调用

没重写之前调用的是Object类的toString方法,默认输出的是全类名和哈希值转换后的十六进制字符串

public class ToString {
    public static void main(String[] args) {

        Monster monster = new Monster("jack", "打杂", 1000);
        //没重写之前调用的是Object类的toString方法,输出的是全类名和哈希值转换后的十六进制字符串
        //我们把哈希值也输出,用计算器算一下是否正确
        System.out.println(monster.toString() +" "+ monster.hashCode());
    }
}
class Monster{
    private String name;
    private String job;
    private double sal;

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

输出结果:

计算器转换的结果:

 

结果是一样的。 

3.2.2 重写后的调用

重写的目的:

打印对象或拼接对象时,都会自动调用该对象的toString形式

public class ToString {
    public static void main(String[] args) {

        Monster monster = new Monster("jack", "打杂", 1000);
        //没重写之前调用的是Object类的toString方法,输出的是全类名和哈希值转换后的十六进制字符串
        //我们把哈希值也输出,用计算器算一下是否正确
        System.out.println(monster.toString() +" "+ monster.hashCode());
    }
}
class Monster{
    private String name;
    private String job;
    private double sal;

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

    //重写toString方法,输出对象的属性

    @Override
    public String toString() {  //一般是把对象的属性值输出,当然也可以自己定制
        return "Monster{" +
                "name='" + name + '\'' +
                ", job='" + job + '\'' +
                ", sal=" + sal +
                '}';
    }
}

重写之后的输出结果:

 当直接输出一个对象时,toString方法会被默认的调用

换言之,System.out.println(monster.toString());和System.out.println(monster)的效果是一样的

 4. finalize方法

 4.1 finalize概念

finalize在JDK文档中的描述:

4.2 finalize要点

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

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

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

通过一个案例来看一下:

public class Finalize {
    public static void main(String[] args) {

        Car Audi = new Car("奥迪");
        Audi = null;
        System.gc(); //主动调用垃圾回收器
        //断掉引用,使Car对象变成垃圾,垃圾回收器就会销毁(回收)对象,在销毁前会调用该对象的finalize方法
        //程序员可以在finalize中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开文件)
        //如果程序员不重写finalize,那么就会调用Object类的finalize,即默认处理
        //如果重写了finalize,就可以实现自己的逻辑

        System.out.println("程序退出了");
    }
}

class Car{
    private String name;

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

    @Override
    protected void finalize() throws Throwable {
        System.out.println("销毁 汽车"+name);
        System.out.println("释放了某些资源...");
    }
}

这里主动调用了gc垃圾回收器,如果没有主动调用垃圾回收器输出的结果如下

我们看到我们重写的finalize方法并没有被执行,这里有个问题是不是对象变成垃圾后马上就回收了

它是有自己的一个算法,比较复杂,后续再说。所以我们主动调用了gc垃圾回收器,输出结果如下

 


本次分享结束,seeya!

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值