1.抽象类的定义与使用
定义:抽象类只是在普通类的基础上多扩充了一些抽象方法而已。
抽象方法:指的是只声明而未实现的方法,(没有方法体)
所有抽象方法要求使用abstract来定义,并且抽象方法所在的类也一定要使用abstract来定义,表示抽象类。
2.抽象类的使用原则
看一道例题:
abstract class A{
public A(){ //3.调用父类构造
this.print() ; //4.调用被子类覆写的方法
}
public abstract void print() ;
}
class B extends A{
private int num = 100 ;
public B(int num) { //2.调用子类实例化对象
super() ; //3.隐含一行语句,实际要先调用父类构造
this.num = num ; //7.为类中属性初始化,30
}
public void print() { //5.此时子类对象的属性还没有被初始化
System.out.println(this.num) ; //6.对应其数据类型的默认值,输出0
}
}
public class Test{
public static void main(String[] args) {
new B(30).print(); //1.实例化子类对象
}
}
调用过程:1234567
输出:
抽象类的使用原则:
a. 所有的抽象类必须有子类(abstract与final不能同时使用)
b. 抽象类的子类必须覆写抽象类的所有抽象方法(abstract与private也不能同时使用)
c. 抽象类要想创建实例化对象,必须通过子类向上转型为其实例化。(抽象类无法创建实例化对象)
abstract class Person {
private String name;
private String getName(){
return name;
}
public abstract void fun();
}
class Student extends Person{
public void fun(){
System.out.println("子类继承抽象类");
}
}
public class Test{
public static void main(String[] args){
Person per = new Student();
per.fun();
}
}
3.抽象类的相关规定
a. 抽象类也允许提供构造方法,并且子类也照样遵循对象实例化流程,先调用父类的调用方法而后调用子类构造方法
b. 抽象类中不允许定义任何的抽象方法,但抽象类依然无法直接创建实例化对象。
c. abstract与final不能一起用;abstract与private也不能一起用;
d. 内部抽象类(了解)
内部抽象类中也可以使用static定义来描述外部抽象类。如果现在外部抽象类中使用了static那么就是语法错误,但是内部抽象类允许使用static。
4.模板设计模式(封装算法)
-------基于抽象类,核心是封装算法。
看一个例子:
咖啡冲泡法
- 将水煮沸
- 用沸水冲泡咖啡
- 将咖啡倒进杯子
- 加糖和牛奶
茶冲泡法
- 将水煮沸
- 用沸水浸泡茶叶
- 把茶倒进杯子
- 加柠檬
class Coffee {
/*
咖啡冲泡法(算法)
*/
void prepareRecipe() {
boilWater();
brewCoffeeGrings();
pourInCup();
addSugarAndMilk();
}
/**
* 将水煮沸
*/
// 重复代码
public void boilWater() {
System.out.println("Boiling Water");
}
/**
* 冲泡咖啡
*/
public void brewCoffeeGrings() {
System.out.println("Dripping Coffee through filter");
}
/**
* 把咖啡倒进杯子中
*/
// 重复代码
public void pourInCup() {
System.out.println("Pouring into cup");
}
/**
* 加糖和牛奶
*/
public void addSugarAndMilk() {
System.out.println("Adding Sugar and Milk");
}
}
class Tea {
/*
茶类冲泡法(算法)
*/
void prepareRecipe() {
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
/**
* 将水煮沸
*/
public void boilWater() {
System.out.println("Boiling Water");
}
/**
* 冲泡茶
*/
public void steepTeaBag() {
System.out.println("Steeping the tea");
}
/**
* 把茶倒进杯子中
*/
public void pourInCup() {
System.out.println("Pouring into cup");
}
/**
* 加柠檬
*/
public void addLemon() {
System.out.println("Adding Lemon");
}
}
我们在这两个类中发现了重复代码,因此我们需要重新理一下我们的设计。既然茶和咖啡是如此的相似,因此我们应该将共同的部分抽取出来,放进一个基类中。
abstract class CaffeineBeverage {
/**
* 现在用同一个prepareRecipe()方法处理茶和咖啡。
* 声明为final的原因是我们不希望子类覆盖这个方法!
*/
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
/**
* 咖啡和茶处理这些方法不同,因此这两个方法必须被声明为抽象,留给子类实现
*/
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("Boiling water");
}
void pourInCup() {
System.out.println("Pouring into cup");
}
}
class Tea extends CaffeineBeverage {
void brew() {
System.out.println("Steeping the tea");
}
void addCondiments() {
System.out.println("Adding Lemon");
}
}
class Coffee extends CaffeineBeverage {
void brew() {
System.out.println("Dripping Coffee through filter");
}
void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
}
除了两个方法有共同点之外,咖啡和茶还有什么共同点呢?
观察咖啡和茶的冲泡法我们会发现,两种冲泡法都采用了相同的算法:
- 将水煮沸
- 用热水泡饮料
- 把饮料倒进杯子
- 在饮料内加入适当的调料
那么,我们可以将prepareRecipe()也抽象化。
/*
父类实现
*/
abstract class CaffeineBeverage {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
//询问顾客是否需要加料
if(customerWantsCondiments()) {
addCondiments();
}
}
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("Boilint water");
}
void pourInCup() {
System.out.println("Pouring into cup");
}
/**
* 钩子方法
* 超类中通常是默认实现
* 子类可以选择性的覆写此方法
* @return
*/
boolean customerWantsCondiments() {
return true;
}
}
/*
子类实现
*/
class Coffee extends CaffeineBeverage {
void brew() {
System.out.println("Dripping Coffee through filter");
}
void addCondiments() {
System.out.println("Add Sugar and Milk");
}
/**
* 子类覆写了钩子函数,实现自定义功能
* @return
*/
public boolean customerWantsCondiments() {
String answer = getUserInput();
if(answer.equals("y")) {
return true;
}else {
return false;
}
}
private String getUserInput() {
String answer = null;
System.out.println("您想要在咖啡中加入牛奶或糖吗 (y/n)?");
Scanner scanner = new Scanner(System.in);
answer = scanner.nextLine();
return answer;
}
}
class Tea extends CaffeineBeverage {
void brew() {
System.out.println("Steeping the tea");
}
void addCondiments() {
System.out.println("Adding Lemon");
}
}
测试类:
public class Test {
public static void main(String[] args) {
CaffeineBeverage tea = new Tea();
CaffeineBeverage coffee = new Coffee();
System.out.println("\nMaking tea...");
tea.prepareRecipe();
System.out.println("\nMaking Coffee");
coffee.prepareRecipe();
}
}
输出:
开闭原则(OCP):一个软件实体如类、函数、模块应该对扩展开放对修改关闭。
模板方法定义了一个算法的步骤,并且允许子类为一个或 多个步骤提供具体实现
模板(模板方法)模式:Servlet、AQS
在一个方法中定义一个方法的骨架,并将一些具体步骤延迟到子类中实现。
模板模块使得子类可以在不改变算法结构的基础上,重新具体定义算法中的某些步骤。
5.接口的定义与使用
接口优先原则:在一个操作既可以使用抽象类又可以使用接口的时候,优先考虑用接口。
接口的定义:
接口就是一个抽象方法和全局常量的集合(JDK8以前)
接口使用interface定义
接口的命名规范:接口前加 I 来区分接口与类。
子类实现接口使用implements关键字,并且子类可以同时实现多个父接口(使用接口实现多继承)一般使用接口+Impl来命名子类。
5.1接口的使用限制
5.1.1
接口中只有public权限(不管是属性还是方法。其权限均为public)
接口中public、static、final、abstract均可以省略
阿里编码规约:接口方法和属性不要加任何修饰符,public也不要加,保证代码简洁性。
5.1.2
当一个子类既需要实现接口又需要继承抽象类时,请先使用extend继承一个抽象类,而后使用implements实现多个接口
5.1.3
抽象类可以使用implements实现多个接口,单接口不能extends抽象类。
5.1.4
接口可以使用extends继承多个父接口。
5.2接口应用
a.定义操作标准(USB2.0标准)
b.表示一种能力,进行一项操作
c.在分布式开发中暴露远程服务方法
6.工厂模式
6.1简单工厂:用于没有产品族并且产品个数较少
专门定义一个类(第三方)来创建其他实例(解耦,将客户端创建对象的操作解耦到外部第三方类),被创建的实例通常都具有相同的父类。
(解耦总的一句话来说,减少依赖,抽象业务和逻辑,让各个功能实现独立。)
package practive.bit.Demo2;
import java.util.Scanner;
interface IBook {
void sellBook();
}
class Cartoon implements IBook {
public void sellBook() {
System.out.println("出售漫画书");
}
}
class Literature implements IBook {
public void sellBook() {
System.out.println("出售文学书");
}
}
class Magazine implements IBook {
public void sellBook() {
System.out.println("出售杂志");
}
}
// 第三方工厂类
class BookFactory {
public static IBook getInstance(String type) {
if(type.equals("漫画书")) {
return new Cartoon();
}
else if(type.equals("文学书")) {
return new Literature();
}
else if(type.equals("杂志")) {
return new Magazine();
}
return null;
}
}
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入你想要买的图书类型:");
String type = sc.nextLine();
IBook book = new BookFactory().getInstance(type);
book.sellBook();
}
}
组成:
a.一个抽象产品类
b.N个具体产品类
c.一个工厂类
6.2工厂方法模式(优点在于横向发展)–产品族
定义一个用来创建对象 的接口,让子类实现实例化哪一个工厂。
package practive.bit.Demo2;
import java.util.Scanner;
// 图书接口
interface IBook {
void sellBook();
}
// 漫画书
class Cartoon implements IBook {
public void sellBook() {
System.out.println("出售漫画书");
}
}
// 文学书
class Literature implements IBook {
public void sellBook() {
System.out.println("出售文学书");
}
}
// 杂志
class Magazine implements IBook {
public void sellBook() {
System.out.println("出售杂志");
}
}
// 图书工厂接口
interface IBookFactory {
IBook creatBook();
}
// 漫画书生产商
class CartoonFactory implements IBookFactory {
public IBook creatBook() {
return new Cartoon();
}
}
// 文学书生产商
class LiteratureFactory implements IBookFactory {
public IBook creatBook() {
return new Literature();
}
}
// 杂志生产商
class MagazineFactory implements IBookFactory {
public IBook creatBook() {
return new Magazine();
}
}
public class Test {
public static void main(String[] args) {
IBookFactory factory = new CartoonFactory();
IBook book = factory.creatBook();
book.sellBook();
}
}
组成:
a.一个抽象产品类
b.N个具体产品类
c.一个抽象工厂
d.对于具体工厂(每个产品族对应一个具体工厂)
7.代理模式–Spring-AOP
特点:两个子类共同实现一个接口,其中一个子类负责真是业务实现,另一个子类完成辅助真实业务主题的操作。