目录
前言
根据前面多态的学习我们了解到了父类引用可以指向子类实例,但是有时候父类的实例好像不是很明确,那么这时我们又该如何呢?
一、抽象的引入
例如:设计一个图形类用于计算面积和周长
//定义图形类 class Shape { public double length;//定义边长 //定义无参构造方法 public Shape() { } //定义有参构造方法 public Shape(double length) { this.length = length; } //定义计算面积方法 public void getArea() { } //定义计算周长方法 public void getPerimeter() { } } //定义正方形类继承图形类 class Square extends Shape { public Square() { } public Square(double length) { super(length); } @Override//重写计算面积方法 public void getArea() { System.out.println(length * length); } @Override//重写计算周长方法 public void getPerimeter() { System.out.println(length * 4); } } //定义圆类继承图形类 class Circle extends Shape { public Circle() { } public Circle(double length) { super(length); } @Override//重写计算面积方法 public void getArea() { System.out.println(Math.PI * length * length); } @Override//重写计算周长方法 public void getPerimeter() { System.out.println(2 * Math.PI * length); } }
我们发现,由于父类也就是图形类的实例无法准确定义,就导致他的面积方法和周长方法也无法确定,也就是父类的方法的方法体没有实际意义或者说父类的实例没有具体意义,这时我们就可以把父类设置为抽象类,把父类方法设置为抽象方法
二、抽象是什么
如同现实生活中的抽象一样,无法准确定义和举例的物体我们就称之为抽象的,所以在Java语言中也是一致的
抽象:是对无法确定具体意义的代码块的形容,就好比无法确定准确方法体的方法一般为抽象方法,而无法确定实例的类我们成为抽象类
三、语法规则
基本语法
抽象类:abstract 权限修饰符 class 类名 {
}
抽象方法:abstract 权限修饰符 返回值类型 方法名 ();
这样我们就可以对之前的图形类代码进行优化
//定义图形类 abstract class Shape { public double length;//定义边长 //定义无参构造方法 public Shape() { } //定义有参构造方法 public Shape(double length) { this.length = length; } //定义计算面积方法 abstract public void getArea(); //定义计算周长方法 abstract public void getPerimeter(); }
四、注意事项
1) 抽象类不能直接实例化.
2) 抽象方法不能是 private 的
3) 抽象类中可以包含其他的非抽象方法, 也可以包含字段. 这个非抽象方法和普通方法的规则都是一样的, 可以被重写,也可以被子类直接调用
五、抽象类的意义
抽象类存在的最大意义就是为了被继承.
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.
有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?
确实如此. 但是使用抽象类相当于多了一重编译器的校验.
使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了,
使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.
六、接口的引入
在之前的例子中我们说过Java语言是单继承的语言,那么我们要是出现了重复代码无法用一个抽象类来形容是又如何解决呢?比如之前的动物类中,鸟会飞,可是鸡也会飞,鱼会游,青蛙也会游,这时,我们发现很多共同点又无法归纳到一个类中,所以我们就引入了接口来归纳这些共性,接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量.
七、语法规则
拿刚才的图形类举例,图形类中没有其他非抽象方法,那么我们就可以把他设置为一个接口
//定义图形接口 interface Shape { //定义计算面积方法 abstract public void getArea(); //定义计算周长方法 void getPerimeter(); }
那么原来继承图形类的类也将改变写法,由原来的继承类改为实现接口
//定义正方形类实现图形接口 class Square implements Shape { public double length; public Square() { } public Square(double length) { this.length = length; } @Override//重写计算面积方法 public void getArea() { System.out.println(length * length); } @Override//重写计算周长方法 public void getPerimeter() { System.out.println(length * 4); } } //定义圆类实现图形接口 class Circle implements Shape { public double length; public Circle() { } public Circle(double length) { this.length = length; } @Override//重写计算面积方法 public void getArea() { System.out.println(Math.PI * length * length); } @Override//重写计算周长方法 public void getPerimeter() { System.out.println(2 * Math.PI * length); } }
八、注意事项
由于接口中只能包含抽象方法. 对于字段来说, 接口中只能包含静态常量(final static).
其中的 public, static, final 的关键字都可以省略. 省略后的 num 仍然表示 public 的静态常量.
九、接口的意义
由于Java是单继承语言,所以我们无法让一个类继承多个父类,所以为了解决这类问题,Java就出现了接口,接口是支持多实现的;
继承表达的含义是 is - a 语义, 而接口表达的含义是 具有 xxx 特性 .
这样设计有什么好处呢? 时刻牢记多态的好处, 让程序员忘记类型. 有了接口之后, 类的使用者就不必关注具体类型, 而只关注某个类是否具备某种能力.
十、总结
注意事项