0


详解Java抽象类与接口

抽象类与接口

文章目录

多态的复习

classShape{publicvoiddraw(){System.out.println("画一个图形");}}classRectextendsShape{@Overridepublicvoiddraw(){System.out.println("矩形");}}classCircleextendsShape{@Overridepublicvoiddraw(){System.out.println("圆");}}publicclassTest{publicstaticvoiddrawMap(Shape shape){
        shape.draw();}publicstaticvoidmain(String[] args){/*drawMap(new Circle());
        drawMap(new Rect());*/Circle circle =newCircle();Rect rect =newRect();drawMap(circle);drawMap(rect);}}

抽象类

什么是抽象类

如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类

上面的多态代码中的父类中的draw方法的实现其实没有什么实际的作用,反正下面子类还要进行重写

所以这个draw方法就可以定义为抽象类

抽象类的特点

1、使用abstract修饰的方法就叫做抽象方法

2、包含抽象方法的类必须设计为抽象类,也是使用abstract修饰

3、抽象类里面可以定义实例和静态的成员变量和方法,抽象类最大的特点就是不能进行实例化

4、抽象类虽然不能进行实例化,但是可以被继承,或者说,抽象类最大的作用就是被继承

5、一旦普通类继承了抽象类,要是抽象类里面有抽象方法,就必须要进行重写,否则就会提示编译错误,直接alt + enter 补全就行了【多了一层编译器的检查检验】

6、要是抽象类B继承了抽象类A,那么抽象类B就可以不用重写A里面的抽象方法,但是后面定义的抽象类C还是要进行抽象方法的重写【抽象方法的重写是逃不掉的】

7、 抽象方法不能被private、static和final修饰,因为private只能在同一个包的同一个类里面使用,下面的子类还要进行重写,private、static和final全都是静态绑定,而重写是动态绑定

8、抽象类里面不一定有抽象方法,但是有抽象方法的类一定要是抽象类

abstractclassShape{int a;staticint b;publicvoidfunc1(){}publicstaticvoidfunc2(){}publicabstractvoiddraw();}classRectextendsShape{@Override//重写publicvoiddraw(){System.out.println("矩形");}}classCircleextendsShape{@Overridepublicvoiddraw(){System.out.println("圆");}}publicclassTest{publicstaticvoiddrawMap(Shape shape){
        shape.draw();//尽管Shape不能实例化,但是Shape还是可以引用的}publicstaticvoidmain(String[] args){//Shape shape==new Shape();抽象类不能进行实例化drawMap(newCircle());drawMap(newRect());}}

接口

什么是接口

在Java中,接口可以看成是:多个类的公共规范,接口是一种引用数据类型。

接口的特点

1、使用interface来修饰接口

2、接口的成员变量默认都是public static final修饰的,因为有final,所以要进行初始化赋值

3、接口的成员方法默认都是抽象方法【以后的类要进行重写抽象方法】,由public abstract 修饰

4、接口的普通成员方法一般是不能有具体的实现的,但是在前面加上default就可以加上具体实现【JDK8以后才有的】

5、接口中可以有静态的成员方法,但是不管是default还是静态的方法,都是public的

6、接口也不能进行实例化

7、类与接口之间使用implements关联,接口可以引用具体的实现类,也能实现向上转型

8、接口里面不能有代码块或者构造方法(都不能实例化,要构造方法干什么呢)

9、抽象类实现一个接口,可以在在前面加上abstract,就不用重写接口中的抽象方法,但是下面还是要写别的类,还是要重写抽象方法【重写抽象方法是逃不掉的】

interfaceIShape{int COUNT=12;//接口的成员变量默认是public static final修饰的voidfunc();//接口的成员方法默认是public abstract修饰的voidfunc1();//接口的成员方法一般不具体实现defaultvoidfunc2(){System.out.println("haha");}staticvoidfunc3(){System.out.println("heihei");}}publicclassTest{//IShape iShape=new IShape();   err,接口也是不能实例化的}

接口的具体应用

interfaceIShape{//interface修饰接口voiddraw();}classRectimplementsIShape{//使用implements来连接接口与具体的类,叫做实现@Override//对抽象方法重写publicvoiddraw(){//上面的draw方法是public,所以这里要想访问draw就必须要使用public(大于等于接口的访问修饰限定符)System.out.println("矩形");}}classCircleimplementsIShape{@Overridepublicvoiddraw(){System.out.println("圆");}}publicclassTest{publicstaticvoiddrawMap(IShape shape){
        shape.draw();//动态绑定}publicstaticvoidmain(String[] args){//IShape iShape=new IShape();//err,接口不能实例化IShape rect=newRect();//向上转型drawMap(rect);IShape circle =newCircle();drawMap(circle);//不进行实例化/* drawMap(new Rect());
        drawMap(new Circle());*/}}

多个接口的具体使用

abstractclassAnimal{String name;int age;publicAnimal(String name,int age){this.name = name;this.age = age;}publicabstractvoideat();//吃}//如何实现跑的动作?//首先,不是所有的动物都能跑,所以跑这个动作不能放在Animal里面,其次,要是再写一个跑的类,但是Java是不允许多继承的(一个子类继承两个父类)//所以为了实现这样的灵活调用,就要用到多个接口interfaceIRUNNING{//跑步的接口voidrun();}interfaceIFLYING{//飞的接口voidfly();}classDogextendsAnimalimplementsIRUNNING{//Dog这个类继承了Animal类并且实现了IRUNNING接口publicDog(String name,int age){super(name, age);}@Overridepublicvoideat(){System.out.println(this.name +"正在吃狗粮");}@Overridepublicvoidrun(){System.out.println(this.name+"正在四处跑");}}classBirdextendsAnimalimplementsIRUNNING,IFLYING{//Bird类继承了Animal类并且实现了IRUNNING,IFLYING多个接口,而狗就没有飞这个功能/接口,所以使用接口十分灵活publicBird(String name,int age){super(name, age);}@Overridepublicvoideat(){System.out.println(this.name+"正在吃鸟粮");}@Overridepublicvoidrun(){System.out.println(this.name+"正在用细细的腿跳着跑");}@Overridepublicvoidfly(){System.out.println(this.name+"准备起飞");}}publicclass test {publicstaticvoidRun(IRUNNING irunning){//传过来的都已经实现了接口,所以可以成功
        irunning.run();}publicstaticvoidmain(String[] args){Run(newDog("旺财",2));Run(newBird("小鸟",3));}//以下是多态的使用,可以对比一下publicstaticvoidfunc(Animal animal){
        animal.eat();publicstaticvoidmain1(String[] args){func(newDog("旺财",2));func(newBird("小鸟",3));}}}

继承是is-a的关系(子类父类包含关系),接口是XXX特性,十分灵活

接口的继承

接口可以继承别的接口,也是使用extends,实现代码的复用

interfaceIA{voidfunc1();}interfaceIB{voidfunc2();}//接口的继承---接口功能的拓展//IC拥有IA和IB的功能interfaceICextendsIA, IB{voidfunc3();}classAimplementsIC{@Overridepublicvoidfunc1(){}@Overridepublicvoidfunc2(){}@Overridepublicvoidfunc3(){}}publicclass test {}

几种常用的接口

1、比较自定义类型的大小 / 排序(Comparable接口)

注意:实现接口后面还要加上<自定义类名>

//比较大小classStudentimplementsComparable<Student>{//此时会报错,直接alt+enter,就可以重写compareTo方法String name;int age;double score;publicStudent(String name,int age,double score){this.name = name;this.age = age;this.score = score;}@OverridepublicStringtoString(){return"Student{"+"name='"+ name +'\''+", age="+ age +", score="+ score +'}';}@OverridepublicintcompareTo(Student o){//重写compareTo方法/*if (this.age > o.age) {
            return 1;
        } else if (this.age == o.age) {
            return 0;
        } else {
            return -1;
        }
*/returnthis.age-o.age;//o就是传的参数,谁调用compareTo谁就是this//两种代码效果是一样的,按照年龄比较大小}}publicclass test {publicstaticvoidmain(String[] args){Student student1 =newStudent("zhangsan",32,29.0);Student student2 =newStudent("lisi",20,1.2);if(student1.compareTo(student2)>0){System.out.println("student1>student2");}}}
//排序importjava.util.Arrays;classStudentimplementsComparable<Student>{String name;int age;double score;publicStudent(String name,int age,double score){this.name = name;this.age = age;this.score = score;}@OverridepublicStringtoString(){return"Student{"+"name='"+ name +'\''+", age="+ age +", score="+ score +'}';}@OverridepublicintcompareTo(Student o){//重写returnthis.age-o.age;//o就是传的参数,谁调用compareTo谁就是this//要是想要改为降序就调换一下减的顺序}}publicclass test {publicstaticvoidmain(String[] args){Student[]  student=newStudent[3];//定义Student类型的数组
        student[0]=newStudent("zhangsan",32,29.0);
        student[1]=newStudent("lisi",20,1.2);
        student[2]=newStudent("wangwu",52,89);System.out.println("排序前"+Arrays.toString(student));Arrays.sort(student);//要是不写Comparable接口就不知道是按照什么进行排序的System.out.println("排序后"+Arrays.toString(student));//升序打印}}

但是上面使用Comparable接口,就将比较/排序的规则定死了,已经加入到了Student类里面,后期就会不方便改

此时既可以使用Comparator接口

2、Comparator接口(比较器)

importjava.util.Comparator;//比较大小classStudent{String name;int age;double score;publicStudent(String name,int age,double score){this.name = name;this.age = age;this.score = score;}@OverridepublicStringtoString(){return"Student{"+"name='"+ name +'\''+", age="+ age +", score="+ score +'}';}}classAgeComparatorimplementsComparator<Student>{//定义一个年龄比较器的类@Overridepublicintcompare(Student o1,Student o2){return o1.age - o2.age;}}classScoreComparatorimplementsComparator<Student>{//定义一个分数比较器的类@Overridepublicintcompare(Student o1,Student o2){return(int)(o1.score - o2.score);}}classNameComparatorimplementsComparator<Student>{//定义一个名字比较器的类@Overridepublicintcompare(Student o1,Student o2){return o1.name.compareTo(o2.name);//compareTo的返回值是int}}publicclass test {publicstaticvoidmain(String[] args){Student student1 =newStudent("zhangsan",32,29.0);Student student2 =newStudent("lisi",20,1.2);AgeComparator ageComparator =newAgeComparator();//实例化年龄比较器int ret=ageComparator.compare(student1, student2);System.out.println(ret);ScoreComparator scoreComparator =newScoreComparator();//实例化分数比较器int ret2 = scoreComparator.compare(student1, student2);System.out.println(ret2);NameComparator nameComparator =newNameComparator();//实例化名字比较器int ret3 = nameComparator.compare(student1, student2);System.out.println(ret3);}//年龄之差12//分数之差是27//名字的首字母相差14

使用比较器对数组元素进行排序

importjava.util.Arrays;importjava.util.Comparator;classStudent{String name;int age;double score;publicStudent(String name,int age,double score){this.name = name;this.age = age;this.score = score;}@OverridepublicStringtoString(){return"Student{"+"name='"+ name +'\''+", age="+ age +", score="+ score +'}';}}classAgeComparatorimplementsComparator<Student>{@Overridepublicintcompare(Student o1,Student o2){return o1.age - o2.age;}}classScoreComparatorimplementsComparator<Student>{@Overridepublicintcompare(Student o1,Student o2){return(int)(o1.score - o2.score);}}classNameComparatorimplementsComparator<Student>{@Overridepublicintcompare(Student o1,Student o2){return o1.name.compareTo(o2.name);//compareTo的返回值是int}}publicclass test {publicstaticvoidmain(String[] args){Student[]  student=newStudent[3];
        student[0]=newStudent("zhangsan",32,29.0);
        student[1]=newStudent("lisi",20,1.2);
        student[2]=newStudent("wangwu",52,89);AgeComparator ageComparator =newAgeComparator();//实例化年龄比较器Arrays.sort(student, ageComparator);//Arrays.sort是可以重载的(既可以只放一个数组名,还能再放一个比较器对象)System.out.println(Arrays.toString(student));ScoreComparator scoreComparator =newScoreComparator();Arrays.sort(student, scoreComparator);System.out.println(Arrays.toString(student));NameComparator nameComparator =newNameComparator();Arrays.sort(student, nameComparator);System.out.println(Arrays.toString(student));}//[Student{name='lisi', age=20, score=1.2}, Student{name='zhangsan', age=32, score=29.0}, Student{name='wangwu', age=52, score=89.0}]//    //[Student{name='lisi', age=20, score=1.2}, Student{name='zhangsan', age=32, score=29.0}, Student{name='wangwu', age=52, score=89.0}]//    //[Student{name='lisi', age=20, score=1.2}, Student{name='wangwu', age=52, score=89.0}, Student{name='zhangsan', age=32, score=29.0}]

使用比较器不会涉及到自定义的类,想要按照什么比较就写一个专门的比较器,实例化 比较器来进行比较,所以写比较器是十分灵活的,十分好用

3、克隆当前对象(Cloneable接口)

classPersonimplementsCloneable{//Cloneable里面没有任何东西,是一个空接口/标记接口:说明当前类可以被克隆publicint age;publicPerson(int age){this.age = age;}@OverrideprotectedObjectclone()throwsCloneNotSupportedException{//正式实现克隆  alt+enter,选中clone即可//重写的是Object里面的clone方法(由C/C++写的,所以看不见)returnsuper.clone();}@OverridepublicStringtoString(){return"Person{"+"age="+ age +'}';}}publicclassTest{publicstaticvoidmain(String[] args)throwsCloneNotSupportedException{Person person =newPerson(10);Person person2=(Person) person.clone();//要将Object类型的强转为PersonSystem.out.println(person);System.out.println(person2);}}

image-20220518223545524

深拷贝与浅拷贝
classPersonimplementsCloneable{//Cloneable里面没有任何东西,是一个空接口/标记接口:当前类可以被克隆publicint age;publicPerson(int age){this.age = age;}@OverrideprotectedObjectclone()throwsCloneNotSupportedException{returnsuper.clone();}@OverridepublicStringtoString(){return"Person{"+"age="+ age +'}';}}publicclassTest{publicstaticvoidmain(String[] args)throwsCloneNotSupportedException{Person person =newPerson(10);Person person2=(Person) person.clone();//要将Object类型的强转为PersonSystem.out.println(person.age);//10System.out.println(person2.age);//10System.out.println("=============================");
        person.age =100;System.out.println(person.age);//100System.out.println(person2.age);//10}}//深拷贝:改变克隆前后的值,不会影响到对方的值

要是在Person类里面定义一个别的类的对象呢,是否会发生深拷贝?

class Clothes{
    public int clothes = 12;
}
class Person implements Cloneable{ //Cloneable里面没有任何东西,是一个空接口/标记接口:当前类可以被克隆
    public int age;
    Clothes clo = new Clothes();
    public Person(int age) {
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person(10);
        Person person2= (Person) person.clone();//要将Object类型的强转为Person
        System.out.println(person.age);
        System.out.println(person2.age);
        System.out.println(person.clo.clothes);//12
        System.out.println(person2.clo.clothes);//12   一起改变
    }
}

image-20220518232844965

要是想要实现深拷贝,就要将Clothes类也克隆一份

classClothesimplementsCloneable{publicint clothes =12;@OverrideprotectedObjectclone()throwsCloneNotSupportedException{returnsuper.clone();}}classPersonimplementsCloneable{//Cloneable里面没有任何东西,是一个空接口/标记接口:当前类可以被克隆publicint age;Clothes clo =newClothes();publicPerson(int age){this.age = age;}@OverrideprotectedObjectclone()throwsCloneNotSupportedException{Person tmp =(Person)super.clone();
        tmp.clo =(Clothes)this.clo.clone();return tmp;}@OverridepublicStringtoString(){return"Person{"+"age="+ age +'}';}}publicclassTest{publicstaticvoidmain(String[] args)throwsCloneNotSupportedException{Person person =newPerson(10);Person person2=(Person) person.clone();//要将Object类型的强转为Person
        person2.clo.clothes=23;System.out.println(person.clo.clothes);//12System.out.println(person2.clo.clothes);//23}}//深拷贝

image-20220519000147521

所以对于自定义类型,很多情况下都是要进行重写的

抽象类与接口

核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中
不能包含普通方法, 子类必须重写所有的抽象方法.

image-20220519215711961

Object类

Object是Java默认提供的一个类,Java里面出来Object类,其他的所有的类都存在继承关系。都会默认继承
Object类,Object是祖先类

classPerson{//默认就会继承Object类String name;publicPerson(String name){this.name = name;}@OverridepublicStringtoString(){return"Person{"+"name='"+ name +'\''+'}';}}classStudent{}publicclass test {publicstaticvoidfunc(Object o){System.out.println(o.toString());System.out.println(o.toString(o));//效果是一样的}publicstaticvoidmain(String[] args){func(newPerson("xiaozhang"));func(newStudent());}}

Object类的所有方法

image-20220520210409163

equals方法(对象比较)

classPerson{//默认就会继承Object类String tele;}publicclass test {publicstaticvoidmain(String[] args){Person person1 =newPerson();Person person2 =newPerson();
        person1.tele ="123";
        person2.tele ="123";System.out.println(person1 == person2);//false,比较的是person1和person2的地址System.out.println(person1.equals(person2));//false,person1里面并没有重写equals方法,使用原本的        equals方法还是在比较地址}}

image-20220520211756278

由上面的代码知道,要想要比较两个对象就需要进行equals方法的重写

classPerson{//默认就会继承Object类String tele;@Override//记得加上注解,要是重写的方法名不一致,就会提示报错publicbooleanequals(Object obj){//原本的equals方法传过来的就是objPerson tmp =(Person) obj;//进行强转returnthis.tele.equals(tmp.tele);//this是当前对象的引用,注意,引用类型的比较要用equals,此时的equals方法是用来比较两种字符串的,与我实现的equals不一样}}publicclass test {publicstaticvoidmain(String[] args){Person person1 =newPerson();Person person2 =newPerson();
        person1.tele ="123";
        person2.tele ="123";System.out.println(person1 == person2);//falseSystem.out.println(person1.equals(person2));}}

这样子就实现了equals方法的重写,但是考虑的不够完善

classPerson{//默认就会继承Object类String tele;@Overridepublicbooleanequals(Object obj){//传过来的是nullif(obj ==null){returnfalse;}//传过来的与当前对象一样/*
        Person person1 = new Person();
        Person person2 = person1;
         */if(this==obj){returntrue;}//传过来的与Person类无关if(!(obj instanceofPerson)){//insatnceof的作用是判断其左边对象是否为其右边类的实例returnfalse;}Person tmp =(Person) obj;//只有转化为Person才能.telereturnthis.tele.equals(tmp.tele);}}publicclass test {publicstaticvoidmain(String[] args){Person person1 =newPerson();Person person2 =newPerson();
        person1.tele ="123";
        person2.tele ="123";System.out.println(person1 == person2);//falseSystem.out.println(person1.equals(person2));//true}}

hashcode方法

hashcode方法可以帮我定位出一个对象的地址

classPerson{//默认就会继承Object类String tele;publicPerson(String tele){this.tele = tele;}}publicclass test {publicstaticvoidmain(String[] args){Person person1 =newPerson("1234");Person person2 =newPerson("1234");System.out.println(person1.hashCode());System.out.println(person2.hashCode());}}//这里的两个对象的地址是不一样的

要是我想要将tele一样的对象放在一个位置上,就要重写hashcode方法,这里可以让IDEA帮我生成

classPerson{//默认就会继承Object类String tele;publicPerson(String tele){this.tele = tele;}@OverridepublicinthashCode(){returnObjects.hash(tele);//按照tele进行放位置}}publicclass test {publicstaticvoidmain(String[] args){Person person1 =newPerson("1234");Person person2 =newPerson("1234");System.out.println(person1.hashCode());System.out.println(person2.hashCode());}}//此时输出的结果就是一样的

但是,我们alt+insert,就可以直接生成equals和hashcode方法

classPerson{//默认就会继承Object类String tele;publicPerson(String tele){this.tele = tele;}@Overridepublicbooleanequals(Object o){if(this== o)returntrue;if(o ==null||getClass()!= o.getClass())returnfalse;//这里的getClass() != o.getClass()其实与我自己实现的!(obj instanceof Person)差不多,都是判断传过来的是不是同一个类Person person =(Person) o;returnObjects.equals(tele, person.tele);}@OverridepublicinthashCode(){returnObjects.hash(tele);}}publicclass test {//hashcodepublicstaticvoidmain2(String[] args){Person person1 =newPerson("1234");Person person2 =newPerson("1234");System.out.println(person1.hashCode());System.out.println(person2.hashCode());}//equalspublicstaticvoidmain(String[] args){Person person1 =newPerson("1234");Person person2 =newPerson("1234");
        person1.tele ="123";
        person2.tele ="123";System.out.println(person1 == person2);//falseSystem.out.println(person1.equals(person2));}}

equals和hashcode在自定义类型最好加一下,很可能会用上
如有错误,请各位在评论区指正,我们一起进步!

标签: java 开发语言

本文转载自: https://blog.csdn.net/m0_60354608/article/details/124925681
版权归原作者 fiance111 所有, 如有侵权,请联系我们删除。

“详解Java抽象类与接口”的评论:

还没有评论