• Object类


    1、Object类

      java.lang.Object类是类层次结构的根类,每个类(除了Object类本身)都使用Object类作为超类。一个类如果没有显示声明继承另一个类,则相当于默认继承了Object类。换句话说,Object类的变量可以接收任意类型的对象。Java规定Object[]可以接收任意类型对象的数组,但是不能接收基本数据类型的数组。

      Object类只有一个空参构造器,所有类的对象创建最终都会通过super()语句调用到Object类的无参构造器中。如果一个类没有显示继承另一个类,那么在它的构造器中出现的super()语句表示调用的就是Object类的无参构造器。

      Object类是其他类的根父类,因此Object类的所有方法都会继承到子类中,包括数组对象,Object类的主要方法如下所示。

    image-20220920172721468

    1.1 toString方法

      源码如下:

    public String toString() {
            return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    

      toString方法的作用是返回对象的字符串形式,也就是任意类型对象想转换成String类型,都可以调用toString方法。toString方法的原型返回的是一个类似地址值的字符串,不够简明并且对开发人员来讲没有意义,所以建议子类在重写该方法时,返回一个简明易懂的信息表达式,一般为对象的属性信息。

      没有重写toString()之前的Person类代码

    public class ToStringTest {
        public static void main(String[] args) {
            Person per = new Person("codeleader", 32);
            System.out.println(per);
            System.out.println(per.toString());
        }
    }
    class Person{
        private String name;
        private int age;
    
        public Person(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    }
    

    image-20220920173315544

      从上述结果来看,当我们打印一个对象时,调用toString方法和不调用toString方法的最终打印结果都是一样的。这说明当我们使用System.out.println或System.out.print方法打印对象时,会默认调用对象的toString方法。其实在Java中当一个对象与字符串进行拼接时,也会自动调用该对象的toString方法

      另外,toString方法默认返回的是“全类名+@+对象的哈希值”

      重写toString方法之后的Person类的实例代码

    package chap11.test;
    
    public class ToStringTest {
        public static void main(String[] args) {
            Person per = new Person("codeleader", 32);
            System.out.println(per);
            System.out.println(per.toString());
        }
    }
    class Person{
        private String name;
        private int age;
    
        public Person(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    

    image-20220920173647703

    1.2 equals方法

      比较两个基本数据类型的值是否相等应使用"==",而比较两个字符串的内容是否相等应使用equals方法。

      这是因为对于基本数据类型来说,变量中存储的是数据值,所以直接使用==比较即可。对于引用数据类型来说,变量中存储的是对象的首地址,所以直接用==比较时,只是比较两个对象的首地址是否相等,而不是比较两个对象的内容是否相等。

      如果要比较两个对象的内容是否相等,则需要调用equals方法,该方法在Object类中的源码如下所示:

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

      Object类中该方法的作用是比较两个对象的内容是否相等。从上面的代码可以看出,在Object类中,默认实现的equals方法与==的效果是一样的。

      没有重写Person类的示例代码:

    public class TestPersonEquals {
        public static void main(String[] args) {
            Person per1 = new Person("codeleader", 32);
            Person per2 = new Person("codeleader", 32);
            System.out.println(per1==per2);
            System.out.println(per1.equals(per2));
        }
    }
    class Person{
        private String name;
        private int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    

    image-20220920174414822

       正如前面所看到的,Object类中默认实现的equals方法也是通过==判断对象是否相等。而对于引用类型变量,==判断的不是属性信息,而实而这引用的是否是同一个对象,也就是地址是否相等。一般来讲,开发人员希望判断的是两个对象的内容是否相等,所以往往需要重写equals方法。

      重写了equals方法和hashCode方法之后的代码:

    package chap11.equals;
    
    import java.util.Objects;
    
    public class TestPersonEquals {
        public static void main(String[] args) {
            Person per1 = new Person("codeleader", 32);
            Person per2 = new Person("codeleader", 32);
            System.out.println(per1==per2);
            System.out.println(per1.equals(per2));
        }
    }
    class Person{
        private String name;
        private int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age &&
                    Objects.equals(name, person.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }
    

    image-20220920174723671

    从上面模板生成的equals方法代码主要分为三个方面。

    • 两个对象的地址不一样,肯定返回true
    • 两个对象的类型不一样,肯定返回false
    • 两个对象被选择比较的属性信息完全一样,肯定返回true,有不一样的返回false

      equals方法的重写需要满足自反性、传递性、一致性、对称性、非空对象。

      ==和equals方法的区别?

    • ==可用于判断两个基本数据类型变量,也可以用于判断两个引用类型变量。但都需要保证判断双方的类型一致或兼容,否则编译出错。
    • equals方法只能用于判断应用类型的变量,因为只有对象才有方法,默认判断的是对象的内容,如果重写Object类的equals方法,则一般判断的是对象的内容是否相等。

    1.3 hashCode方法

      源码如下:

    image-20220920175157751

      hashCode方法的说明有以下几点:

    • hashCode方法用于返回对象的哈希码值。支持此方法是为了提高哈希表(如java.util.Hashtable提供的哈希表)的性能。
    • hashCode在Object类中有native修饰,是本地方法,该方法的方法体不是Java实现的,是由C/C++实现的,最后编译为.dll文件,然后由Java调用。

      示例代码:

    public class HashCodeTest {
        public static void main(String[] args) {
            Person per1 = new Person("康师傅", 32);
            Person per2 = new Person("康师傅", 32);
            System.out.println(per1.hashCode());
            System.out.println(per2.hashCode());
            System.out.println(per1.equals(per2));
        }
    }
    class Person{
        private String name;
        private int age;
    
        public Person(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    }
    

    image-20220920185411512

      我们也可以重写hashCode方法,手动重写或使用IDE开发工具提供的模板重写。

      hashCode方法重写时要满足如下几个要求:

    • 如果两个对象调用equals方法返回true,那么要求这两个对象的hashCode值一定是相等的。
    • 如果两个对象的hashCode值不相等,那么要求这两个对象调用equals方法一定是false。
    • 如果两个对象的hashCode值相等,那么这两个对象调用equals方法可能是true,也可能是false。

      重写Person类的hashCode和equals方法:

    import java.util.Objects;
    
    public class HashCodeTest {
        public static void main(String[] args) {
            Person per1 = new Person("康师傅", 32);
            Person per2 = new Person("康师傅", 32);
            System.out.println(per1.hashCode());
            System.out.println(per2.hashCode());
            System.out.println(per1.equals(per2));
        }
    }
    class Person{
        private String name;
        private int age;
    
        public Person(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age &&
                    Objects.equals(name, person.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }
    

    image-20220920185742122

      Java中的hashCode值遵循如下规定:

    • 如果两个对象的hashCode值不相等,那么这两个对象一定不相等。
    • 如果两个对象的hashCode值是相等的,那么这两个对象不一定相等。

    1.4 getClass方法

      对象有编译时类型和运行时类型两种类型,而且编译时类型可能与运行时类型不一致。编译时类型就是变量声明时的类型,那么如何在运行时获取某个变量中对象的运行时类型呢,Object类为我们提供了一个getClass方法,可以获取对象的运行时类型。该方法源码如下:

    image-20220920190030904

      示例代码:

    public class GetClassTest {
    
        public static void listen(Animal animal){
            System.out.println("The animal is a "+animal.getClass());
            animal.shut();
        }
    
        public static void main(String[] args) {
            listen(new Dog());
            listen(new Cat());
        }
    }
    abstract class Animal{
        public abstract void shut();
    }
    class Dog extends Animal{
        @Override
        public void shut() {
            System.out.println("汪汪汪~");
        }
    }
    class Cat extends Animal{
        @Override
        public void shut() {
            System.out.println("喵喵喵~");
        }
    }
    

    image-20220920190110463

      在上述代码的listen方法中,animal变量的编译时类型是Animal,而运行时类型变量由传入的实参对象决定,可能是Dog,也可能是Cat。

    1.5 clone方法

      开发中如果要复制一个对象,则可以使用Object类提供的clone方法。源码如下:

    image-20220920190250103

      调用该方法时可以创建并返回当前对象的一个副本。从源码中可以发现该方法的权限修饰符是protected,说明默认Object类的clone方法只能在java.lang包或其他包的子类中调用。因此,如果在测试类中要通过自定义类的对象来调用clone方法,则必须重写该方法。如果重写该方法,则子类必须实现java.lang.Cloneable接口,否则会抛出CloneNotSupportedException

    示例代码:

    import java.util.Objects;
    
    public class CloneTest {
        public static void main(String[] args) {
            try {
                Person1 per = new Person1("codeleader", 18);
                Object clone = per.clone();
                System.out.println(per==clone);
                System.out.println(per.equals(clone));
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
        }
    }
    class Person1 implements Cloneable{
        private String name;
        private int age;
    
        public Person1(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name,age);
        }
    
        @Override
        public boolean equals(Object obj) {
            if(this==obj) return true;
            if(obj ==null||getClass()!=obj.getClass()) return false;
            Person1 person1=(Person1) obj;
            return age==person1.age&&Objects.equals(name,person1.name);
        }
    }
    

    image-20220920190606682

    1.6 finalize方法

      对于初学者来说,finalize方法是最没有机会接触到的方法,简单了解以下即可。源码如下:

    image-20220920190728199

      finalize方法是Object类中的protected方法,子类可以重写该方法以实现资源清理工作,GC在回收对象之前会调用该方法,即该方法不是由开发人员手动调用的。

      当对象变为不可达时,即对象成为需要被回收的垃圾对象时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。若对象未执行过finalize方法,则将其放入F-Queue队列,有一个低优先级线程执行该队列中对象的finalize方法。执行完finalize方法后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则对象复活,复活后的对象下次回收时,将不再放入F-Queue队列,即不再执行finalize方法。

      Java语言规范并不能保证finalize方法会被及时执行,而且根本不能保证它们会被执行。所以不建议finalize方法完成非内存资源清理工作以外的任务。

  • 相关阅读:
    Vulnhub靶场之Funbox
    【实战】流动的箭头 —— 线性流动组件(repeating-linear-gradient,@keyFrames)
    Ae 效果:CC Bender
    Program Header Table(转载)
    8卡4090服务器适合用于transformer图像生成模型训练吗?速度怎么样?可以在windows下训练吗
    Javascript命令模式
    论文综述的重要性及写作
    暑期JAVA学习(40.1)UDP通信——广播、组播
    maven+mybatis—实现数据库中图书信息的增删改查
    有奖提问|《新程序员》专访“Apache之父”Brian Behlendorf
  • 原文地址:https://blog.csdn.net/qq_43753724/article/details/126959632