• Java基础39 Object类(节选)




    Object类

    Object类:Java中lang包的类,是类层次结构的根类,每个类都使用Object作为超类。所有对象(包括数组)都实现这个类的方法。

    Object类所含有的方法

    • clone()
    • equals(Object obj)
    • finalize()
    • getClass()
    • hashCode()
    • notify()
    • notifyAll()
    • toString()
    • wait()
    • wait(long timeout)
    • wait(long timeout, int nanos)

    一、 equals( )

    boolen | equals(Object ob) : 指示其他某个对象是否与此对象"相等"。

    ● == 与 equals的对比(☆)

    (1)== 运算符

    1. == : 既可以判断基本类型,又可以判断引用类型
    2. == : 如果判断基本类型,判断的是值是否相等。
    3. == : 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象。

    例如:

    public static void main(String[] args){
        	A a = new A();
        	A b = a;
        	A c = b;
        	System.out.println( a == c);
        	System.out.println( b == c);
    }
    
    class B{}
    class A extends B{}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    创建了一个A对象,现在有一个a引用,指向了A对象,再有一个b引用,指向了A对象,最后再创建一个c引用也指向A对象,现在用 == 号来比较a与c,而由上述概念得知:== 符号在比较引用类型时,比较的是它们所指向的地址值,而明显a与c指向的都是A对象,即地址值相同,所以a与c比较后返回的是true,同理b == c返回的也是true。

    现在增加一个判断

    B bObj = a;
    Sysout.println(bObj == c);
    
    • 1
    • 2

    这时创建一个向上转型的对象,而此时bObj指向的也是A对象,所以最后比较的地址值与c依然相等,最后输出的结果也是true。

    (2)equals方法

    equals

    1. Object类中的方法,只能判断引用类型
    2. 默认判断的是地址值是否相等,子类中往往重写该方法,用于判断内容是否相等。比如:Integer,String

    String类的equals方法(JDK源码)

    //把Object的equals方法重写了,变成了比较两个字符串值是否相同
    if(anObject instanceof String){//判断类型
          String anotherString = (String)anObject; //向下转型
          int n = value.lenghth;
          if(n == anotherString.value.length){ //如果长度相同
               char v1[] = value;
               char v2[] = anotherString.value;
               int i = 0;
               while(n-- ! = 0){ //一个一个比较字符
                   if(v1[i] != v2[i])
                       return false;
                   i++;
               }
               return true; //如果两个字符串的所有字符都相等,则返回true
           }
    }
    return false //如果比较的不是字符串,则直接返回false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Object类的equals方法(JDK源码)

    //Object 的 equals 方法默认就是比较对象地址是否相同
    //也就是判断两个对象是不是同一个对象。
    public boolean equals(Object obj){
    	return (this == obj);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Integer类的equals方法(JDK源码)

    public boolean equals(Object obj){
    	if(obj instanceof Integer){
    		return value == ((Integer)obj).intValue();
    	}
    	return false;
    }
    
    Integer integer1 = new Integer(1000);
    Integer integer2 = new Integer(1000);
    System.out.println(integer1 == integer2); //false
    System.out.println(integer1.equals(integer2)); //true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ● equals方法练习

    实例1

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

    创建Person类

    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;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public char getGender() {
            return gender;
        }
    
        public void setGender(char gender) {
            this.gender = gender;
        }
    }
    
    • 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

    创建test类

    如果现在在主方法里直接调用equals方法,则调用的是Object类中的equals方法,因为Person没有继承任何类,默认继承的是Object类,而Object类中的equals方法相当于 “==” 来判断,比较的是地址值,很明显现在创建了两个对象,ps1和ps2,直接对比的话肯定返回的值是false,显然和我们想要的true不同。

    public class test {
        public static void main(String[] args) {
        
            Person ps1 = new Person("jack",19,'男');
            Person ps2 = new Person("jack",19,'男');
    
            System.out.println(ps1.equals(ps2)); //false
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    那如何得到正确的结果?那就是在Person中重写Object 的 equals方法。

    在Person中重写Object的equals方法

        //重写Object的equals方法
        public boolean equals(Object obj){ //这里的Object obj指的是输出语句中传入的ps2
            //判断如果比较的两个对象是同一个对象,则直接返回true
            if(this == obj){ //这里的this指的是ps1
                return true;
            }
            //类型判断
            if(obj instanceof Person){ //是Person,才进行比较
    
                //向下转型,因为现在需要得到obj的各个属性
                Person p = (Person) obj;
                return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
            }
            //如果不是Person,则直接返回false
            return false;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    实例2

    看下列代码,分析输出的值为多少?

    class Person{ //类 	
    	public String name; 
     }
    
    • 1
    • 2
    • 3

    主方法

    Person p1 = new Person();
    p1.name = "ccc";
    
    Person p2 = new Person();
    p2.name = "CCC";
    
    System.out.println(p1 == p2);
    System.out.println(p1.name.equals(p2.name));
    System.out.println(p1.equals(p2));
    
    String s1 = new String("asdf");
    
    String s2 = new String("asdf");
    System.out.println(s1.equals(s2));
    System.out.println(s1 == s2);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    第一个输出语句

    p1与p2的比较,由于是使用"==",所以比较的是地址值,而此时p1、p2分别是new出来的,并不是同一个对象,所以最后的结果为false

    System.out.println(p1 == p2); //false
    
    • 1

    第二个输出语句

    p1.name是字符串,已经把equals方法重写了,所以p1与p2比较的就是内容,显然内容一直,所以得到的结果应为true

    System.out.println(p1.name.equals(p2.name)); //true
    
    • 1

    第三个输出语句

    由于p1与p2都是自定义的类(Person),而此时的Person类中并没有重写equals方法,那么使用的就是他的父类Object的equals方法,所以比较的还是两者的地址值,显然最后的结果为false

    System.out.println(p1.equals(p2)); //false
    
    • 1

    第四个输出语句

    由于创建的是String类型的对象,必然使用的是String类重写的equals方法,所以两者比较的是内容的差异,显然二者都是一致的,最后的结果为true

    System.out.println(s1.equals(s2)); //true
    
    • 1

    第五个输出语句

    显然两者比较的是地址值,指向的不是同一个对象,所以最后的结果为false

    System.out.println(s1 == s2); //false
    
    • 1

    运行结果

    在这里插入图片描述

    ● 实例3

    看下面的代码输出的是什么值?

    int it = 65;
    float fl = 65.0f;
    System.out.println("65和65.0f是否相等? " + (it == fl));
    char ch1 = 'A'; char ch2 = 12;
    System.out.println("65和 'A' 是否相等? " + (it == ch1));
    System.out.println("12和ch2是否相等? " + (12 == ch2));
    
    String st1 = new String("hello");
    String st2 = new String("hello");
    System.out.println("str1和str2是否相等? " + (str1 == str2));
    
    System.out.println("str1是否equals str2?" + (str1.equals(str2)));
    System.out.println("hello" == new java.sql.Date());
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    false,false,true,true,true,true

    第一个输出语句

    由于是"==",所以判断的是值是否相等,而一个是int类型,一个是float类型,判断时自动转换,所以为true。

    System.out.println("65和65.0f是否相等? " + (it == fl)); // true
    
    • 1

    第二个输出语句

    同样是双等号,所以判断值是否相等,而 A 的ascll码值就为65,所以输出结果为true。

    System.out.println("65和 'A' 是否相等? " + (it == ch1)); //true
    
    • 1

    第三个输出语句

    同样是比较值,显然相等,所以输出结果为true。

    System.out.println("12和ch2是否相等? " + (12 == ch2)); //true
    
    • 1

    第四个输出语句

    两个对象str1、str2都是被new出来的新对象,所以地址值不同,结果为false

    System.out.println("str1和str2是否相等? " + (str1 == str2)); //false
    
    • 1

    第五个输出语句

    String类的equals方法必然重写,比较两者的值,相等,所以输出true。

    System.out.println("str1是否equals str2?" + (str1.equals(str2))); //true
    
    • 1

    最后一个输出语句

    编译器报错,因为编译器无法识别这两个对象的关系,它们既不是同一个类型,也没有继承关系,所以报错。

    System.out.println("hello" == new java.sql.Date()); //报错
    
    • 1

    二、hashCode( )

    hashCode( ) :返回该对象的哈希码值。

    hashCode

    1. 提高具有哈希结构的容器的效率。
    2. 两个引用,如果指向的是同一个对象,则哈希值肯定相同,反之则不同(特殊情况除外)
    3. 哈希值主要根据地址号来的,不能完全将哈希值等价于地址!
    4. 后续在集合中介绍hashCode的重写。

    hashCode的使用

    现创建三个对象,一个aa1、aa2、aa3,其中aa1与aa2是新创建的对象,而aa3是指向aa1创建的对象。

    public class test {
         public static void main(String[] args) {
    
            AA aa1 = new AA();
            AA aa2 = new AA();
            AA aa3 = aa1;
    
            System.out.println("aa1.hashCode() = " + aa1.hashCode());
            System.out.println("aa2.hashCode() = " + aa2.hashCode());
            System.out.println("aa3.hashCode() = " + aa3.hashCode());
        }
    }
    
    class AA{}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    现在看通过hashCode输出的结果:

    在这里插入图片描述
    显然aa1与aa3是同一个对象,所以输出的值一致。

    三、toString( )

    toString( ) : 返回该对象的字符串表示。

    • 默认返回:全类名(包名 + 类名) + @ + 哈希值的十六进制(通过hashCode得到的整数),子类往往重写toString方法,用于返回对象的属性信息。

    • 重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式。

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

    Object的toString( ) 原码

    ObjecttoString() 原码
    
    (1) getClass().getName() 类的全类名(包名 + 类名)
    (2) Integer.toHexString(hashCode()) 将对象的hashCode值转成16进制字符串
    
    public String toString(){
    	return getClass().getName() + "@" + Integer.toHexString(hashCode())
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    hashcode使用案例

    创建Monster[name,job,sal]

    直接使用toString

    public class test {
         public static void main(String[] args) {
    
            Monster monster = new Monster("章鱼哥","恰人",1000);
             System.out.println(monster.toString() + "hashCode值 = " + 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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行效果

    在这里插入图片描述

    重写toString方法

    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方法,输出对象的属性
        //使用快捷键 alt + insert - >
        @Override
        //重写后,一般是把对象的属性值输出,当然程序员也可以自定义内容
        public String toString() {
            return "Monster{" +
                    "name='" + name + '\'' +
                    ", job='" + job + '\'' +
                    ", sal=" + sal +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    重写后继续调用monster,运行的结果如下所示:

    在这里插入图片描述

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

    还是以上面的代码为例,直接在输出语句中传入monster,实际上等价于monster.toString:

    System.out.println(monster);//等价 monster.toString()
    
    • 1

    得到的结果如下所示:

    在这里插入图片描述
    但是可以注意到一个问题,那就是输出语句并没有重写hashcode,所以并没有输出哈希值。


    四、finalize( )

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

    finalize()

    1. 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作。
    2. 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法。
    3. 垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制

    finalize使用实例

    public class test {
         public static void main(String[] args) {
    
             Car bmw = new Car("宝马");
             //把bmw置空,此时Car现在就没人使用,就变为了垃圾对象
             //这时垃圾回收器就会回收(销毁)这个对象(堆释放)
             //在销毁对象前,会调用该对象的finalize方法
             // 此时程序员就可以在finalize中,写自己的业务逻辑代码(比如释放资源:数据库链接,或者打开文件等等)
             //如果程序员不重写finalize,那么就会默认调用Object类的finalize方法,如果程序员重写了finalize,就可以实现自己的逻辑。
             bmw = null;
         }
    }
    
    class Car{
        private String name;
    
        public Car(String name){
            this.name = name;
        }
    }      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    (WebFlux)002、如何打印日志与链路ID
    合并区间(排序、贪心)
    leetcode94 -- 二叉树的中序遍历
    PHP即刻送达同城派送小程序系统
    JavaWeb基础3——Maven&MyBatis
    开源PDF工具 Apache PDFBox 认识及使用(知识点+案例)
    SSH详解
    期末前端web大作业:用DIV+CSS技术设计的动漫网站——火影忍者6页 带报告
    fastjson(反序列化)漏洞复现
    一篇文章了解MySQL的group by
  • 原文地址:https://blog.csdn.net/chenjiap/article/details/127936989