Skip to content

方法重写

方法重写的特性与条件

java
方法重写的特性与条件:
    1. 存在于父子类之间
    2. 方法名称相同
    3. 参数列表相同
    4. 返回值相同或者是其子类
    5. 访问权限不能严于父类,子类的访问权限不能小于父类的访问权限
    6. 静态方法可以被继承,但是不能被重写
    7. 不能抛出、声明比父类更多的异常

注解的作用:

java
@override // 注解
public void print(){
    
}

辅助提示:检查方法重写是否正确。

@Override 注解,此注解只能加在方法上,表示此方法属于重写父类的方法,如果没有正确重写,则编译报错。

自定义类中方法重写

自定义类方法重写也就是自己写的类的方法进行重写。

方法重写案例:

父类

java
package com.methodRenamePart;

public class Vtuber {
    String name;
    int age;
    char gender;
    String height;

    public Vtuber() {
    }

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

    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;
    }

    public String getHeight() {
        return height;
    }

    public void setHeight(String height) {
        this.height = height;
    }

    public void info() {
        System.out.println("Vtuber" +
                " name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", height='" + height + '\'');
    }
}

子类

java
package com.methodRenamePart;

public class Gamer extends Vtuber{
    String games;

    public Gamer(String games) {
        this.games = games;
    }

    public Gamer(String name, int age, char gender, String height, String games) {
        super(name, age, gender, height);
        this.games = games;
    }

    public String getGames() {
        return games;
    }

    public void setGames(String games) {
        this.games = games;
    }
    // 子类方法重写
    public void info(){
        // 调用父类的方法
        super.info();
        System.out.println("爱好的游戏是" + this.games);
    }
}

测试类

java
package com.methodRenamePart;

public class Test {
    public static void main(String[] args) {
        // 创建对象
        Gamer gamer = new Gamer("凑阿夸",5,'女',"156","Apex");
        gamer.info();
    }
}

Object类中方法重写

对Object类中方法进行重写

java
toString()属于Object类中方法
    
equals()属于Object类中方法
    
hashCode()属于Object类中方法

toString()方法重写

toString()属于Object类中方法,用于返回当前对象的String描述

java
直接打印一个对象,相当于调用此对象的toString方法,toString方法从顶层父类Object类中继承而来。

sout(object)
直接打印对象相当于调用了这个对象的toString方法
objcet.toString() 

我们在实际开发中通常需要重写toString方法,用于将本类中的属性名和属性值进行拼接,以方便直接打印对象实现输出 属性信息的效果。

重写toSting在开发中99%的作用就是为了看对象属性。(可通过idea插件快速生成)

ts
通过 alt+insert 快捷键,然后点击toString,就可以快速生成一个toString重写方法

toString方法重写案例

java
package com.methodRenamePart;

public class Vtuber {
    String name;
    int age;
    char gender;
    String height;

    public Vtuber() {
    }

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

    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;
    }

    public String getHeight() {
        return height;
    }

    public void setHeight(String height) {
        this.height = height;
    }

    @Override
    public String toString() {
        return "Vtuber{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", height='" + height + '\'' +
                '}';
    }
}

equals()方法重写

equals()属于Object类中方法,用于比较两个对象是否相同

equals()源代码

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

== 和 equals 的区别

java
首先,当 "==" 比较基本数据类型时,比较值;当 "==" 比较引用数据类型时,比较地址;
    
equals()方法底层(源码)实现依然使用"=="比较地址;我们可以重写equals方法,自定义比较规则,将原本的比较地址重写,比较地址并且比较内容;

例子:String类就是对equals方法进行了重写;
例子2: 只要是new出来的两个对象 地址是绝对不相同的"=="比较就为false

细节

ts
通过鼠标点击查看源代码时,需要先看下标题文件是哪一个类。然后再看重写的方法。
在子类中的重写方法附近,可以通过点击左侧的↑箭头,查看父类的方法。

String类的equals()方法重写模拟

java
// 重写的话 形参返回值都要一样
package com.rewrite;

public class MyString {
    // 定义一个静态方法
    public static boolean myEquals(String str1,String str2){
        // 首先判断两个对象地址是否相同
        if(str1 == str2){
            return true;
        }
        // 使用遍历比较内容是否相同
        if (str1.length() != str2.length()){
            return false;
        }
        // 将两个字符串转为char数组
        char [] charArray1 = str1.toCharArray();
        char [] charArray2 = str2.toCharArray();
        // 比较两个char数组内容是否相同
        for (int i = 0; i < charArray1.length; i++) {
            if (charArray1[i] != charArray2[i]){
                return false;
            }
        }
        return true;
    }
    // 测试类
}
java
package com.rewrite;

public class Test {
    public static void main(String[] args) {
        Person per1 = new Person("sakuna","410883200205067898");
        Person per2 = new Person("saba","410883200205067899");
        // 访问同包中的MyString类中的静态方法
        Boolean result = MyString.myEquals(per1.getIdCard(), per2.getIdCard());
        System.out.println("idCard对比的结果为 "+result);
    }
}

自定义类中equals方法重写

先比较地址,再比较内容。

java
package com.rewrite;

public class Person {
    private String name;
    private String idCard;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getIdCard() {
        return idCard;
    }

    public void setIdCard(String idCard) {
        this.idCard = idCard;
    }

    public Person(String name, String idCard) {
        this.name = name;
        this.idCard = idCard;
    }

    public Person() {
    }

    public void printInfo(){
        System.out.println("我的名字是" + name);
        System.out.println("我的id是" + idCard);
    }

    // 自定义类的equals方法重写
    public boolean equals(Object obj){ // 调用的过程 符合多态向上转型
        // 首先判断地址
        if(this == obj){
            return true;
        }
        // 因为形参为Object类型的 而Object类中没有任何属性 所以不能直接访问 name  以及 idCard
        // 如需访问 必须向下转型(强制类型转换)
        Person p1 = (Person) obj;
        // 如何对象确定调用的方法属于哪个类的方法
        // 根据调用方法的对象 所属的类型来判断
        if (this.name.equals(p1.name) && this.idCard.equals(p1.idCard)){
            return true;
        }else {
            return false;
        }
    }
}
java
// 使用instanceof关键字,加一层判断
package com.rewrite;

public class Person {
    private String name;
    private String idCard;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getIdCard() {
        return idCard;
    }

    public void setIdCard(String idCard) {
        this.idCard = idCard;
    }

    public Person(String name, String idCard) {
        this.name = name;
        this.idCard = idCard;
    }

    public Person() {
    }

    public void printInfo(){
        System.out.println("我的名字是" + name);
        System.out.println("我的id是" + idCard);
    }

    @Override
    // 自定义类的equals方法重写
    public boolean equals(Object obj){ // 调用的过程 符合向上转型
        // 首先判断地址
        if(this == obj){
            return true;
        }
        // 加一层判断,如果类型不相同直接返回false
        if (obj instanceof  Person){
            // 因为形参为Object类型的 而Object类中没有任何属性 所以不能直接访问 name  以及 idCard
            // 如需访问 必须向下转型(强制类型转换)
            Person p1 = (Person) obj;
            // 如何对象确定调用的方法属于哪个类的方法
            // 根据调用方法的对象 所属的类型来判断
            if (this.name.equals(p1.name) && this.idCard.equals(p1.idCard)){
                return true;
            }
        }
        return false;
    }

    // 自定义类重写哈希值计算
    // 要保证在equals比较为true的情况下 两个对象hashCode相同
    // 上边重写的 equals 方法是根据人的名字和身份证号比较的
    // 所以我们也要根据人的名字和身份证号来计算hash值
    @Override
    public int hashCode() {
        int result = 1; // 哈希值结果
        int prime = 31; // 权重
        result = result * prime + (this.name == null ? 0 : this.name.hashCode()); // 这里使用了 String类重写的hashCode方法
        result = result * prime + (this.idCard == null ? 0 : this.idCard.hashCode()); // 这里使用了 String类重写的hashCode方法
        return result;
    }
}

测试类

java
package com.rewrite;

public class Test {
    public static void main(String[] args) {
        Person per1 = new Person("sakuna","410883200205067898");
        Person per2 = new Person("saba","410883200205067899");
        // 使用 Person类中的方法
        boolean result = per1.equals(per2);
        System.out.println("idCard对比的结果为 "+result);
    }
}

如何确定调用的方法属于哪个类的方法?

java
根据调用方法的对象,所属类型来判断

hashCode()方法重写

hashCode()属于Object类中方法,用于计算对象的哈希值

什么是哈希值?

java
hash值是根据杂凑算法所计算出来的一个数值。
哈希值的特性:
    1.hash值并不是地址值,而是根据对象的地址等一些信息,使用杂凑算法所计算出来的一个十进制的数值。
    2.Java中的地址值我们是无法获取的,计算hash值的方式也无法获取。

为什么重写哈希值?

java
1.因为默认情况下,两个对象equals比较为true,则hashCode必须是相同的
2.在后续学习的集合类中,默认是以equals比较为true,并且hashCode相同作为去除重复元素的依据。
    
现在我们重写了equals改变了对象的比较规则,所以应当继续重写hashCode,以维持两个对象equals比较为true,则hashCode必须是相同的,哈希值的计算运用了equals方法的比较规则。

重写哈希值示例

java
package com.rewrite;

public class Person {
    private String name;
    private String idCard;

    ......

    public Person() {
    }

    public void printInfo(){
        System.out.println("我的名字是" + name);
        System.out.println("我的id是" + idCard);
    }

    @Override
    // 自定义类的equals方法重写
    public boolean equals(Object obj){ // 调用的过程 符合向上转型
        // 首先判断地址
        if(this == obj){
            return true;
        }
        // 因为形参为Object类型的 而Object类中没有任何属性 所以不能直接访问 name  以及 idCard
        // 如需访问 必须向下转型(强制类型转换)
        Person p1 = (Person) obj;
        // 如何对象确定调用的方法属于哪个类的方法
        // 根据调用方法的对象 所属的类型来判断
        if (this.name.equals(p1.name) && this.idCard.equals(p1.idCard)){
            return true;
        }else {
            return false;
        }
    }

    // 自定义类重写哈希值计算
    // 要保证在equals比较为true的情况下 两个对象hashCode相同
    // 上边重写的 equals 方法是根据人的名字和身份证号比较的
    // 所以我们也要根据人的名字和身份证号来计算hash值
    @Override
    public int hashCode() {
        int result = 1; // 哈希值结果
        int prime = 31; // 权重
        result = result * prime + (this.name == null ? 0 : this.name.hashCode()); // 这里使用了 String类重写的hashCode方法
        result = result * prime + (this.idCard == null ? 0 : this.idCard.hashCode()); // 这里使用了 String类重写的hashCode方法
        return result;
    }
}

测试类

java
package com.rewrite;

public class Test {
    public static void main(String[] args) {
        Person per1 = new Person("sakuna","410883200205067898");
        Person per2 = new Person("saba","410883200205067899");
        // 使用 Person类中重写的 equals 方法
        boolean result = per1.equals(per2);
        System.out.println("idCard对比的结果为 "+result);
        // 使用 Person类中重写的 hashCode 方法
        System.out.println("p1的哈希值为 "+per1.hashCode());
        System.out.println("p2的哈希值为 "+per1.hashCode());
    }
}

为什么要使用31作为权重

java
1.因为JDK也使用31
2.因为31是一个特殊的质数 任何数乘以31 等于这个数左移5位 减去这个数 本身

n * 31 = (n << 5) - n
位运算符效率是最高 但是优先级是最低的
总结:因为使用31计算hash值效率更高