# 抽象方法

# 抽象方法格式和作用

抽象方法场景:父类知道子类一定要完成某个功能,但是每个子类实现的情况都不一样,父类的该功能就可以定义成抽象方法。

抽象方法:没有方法体的方法

抽象方法的格式:

public abstract 返回值类型 方法名(参数列表);

注意:抽象方法必须放在抽象类中。

父类中抽象方法有必要存在吗?
从设计的角度看,父类不写抽象方法,子类也可以不写这个方法,导致功能缺失,也容易造成扯皮。比如下面的例子中,如果我们不在 Card 中声明抽象类 pay,GoldCard 和 SliverCard 可能就没实现 pay 方法,Demo1 中调用 pay 就有会有问题,这个时候写 Card 类的人就有可能背锅,因为你没有强制要求写 GoldCard 和 SliverCard 类的人实现 pay 方法:

public class Demo1 {
    public static void main(String[] args) {
        GoldCard goldCard = new GoldCard("金卡", 1000);
        goldCard.pay();

        SliverCard sliverCard = new SliverCard("银卡", 1000);
        sliverCard.pay();
    }
}
public abstract class Card {
    private String name;
    private double balance;

    public Card() {
    }

    public Card(String name, double balance) {
        this.name = name;
        this.balance = balance;
    }

    public abstract void pay();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}
// 定义类继承抽象类   alt + 回车: 万能键,IDEA会给我们各种提示
public class GoldCard extends Card{
    public GoldCard() {
    }

    public GoldCard(String name, double balance) {
        super(name, balance);
    }

    @Override
    public void pay() {
        System.out.println("金卡打八折");
    }
}
public class SliverCard extends Card {
    public SliverCard() {
    }

    public SliverCard(String name, double balance) {
        super(name, balance);
    }

    @Override
    public void pay() {
        System.out.println("银卡打八五折");
    }
}

# 抽象类

# 抽象类格式

public abstract class 类名 {}

抽象类的使用步骤:

  • 定义类继承抽象类
  • 重写抽象方法
  • 使用子类对象

抽象类设计目的:抽象类不能创建对象,让子类继承抽象类,重写抽象方法。

# 抽象类注意事项

  • 抽象类不能创建对象
public class Demo1 {
    public static void main(String[] args) {
//     'Card' is abstract; cannot be instantiated
        Card card = new Card();
    }
}
  • 抽象类中有构造方法, 让构造方法给成员变量赋值
public class GoldCard extends Card{
    public GoldCard() {
    }

    public GoldCard(String name, double balance) {
        super(name, balance);
    }

    @Override
    public void pay() {
        System.out.println("金卡打八折");
    }
}
  • 抽象方法必须放在抽象类中, 抽象类中可以没有抽象方法
// 抽象方法必须放在抽象类中
// Class 'Parent' must either be declared abstract or implement abstract method 'test()' in 'Parent'
public class Test1 {
//    Abstract method in non-abstract class
    public abstract void test();
}

// 抽象类中可以没有抽象方法
abstract class Test2 {}
  • 子类必须重写抽象类中的所有抽象方法.如果子类只重写一部分抽象方法,子类还是抽象类
abstract class Parent {
    public abstract void fn1();
    public abstract void fn2();
}

// Class 'Child' must either be declared abstract or implement abstract method 'fn1()' in 'Parent'
//class Child extends Parent {
//
//}

//Class 'Child' must either be declared abstract or implement abstract method 'fn2()' in 'Parent'
//class Child extends Parent{
//    @Override
//    public void fn1() {
//
//    }
//}

// 如果子类只重写一部分抽象方法,子类还是抽象类
abstract class Child extends Parent {}

class Child extends Parent {
    @Override
    public void fn1() {

    }

    @Override
    public void fn2() {

    }
}

# 模板方法模式

什么是设计模式(Design pattern)

  • 软件设计模式是一套被前人反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
  • 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

现在学习 Java 中常用的设计模式之一:模板方法模式

生活中的模板:
image.png
什么时候使用模板方法模式?当项目中多个子类有相同功能,而且大部分代码是一样的,只有部分不同的时候。

需求
某加油站推出了2种支付卡,一种是金卡,后续加油享受8折优惠。
另一种是银卡 ,后续加油享受8.5折优惠。
请分别实现2种卡片进入收银系统后的逻辑,卡片需要包含主人名称,余额,支付功能。
支付功能:1.输入用户名和密码、2.登录成功、3.扣款、4.打印消费额。
分析:
1. 创建一个抽象的卡片类Card作为父类模板
2. 在父类Card中提供一个模板方法实现支付要先输入用户名和密码、登录成功、扣款、打印消费额。
3. 具体的扣款定义成抽象方法,交给子类实现
4. 定义金卡(GoldCard)类,让子类重写实现具体的扣款方法
5. 定义银卡(SilverCard)类,让子类重写实现具体的扣款方法

直接设计完成该案例,存在什么问题?代码重复度过高。
使用模板方法模式设计将 2 种卡片通用的代码抽成一个父类模板。
image.png

public class Demo {
    public static void main(String[] args) {
        GoldCard goldCard = new GoldCard();
        goldCard.pay(500);

        System.out.println("--------------------");

        SilverCard silverCard = new SilverCard();
        silverCard.pay(400);
    }
}
public abstract class Card {
    private String name;
    private double balance;

    public Card() {
    }

    public Card(String name, double balance) {
        this.name = name;
        this.balance = balance;
    }

    public abstract double payment(double money);

    public void pay(double money) {
        System.out.println("请输入账号密码");
        System.out.println("登陆成功");
        double payment = payment(money);
        System.out.println("总共花费了" + payment + "元");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}
public class GoldCard extends Card {
    public GoldCard() {
    }

    public GoldCard(String name, double balance) {
        super(name, balance);
    }

    @Override
    public double payment(double money) {
        return money * 0.8;
    }
}
public class SilverCard extends Card {
    public SilverCard() {
    }

    public SilverCard(String name, double balance) {
        super(name, balance);
    }

    @Override
    public double payment(double money) {
        return money * 0.85;
    }

}

抽象类中调用自己的抽象方法,调用的是相应子类的实现的抽象方法。
总结:
模板设计模式思想总结:

  • 父类模板中定义确定的内容,不确定的内容定义成抽象方法,由具体子类完成。
  • 多个子类直接使用模板以此提高代码复用。

# 接口

# 接口的概念和格式

什么是接口?
接口可以理解为比抽象类还抽象的类,接口中全是抽象方法。接口体现的是规范,接口中的抽象方法定义的一组行为规范,体现了现实世界中“事物遵守某种规范就必须拥有规定的行为”的思想。

接口存在的两个重要意义:

  • 定义统一规范,约束一类事物的功能设计
  • 为后续软件设计,提高程序的扩展性(后续了解)

接口的使用:接口不能直接创建对象。接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类。

实现类的格式

class 实现类 implements 接口1, 接口2 {
}

接口实现的注意事项:一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类。

# 案例-电脑 USB 接口

笔记本电脑会提供USB接口,只要符合USB接口的设备都能连接电脑。定义USB鼠标和USB键盘。

实现步骤
1. 定义USB接口包含(连接电脑, 传输数据, 断开连接)
2. 定义USB鼠标实现USB接口
3. 定义USB键盘实现USB接口
public class Demo {
    public static void main(String[] args) {
        UsbKeyboard usbKeyboard = new UsbKeyboard();
        usbKeyboard.connect();
        usbKeyboard.sendData();
        usbKeyboard.disconnect();

        System.out.println("-----------");

        UsbMouse usbMouse = new UsbMouse();
        usbMouse.connect();
        usbMouse.sendData();
        usbMouse.disconnect();
    }
}
public interface USB {
    public abstract void connect();
    public abstract void disconnect();
    public abstract void sendData();
}
public class UsbKeyboard implements USB {
    @Override
    public void connect() {
        System.out.println("键盘连接了");
    }

    @Override
    public void disconnect() {
        System.out.println("键盘断开连接了");
    }

    @Override
    public void sendData() {
        System.out.println("键盘传输数据了");
    }
}
public class UsbMouse implements USB {
    @Override
    public void connect() {
        System.out.println("鼠标连接了");
    }

    @Override
    public void disconnect() {
        System.out.println("鼠标断开连接了");
    }

    @Override
    public void sendData() {
        System.out.println("鼠标传输数据了");
    }
}

# 接口中成员的特点

  • 接口中的方法默认就是抽象方法,会自动添加: public abstract
public interface USB {
    public abstract void connect();
    // 可以看到,不加 public abstract 也不会报错
    void disconnect();
}
  • 接口中是常量,会自动添加: public static final
public interface USB {
    public static final int money = 100;
    // 接口中是常量,会自动添加: public static final
    String name = "usb";
}

# 实现多个接口

Java 中类只能单继承,一个类只有一个父类:

class 子类 extends 父类 {}

一个类可以同时实现多个接口:

class 实现类 implements 接口1, 接口2 {}

# 接口与接口的多继承

一个接口可以同时继承多个接口:

interface 接口名 extends 接口1, 接口2 {}

接口多继承的作用:规范合并,整合多个接口为同一个接口,便于子类实现。

# JDK8 后接口新增方法

JDK8 版本开始后,Java 只对接口的成员方法进行了改进:

  • 默认方法(就是类似之前写的普通实例方法):必须用 default 修饰。需要用接口的实现类的对象来调用。
// public default 返回值类型 方法名(参数列表) {}

public interface USB {
    public default void fn1() {}
}
  • 静态方法: 默认会 public 修饰。注意:接口的静态方法必须用本身的接口名来调用。
// public static 返回值类型 方法名() {}

public interface USB {
    public static void fn2() {};
}
  • 私有方法(就是私有的实例方法): JDK 9 才开始有的。只能在本类中被其他的默认方法或者私有方法访问。
// private 返回值类型 方法名() {}

public interface USB {
    private void fn3() {}
}

# 接口和抽象类的区别

  • 语法区别
    • 一个类只能继承一个抽象类,一个类可以实现多个接口
    • 抽象类中可以有抽象方法和普通方法,接口中只有抽象方法
  • 思想区别:所以子类都具有的行为放到抽象类中,部分子类具有的行为放到接口中

#

# 包的概念和作用

包的概念:包类似于 Windows 系统中的文件夹、通过包可以分门别类的管理各种不同的类和资源。
新建包:
image.png
声明包的语法格式:package 包名; 报名建议全部英文小写,且具备意义。

package demo05接口的定义和使用;

public interface USB {}

声明包的语句必须在第一行,一般 IDEA 工具会帮助创建。

# 包的概念和导入包

怎么导包?相同包下的类可以直接访问。不同包下的类必须导包,才可以使用!
导包格式:

// import 包名.类名;
import java.util.Random;

# 权限修饰符

# 四个权限修饰符的作用

权限修饰符概述:是用来控制一个类中成员能够被访问的范围。可以修饰成员变量,成员方法,构造器,内部类。
权限修饰符分类和作用:权限修饰符有四种,作用范围由小到大(private -> 缺省 -> protected - > public)

修饰符 同一个类中 同一个包中
其他类
不同包下的
子类
不同包下的
无关类
public
protected

| | 缺省 | √ | √ |

|

| | private | √ |

|

|

|

# 总结

  • 常用的
    • private: 私有的,只有本类可以使用
    • public: 公开的,所有地方都能使用
  • 不常用的
    • 缺省的(默认的),给本包的类使用
    • protected: 受保护的,给子类使用
  • 自己定义成员(方法,成员变量,构造器等)一般满足如下要求
    • 成员变量一般私有
    • 方法一般公开

# final

# final 关键字修饰类和方法的特点

final 的作用:final 关键字是最终、不可变的意思,可以修饰类,方法,变量。
final 修饰的特点:

  • 修饰类:表明该类是最终类,不能被继承。
  • 修饰方法:表明该方法是最终方法,不能被重写。
  • 修饰变量:表明该变量是常量,表示该变量第一次赋值后,不能多次被赋值。

final 修饰变量的注意:

  • final 修饰基本类型的数据值不能发生改变
  • final 修饰引用类型的地址值不能发生改变,但是地址里面的内容是可以发生改变的。

# 总结

final 关键字修饰的特点

  • 修饰类:表明该类是最终类,不能被继承。
  • 修饰方法:表明该方法是最终方法,不能被重写。
  • 修饰变量:表明该变量是常量,表示该变量第一次赋值后,不能多次被赋值。

# final 关键字变量的特点

final 的作用:final 关键字是最终、不可变的意思,可以修饰类,方法,变量。

# 代码块

# 代码块格式和特点

代码块介绍:代码块是类的 5 大成分之一(成员变量、构造器,方法,代码块,内部类),定义在类中方法外。在 Java 类下,使用 { } 括起来的代码被称为代码块 。

代码块分类:

  • 静态代码块
    • 格式:static{}
    • 特点:需要通过 static 关键字修饰,随着类的加载而加载,并且自动触发、只执行一次
    • 使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。
public class Student {
    static {
        System.out.println("我是静态代码块!");
    }
}
  • 构造代码块(了解,几乎不用)
    • 格式:{}
    • 特点:每次创建对象,调用构造器执行前都会执行该代码块中的代码
    • 使用场景:初始化实例资源。放在类中方法外(成员位置)
public class Student {
    {
        System.out.println("我是构造代码块!");
    }
}

# 案例-斗地主

需求
在启动游戏房间的时候,应该提前准备好54张牌,后续才可以直接使用这些牌数据。

分析
1. 当系统启动的同时需要准备好数据的时候,就可以用静态代码块了。
2. 该房间只需要一副牌。
3. 定义一个静态的数组存储54张牌对象,静态的数组只会加载一份。
4. 在启动游戏房间前,应该将54张牌初始化好。
5. 使用静态代码块进行静态数组的数据初始化,将54张牌存入到静态数组中去。