抽象类的定义与使用
定义: 抽象类只是在普通类的基础上扩充了一些抽象方法而已。
抽象方法:
- 只声明未定义的方法(没有方法体)
- 所有抽象方法要求使用abstract来定义,并且抽象方法所在类必须用abstract来定义,表示抽象类。
抽象类的使用原则:
- 所有抽象类必须有子类(abstract与final不能同时使用,是因为final声明的类不能有子类);
- 抽象类的子类必须覆写抽象类的所有抽象方法,前提是这个子类不在是一个抽象类,如果是子类是一个抽象类,可以不全覆写父类抽象方法(abstract与private不能同时使用);
- 如果抽象类需要实例化对象,必须通过子类向上转型为其实例化(抽象类无法创建实例化对象)
abstract class Person //抽象类
{
private String name;
abstract public void fun1(); //抽象方法
//abstract public void func2(); //两个抽象方法必须全覆写
}
class Student extends Person
{
public void fun1() //重写抽象方法
{
System.out.println("子类继承抽象类");
}
}
public class Abstract
{
public static void main(String[] args)
{
Person p=new Student(); //向上转型
p.fun1(); //调用的子类中被覆写的fun1();
}
}
抽象类的相关规定
1.抽象类允许提供构造方法,并且子类也遵循对象实例化流程,先调用父类构造方法而后调用子类构造方法。
对象实例化操作包括:
- 进行类加载
- 进行类对象的空间开辟
- 进行类对象的属性初始化(构造方法)
/////面试题
abstract class Person //抽象类
{
public Person() //3.调用父类构造
{
this.fun1(); //4.调用fun1,发现父类中fun1为抽象类,就调子类中覆写的fun1
}
abstract public void fun1(); //抽象方法
}
class Student extends Person
{
private int num=100; //7.调子类构造时初始化属性变为100
public Student(int num) //2.调用子类构造
{
this.num=num; //赋值后num变为30
}
public void fun1() //5.此时还没有调子类构造,属性是在调构造函数时初始化,所以此时还没有初始化属性
{
System.out.println(this.num); // 6.没有初始化,属性默认值为0
}
}
public class Abstract
{
public static void main(String[] args)
{
new Student(30);//1.实例化子类对象 结果为0
new Student(30).fun1(); //结果为0 30 ,因为先调了父类构造,所以有0
}
}
2.抽象类中允许不定义任何的抽象方法,但是此时抽象类依然无法直接创建实例化对象。
3.abstract不能与final一起使用:abstract定义类必须有子类,final定义类不能有子类;
abstract不能与private一起使用:abstract定义方法必须被重写,private定义方法在子类不可见。
内部抽象类
子类可以只覆写父类的直接抽象方法,不管父类内部类抽象方法;如果需要重写父类内部类抽象方法,可以在子类中定义一个父类内部抽象类的子类来重写:
abstract class Person
{
public abstract void fun1();
abstract class B
{
public abstract void fun2();
}
}
class Student extends Person
{
public void fun1() //对父类直接抽象方法覆写
{
}
class C extends B //重新定义一个子类
{
public void fun2()
{
}
}
}
模板设计算法
模板设计算法基于抽象类,核心是封装算法。模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供具体实现。
模板(模板方法)模式:
在一个方法中定义一个算法的骨架,并将一些具体步骤延迟到子类实现。模板模式使得子类可以在不改变算法结构的基础上,重新定义算法中的某些步骤。
如下列的咖啡因饮料:咖啡和茶都有相同的步骤:煮沸水、泡咖啡或者浸泡茶、向咖啡或茶添加东西、将饮料倒入杯中,这四个步骤咖啡和茶都有,即是相同的算法结构,那么就可以把这四个步骤定义在一个算法模板中,不同的步骤子类重写:
import java.util.Scanner;
abstract class CaffeineBerverage //父类是咖啡因饮料
{
//模板方法 声明为final:只允许子类使用,不允许覆写
final void prepareRecipe()
{
boilWater();
brew();
if(customerWantsCondiments())
{
addCondiments();
}
pourInCup();
}
public void boilWater() //烧水
{
System.out.println("将水煮沸");
}
public void pourInCup()
{
System.out.println("将饮料调入杯中");
}
////钩子方法:子类选择性覆盖
boolean customerWantsCondiments() //顾客是否想要添加东西,因为有的顾客不想添加东西
{
return true;
}
abstract public void brew();//处理饮料的方式
abstract public void addCondiments(); //向饮料里加东西
}
class Coffee extends CaffeineBerverage //咖啡
{
public void brew()
{
System.out.println("冲泡咖啡");
}
public void addCondiments()
{
System.out.println("加糖和奶");
}
boolean customerWantsCondiments() //重写
{
System.out.println("请问您想要加糖或奶吗? y/n");
String result=getUserInfo();
if(result.equals("y"))
{
return true;
}
else
{
return false;
}
}
private String getUserInfo()
{
Scanner scanner=new Scanner(System.in);
String str=scanner.nextLine();
return str;
}
}
class Tea extends CaffeineBerverage
{
public void brew()
{
System.out.println("浸泡茶叶");
}
public void addCondiments()
{
System.out.println("加柠檬");
}
}
public class Abstract
{
public static void main(String[] args)
{
CaffeineBerverage coffee=new Coffee();
coffee.prepareRecipe();
CaffeineBerverage tea=new Tea();
tea.prepareRecipe();
}
}