Javaの带你攻破Object类

文章目录

  • 1. Object类的概念
    • 2. Object类的构造方法
  • 3. Object类的一些方法和属性
    • 3.1 重写的回顾
      • 规则
      • 重写和重载的区别
    • 3.2 toString方法
    • 3.3 equals方法
    • 3.4 hashCode方法
    • 3.5 用来接受引用数据类型
      • 接收数组对象
      • 接收接口引用的对象


本篇博客干货较多,建议收藏食用哦

1. Object类的概念

Javaの带你攻破Object类_第1张图片

Object,翻译一下,对象;物体;目标;目的;物品;东西;宗旨;宾语
是的,这篇博客要讲的跟这些都没有太大关系(哈哈哈)
但有一点你必须记住!!!
它是所有类的爹!!!

言归正传,我们都知道,Object是Java默认提供的一个类。
Java中除了Object类,所有的类都是存在继承关系的。
默认所有的类会继承Object父类

这里就会有小伙伴会问了:
我没看到呀!哪里继承了!一个extends都没有!
咱们贴心的编译器会帮你继承得嘛
你看不见的就叫隐式继承
你能看见的就叫显式继承
你看不见,总不能忽略编译器的功劳吧
(编译器:啊对对对,我就是那个舔狗)

说人话:即所有类的对象都可以用Object的引用进行接收
…emmm,好像不太像人话
那就用代码再说一次人话:

class Person{}
class Student{}
public class Test {
    public static void main(String[] args){
        func(new Person());
        func(new Student());
    }
    public static void func(Object obj){
        System.out.println(obj);
    }
}

输出结果为

Person@4eec7777
Student@41629346

所以我们在开发过程中Object类是参数得最高统一类型。
Object 类位于 java.lang 包中,编译时会自动导入,我们创建一个类时,如果没有明确继承一个父类,那么它就会自动继承 Object,成为 Object 的子类。

2. Object类的构造方法

public Object(){

}

对,就这样,哈哈哈

3. Object类的一些方法和属性

Object中存有一些定义好的方法
Javaの带你攻破Object类_第2张图片
Javaの带你攻破Object类_第3张图片
Javaの带你攻破Object类_第4张图片
那么这里就为大家讲解一下其中的几个方法,这几个方法往往都是要牵扯到重写,以此来实现自己想要的功能,所以带大家简单回顾一下重写

3.1 重写的回顾

规则

重写的那个方法不可以比 被重写方法(就是父类中的方法) 有更严格的访问级别,可以更加广泛
父类方法中如果是包访问权限,那么子类的重写方法就可以是包访问权限或者是public访问权限

比如,Object类中有个toString方法,我们开始重写这个方法的时候总是忘记public修饰符,贴心的编译器会帮我们纠正错误,这里出错的原因就是:如果没有加上任何访问限定修饰符的方法就是包访问权限,包访问权限比public范围更小,更严格,所以会报错

那么这里又要附上那张传家宝表格了

No 范围 private defaul protected public
1 同一包中的同一类
2 同一包中的不同类
3 不同包中的子类
4 不同包的非子类

重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等进行重新编写的一个过程,返回值和形参都不能改变即外壳不改变,核心重写
重写的好处在于子类可以根据需要,定义特定的属于子类自己的行为。
也就是说子类能够根据需要来实现父类的方法,又和父类的方法不完全一样,实现自己的特色
那么总结一下:

  • 子类在重写父类的方法时,一般必须与父类方法原型一致:修饰符 返回值类型 方法名(参数列表)要完全一致
  • JDK7以后,被重写的方法返回值类型可以不同,但是必须是具有父子关系
  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为protected
  • 父类被static、private修饰的方法都不能被重写
  • 子类和父类在同一个包中,那么子类可以重写父类中的所有方法,除了声明为private和final的方法
  • 子类和父类不在同一个包中,那么子类只能够重写父类的 声明为public 和protected的非final方法
  • 重写的方法,可以使用 @Override 注解来显式指定。有了这个注解能够帮我们检查这个方法有没有被正确重写。例如不小心讲方法名拼写错了,此时编译器就会发现父类中并没有这个方法,就会编译报错,构不成重写。

Javaの带你攻破Object类_第5张图片

重写和重载的区别

区别点 重载(overloading) 重写(override)
参数列表 必须修改 不同JDK版本不一样,如果修改那必须是继承关系
返回类型 可以修改 一定不能修改
访问限定符 可以修改 不能做出更严格的限制(子类权限大于父类)

即:方法重载式一个类的多态性的表现,而方法重写式子类与父类的一种多态性表现
Javaの带你攻破Object类_第6张图片
Javaの带你攻破Object类_第7张图片

3.2 toString方法

Object类中的 toString() 方法用于返回对象的字符串表示形式。
就是用来返回对象里面的一些信息并用字符串的形式返回。
不用传入参数,默认返回格式为
对象的class名称 + @ + hasCode 的十六进制字符串
老规矩,概念看不懂,代码上来凑:

/**
 * @author Gremmie102
 * @date 2022/4/16 23:32
 * @purpose: 调用Object中的toString方法
 */
public class ToStringTest {
    public static void main(String[] args) {

        // toString() with Object
        Object obj1 = new Object();
        System.out.println(obj1.toString());

        Object obj2 = new Object();
        System.out.println(obj2.toString());

        Object obj3 = new Object();
        System.out.println(obj3.toString());
    }
}

那么我们运行出来的结果为

java.lang.Object@776ec8df
java.lang.Object@4eec7777
java.lang.Object@3b07d329

Tips:这里的哈希值(你可以当作地址值)大家如果运行出来和我的不一样也没关系的,因为对象储存的位置都是不同的
那么我们现在用Array类来调用toString方法

/**
 * @author Gremmie102
 * @date 2022/4/16 23:45
 * @purpose : 用Array的类来调用其父类的toString方法
 */
public class ToStringTest1 {
    public static void main(String[] args) {
        //创建数组
        String[] array = {"葛玉礼","is","假面骑士"};
        System.out.println(array.toString());

        //数组元素值返回一个字符串的表示形式
        //Array继承了Object类,子类可以直接使用父类中的方法
        System.out.println(array[0].toString() +
                           array[1].toString() +
                           array[2].toString() );
    }
}

运行结果为

[Ljava.lang.String;@776ec8df
葛玉礼is假面骑士

那么我们现在自己定义一个类,去重写它父类Object中的toString方法

/**
 * @author Gremmie102
 * @date 2022/4/17 9:53
 * @purpose :重写Object中的toString中的方法
 */
public class Student {
    String name;
    int age;
    double score;
    
     public Student() {
    }

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    
    public void study(){
        System.out.println(name+" is studying");
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

我们在重写的方法中自定义了需要显现出来的内容
那么我们写个main方法来测试一下

    public static void main(String[] args) {
        Student student = new Student("Gremmie",19,95);
        System.out.println(student);
    }

那么运行结果为:

Student{name=‘Gremmie’, age=19, score=95.0}

这里为啥直接输入一个引用变量就可以直接显示出这么一堆信息捏?
带大家调试一下,看清它的真面目
Javaの带你攻破Object类_第8张图片
首先第一行代码new了一个对象,并进入构造方法进行初始化
Javaの带你攻破Object类_第9张图片
?不给机会,直接就跳到toString方法那里去了,可能这里是编译器暗中捣鬼,我们就不再深究,会用就OK啦

3.3 equals方法

我们在Java中使用 == 进行比较的时候遵循以下几点:

  1. 如果==左右两边是基本类型变量,那么比较的就是变量中的值是否相同
  2. 如果==左右两边是引用类型变量,那么比较的就是引用变量的地址是否相同
  3. 如果要比较对象中的内容,那就必须要重写Object中的equals方法,因为equals方法也是默认按照地址来比较的

那么用代码来演示一下没有重写之前的用法

/**
 * @author Gremmie102
 * @date 2022/4/17 10:27
 * @purpose :介绍Object中的equals方法
 */
public class EqualsTest1 {
    public static void main(String[] args) {

        // Object 类使用 equals() 方法
        // 创建两个对象
        Object object1 = new Object();
        Object object2 = new Object();

        // 判断 obj1 与 obj2 是否相等
        // 不同对象,内存地址不同,不相等,返回 false
        System.out.println(object1.equals(object2)); // false

        // obj1 赋值给 obj3
        // String 重写了 equals() 方法
        // 对象引用,内存地址相同,相等,返回 true
        Object object3 = object1;
        System.out.println(object1.equals(object3)); // true
    }
}
/**
 * 运行结果:
 * E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=51082:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest EqualsTest1
 * false
 * true
 *
 * Process finished with exit code 0
 */

那么我们再写一个Person类,首先是未重写版

/**
 * @author Gremmie102
 * @date 2022/4/17 10:33
 * @purpose :未重写equals的Person类的Person类
 */
public class Person {
    private String name;
    private int age;
    public Person(String name,int age){
        this.age = age;
        this.name = name;
    }

    public static void main(String[] args) {
        int a = 10;
        int b = 10;
        Person p1 = new Person("Gremmie",19);
        Person p2 = new Person("Gremmie",19);

        System.out.println(a==b);
        System.out.println(p1==p2);
        System.out.println(p1.equals(p2));
    }
}
/**
 * 运行结果
 * E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=51109:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest Person
 * true
 * false
 * false
 *
 * Process finished with exit code 0
 */

那么下面重写equals方法

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;//这里向下转型来比较属性值
        return Objects.equals(name, person.name);
        //这里运用了方法递归
    }

结论:比较对象中内容是否相同的时候,我们就一定要重写equals方法了

3.4 hashCode方法

Object hashCode() 方法用于获取对象的 hash 值。
我们看一下hashCode的源码
Javaの带你攻破Object类_第10张图片
这个方法是由native修饰的,返回一个内存地址值
方法的底层是由C/C++编写,我们看不到

/**
 * @author Gremmie102
 * @date 2022/4/17 11:39
 * @purpose :演示了 hashCode() 方法的使用
 */
public class HashCodeTest1 {
    public static void main(String[] args) {

        // Object 使用 hashCode()
        Object obj1 = new Object();
        System.out.println(obj1.hashCode());

        Object obj2 = new Object();
        System.out.println(obj2.hashCode());

        Object obj3 = new Object();
        System.out.println(obj3.hashCode());
    }
}
/**
 * 运行结果
 * E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=51403:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest HashCodeTest1
 * 2003749087
 * 1324119927
 * 990368553
 *
 * Process finished with exit code 0
 */

那么我们在String和ArrayList类使用hashCode()方法
因为所有的类都继承了Object,所以可以直接调用

/**
 * @author Gremmie102
 * @date 2022/4/17 11:43
 * @purpose :String 和 ArrayList 类使用 hashCode() 方法
 */
import java.util.ArrayList;

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

        // String 使用 hashCode()
        String str = new String();
        System.out.println(str.hashCode()); // 0

        // ArrayList 使用 hashCode()
        ArrayList<Integer> list = new ArrayList<>();
        System.out.println(list.hashCode()); // 1
    }
}

那么如果两个引用变量指向的对象都是一样的,那么它们所调用返回的哈希值也是相等的

/**
 * @author Gremmie102
 * @date 2022/4/17 11:45
 * @purpose :引用变量指向一个对象情况下,它们调用hashCode方法
 */
public class HashCodeTest3 {
    public static void main(String[] args) {

        // Object 使用 hashCode()
        Object obj1 = new Object();

        // obj1 赋值给 obj2
        Object obj2 = obj1;

        // 判断两个对象是否相等
        System.out.println(obj1.equals(obj2)); // true

        // 获取 obj1 与 obj2 的哈希值
        System.out.println(obj1.hashCode());
        System.out.println(obj2.hashCode());

    }
}
/**
 * 运行结果
 * E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=51415:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest HashCodeTest3
 * true
 * 2003749087
 * 2003749087
 *
 * Process finished with exit code 0
 */

那么同样,我们也可以重写hashCode方法
现在我们想要两个对象如果所有属性都相同,调用hashCode返回的值都相同

import java.util.Objects;

/**
 * @author Gremmie102
 * @date 2022/4/17 11:51
 * @purpose :重写hashCode()方法
 */
class Human{
    public String name;
    public int age;

    public Human(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() {
        return Objects.hash(name,age);
    }
}

public class HashCodeTest4 {
    public static void main(String[] args) {
        Human human1 = new Human("Gremmie",19);
        Human human2 = new Human("Gremmie",19);

        System.out.println(human1.hashCode());
        System.out.println(human2.hashCode());
    }
}
/**
 * 运行结果
 * E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=51508:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest HashCodeTest4
 * 277247006
 * 277247006
 *
 * Process finished with exit code 0
 */

这样,我们就可以让属性相同的对象返回相同的哈希值了
结论:

  1. hashCode方法用来确定对象在内存中存储的位置是否相同
  2. 事实上hashCode()在散列表中才有用,其他情况下是没用的,在散列表中hashCode()的作用是获取对象的散列码,进而去确定该对象在散列表中的位置

这个方法牵扯到了数据结构,等学习进度差不多了再重新为大家讲解,看不懂没关系

3.5 用来接受引用数据类型

我们已经知道Object类可以接受任意类型的对象,因为Object是所有类的父类,但是Object其实并不仅限于接受类的对象,它可以接受所有的数据类型
比如:类、数组、接口

接收数组对象

/**
 * @author Gremmie102
 * @date 2022/4/17 12:30
 * @purpose :接收数组对象
 */
public class ObjectTest1 {
    public static void main(String[] args) {
        //Object接收数组对象,向上转型
        Object obj = new int[]{1,2,3,4,5,6};

        //向下转型,需要强制转换
        int[] data = (int[])obj;
        for (int i :data) {
            System.out.print(i+" ");
        }
    }
}
/**
 * 运行结果
 * E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=52075:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest ObjectTest1
 * 1 2 3 4 5 6 
 * Process finished with exit code 0
 */

接收接口引用的对象

在Java中,接口本身是不能继承任何类的,它只能继承接口,但Object是很特殊的例子,是强制规定的

/**
 * @author Gremmie102
 * @date 2022/4/17 12:35
 * @purpose :用Object接收接口对象
 */
interface IMessage {
    String getMessage();
}
class Message implements IMessage {
    String str = "我爱Java";
    @Override
    public String toString() {
        return "Life is not satisfactory";
    }
    @Override
    public String getMessage() {
        return str;
    }
}
public class ObjectTest2 {
    public static void main(String[] args) {
        IMessage message = new Message();
//        子类向父接口转型
        Object object = message;
//        接口向Object转型
        System.out.println(object);
        IMessage temp = (IMessage) object;
        System.out.println(temp.getMessage());
    }
}
/**
 *运行结果
 * E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=52215:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\ObjectTest\out\production\ObjectTest ObjectTest2
 * Life is not satisfactory
 * 我爱Java
 * Process finished with exit code 0
 */

我们可以通过合理运用Object来真正达到参数的统一
如果一个类编写的时候希望能够接收所有的数据类型,那么就用Object来完成。
对于Object就介绍到这里,如果哪里讲解的不到位还请大家指出
感谢阅读~

你可能感兴趣的:(菜鸟猛啄JavaSE,java,idea)