1.抽象类的定义与使用
定义:抽象类只是在普通类的基础上扩充了一些抽象方法而已。
抽象方法:指的是只声明而未实现的方法(没有方法体)
所有抽象方法要求使用abstract来定义,并且抽象方法所在的类也一定要使用abstract来定义,表示抽象类。
例:定义一个抽象类
abstract class Person{
//属性
private String name;
//普通方法
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
//抽象方法,所有抽象方法上不包含方法体
public abstract void getPersonInfo();
}
通过以上代码发现,其实抽象类就是比普通类多了一些抽象方法而已。
2.抽象类的使用原则
a.所有的抽象类必须有子类,因此abstract与final不能共同使用(用abstract修饰的类必须有子类,用final修饰的不能被继承,即不能有子类)
b.抽象类的子类必须覆写抽象类的所有抽象方法(前提是子类不是一个抽象类,是一个普通类),因此abstract不能与private同时使用(被private封装后,子类看不到方法,所以不会被覆写)
c.抽象类要想创建实例化对象,必须通过子类向上转型为其实例化(抽象类无法创建实例化对象)
abstract class Person{
//属性
private String name;
//普通方法
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
//抽象方法,所有抽象方法上不包含方法体
public abstract void getPersonInfo();
}
class Student extends Person{
@Override
public void getPersonInfo() {
System.out.println("子类继承抽象类");
}
}
public class Test2{
public static void main(String[] args){
Person per = new Student();
per.getPersonInfo();
}
}
3.抽象类的相关规定
3.1抽象类也允许提供构造方法,并且子类也照样遵循对象实例化流程,先调用父类构造方法而后调用子类构造方法
abstract class Person{
//属性
private String name;
public Person(){
System.out.println("******");
}
//普通方法
public String getName(){
return this.name;
}
public void setName(String name){
this.name = name;
}
//抽象方法,所有抽象方法上不包含方法体
public abstract void getPersonInfo();
}
class Student extends Person{
public Student(){
System.out.println("######");
}
@Override
public void getPersonInfo() {
System.out.println("子类继承抽象类");
}
}
public class Test2{
public static void main(String[] args){
new Student();
}
}
3.2抽象类中允许不定义任何抽象方法,但是此时抽象类依然无法创建实例化对象
abstract class Person{
public Person(){
System.out.println("******");
}
}
class Student extends Person{
public Student(){
System.out.println("######");
}
}
public class Test2{
public static void main(String[] args){
new Person();
}
}
3.3abstract与final不能一起使用,abstract与private也不能一起使用
3.4内部抽象类(子类继承抽象类,只需要关心父类的直接抽象方法,不管内部类的抽象方法)
abstract class Person{
public abstract void fun();
abstract class B{
public abstract void fun1();
}
}
class Student extends Person{
@Override
public void fun() {}
class C extends B{
@Override
public void fun1() {}
}
}
public class Test2{
public static void main(String[] args){
new Student();
}
}
注意:只有方法声明,没有方法体不一定就是抽象方法,还有可能是本地方法
4.模板设计模式-基于抽象类的设计模式,核心是封装算法
模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供具体实现。
模板模式的具体定义:在一个方法中定义一个算法的骨架,并将一些具体步骤延迟到子类中实现。模板模式使得子类可以在不改变算法结构的基础上,重新具体定义算法中的某些步骤。
例:咖啡与茶的冲泡方法
初级版本:
class Coffee{
//咖啡冲泡法
void prepareRecipe(){
boilWater();
brewCoffeeGrings();
pourInCup();
addSugarAndMilk();
}
//将水煮沸
public void boilWater(){
System.out.println("将水煮沸");
}
//冲泡咖啡
public void brewCoffeeGrings(){
System.out.println("冲泡咖啡");
}
//把咖啡倒进杯子中
public void pourInCup(){
System.out.println("把咖啡倒进杯子中");
}
//加糖和牛奶
public void addSugarAndMilk(){
System.out.println("加糖和牛奶");
}
}
class Tea{
void prepareRecipe(){
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
//将水煮沸
public void boilWater(){
System.out.println("将水煮沸");
}
//冲泡茶
public void steepTeaBag(){
System.out.println("冲泡茶");
}
//把茶倒进被子里
public void pourInCup(){
System.out.println("把茶倒进杯子中");
}
//加柠檬
public void addLemon(){
System.out.println("加柠檬");
}
}
public class test{
public static void main(String[] args){
Coffee coffee = new Coffee();
Tea tea = new Tea();
coffee.prepareRecipe();
tea.prepareRecipe();
}
}
但是通过以上的方法,可以发现在这两个类中发现了重复代码。既然茶和咖啡是如此的相似,就可以将共同的部分提取出来,放在一个基类中。
由于prepareRecipe()在每个类中都不一样,所以定义为抽象方法,boilWater()和pourInCup()方法被两个子类所共享,因此定义在超类中。每个子类都覆盖了prepareRecipe()方法并实现了自己的冲泡法。
重新设计后,使用模板模式方法如下:
//咖啡因饮料是一个抽象类
abstract class CaffeineBeverge{
//模板方法
//现在用一个prepareRecipe()方法处理茶和咖啡
//声明为final的原因是想让子类只能用这个方法,而不能覆盖这个方法
final void prepareRecipe(){
boilWater();
brew();
pourInCup();
addCondiments();
}
//咖啡喝茶处理这些方法不同,因此这两个方法必须被声明为抽象方法,留给子类实现
abstract void brew();
abstract void addCondiments();
public void boilWater(){
System.out.println("将水煮沸");
}
public void pourInCup(){
System.out.println("将饮料到进杯子中");
}
}
class Coffee extends CaffeineBeverge{
public void brew(){
System.out.println("冲泡咖啡");
}
public void addCondiments(){
System.out.println("加糖和牛奶");
}
}
class Tea extends CaffeineBeverge{
public void brew(){
System.out.println("冲泡茶");
}
public void addCondiments(){
System.out.println("加柠檬");
}
}
public class test{
public static void main(String[] args){
CaffeineBeverge coffee = new Coffee();
CaffeineBeverge tea = new Tea();
coffee.prepareRecipe();
tea.prepareRecipe();
}
}
此时的类图为:
模板方法带来的好处:
不好的茶或咖啡的实现 | 模板方法提供的咖啡因饮料 |
Coffee或Tea主导的一切,控制算法 | 由超类主导一切,它拥有算法,并且保护这个算法 |
Coffee与Tea之间存在重复代码 | 有超类的存在,因此可以将代码复用最大化 |
对于算法所做的代码改变,需要打开各个子类修改很多地方 | 算法只存在一个地方,容易修改 |
弹性差,新种类的饮料加入需要做很多工作 | 弹性高,新饮料的加入只需要实现自己的冲泡和加料方法即可 |
算法的知识和它的实现分散在许多类中 | 超类专注于算法本身,而由子类提供完整的实现 |
重点:在所有的设计模式中,共同的特点就是第三方解耦(提出公共部分)。
5.接口的定义与使用
接口优先原则:在一个操作既可以使用抽象类又可以使用接口的时候,优先考虑使用接口。
接口的定义:接口就是抽象方法与全局常量的集合(JDK8之前)。
接口使用interface关键字定义。
interface IMessage{
public static final String MSG = "10"; //全局常量
public abstract void print(); //抽象方法
}
子类实现接口使用implements关键字,并且可以同时实现多个父接口(可以使用接口来实现多继承)。
由于接口里面存在有抽象方法,所以接口对象不可能直接使用关键字new进行实例化的操作,但是可以通过子类对象向上转型进行实例化的操作。
interface IMessage{
public static final String MSG = "10"; //全局
public abstract void print(); //抽象方法
}
interface INews{
public abstract String getNews();
}
class MessageImpl implements IMessage,INews{
public void print(){
System.out.println(IMessage.MSG);
}
public String getNews(){
return IMessage.MSG; //访问常量都建议加上类名称
}
}
public class test{
public static void main(String[] args){
IMessage m = new MessageImpl(); //子类向上转型,为父接口实例化对象
m.print(); //调用被子类覆写过的方法
//父接口间的相互转换
I News n = (INews) m;
System.out.println(n.getNews());
}
}
向上转型为的是参数统一化,标准化。抽象类和接口中所有的抽象方法在子类中都被覆写,实现了多态的特性。
5.1接口的使用限制
5.1.1接口中只有public权限(不管是属性还是方法,其权限均为public,如果不写public也是public权限)
interface IMessage{
public static final String MSG = "10"; //全局
abstract void print(); //抽象方法
}
class MessageImpl implements IMessage{
void print(){ //默认为default权限
System.out.println(IMessage.MSG);
}
}
由于接口之中只是全局常量和抽象方法的集合,所以以下两种的定义格式效果都是一样的。
完整格式 | 简化格式 |
interface IMessage{ public static final String MSG = “10”; public abstract void print() } | interface IMessage{ String MSG = “10”; void print(); } |
接口中public static final,abstract均可以省略
阿里编码规约:接口中方法和属性不要任何修饰符,public也不要加,保证代码简洁性。
5.1.2当一个子类既需要实现接口又需要继承抽象类时,请先使用extends继承一个抽象类,而后使用implements实现多个接口。
父类和父接口之间也可以互相转化
interface IMessage{
void print(); //抽象方法
}
abstract class News{
//抽象类中方法前面的abstract不能省略,否则就是普通方法
public abstract void getNews();
}
class MessageImpl extends News implements IMessage{
public void print(){ //默认为default权限
System.out.println("I am a Student");
}
public void getNews(){
System.out.println("I am News");
}
}
public class test{
public static void main(String[] args){
IMessage m = new MessageImpl(); //子类向上转型,为父接口实例化对象
m.print(); //MessageImpl是抽象类和接口的共同子类
News news = (News) m;
news.getNews();
}
}
5.1.3抽象类可以使用implements实现接口,但是接口不能entends抽象类(抽象类中可以有普通方法,构造方法,抽象方法,但是接口中只有抽象方法和全局常量。)
抽象类可以不实现接口中的抽象方法。
interface IMessage{
void print(); //抽象方法
}
abstract class News implements IMessage{ //News为抽象类,可以不实现Imessage中的抽象方法
public abstract void getNews(); //抽象类中方法前面的abstract不能省略,否则就是普通方法
}
class MessageImpl extends News implements IMessage{
public void print(){
System.out.println("I am a Student");
}
public void getNews(){
System.out.println("I am News");
}
}
public class test{
public static void main(String[] args){
IMessage m = new MessageImpl(); //子类向上转型,为父接口实例化对象
m.print(); //MessageImpl是抽象类和接口的共同子类
News news = (News) m;
news.getNews();
}
}
5.1.4接口可以使用extends继承多个父接口(接口多继承)
interface A{
void printA();
}
interface B{
void printB();
}
interface C extends A,B{ //接口多继承
void printC();
}
public class test{
public static void main(String[] args){
}
}
5.2接口的应用
5.2.1定义操作标准
5.2.2表示一种能力或者进行一项操作
5.2.3在分布式开发中暴露远程服务方法
例:实现电脑上可以使用任何usb设备(U盘,打印机等等)
定义一个usb标准
interface USB{ public void plugIn(); //将USB设备插入电脑 public void setup(); //安装USB驱动 public void work(); //进行工作 }
定义电脑类
class Computer{ public void plugin(USB usb){ usb.plugIn(); usb.setup(); usb.work(); } }
定义USB子类
class UDisk implements USB{ @Override public void plugIn() { System.out.println("U盘插入电脑"); } @Override public void setup() { System.out.println("安装U盘驱动"); } @Override public void work() { System.out.println("U盘正常使用"); } } class PrintDisk implements USB{ @Override public void plugIn() { System.out.println("打印机插入电脑"); } @Override public void setup() { System.out.println("安装打印机驱动"); } @Override public void work() { System.out.println("打印机正常使用"); } }
测试类
public class Test2{ public static void main(String[] args){ Computer computer = new Computer(); computer.plugin(new UDisk()); computer.plugin(new PrintDisk()); } }