Java抽象类与接口

在这里插入图片描述

🎉🎉🎉写在前面:
博主主页:🌹🌹🌹戳一戳,欢迎大佬指点!
博主秋秋:QQ:1477649017 欢迎志同道合的朋友一起加油喔💪
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个小菜鸟嘿嘿
-----------------------------谢谢你这么帅气美丽还给我点赞!比个心-----------------------------

在这里插入图片描述



一,抽象类

1.1,抽象类的概念

如果说某一个类里面,它没有具体的信息来描绘一个对象,那么这个类就叫做抽象类

abstract class Sheap{
    public abstract void draw();//抽象方法可以没有具体的实现,也不用写{}
}

例如这里的Sheap类就是一个抽象类,它里面只有一个draw方法,并且没有具体的实现。此时,这个用abstract修饰的方法叫做抽象方法,用abstract修饰的类叫做抽象类。


1.2,抽象类的特点

🚀1,如果某个方法被定义为抽象方法,那么这个类必须也被定义为抽象类才可以。(抽象类的存在与抽象方法的存在是一个必要不充分条件,抽象类不一定含有抽象方法,但是有了抽象方法那么这个类必须是抽象类)

🚀2,抽象类是不能够进行实例化的

abstract class Sheap{
    public abstract void draw();
}
Sheap sheap = new Sheap();//这是绝对不允许的,会编译报错

🚀3,抽象类里面可以有普通的方法与成员,它最突出的区别就是不能实例化对象。

abstract class Sheap{
    public int sheapName;//普通的成员
    public static void func(){//普通的静态方法
        
    }
    public abstract void draw();
}

🚀4,抽象类不可以被实例化,但是必须被继承,可以说抽象类的存在就是为了被继承。(因为抽象类本身是不能实例化的,抽象类中的那些方法,成员本身就不是为了让它自己的对象去使用的,主要是继承,然后子类去重写,去使用,如果不继承,那抽象类确实是没有什么存在的必要)

🚀5,抽象方法不能是private,final,static所修饰的,因为子类抽象方法还要被子类重写。(static所修饰的方法都是依托类的,所以即使你用父类的引用去接收了子类的对象,但是最后它还是只会绑定到父类自己的static方法,所以根本就达不到重写的目的,所以static修饰的方法,语法规定就是不能重写的)

🚀6,抽象方法当你没有加访问修饰符的时候默认是public。

🚀7,当一个普通类继承了抽象类后,那么这个普通类里面必须重写所有的抽象方法,不然编译报错

🚀8,当一个抽象类B继承了一个抽象类A后,这个时候抽象类B里面可以不重写A中的抽象方法。但是,你也不可能一直抽象类的继承下去,所以就是你总得在某一个普通类里面把所有的抽象方法全部重写,躲得过初一,躲不过十五。

🚀9,抽象类里面可以有构造方法,帮助子类对象创建的时候初始化继承过去的父类成员。


1.3,抽象类的作用

抽象类的作用,就是为了被继承,一是父类中的抽象方法在定义的时候可以不用写具体的执行体(帮你少写几个英文单词),二是编译器多了一层校验,当我们没有重写抽象方法的时候会直接报错


二,接口

2.1,接口的概念

接口就是公共的行为规范,大家在实现的时候,只要按着规范,那就都可以通用。在Java中就是多个类的规范,是一种引用数据类型

就拿我们生活中的插座举例:

在这里插入图片描述

其中,插座的接口的间距大小就是一种标准的规范,只要制造商按照这个规范制作的插座就能通用于所有的电器。那么迁移到Java中,这个插座接口的间距大小的标准就是接口,引用这个接口的比如说插座类,就必须按照这个接口中的制作方法的标准样子来定义自己的制作插座的方法,但是这个时候只是规定了插座口的大小标准的规范,所以具体最后到制作出的插座的外观是不一样的,这里其实涉及到的就是重写,接口定义的是规范,但是引用接口的子类按照规范实现的结果不一定是一样的。


2.2,语法规则

interface Ianimal{
    public abstract void eat();
}

🚩提示:

🚀1,创建接口时,接口的名字一般以大写字母 I 开头

🚀2,接口的命名一般用形容词性的单词。

🚀3,阿里编程规范中要求,接口中的方法和成员前不要加任何修饰符,保持代码的简洁,比如上面的public abstract,当然虽然我们没写,但是编译器时是知道的。


2.3,接口的使用

接口是不能直接使用的,必须要有一个类来实现这个接口,并且重写接口里面所有的抽象方法才可。

interface AnimalEat{
    void eat();
}
class Dog implements AnimalEat{
    @Override
    public void eat() {
        System.out.println("狗狗吃狗粮!");
    }
}
class Cat implements AnimalEat{
    @Override
    public void eat() {
        System.out.println("猫猫吃猫粮!");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        AnimalEat animal1 = new Dog();
        AnimalEat animal2 = new Cat();
        animal1.eat();
        animal2.eat();
    }
}

接口与类之间的关联使用implements实现的


2.4,接口的特性

🚀1,接口用interface进行修饰,是一种引用数据类型,但是接口也是不能进行实例化的,不能new接口的对象。

🚀2,接口中可以有普通的成员变量,并且全部是隐式由public static final 所修饰的。

interface AnimalEat{
    public int a = 10;
    public boolean b = false;//全写完应该是public static final boolean b = false
    public static final int c = 20;
    void eat();
}

因为是final修饰的,所以这些成员变量在定义的时候就必须赋值,并且后续无法进行修改。


🚀3,接口当中的普通成员方法默认都是抽象方法,由public abstract所修饰,不能有具体的实现。

🚀4,如果你一定要让普通的成员方法有具体的实现话,那么必须用default进行修饰。【jdk8开始才有的】

interface AnimalEat{
    default void eat(){
        
    };
}

🚀5,接口中也可以有静态的成员方法,可以直接有具体的实现。但是不管是default修饰的方法还是静态的成员方法,都是由public进行修饰的。

🚀6,和接口关联的类中,是必须要对接口中的抽象方法进行重写的,不然就会直接报错(抽象类除外)。注意,我们的抽象类是必须要重写的,所以在执行的时候会发生动态的绑定,那对于接口中的普通方法,你也是可以在引用了这个接口的类里面进行重写,也是会发生动态的绑定的。

🚀7,在对接口中的方法进行重写的时候,不能使用default进行修饰。

🚀8,接口中不能有静态代码块与构造方法。

🚀9,接口虽然不是类,但是它最终编译形成的字节码文件也是 .class文件


2.5,实现多个接口

在Java里面,类是只能单继承的,但是对于接口而言,一个类是可以实现多个接口的。

package demo2;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 14776
 * Date: 2022-05-18
 * Time: 11:33
 */
interface ISwimming{
    void swim();
}
interface IRunning{
    void run();
}
abstract class Animal{
    public  String name;
    public  int age;

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

    public abstract void eat();
}
class Cat extends Animal implements IRunning{
    public Cat(String name,int age){
        super(name,age);
    }
    public void eat(){
        System.out.println(name + "吃猫粮!");
    }
    public void run(){
        System.out.println(name + "正在跑!");
    }
}

class duck extends Animal implements ISwimming{
    public duck(String name,int age){
        super(name,age);
    }
    public void eat(){
        System.out.println(name + "吃鸭粮!");
    }
    public void swim(){
        System.out.println(name + "正在游泳!");
    }
}
public class Test {
    public static void doRun(IRunning irun){
        irun.run();
    }
    public static void doSwim(ISwimming iswim){
        iswim.swim();
    }
    public static void doEat(Animal animal){
        animal.eat();
    }
    public static void main(String[] args) {
        doEat(new Cat("汤姆",5));
        doEat(new duck("唐老鸭",6));
        doSwim(new duck("唐老鸭",6));
        doRun(new Cat("汤姆",5));

    }
}

程序运行截图:

在这里插入图片描述


看了上面的例子,我深深的感受到了接口的巧妙与强大。因为我们可以看到,继承是为了实现共性的抽离和代码的复用,但是对于我们的猫猫和鸭子而言,他们有的行为肯定是不一样的,比如说游泳,那这个时候你肯定不能把游泳的方法定义在父类Animal里面了,因为Cat类也会继承到这个方法,但是猫不会游泳,这个时候就显得不是很恰当。那如果我们又多定义一个游泳的类,但是duck已经继承了Animal类,不可能再继承游泳类了。所以,接口这个时候就显得十分的好用,一个类可以实现多个接口,并且也可以实现多态的思想,对于各种各样不同的行为功能,我们只要在类的后面实现各个接口就好了。


2.6,接口间的继承

package demo3;
interface IRun{
    void run();
}
interface ISwim{
    void swim();
}
interface IFly extends IRun,ISwim{//IFly接口直接继承了前面两个接口
    void fly();
}
class Duck implements IFly{//现在引用IFly这个接口后,必须重写上面所有的抽象方法
    @Override
    public void run() {
        System.out.println("鸭子在跑~");
    }
    @Override
    public void swim() {
        System.out.println("鸭子在游~");
    }
    @Override
    public void fly() {
        System.out.println("鸭子在飞~");
    }
}
public class Test {
    public static void doDuck(IFly fly){
        fly.run();
        fly.fly();
        fly.swim();
    }
    public static void main(String[] args) {
        doDuck(new Duck());
    }
}

程序运行截图:

在这里插入图片描述


2.7,几个重要接口使用实例

🔥Comparable接口

public interface Comparable<T> {
        public int compareTo(T o);
}
//这是Comparable接口里面的方法情况,就一个comparableTo方法,在实现的类里面需要进行重写

假如这里有一个学生类,我们定义了一个学生数组,需要我们按照学生的年龄进行排序,那么如下实现:

package demo4;
import java.util.Arrays;
class Student implements Comparable<Student>{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

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

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}
public class Test {
    public static void main(String[] args) {
        Student[] stu = new Student[3];
        stu[0] = new Student("xiaowang",18,88.0);
        stu[1] = new Student("zhangsan",19,66.0);
        stu[2] = new Student("lisi",12,88.9);
        Comparable com = stu[0];
        int ret = com.compareTo(stu[1]);//单纯两个学生对象之间的比较
        if(ret < 0){
            System.out.println("<");
        }else if(ret == 0){
            System.out.println("==");
        }else{
            System.out.println(">");
        }

//        对这个学生数组按照年龄进行排序
        Arrays.sort(stu);
        System.out.println("排序后:" + Arrays.toString(stu));//将数组以字符串的形式进行打印
    }
}

程序运行截图:

在这里插入图片描述


@Override
public int compareTo(Student o) {
    return this.age - o.age;
}

这是我们在学生类里面重写了compareTo方法,按照两个学生对象的年龄进行比较。其中,this代表你调用这个方法的对象,比如上面的com,o代表你传参传入的对象。当我们对数组进行排序调用sort方法的时候,sort方法的内部会自动调用compareTo方法,在默认的逻辑上,当this.age - o.age < 0 的时候不会进行交换,所以排序出来也就是升序的;但如果我们交换一下变成 o.age - this.age的话,那么原本是升序的,比如3,5,这个时候你用5-3 > 0,那么大于0就需要进行交换,所以最后排序出的结果也就是降序的。

总结:所以对于自定义的类型,如果说我们涉及到比较或者是排序,需要有一个比较的方法依据的话,就需要进行一些设计,比如这里实现接口。


✅但是呢,可以看到,我们上面的compareTo方法是定义在类里面的,并且写死了,那么这个时候就会有一个问题,如果某一个人拿到这个学生类了想用分数大小来作为依据比较大小怎么办,因为你在类里面对于compareTo实现的是依据年龄比较,这个时候肯定是无法依据分数进行比较的,那怎么办呢?
Java的设计者自有妙计,也就是我们接下来要说的接口。


🔥Comparator接口

Comparator接口你也可以将其称为一个比较器。

package demo4;
import java.util.Arrays;
import java.util.Comparator;
class Student implements Comparable<Student>{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

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

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}
class AgeCompare implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}
class ScoreCompare implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return (int)(o1.score - o2.score);
    }
}
public class Test {
    public static void main(String[] args) {
        Student[] stu = new Student[3];
        stu[0] = new Student("xiaowang",18,88.0);
        stu[1] = new Student("zhangsan",19,66.0);
        stu[2] = new Student("lisi",12,98.9);
        System.out.println("按照年龄比较:");
        AgeCompare com1 = new AgeCompare();
        int ret1 = com1.compare(stu[0],stu[1]);
        if(ret1 < 0){
            System.out.println("<");
        }else if(ret1 == 0){
            System.out.println("==");
        }else{
            System.out.println(">");
        }
        System.out.println("按照分数比较:");
        ScoreCompare com2 = new ScoreCompare();
        int ret2 = com2.compare(stu[1],stu[2]);
        if(ret2 < 0){
            System.out.println("<");
        }else if(ret2 == 0){
            System.out.println("==");
        }else{
            System.out.println(">");
        }
    }

程序运行截图:

在这里插入图片描述

那么可以看到,我们这个时候将我们的比较方法单独抽离出来成为了一个类,依据年龄比较的是AgeCompare类,依据分数比较的是ScoreCompare类,这些类都会实现我们的Comparator接口,重写了里面的compare方法,这样就可以将我们具体的比较过程与我们的学生类分离开,就可以定义多个不同的比较类,多个比较依据,根据不同的比较类去定义不同的对象,然后将学生对象作为参数进行传入到compare方法里面,这样调用不同的比较类的比较方法,就可以实现灵活的去进行比较,不再像原本的那么死板。


这里可能会有同学灵光一闪,既然可以把比较方法单独抽离出来成类,那我还是实现Comparable接口是不是应该也可以,答案是不行的,因为compareTo的参数只有一个对象,假如你抽离出来了,如下:

AgeCompare com1 = new AgeCompare();
com1.compareTo(stu[1])

这怎么比较,根本就比较不了,原先我们没有抽离出来的时候能调用是因为我们用的学生对象去调用的,stu[0].compareTo(stu[1]),这个时候就是可以比较的。


我们之前说过sort的底层会调用compareTo方法,但是这个时候我们用的是Comparator接口,是不含compareTo方法的,那要怎么对数字进行排序呢?其实还是sort,只不过是sort重载的一个方法。(默认的是调用compareTo方法,但是如下会直接调用重载的sort函数,所以Student类实现Comparable接口与Comparator接口的使用二者是不冲突的,可以同时存在)

class Student implements Comparable<Student>{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}
class AgeCompare implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}
public class Test {
    public static void main(String[] args) {
        Student[] stu = new Student[3];
        stu[0] = new Student("xiaowang",18,88.0);
        stu[1] = new Student("zhangsan",19,66.0);
        stu[2] = new Student("lisi",12,98.9);
        System.out.println("按照年龄比较:");
        AgeCompare com1 = new AgeCompare();
        Arrays.sort(stu,com1);
        System.out.println("排序后:" + Arrays.toString(stu));

    }
}

Arrays.sort(stu,com1),传参的时候不仅传入数组引用,另外把比较类方法实例化的对象也传入就好。


✅为了进一步的理解接口,对于数组的排序sort函数,我们是可以自己模拟实现下的:

public static void sort(Comparable[] array){//因为学生类实现了Comparable接口的,所以用来接收学生数组
        for(int i = 0;i < array.length - 1;i++){
            for(int j = 0;j < array.length - 1 - i;j++){
                if(array[j].compareTo(array[j+1]) > 0){
                    Comparable tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                }
            }
        }
}
.....
    
//在main函数里面就可以直接用sort(stu)就行,只不过这里的compareTo依旧是你在Student里面重写的那个,以年龄为依据比较

上面的比较都还是比较直观,因为参照点是数字,那如果要是以名字作为比较依据又会如何呢?

class NameCompare implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}

这是名字的比较类,照样是重写compare方法,只不过因为name是String类型,所以内部具体比较的时候是直接调用了compareTo方法,String类是实现了Comparable类的,所以可以直接用。

在这里插入图片描述


🔥Clonable接口与深拷贝

Object类里面有一个clone方法,可以用来创建一个对象的拷贝,但是如果想要使用clone方法,必须先实现Clonable接口,不然就会抛出一个CloneNotSupportedException 异常。

在这里插入图片描述

可以看到,我们的Clonable接口里面实际上是没有任何抽象方法的,这叫做空接口或者标记接口,表示该类可以进行克隆。

class Person implements Cloneable{
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {//需要抛出异常
        Person per1 = new Person();
        Person per2 = (Person) per1.clone();//Object类的克隆方法的返回值是Object类型
        System.out.println(per1 == per2);//输出false,克隆出的两个对象不是同一个东西
    }
}

❌那这里就有一个问题了,既然Object类是所有类的父类,那么我们是不是就没有必要去Person类里面的clone方法里面去调用Object类的clone方法,直接在main函数里面间接去调用Object类的clone方法就好了,如下:

class Person implements Cloneable{
    
}
public class Test {
    public  void func() throws CloneNotSupportedException{
        Person per1 = new Person();
        Person per2 = (Person) super.clone();//super调用父类Object类的clone方法
        System.out.println(per1 == per2);
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        Test test = new Test();
        test.func();
    }
}

但是这是会报错的,因为它根本就不知道要克隆谁,你直接使用super去调用,但是克隆的对象是谁不知道,所以正确的用法是在Person类里面重写clone方法,在里面去使用super,因为当我们使用Person类的对象per1调用类里面的clone()方法时,就告诉了编译器我这个时候要克隆的是per1这个对象,所以使用super调用Object方法克隆的对象就是per1。


【深拷贝问题】:

class Boy implements Cloneable{
    public int age = 10;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Person implements Cloneable{
    public Boy boy = new Boy();//也就是说Person类的实例成员是一个引用类型的变量
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person tmp = (Person)super.clone();
        tmp.boy = (Boy)(tmp.boy.clone());//也可以用tmp.boy = (Boy)(this.clone())
        return tmp;
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person per1 = new Person();
        Person per2 = (Person)per1.clone();
        System.out.println("修改前:" + per1.boy.age + " " + per2.boy.age);
        per1.boy.age = 20;
        System.out.println("修改后:" + per1.boy.age + " " + per2.boy.age);
    }
}

程序运行截图:

在这里插入图片描述


我们可以看到,Person类里面的实例成员变量是一个引用类型的变量,所以我们就只是单纯拷贝Person类的对象的时候,这个时候其实是一个浅拷贝,因为两个person对象访问到的age都是同一个,一修改都会收到影响,那解决的办法就是把Person类里面的这个引用类型的成员也拷贝一份。

图解:

在这里插入图片描述

所以到这里其实我们可以发现,对于一个拷贝到底是深拷贝还是浅拷贝,不取决于数据的类型,完全是取决于代码的实现,因为就算是浅拷贝,我也可以通过代码实现强制让它变成一个深拷贝,所以以后再问某一个拷贝过程是深拷贝或者是浅拷贝,可不敢再妄下定论了,这是要看代码实现的。


2.8,抽象类与接口对比

在这里插入图片描述

其实对于接口的存在主要是为了定义一种行为的规范,所以在接口里面确实是可以定义public staticfinal修饰的成员变量,也可以有static修饰的静态方法,也可以有default修饰的普通方法,我觉得这些都是一些无关紧要的内容,接口的存在并不是为了这个,我们要关注的核心区别是抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法(不考虑说的种种无关紧要的类型),只能是public abstract修饰的抽象方法, 子类必须重写所有的抽象方法


三,Object类

Object类是Java默认提供的一个类,它是所有类的父类,可与称之为超类。

class Person{

}
public class TestDemo {
    public static void main(String[] args) {
        Object o = new Person();
    }
}

可以用Object接收任意类的对象。


3.1,Object类方法总览

在这里插入图片描述

目前我们用到的还就只是上面红框圈住的几个方法,但是对于其他的方法,后续也会学到,对于Object类中的每一个方法,我们都需要知道。


3.2,equals方法

equals方法是用来比较两个对象是否相等的。

== 有以下几种比较的情况:

🚀1,比较的两边都是基本的数据类型,那就是直接比较值是否相等。

🚀2,比较的两个都是引用的类型变量,那就是直接比较两个引用的值,即地址是否相等。

🚀3,如果要比较的是对象中具体的内容,那就必须要重写equals方法,因为Object类中的equals默认也是按照地址进行比较的。

在这里插入图片描述


package demo1;
class Person{
    public String name;

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

    @Override//重写equals方法
    public boolean equals(Object obj) {
        if(obj == null){
            return false;//obj为null,肯定是不相等的
        }
        if(this == obj){
            return true;//有可能是对象之间的赋值,例如per2 == per1,那两个肯定是相等的
        }

        if(!(obj instanceof Person)){
            return false;//obj根本就不是Person类的对象,所以肯定不相等
        }

        Person tmp = (Person)obj;
        return this.name.equals(tmp.name);//因为字符串的比较,String类里面也有自己的equals方法
    }

}
class Student{
}
public class TestDemo {
    public static void main(String[] args) {
        int a = 10;
        int b = 10;
        System.out.println(a == b);//基本的数据类型直接比较值

        Person per1 = new Person("小王");
        Person per2 = new Person("小王");
        System.out.println(per1 == per2);//引用数据类型比较的是地址
//        但是对于一些情况,例如要比较对象具体的值,比如我们认为的是名字一样,这两个对象应该是相等的
        System.out.println(per1.equals(per2));//默认也是比较的引用的值即地址
//      所以这个时候我们想名字一样就是相等的话,那就得在Person类里面重写equals方法

        System.out.println(per1.equals(null));

        Person per3 = per1;//地址是一样的
        System.out.println(per3.equals(per1));

        Student stu1 = new Student();
        System.out.println(per1.equals(stu1));
    }
}

程序运行截图:

在这里插入图片描述


3.3,hashcode

hashcode方法的作用是帮助我们定位一个对象的具体的位置。

public native int hashCode();
//本地方法,底层是C、C++实现,看不到
class Person{
    public String name;

    public Person(String name) {
        this.name = name;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Person per1 = new Person("小王");
        Person per2 = new Person("小王");

        System.out.println(per1.hashCode());
        System.out.println(per2.hashCode());
    }
}

对于两个名字相同的对象,假如我们认定他们应该就是同一个,但是我们通过Object类hashcode方法定位对象的地址可以看到是不一样的,如下:

在这里插入图片描述

那有没有办法让他们变成一样的呢?有的,只需要重写一下hashcode方法。

class Person{
    public String name;

    public Person(String name) {
        this.name = name;
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Person per1 = new Person("小王");
        Person per2 = new Person("小王");

        System.out.println(per1.hashCode());
        System.out.println(per2.hashCode());
    }
}

程序运行截图:

在这里插入图片描述

现在重写之后生成的两个地址它就是一样的了。


其实一般情况下,对于自定义的类型,上面的equals和hashcode方法最好都是需要重写下的,因为你不清楚什么时候可能就会用到,而编译器也是为我们提供了自动生成的快捷方式。

按住快捷键 Alt + Insert,在弹出的窗口中选择hashcode() and equals(),然后一路点next就可以自动生成了。

在这里插入图片描述

@Override
public boolean equals(Object o) {
    if (this == o) return true;//这是两个对象地址一样的情况
    if (o == null || getClass() != o.getClass()) return false;//这是传入对象为空,或者两个对象不是同一类
    Person person = (Person) o;
    return Objects.equals(name, person.name);
}

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

最后,今天的文章分享比较简单,就到这了,如果大家觉得还不错的话,还请帮忙点点赞咯,十分感谢!🥰🥰🥰
在这里插入图片描述

  • 45
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 34
    评论
评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力学习.java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值