抽象类
面向对象编程的核心是面向抽象编程,一般依赖抽象不依赖具体
public class A{
public void pp(Pig pig){} //如果进行切换类型,则必须进行修改源代码
}
//-------------
public class A{
public void pp(Animal animal){} //这里可以任意更换Animal的子类
}
class Animal{}
class Pig extends Animal{}
class Cat extends Animal{}
public class 游戏 {
public void 玩() {
//定义父类时,有些方法必须声明,因为不做声明则无法正常调用 游戏 a=new 篮球();
//但是在游戏类中由于具体不够明确,所以没有办法具体定义
}
}
包含了抽象方法的类叫作“抽象类”
所谓的抽象方法是指没有函数体的方法,抽象方法必须在子类中给出具体实现,而抽象类本身不能创建对象
public abstract class 游戏 { //如果一个类中直接或者间接的包含抽象方法,则类必须为抽象类
public abstract void 玩();//定义抽象方法,注意【public abstract void 玩(){}】语法错误,因为
{}不是表示没有实现,而是有具体的实现,支持实现为空
}
抽象类的特点
- 方法只有声明没有实现时,该方法就是抽象方法【不是空实现方法体,没有{}】,需要被abstract修饰,否则语法报错。抽象方法必须定义在抽象类中,该类必须也被abstract修饰
public abstract class 游戏 { //如果一个类中直接或者间接的包含抽象方法,则类必须为抽象类
public abstract void 玩();//定义抽象方法,注意【public abstract void 玩(){}】语法错
误,因为{}不是表示没有实现,而是有具体的实现,支持实现为空
}
public void pp(){}不是抽象方法,只是方法的实现为空,有方法体
public void pp(); 没有{}才是没有方法体,才是抽象方法,当然需要添加关键字abstract
public class A {
public static void main(String[] args) {
// Fa f1=new Fa(); //抽象类不允许直接实例化操作,这是语意决定的,是规则,和抽象类
中是否包含抽象方法,是否有构造器无关
new Son(10).pp();
}
}
abstract class Fa { // 定义抽象类时可以包含抽象方法,也可以没有抽象
public Fa() { // 可以定义构造器
System.out.println("build Fa.");
}
// public abstract Fa(int k);没有抽象构造器的概念
public void pp() { // 抽象类中可以定义成员
System.out.println("Fa...pp()");
}
public abstract void cc(); //抽象方法
public static void dd() {}//在抽象类中可以定义静态成员,但是静态方法不能为抽象
}
class Son extends Fa { // 子类必须直接或者间接的给父类中的所有抽象方法提供实现,否则子
类必须抽象
public Son(int k) {
// super(); 要么使用this()或者super()作为第一句,也可以不写,不写则默认为
super()
System.out.println("Son" + k);
}
@Override
public void cc() {
}
}
- 不能定义抽象构造函数
抽象类中可以定义构造器,也可以不定义构造器,使用系统默认提供的无参构造器,但是自定义构造器不能private
抽象类不能是nal class,nal类不能被继承,从语法的角度上说不允许继承,不是构造器的原因。
- 不能定义抽象静态方法
抽象类中可以有静态方法,但是必须有方法体,不能是抽象方法
允许抽象类直接调用静态方法
- 抽象类不能直接创建对象,只能通过继承的方式由子类实现对应的抽象方法;
一般的使用方法为【动物 x=new 人();】
- 所有抽象类的子类必须实现抽象父类中的所有抽象方法或者自己也声明成抽象类[没有实现所有的抽象方法]
- 抽象类除了可以有抽象方法,也可以有非抽象方法,允许静态方法【没有抽象静态方法的概念】
没有任何限制,允许属性、方法,也允许抽象方法
抽象类不可以被实例化
是因为调用抽象方法没有意义?
抽象类必须有其子类覆盖了所有的抽象方法后,该子类才可以实例化,否则这个子类还是抽象类
强调:注意抽象类中可以包含构造器、析构器、抽象方法和方法以及静态方法等,也可以没有抽象方法
什么时候使用抽象类
- 当一个类的一个或多个方法为抽象方法时
- 当该类为一个抽象类的子类,并且没有为所有抽象方法提供实现细节或方法主体时
- 当一个类实现一个接口,并且没有为所有抽象方法提供实现细节或方法主体时
抽象类和普通类的区别
- 抽象类不能直接实例化,并且对抽象类使用 new 运算符是编译时错误
- 抽象类允许(但不要求)抽象类包含抽象成员
public abstract class A{
private int age; // 可以定义属性、常量
public void pp(){}//允许定义非抽象方法,允许定义静态方法,没有抽象构造器的概念
public abstract int pp(int k); //抽象方法,抽象类中可以定义抽象方法,也可以没有抽象方法
}
- 抽象类不能被密封
简单说就是被nal修饰的类,密封类不能被继承,防止了恶意的派生
模板模式
定义抽象类的目的是提供可由其子类共享的一般形式,子类可以根据自身需要扩展抽象类
什么是模板模式
在模板模式Template Pattern中一个抽象类公开定义了总体的算法【算法骨架】,把没有办法在父类中实现的方法延迟到子类中具体实现。这种类型的设计模式属于行为型模式
何时使用
有多个子类共有的方法,且逻辑相同
重要的、复杂的方法,可以考虑作为模板方法
注意事项
为防止恶意操作,一般模板方法都加上 fnal 关键词
优点
- 封装不变部分,扩展可变部分
- 提取公共代码,便于维护
- 行为由父类控制,子类实现。
缺点
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
分析
冒泡排序算法固定可以定义在父类中,但是两个比较的算法不同确定,所以可以将具体比较算法延迟到子类中实现。抽象父类中定义算法模板,具体的比较延迟到子类中进行实现
public abstract class BubbleSorter{
//在父类中固化算法实现,没有办法实现的比较方法延迟到子类中提供实现
public final void sort(Object[] arr) { // final表示这个方法是最终方法,不允许子类覆盖
for (int i = 1; i < arr.length; i++) {
for (int k = 0; k < arr.length - i; k++) {
if (bigger(arr[k], arr[k + 1])) {
Object temp = arr[k];
arr[k] = arr[k + 1];
arr[k + 1] = temp;
}
}
}
}
//定义protected目的在于子类中提供实现
protected abstract boolean bigger(Object obj1, Object obj2);
}
具体的比较器,抽象父类的目的在于固化算法实现fnal
//专门用于对int类型数据进行冒泡排序
public class IntSorter extends BubbleSorter {
@Override
protected boolean bigger(Object obj1, Object obj2) {
if (obj1 != null && obj2 != null) {
if (obj1 instanceof Integer && obj2 instanceof Integer) {
//不用简单写法
int k1 = (Integer) obj1;
int k2 = (Integer) obj2;
return k1 > k2;
}
}
return false;
}
}
测试调用
public class Text1 {
public static void main(String[] args) {
Object[] arr=new Integer[10];
Random r=new Random();
for(int i=0;i<arr.length;i++)
arr[i]=r.nextInt(100);
for(Object temp:arr)
System.out.print(temp+"\t");
System.out.println();
IntSorter is=new IntSorter();//是否可以使用BubbleSorter定义is类型? 可以。抽象类只是不允许直接new,但是仍旧可以用于声明变量类型。例如前面一直使用的Number就是抽象类
is.sort(arr);
for(Object temp:arr)
System.out.print(temp+"\t");
System.out.println();
}
}
定义猪的比较器 实现猪的排序
- 描述猪—定义猪这个类
public class Pig {
private String color;
private double weight;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
- 定义猪对应的排序器实现
public class PigSorter extends BubbleSorter {
@Override
protected boolean bigger(Object obj1, Object obj2) {
if (obj1 != null && obj2 != null) {
if (obj1 instanceof Pig && obj2 instanceof Pig) {
Pig p1 = (Pig) obj1;
Pig p2 = (Pig) obj2;
if (p1.getColor().equals(p2.getColor())) {
return p1.getWeight() - p2.getWeight() > 1e-6;
} else {
String col1 = p1.getColor();
String col2 = p2.getColor();
if ("黑色".equals(col1) && "白色".equals(col2))
return true;
}
}
}
return false;
}
}
3.定义测试程序
public class Test1 {
public static void main(String[] args) {
Pig[] arr=new Pig[10];
Random r=new Random();
for(int i=0;i<arr.length;i++) {
//第一种写法比较繁琐
// Pig tmp=new Pig();
// tmp.setColor(r.nextBoolean()?"黑色":"白色");
// tmp.setWeight(r.nextInt(200)+200);
Pig tmp=new Pig(r.nextBoolean()?"黑色":"白色",r.nextInt(200)+200);
arr[i]=tmp;
}
int counter=0;
for(Pig temp:arr) {
counter++;
System.out.print(temp+"\t");
if(counter%3==0)
System.out.println();
}
System.out.println("\n===============================");
BubbleSorter bs=new PigSorter();
bs.sort(arr);
counter=0;
for(Pig temp:arr) {
counter++;
System.out.print(temp+"\t");
if(counter%3==0)
System.out.println();
}
}
}
}
抽象类的作用
在面向对象方法中,抽象类主要用来进行类型隐藏。构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的
通过从这个抽象体派生,也可扩展此模块的行为功能。为了能够实现面向对象设计的一个最核心的原则开闭原则OCP,抽象类是其中的关键所在
抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具
体概念的抽象
OOP的核心是面向抽象编程
- 在父类中固化算法骨架,在子类中实现特殊的方法
- 依赖抽象
常见问题
- 抽象类中有构造器吗?
有,用于给子类对象进行初始化
new 子类时抽象父类的构造器一定会被执行
- 抽象类可以不定义抽象方法吗?
可以的,但是很少见
目的就是不让该类创建对象
- 抽象类一定是个父类吗?
是的
因为需要子类覆盖其方法后才可以对子类实例化
类和类之间的耦合问题
OOP要求类内高内聚、类间弱耦合—客户需求变动
如何实现类和类之间松耦合---使用抽象不用具体
public class A{
private BubbleSorter bs;//PigSorter
}
1、设计一个能细分为矩形、三角形、圆形和椭圆形的图形类。使用继承将这些图形分类,找出能作为基类部分的共同特征(如校准点)和方法(如画法、初始化) 并看看这些图形是否能进一步划分为子类。
2、创建一个Vehicle类并将它声明为抽象类。在Vehicle类中声明一个NoOfWheels方法使它返回一个字符串值。创建两个类Car和Motorbike从Vehicle类继承,并在这两个类中实现NoOfWheels方法。
在Car类中应当显示“四轮车”信息,而在Motorbike类中应当显示“双轮车”信息。创建另一个带main方法的类,在该类中创建Car和Motorbike的实例,并在控制台中显示消息