一、抽象类概述:
我们知道,类用来模拟现实事物。一个类可以模拟一类事物,而某个类的一个实例化对象可以模拟某个属于该类的具体的事物。类中描绘了该类所有对象共同的特性,当一个类中给出的信息足够全面时,我们就可以实例化该类;比方说,在Dog类中定义了name,age,fur_color,sex等属性,以及habit,eat等行为时,我们就可以创建一个Dog类对象,来模拟某个具体的Dog,比如你家的宠物狗,或者是神犬小七等。
但是,当一个类中给出的信息不够全面时,(比方说有无法确定的行为),它给出的信息不足以描绘出一个具体的对象,这时我们往往不会实例化该类,这种类就是抽象类。打个比方,对于Animal类,是,所有的动物都有吃喝的行为,定义eat方法可以描述动物“吃”这一行为,但是每种动物吃的都不一样,因此一个eat方法并不能准确描述吃什么,怎么吃。这时Animal给出的信息就不足够描述一个对象,我们就不能去实例化Animal类。
在Java中,我们通过使用abstract关键字来修饰该方法,这个方法就时抽象方法,在类前添加关键字abstract(抽象的)来定义抽象类。如下图所示 :
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
//实现这一步,没有意义,即出现了父类方法不确定的问题,
// 此时就考虑将该方法 设计为 抽象(abstract)方法
//所谓抽象方法 就是指 没有实现的方法(没有方法体)
//当一个类中存在抽象方法时,需要将该类 声明为 abstract类
//一般来说,抽象类会被继承,由其子类来实现抽象方法
public abstract void eat();
}
二、抽象方法 :
1.概述 :
我们将“只有方法声明,没有方法体”的一类方法统称为抽象方法,抽象方法用关键字abstract修饰。需要注意的是,如果一个方法已经确定是抽象类,那么它绝不能再有方法体,即不能出现大括号,而是只需要在()后面添加一个分号即可,否则IDEA会提示报错信息
还要注意一点,如果某个类中已经出现了抽象方法,那这个类必须定义成抽象类, 也就是说,拥有抽象方法的类一定是抽象类;但是抽象类不一定有抽象方法。
2.应用 :
当父类需要定义一个方法,却不能明确该方法的具体实现细节时,可以将该方法定义为abstract,具体实现细节延迟到子类。(让子类重写这个方法)
就比如我们刚才说的——Animal类中的eat() 方法,我们可以先将其定义为抽象类,然后在子类中,比如说Dog类中重写eat() 方法,给出对Dog类对象“吃”这一行为的一些具体描述。
up以Animal类,Dog类和Test类为例,代码如下 :
package knowledge.polymorphism.about_abstract.introduction;
public abstract class Animal { /** 父类 */
//将Animal类中的eat() 方法定义为抽象类,具体实现延迟到子类。
public abstract void eat();
}
class Dog extends Animal { /** 子类 */
//子类重写父类的抽象方法,也称为子类实现了该抽象方法。
public void eat() {
System.out.println("狗是杂食性动物,喜食肉类,喂养时应该以动物蛋白为主,素食为辅。");
}
}
class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
}
}
3.特点 :
①若父类中定义了一个抽象方法,要求其所有非抽象子类都必须重写该抽象方法。否则IDEA会报错,如下图所示 :
②前面我们说了,抽象方法用abstract关键字修饰。这里再补充一点——抽象方法不能再使用private,final 或者static关键字来修饰,即abstract不能与private,final或static共同出现,这是因为定义抽象方法的目的就是想将方法的具体实现延迟到子类,最终是要被子类重写的,而private,final,static这几个关键字都和“方法重写”的目的背道而驰。
如果你固执己见,非要让abstract和这几个关键字一同出现,IDEA也是毫不客气,直接报错,如下图所示 :
三、抽象类特点 :
1.关于abstract关键字 :
abstract关键字只能用于修饰类和方法,用于声明抽象类和抽象方法。其中,抽象类必须使用abstract关键字修饰。声明时格式如下 :
访问权限修饰符 abstract class 类名{ // }
访问权限修饰符 abstract 返回值类型 方法名(形参列表);
//抽象类
public abstract class Animal {
//抽象方法
public abstract void eat(); //抽象方法最后加一个分号即可,不可以有大括号。
}
2.抽象类不能被实例化,只能创建其子类对象 :
即,我们不能创建抽象类对象(这里的对象指的是堆空间中真正的对象,即不能“new 抽象类”),原因我们在开篇抽象类的概述中也提到了,这里不再赘述。如果你头铁,非要创 建抽象类对象,IDEA也不是吃素的,直接报错,如下图所示 :
当然,如果抽象类的子类没有用abstract关键字修饰,那么我们可以创建其子类对象,如下图所示 :
3.抽象类子类的两个选择 :
如果某个类继承了一个抽象类,那么这个类有两个选择——要么实现父类所有的抽象方法,要么子类本身也定义成抽象类。
当然,肯定也不会是瞎jb想定义成抽象类就定义成抽象类的😂,要满足我们我们上面所说的定义抽象类的条件——类中提供的信息不足以描述一个对象,或者类中有无法确 定的行为需要延迟到子类实现。
还是给大家举个栗子。up现在在character包下创建一个Food类,将Food类定义为抽象类,并定义showNutrition()抽象方法,该方法将来要打印出食物的主要营养,具体实 现延迟到子类;然后分别创建子类Meat类和Fruit类去继承Food类,我们在Meat类中重写Food类的showNutrition() 方法,使其打印出肉类的主要营养价值;同时,另一个子 类Fruit类不去实现showNutrition() 方法,而是将其定义为抽象类。最后,以Test类为测试类,在测试类中创建子类对象并调用showNutrition() 方法。
package knowledge.polymorphism.about_abstract.character;
public abstract class Food { /** 父类 : Food类 */
//记住,抽象方法没有方法体
public abstract void showNutrition();
}
class Meat extends Food { /** 子类 : Meat类 */
@Override
public void showNutrition() {
System.out.println("肉类是蛋白质、脂肪、维生素B2、维生素B1、烟酸和铁的重要来源。");
}
}
abstract class Fruit extends Food { /** 子类 : Fruit类 */
}
class Test { /** 测试类 : Test类 */
public static void main(String[] args) {
Meat meat = new Meat();
meat.showNutrition();
}
}
我们也可以再定义一个CiLi类表示水果中的刺梨,然后让刺梨类去继承Fruit类,并在CiLi类中去实现showNutrition() 方法;Fruit类不变,Test类和CiLi类代码如下 :
class CiLi extends Fruit {
@Override
public void showNutrition() {
System.out.println("刺梨是当之无愧的水果界的VC之王,VC含量高达2585mg/100g!");
}
}
class Test {
public static void main(String[] args) {/** 测试类 : Test类 */
Meat meat = new Meat();
meat.showNutrition();
System.out.println("----------------------------------------");
CiLi ciLi = new CiLi();
ciLi.showNutrition();
}
}
四、抽象类的成员 :
抽象类本质也是类
1.成员变量 :
抽象类既可以有静态的成员变量,也可以有非静态的成员变量。
既可以有静态的成员常量,也可以有非静态的成员常量。
2.成员方法 :
抽象类既可以有(非私有的)抽象方法(注意抽象方法一定是非私有非静态非final,因为abstract关键字与private关键字,final关键字,static关键字不能同时存在);
也可以有非抽象方法(非抽象方法就可以用private,final和static关键字来修饰了,具体使用时,根据实际需求合理应用)。
3.构造器 :
抽象类可以和非抽象类一样拥有构造器,并且支持构造器的重载。
4.总结 :
其实吧,说上面一大堆都是废话😂。
抽象类中的成员只比非抽象类多一种——抽象方法。其他都和非抽象类一样。
大家只要记住抽象方法怎么写,怎么用就彳亍了。😎
五、练习
package com.lmdedu.abstract_;
public class AbstractExercise {
public static void main(String[] args) {
CommonEmployee jjf = new CommonEmployee("jjf", "118", 5000);
jjf.work();
Manager lmd = new Manager("lmd", "001", 50000, 50000);
lmd.work();
}
}
//将父类 Employee定义为抽象类
abstract class Employee {//父类
private String name;
private String id;
private double salary;
public Employee(String name, String id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
//抽象方法work
public abstract void work() ;
}
class CommonEmployee extends Employee{//子类
public CommonEmployee(String name, String id, double salary) {
super(name, id, salary);
}
//在子类中重写抽象类work
@Override
public void work() {
System.out.println("普通员工" + getName() +"工作中");
}
}
class Manager extends Employee{//子类
private double bonus;
public Manager(String name, String id, double salary, double bonus) {
super(name, id, salary);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
//在子类中重写抽象类work
@Override
public void work() {
System.out.println("经理" + getName() + "工作中");
}
}