java 抽象类 详解

简介: java 抽象类 内容分享,本篇博文为java 面向对象三大特性——多态篇的补充。

 目录

一、抽象类概述:

二、抽象方法 :

       1.概述 :

       2.应用 :

       3.特点 :

三、抽象类特点 :

       1.关于abstract关键字 :

       2.抽象类不能被实例化,只能创建其子类对象 :

       3.抽象类子类的两个选择 :

四、抽象类的成员 :

       1.成员变量 :

       2.成员方法 :

       3.构造器 :

       4.总结 :

       5.代码演示 :

五、抽象类课堂练习 :

       1.要求 :

       2.思路 :

       3.代码 :

六、总结 :


image.png

一、抽象类概述:

       我们知道,类用来模拟现实事物。一个类可以模拟一类事物,而某个类的一个实例化对象可以模拟某个属于该类的具体的事物。类中描绘了该类所有对象共同的特性,当一个类中给出的信息足够全面时,我们就可以实例化该类;比方说,在Dog类中定义了name,age,fur_color,sex等属性,以及habit,eat等行为时,我们就可以创建一个Dog类对象,来模拟某个具体的Dog,比如你家的宠物狗,或者是神犬小七等。但是,当一个类中给出的信息不够全面时,(比方说有无法确定的行为),它给出的信息不足以描绘出一个具体的对象,这时我们往往不会实例化该类,这种类就是抽象类打个比方,对于Animal类,是,所有的动物都有吃喝的行为,定义eat方法可以描述动物“吃”这一行为,但是每种动物吃的都不一样,因此一个eat方法并不能准确描述吃什么,怎么吃。这时Animal给出的信息就不足够描述一个对象,我们就不能去实例化Animal类。

       在Java中,我们通过在类前添加关键字abstract(抽象的)来定义抽象类。如下图所示 :

publicabstractclassAnimal {
//Animal类此时就是一个抽象类。}
classDogextendsAnimal {
//Dog类继承了Animal类,是Animal类的子类。}

image.gif

二、抽象方法 :

       1.概述 :

               我们将“只有方法声明,没有方法体”的一类方法统称为抽象方法抽象方法用关键字abstract修饰。需要注意的是,如果一个方法已经确定是抽象类,那么它绝不能再有方法体,即不能出现大括号,只需要在后面添加一个分号即可,否则IDEA会提示报错信息,如下图所示 :

image.png

               还要注意一点,如果某个类中已经出现了抽象方法,那这个类必须定义成抽象类,否则会报错,如下GIF动图演示——我们删掉Animal类前的abstract修饰符,IDEA立马就会给出提示信息,如下 :

image.png

               也就是说,拥有抽象方法的类一定是抽象类,但是抽象类不一定有抽象方法。

       2.应用 :

               当父类需要定义一个方法,却不能明确该方法的具体实现细节时,可以将方法定义为abstract,具体实现细节延迟到子类。(让子类重写这个方法)

               就比如我们刚才说的,Animal类中的eat() 方法——我们可以将其先定义为抽象类,然后在子类,比如说Dog类中重写eat() 方法,给出对Dog对象“吃”这一行为的一些描述。如下 :

               up以Animal类,Dog类和Test类为例代码如下 :

packageknowledge.polymorphism.about_abstract.introduction;
publicabstractclassAnimal {   /** 父类 *///将Animal类中的eat() 方法定义为抽象类,具体实现延迟到子类。publicabstractvoideat();
}
classDogextendsAnimal {      /** 子类 *///子类重写父类的抽象方法,也称为子类实现了该抽象方法。publicvoideat() {
System.out.println("狗是杂食性动物,喜食肉类,喂养时应该以动物蛋白为主,素食为辅。");
    }
}
classTest {
publicstaticvoidmain(String[] args) {
Dogdog=newDog();
dog.eat();
    }
}

image.gif

                运行结果 :

image.png

       3.特点 :

               ①若父类中定义了一个抽象方法要求其所有非抽象子类都必须重写该抽象方法。否则IDEA会报错如下图所示 :

image.png

image.png

               ②前面我们说了,抽象方法用abstract关键字修饰。这里再补充一点——抽象方法不能再使用private,final 或者static关键字来修饰,即abstract不能与private,final和static共同出现,这是因为定义抽象方法的目的就是想将方法的具体实现延迟到子类,最终是要被子类重写的,而private,final,static这几个关键字都和“方法重写”的目的背道而驰

               如果你固执己见,非要让abstract和这几个关键字一同出现,IDEA也是毫不客气,直接报错,如下图所示 :

image.png

三、抽象类特点 :

       1.关于abstract关键字 :

               abstract关键字只能用于修饰类和方法,用于声明抽象类和抽象方法。抽象类必须使用abstract关键字修饰。声明时格式如下 :

       访问权限修饰符  abstract  class  类名{ // }

       访问权限修饰符  abstract  返回值类型  方法名(形参列表);

               举个例子,如下 :

//抽象类publicclassAnimal {
//抽象方法publicvoideat();        //抽象方法最后加一个分号即可,不可以有大括号。}

image.gif

       2.抽象类不能被实例化,只能创建其子类对象 :

               即,我们不能创建抽象类对象(这里的对象指的是堆空间中真正的对象,即不能“new 抽象类”),原因我们在开篇抽象类的概述中也提到了,这里不再赘述。如果你头铁,非要创建抽象类对象,IDEA也不是吃素的,直接报错,如下图所示 :

image.png

                当然,如果抽象类的子类没有用abstract关键字修饰,那么我们可以创建其子类对象,如下图所示 :

image.png

       3.抽象类子类的两个选择 :

               如果某个类继承了一个抽象类,那么这个类有两个选择——要么实现父类所有的抽象方法,要么子类本身也定义成抽象类。当然,肯定也不会是瞎jb想定义成抽象类就定义成抽象类😂,要满足我们我们上面所说的定义抽象类的条件——类中提供的信息不足以描述一个对象,或者类中有无法确定的行为需要延迟到子类实现。

               还是给大家举个栗子。up现在在character包下创建一个Food类,将Food类定义为抽象类,并定义showNutrition()抽象方法,该方法将来要打印出食物的主要营养,具体实现延迟到子类;然后分别创建子类Meat类和Fruit类去继承Food类,我们在Meat类中重写Food类的showNutrition() 方法,使其打印出肉类的主要营养价值;同时,另一个子类Fruit类不去实现showNutrition() 方法,而是将其定义为抽象类。最后,以Test类为测试类,在测试类中创建子类对象并调用showNutrition() 方法。

              Food类,Meat类,Fruit类,Test类代码如下 :

packageknowledge.polymorphism.about_abstract.character;
publicabstractclassFood {            /** 父类 : Food类 *///记住,抽象方法没有方法体publicabstractvoidshowNutrition();
}
classMeatextendsFood {               /** 子类 : Meat类 */@OverridepublicvoidshowNutrition() {
System.out.println("肉类是蛋白质、脂肪、维生素B2、维生素B1、烟酸和铁的重要来源。");
    }
}
abstractclassFruitextendsFood {     /** 子类 : Fruit类 */}
classTest {                            /** 测试类 : Test类 */publicstaticvoidmain(String[] args) {
Meatmeat=newMeat();
meat.showNutrition();
    }
}

image.gif

                运行结果 :

image.png

                我们也可以再定义一个CiLi类表示水果中的刺梨,然后让刺梨类去继承Fruit类,并在CiLi类中去实现showNutrition() 方法Fruit类不变,Test类和CiLi类代码如下 :

classCiLiextendsFruit {
@OverridepublicvoidshowNutrition() {
System.out.println("刺梨是当之无愧的水果界的VC之王,VC含量高达2585mg/100g!");
    }
}
classTest {
publicstaticvoidmain(String[] args) {/** 测试类 : Test类 */Meatmeat=newMeat();
meat.showNutrition();
System.out.println("----------------------------------------");
CiLiciLi=newCiLi();
ciLi.showNutrition();
    }
}

image.gif

               运行结果 :

image.png

四、抽象类的成员 :

       1.成员变量 :

       抽象类既可以有静态的成员变量,也可以有非静态的成员变量

       既可以有静态的成员常量,也可以有非静态的成员常量

       2.成员方法 :

       抽象类既可以有(非私有的)抽象方法(注意一定是非私有非静态,因为abstract关键字与private关键字,final关键字,static关键字不能同时存在);

       也可以有非抽象方法(非抽象方法就可以用private,final和static关键字来修饰了,具体使用时,根据实际需求合理应用)。

       3.构造器 :

       抽象类可以和非抽象类一样拥有构造器,并且支持构造器的重载。

       4.总结 :

       其实吧,说上面一大堆都是废话😂。

       抽象类中的成员只比非抽象类多一种——抽象方法。其他都和非抽象类一样。

       大家只要记住抽象方法怎么写,怎么用就彳亍了。😎

       5.代码演示 :

               up以Fruit类为演示类,代码如下 :

packageknowledge.polymorphism.about_abstract.about_members;
publicabstractclassFruit {        //Fruit类是抽象类//抽象类中可定义的成员://1.非静态变量和静态变量privateStringname="水果名儿是有长有短";
privatestaticStringsize="水果的大小是有大有小";
//2.非静态常量和静态常量publicfinalStringCOLOR="水果的颜色是五光十色";
publicstaticfinalStringFORM="水果的形态是千奇百怪";
//3.抽象方法和非抽象方法publicabstractvoidnutrition();
privatefinalstaticvoidsuitCrowds() {
System.out.println("人人都适合吃水果!");
    }
publicvoidinvokeSuitCrowds() {
Fruit.suitCrowds();
    }
//4.构造器可以重载publicFruit() {
System.out.println("Fruit's name = "+name);
    }
publicFruit(Stringname) {
this.name=name;
    }
}

image.gif

               这些成员都可以在抽象类中定义,只要语法正确,IDEA是不会报错的,当然,具体怎么使用这些成员就看你自己了,根据实际情况来定。

五、抽象类课堂练习 :

       1.要求 :

image.png

               如上图所示 :

               已知西风骑士团的三位骑士分别是琴,可莉和优菈,请分别定义类来描述它们,要求给出每一个西风骑士的姓名,年龄和性别;并且,这三人均可以使用元素之力,分别是元素战技和元素爆发,但每位骑士的战技和爆发都不一样。其中,琴使用风元素,元素战技为风压箭,元素爆发为蒲公英之风;可莉使用火元素,元素战技为蹦蹦炸弹,元素爆发为轰轰火花;优菈使用冰元素,元素战技为冰朝的涡旋,元素爆发为凝浪之光剑。请在这些类中正确选择一个类定义成抽象类,并在该类中定义抽象方法elemental_kill() 和 elemental_burst(),要求这两个抽象方法将来在实现时,需要在控制台打印出当前骑士的元素战技和元素爆发;并在该抽象类中定义非抽象方法,要求该方法可以在控制台打印出当前骑士的基本信息。

       2.思路 :

               阅读提干后我们得知,总共有三个角色,这三个角色均属于名为“西风骑士团”的一个组织。因此,我们可以分别定义四个类来分别描述“西风骑士团”,“琴”,“可莉”,“优菈”,再加上测试类,因此我们总共需要定义五个类

               “西风骑士团”可以代表一类人,由于每位骑士的元素战技和元素爆发均不相同,这个类并不能提供足够的信息来描述一个具体的“骑士”对象。所以,我们可以定义Knights类来表示“西风骑士团”,并将其定义为抽象类又因为琴,可莉,优菈均属于西风骑士团的一员,因此我们可以分别定义Qin类,Keli类以及Youla类来描述这三位骑士,并让Qin类,Keli类和Youla类继承Knights类

               题干要求定义两个抽象方法elemental_kill() 和 elemental_burst()来分别打印出当前骑士的元素战技和元素爆发。既然我们已经确定Knights类为抽象类,这就没啥好说了,在Knights类中定义这两个方法即可。

              又因为题干还要求我们在抽象类中定义方法打印出当前骑士的基本信息,因此,我们可以在Knights类定义name,age,sex这些属性;根据JavaBean标准,我们需要将这些属性全部设为私有,并给出公共的访问这些属性的方法,然后给出Knights类的无参构造和带参构造(注意,Knights是抽象类,无法被实例化,因此我们给出Knights构造器的目的不是为了创建Knights类对象,而是为了在子类的带参构造中使用super语句调用父类构造器;接着,再定义一个printInfo方法,用于打印出当前骑士的姓名,年龄和性别

               最后,我们可以定义测试类Test类,并分别创建Qin类,Keli类和Youla类对象,调用elemental_kill()方法,elemental_burst() 方法,以及printInfo方法

       3.代码 :

packageknowledge.polymorphism.about_abstract.exercise;
publicabstractclassKnights {             /** 骑士类 */privateStringname;
privateintage;
privateStringsex;
publicKnights() {
    }
publicKnights(Stringname, intage, Stringsex) {
this.name=name;
this.age=age;
this.sex=sex;
    }
publicStringgetName() {
returnname;
    }
publicvoidsetName(Stringname) {
this.name=name;
    }
publicintgetAge() {
returnage;
    }
publicvoidsetAge(intage) {
this.age=age;
    }
publicStringgetSex() {
returnsex;
    }
publicvoidsetSex(Stringsex) {
this.sex=sex;
    }
publicabstractvoidelemental_skill();
publicabstractvoidelemental_burst();
publicvoidprintInfo() {
System.out.println("西风骑士——"+getName() +","+getAge() +"岁,性别"+getSex());
    }
}
classQinextendsKnights{                  /** 琴类 */publicQin(Stringname, intage, Stringsex) {
super(name, age, sex);
    }
@Overridepublicvoidelemental_skill() {
System.out.println("琴的元素战技是风压箭。");
    }
@Overridepublicvoidelemental_burst() {
System.out.println("琴的元素战技是蒲公英之风。");
    }
}
classKeliextendsKnights{                 /** 可莉类 */publicKeli(Stringname, intage, Stringsex) {
super(name, age, sex);
    }
@Overridepublicvoidelemental_skill() {
System.out.println("可莉的元素战技是蹦蹦炸弹。");
    }
@Overridepublicvoidelemental_burst() {
System.out.println("可莉的元素战技是轰轰火花。");
    }
}
classYoulaextendsKnights{                /** 优菈类 */publicYoula(Stringname, intage, Stringsex) {
super(name, age, sex);
    }
@Overridepublicvoidelemental_skill() {
System.out.println("优菈的元素战技是冰朝的涡旋。");
    }
@Overridepublicvoidelemental_burst() {
System.out.println("优菈的元素战技是凝浪之光剑。");
    }
}
classTest {                                /** 测试类 */publicstaticvoidmain(String[] args) {
Qinqin=newQin("琴", 22, "female");
qin.elemental_skill();
qin.elemental_burst();
qin.printInfo();
System.out.println("-------------------------------------------");
Kelikeli=newKeli("可莉", 500, "female");
keli.elemental_skill();
keli.elemental_burst();
keli.printInfo();
System.out.println("-------------------------------------------");
Youlayoula=newYoula("优菈", 21, "female");
youla.elemental_skill();
youla.elemental_burst();
youla.printInfo();
    }
}

image.gif

六、总结 :

       🆗,以上就是本节抽象类相关的全部内容了,大家一定要牢记抽象类和抽象方法的特点,牢记抽象类和抽象方法之间的关系,掌握abstract关键字的使用。下一节内容是多态章的final关键字,我们不见不散😆。感谢阅读!

目录
相关文章
|
3月前
|
存储 Java 开发者
抽象类和接口,你不知道的秘密!Java编程新视角
抽象类和接口,你不知道的秘密!Java编程新视角
38 5
|
3月前
|
设计模式 Java 开发者
抽象类和接口如何改变你的编程方式?Java界的革命
抽象类和接口如何改变你的编程方式?Java界的革命
48 3
|
20天前
|
Java
Java基础(13)抽象类、接口
本文介绍了Java面向对象编程中的抽象类和接口两个核心概念。抽象类不能被实例化,通常用于定义子类的通用方法和属性;接口则是完全抽象的类,允许声明一组方法但不实现它们。文章通过代码示例详细解析了抽象类和接口的定义及实现,并讨论了它们的区别和使用场景。
|
22天前
|
Java 测试技术 开发者
Java零基础-抽象类详解
【10月更文挑战第17天】Java零基础教学篇,手把手实践教学!
17 2
|
24天前
|
Java 测试技术 开发者
Java零基础-抽象类详解
【10月更文挑战第15天】Java零基础教学篇,手把手实践教学!
21 2
|
2月前
|
Java
java基础(12)抽象类以及抽象方法abstract以及ArrayList对象使用
本文介绍了Java中抽象类和抽象方法的使用,以及ArrayList的基本操作,包括添加、获取、删除元素和判断列表是否为空。
27 2
java基础(12)抽象类以及抽象方法abstract以及ArrayList对象使用
|
1月前
|
安全 Java 数据安全/隐私保护
【一步一步了解Java系列】:探索抽象类与接口的秘密
【一步一步了解Java系列】:探索抽象类与接口的秘密
25 3
|
2月前
|
Java
Java——抽象类和接口
抽象类是一种不能被实例化的类,至少包含一个抽象方法(无实现体的方法),常用于定义一组相关类的共同特征,并强制子类实现特定方法。抽象方法不能被 `static` 或 `final` 修饰,且必须被重写。 接口则是一个完全抽象的类,用于规范类的行为。接口使用 `interface` 关键字定义,不能实例化,并且类与接口之间是实现关系。 内部类是在一个类内定义的类,分为成员内部类、静态内部类、局部内部类和匿名内部类。成员内部类可被修饰符修饰,静态内部类只能访问外部类的静态成员,局部内部类定义在方法内,匿名内部类则隐藏了名字,直接通过 `new` 关键字定义并实现接口或继承类。
20 5
Java——抽象类和接口
|
1月前
|
Java
Java中抽象类和接口有什么区别?
本文阐述了Java中抽象类和接口的区别,包括类型扩展、方法/属性访问控制符、方法实现、使用目的等方面的不同,并提供了抽象类和接口的使用示例,以及Java中其他类型的类(普通类、内部类等)的简介。
98 0
Java中抽象类和接口有什么区别?
|
2月前
|
Java
Java 抽象类详解
在 Java 中,抽象类是一种特殊类,用于提供基础定义并派生具体子类,支持代码复用。它包含抽象方法(无实现)和具体方法(有实现),不能被实例化,需用 `abstract` 关键字定义。子类必须实现所有抽象方法,除非子类也是抽象类。抽象类可用于定义模板或框架,确保子类实现特定方法。通过示例展示了如何定义抽象类 `Animal` 及其子类 `Dog` 和 `Cat`,并在主类中调用相关方法。使用抽象类可以提高代码复用性和设计质量,但也可能增加维护难度和性能开销。