跳至主要內容

抽象类与接口

wangdx大约 16 分钟

抽象类与接口

抽象类简介

在面向对象程序设计之中,如果要想扩充类的功能,最佳的做法是通过继承的形式来进行处理,但是对于普通的类继承的概念来说会有一个问题存在:类在继承的时候父类是不能够对子类提出严格的覆写要求的。例如:在 Java 中默认提供有一个 Object 父类,而这个 Object 父类之中提供有专门的对象输出方法,但是这个输出方法是根据每一个子类自己的要求来决定是否需要覆写的,也就是说 Object 类并没有严格的约束来控制子类的覆写。

案例
public class Test {
    public static void main(String[] args) {
        System.out.println(new Book());
    }
}
class Book{}

abstact.w01.Book@7bb11784

抽象类基本定义

抽象类定义原则 抽象类在 Java 之中必须使用 abstract 关键字来进行类的定义,而在抽象类中所定义的抽象方法也需要使用 abstract 关键字来进行定义,由于抽象类并不是一个完整的功能类,对于抽象类的使用需要按照如下原则进行:

  • 抽象类必须提供有子类,子类使用 extends 继承一个抽象类;
  • 抽象类的子类(不是抽象类)一定要覆写抽象类中的全部抽象方法:
  • 抽象类的对象实例化可以利用对象多态性通过子类向上转型的方式完成。
案例
public class Test {
    public static void main(String[] args) {
        Book book = new MathBook(); //向上转型
        book.read();
    }
}

abstract class Book {
    public abstract void read();

    public String toString() {
        return "【Book】一本图书信息";
    }
}

class MathBook extends Book {

    @Override
    public void read() {
        System.out.println("【MathBook】开始阅读数学图书");
    }
}MathBook】开始阅读数学图书

抽象类相关说明

  • 抽象类由于必须被子类所继承,所以抽象类中是不允许使用 final 来进行类定义,同理抽象方法也必须被子类所覆写,所以抽象方法同样不允许使用 final 进行修饰。
  • 抽象类相比较普通类仅仅是在结构上增加了一些抽象方法的定义,而抽象类中允许不定义任何的抽象方法,但是即便没有定义抽象方法抽象类也不允许被直接实例化。
案例
  • 抽象类之中允许提供有普通属性、普通方法、构造方法,所以抽象类的子类在进行子类对象实例化会按照标准的子类对象实例化的流程来进行处理,即:会默认调用父类中的无参构造,或者通过“super()”的形式选择父类构造。
案例
  • 在类之中如果定义有 static 方法的时候,所有的 static 操作结构实际上并不会受到类实例化对象的影响,所以现在也可以在一个抽象类中定义 static 方法,并且也可以由类名称直接进行调用。
案例

模版设计模式

抽象类是在普通类基础之上的设计抽象,现在假设有如下三种事物:图书、汽车、画板,其中图书和画板拥有信息读取功能,画板可以实现图形的绘制,而汽车拥有驾驶的能力,那么此时就可以考虑设计一个行为的抽象类,而后在此抽象类中定义出这三类事物的基本抽象功能,功能的具体实现就可以通过子类来实现,最终用户在进行行为操作时可以传递一系列的行为命令进行功能调用,这样行为的抽象类就相当于提供了三类事物的操作模版。同时考虑到抽象类中所提供的抽象方法并不是所有的类都需要,则可以在行为抽象类和具体子类之间设置一个过渡类,进行方法的假实现

案例
public abstract class Action {
    public static final int READ = 1;
    public static final int PAINT = 5;
    public static final int RUN = 10;

    public abstract void read();

    public abstract void paint();

    public abstract void run();

    public void command(int code) {
        switch (code) {
            case READ: {
                this.read();
                break;
            }
            case PAINT: {
                this.paint();
                break;
            }
            case RUN: {
                this.run();
                break;
            }
        }
    }
}

abstract class ActionAdapter extends Action {
    @Override
    public void read() {

    }

    @Override
    public void paint() {

    }

    @Override
    public void run() {

    }
}

class Book extends ActionAdapter {
    @Override
    public void read() {
        System.out.println("【Book】认真读书");
    }
}

class Sketchpad extends ActionAdapter {
    @Override
    public void read() {
        System.out.println("【Sketchpad】欣赏该图画");
    }

    @Override
    public void paint() {
        System.out.println("【Sketchpad】在图画上作画");
    }
}

class Car extends ActionAdapter {
    @Override
    public void run() {
        System.out.println("【Car】汽车在奔跑");
    }
}
class Test{
    public static void main(String[] args) {
        Action action = new Book();
        action.command(Action.READ);
        action.command(Action.PAINT);
    }
}

D:\develop\wsoft\jdk\jdk-11.0.20\bin\java.exe -javaagent:D:\develop\soft\ideaIU-2022.3.3.win\lib\idea_rt.jar=59879:D:\develop\soft\ideaIU-2022.3.3.win\bin -Dfile.encoding=UTF-8 -classpath D:\develop\project\wyix\gitee\study_service\out\production\study_service abstact.w03.TestBook】认真读书

Process finished with exit code 0

包装类简介

在 Java 中所有的引用数据类型可以利用向上转型自动的实现 Object 对象的接收,这样就可以通过 Object 实现参数的统一,但是基本数据类型不属于引用,所以无法直接通过 Object 实现参数接收,为了解决这一设计问题,就提出了一种包装类的概念,即:将基本数据类型的内容包装在一个类中,以实现 Object 参数统一。

包装类设计原理

案例
public class IntWrapper {
    private int data;

    public IntWrapper(int data) {
        this.data = data;
    }

    public int intValue() {
        return this.data;
    }
}

class Test {
    public static void main(String[] args) {
        Object obj = new IntWrapper(3);
        IntWrapper intWrapper = (IntWrapper) obj;
        System.out.println(intWrapper.intValue());
    }
}

3

包装类分类

Java 针对于八种基本数据类型提供了统一的包装类定义,并且可以将这八种包装类分为如下两类

  • 对象型包装类(Object 直接子类):boolean(Boolean)、char(Character);

  • 数值型包装类(Number 直接子类):byte(Byte)、short(Short)、int(Integer)、long(Long)、float(Float)、double(Double);

装箱与拆箱

案例
public class Test {
    public static void main(String[] args) {
        /*
         * 手工装箱拆箱
         * */
        Integer num = Integer.valueOf(99); // 手工装箱
        int temp = num.intValue(); //手工拆箱
        System.out.println(temp * 2);
    }
}

class Test1 {
    public static void main(String[] args) {
        /*
         * 自动装箱拆箱
         * */
        Integer numA = 9; // 自动装箱为Integer
        Double numB = 1.5; // 自动装箱为Double
        numA++;  //包装类直接计算
        System.out.println(numA * numB);
    }
}

class Test2 {
    public static void main(String[] args) {
        /*
         * 通过Object接收浮点型数据
         * */
        //10.3自动由double转Double,随后向上转型Object
        Object obj = 10.3;
        double num = (Double) obj; //Object必须向下转型后才可以拆箱
        System.out.println(num * 2);
    }
}


class Test3 {
    public static void main(String[] args) {
        /*
         * Integer数据比较分析
         * */
        Integer numA = 120;
        Integer numB = -96;
        Integer numC = 919;
        System.out.println(numA == 120);
        System.out.println(numB == -96);
        System.out.println(numC);
        System.out.println(numC == 919);
    }
}

true
true
919
true

数据类型转换

字符串转基本数据类型

在程序设计开发中经常性的需要通过键盘实现用户数据的输入,但是在 Java 中所有输入的数据类型全部都属于字符串,这样就需要将字符串转换为基本数据类型,而这样的数据转换的功能就可以通过包装类提供的方法来完成

案例
class Test4 {
    public static void main(String[] args) {
        /*
         * 将字符串转为基本数据类型
         * */
        int numA = Integer.parseInt("10");
        double numB = Double.parseDouble("10.99");
        boolean flag = Boolean.parseBoolean("true");
        if (flag) {
            System.out.println(numA * 2);
            System.out.println(numB * 2);
        }
    }
}

20
21.98

基本类型转字符串

  • 方式一 任何的数据类型使用“+”连接一个空字符串。在 Java 中所有定义的字符串都属于一个字符串匿名对象,而在字符串对象中使用“+就表示所有的数据类型全部转为字符串后,再进行字符串连接操作。
案例
class Book {
    @Override
    public String toString() {
        return "图书类";
    }
}

class Test5 {
    public static void main(String[] args) {
        /*
         * +转字符串
         * */
        String strA = "" + 99;
        String strB = "" + new Book();
        System.out.println(strA);
        System.out.println(strB);
    }
}
99
图书类
  • 方式二 使用 String 类中所提供的数据转换方法“valueOf0”,该方法进行了一系列的重载以适应各种数据类型的转换,利用此方法就可以避免不必要的垃圾空间产生,
案例
class Test6 {
    public static void main(String[] args) {
        /*
         * +转字符串
         * */
        String strA = String.valueOf(99);
        String strB = String.valueOf(new Book());
        System.out.println(strA);
        System.out.println(strB);
    }
}

接口

接口基本定义

interface IBook{}

接口使用原则:

  • 接口无法直接进行对象实例化,必须依靠子类来完成;
  • 接口的子类使用 implements 关键字,一个子类可以同时实现多个接口,子类的定义格式
    • class 子类[extends 父类][implements 父接囗,父接囗]{}
  • 接口的子类(如果不是抽象类)则一定要覆写接口之中的全部抽象方法;
案例
/**
 * 接口对象实例化
 *
 * @author wangdx
 */
public interface IBook {
    public static final String SITE = "www.yix.com";

    public abstract void read();
}

class MathBook implements IBook {

    @Override
    public void read() {
        System.out.println("【MathBook】学好数理化,走遍天下都不怕");
    }
}

class Test {
    public static void main(String[] args) {
        System.out.println("【IBook】常量:" + IBook.SITE);
        IBook book = new MathBook();
        book.read();
    }
}
/**
 * 子类同时实现多个接口
 *
 * @author wangdx
 */
public interface IBook {
    public static final String SITE = "www.yix.com";

    public abstract void read();
}

interface ISpec {
    public double size();
}

class MathBook implements IBook, ISpec {

    @Override
    public void read() {
        System.out.println("【MathBook】学好数理化,走遍天下都不怕");
    }

    @Override
    public double size() {
        return 8.9;
    }
}

class Test {
    public static void main(String[] args) {
        IBook book = new MathBook();
        book.read();
        ISpec spec = new MathBook();
        System.out.println(spec.size());
    }
}MathBook】学好数理化,走遍天下都不怕
8.9

接口相关说明

  • 在子类实现接口的操作过程中是通过 implements 关键字实现了若干个接口,但是如果说此时除了接口之外还要求去继承父类则必须采用先继承后实现的模式进行定义。
案例
/**
 * 继承父类并实现接口
 *
 * @author wangdx
 */
public interface IBook {
    public static final String SITE = "www.yix.com";

    public abstract void read();
}

interface ISpec {
    public double size();
}
abstract class Print{
    public abstract void batch();
}
class MathBook extends Print implements IBook, ISpec {

    @Override
    public void read() {
        System.out.println("【MathBook】学好数理化,走遍天下都不怕");
    }

    @Override
    public double size() {
        return 8.9;
    }

    @Override
    public void batch() {
        System.out.println("【MathBook】多印刷,多学习");
    }
}

class Test {
    public static void main(String[] args) {
        IBook book = new MathBook();
        book.read();
        ISpec spec = new MathBook();
        System.out.println(spec.size());
        Print print = new MathBook();
        print.batch();
    }
}MathBook】学好数理化,走遍天下都不怕
8.9MathBook】多印刷,多学习
  • MathBook 类实现的 IBook 接口、ISpec 接口以及 Print 父类彼此之间是没有任何联系的但是他们有一个公共的后代。但是正是因为现在 MathBook 类这个公共子类的存在,所以 MathBook 类的实例化对象可以任意的像所有的父类或父接口转型(包括父接口之间以及接口和抽象类之间的转型)

案例
class Test1 {
    public static void main(String[] args) {
        IBook book = new MathBook(); //向上转型
        book.read();
        ISpec spec = (ISpec) book;
        System.out.println(spec.size());
        Print print = (Print) book;
        print.batch();
    }
}
  • 在子类实现接口的过程之中,一个接口是不能够继承任何父类的,所以任何的接口一定都没有父类的概念,但是所有的接口对象都可以通过 Object 来进行接收(Object 可以接收一切的引用数据类型,可以实现最终参数的统一)。
案例
class Test2{
    public static void main(String[] args) {
        IBook book = new MathBook(); //向上转型
        Object obj = book;  //通过OBject接收接口引用
        IBook temp = (IBook) obj;  //向下转型
        temp.read();
    }
}
  • 一个接口不能够使用 extends 继承一个父类,但是一个接口却可以通过 extends 同时继承多个父接口,这一点称为接口的多继承。
案例

/**
 * 接口多继承
 * @author wangdx
 */
public interface IPrint {
    public void batch();
}

interface ISpec {
    public double size();
}

interface IBook extends IPrint, ISpec {
    public abstract void read();
}

class MathBook implements IBook {

    @Override
    public void batch() {
        System.out.println("【MathBook】印刷");
    }

    @Override
    public double size() {
        return 8.9;
    }

    @Override
    public void read() {
        System.out.println("【MathBook】读书");
    }
}

class Test {
    public static void main(String[] args) {
        IPrint print = new MathBook();
        print.batch();
        ISpec spec = (ISpec) print;
        System.out.println(spec.size());
    }
}

适配器设计模式

一个接口中可能会定义有大量的抽象方法,按照 Java 语法规定,实现接口的子类必须覆写接口中的全部抽象方法,但是可能接口中的某些抽象方法在部分子类中并没有任何的意义,那么此时可以考虑加入一个中间的过渡类对接口中的全部抽象方法进行“假”实现,同时这个过渡类由于功能并不完善所以不能够进行对象实例化处理,就可以通过抽象类来进行处理,这时的抽象类实际上就被称为适配器类(Adapter)

案例
/**
 * 适配器设计模式
 *
 * @author wangdx
 */
public class Test {
}

interface IBook {
    public void read();

    public void create();

    public void message();
}

abstract class AbstractBookAdapter implements IBook {
    public void read() {

    }

    public void create() {

    }

    public void message() {

    }
}

class MathBook extends AbstractBookAdapter implements IBook {
    @Override
    public void read() {
        System.out.println("【MathBook】读书");
    }
}
class LoveBook extends AbstractBookAdapter implements IBook{
    @Override
    public void message() {
        System.out.println("【LoveBook】读书");
    }
}
class ProgramBook extends AbstractBookAdapter implements IBook{
    @Override
    public void create() {
        System.out.println("【ProgramBook】读书");
    }
}
class Test1{
    public static void main(String[] args) {
        IBook book = new ProgramBook();
        book.create();
    }
}

工厂设计模式

案例
public interface IBook {
    public void read();
}

class ProgramBook implements IBook {

    @Override
    public void read() {
        System.out.println("【ProgramBook子类】");
    }
}

class MathBook implements IBook {

    @Override
    public void read() {
        System.out.println("【MathBook子类】");
    }
}

class Factory {
    public static IBook getInstance(String className) {
        if ("program".equalsIgnoreCase(className)) {
            return new ProgramBook();
        } else if ("math".equalsIgnoreCase(className)) {
            return new MathBook();
        }
        return null;
    }
}

class Test {
    public static void main(String[] args) {
        IBook book = Factory.getInstance("math");
        book.read();
    }
}

代理设计模式

消息发送与网络连接

代理(Proxy)设计模式主要指的是一个核心的业务功能,通过其他的辅助的业务手段来实现完整的业务操作。例如:现在需要进行一个网络消息的发送操作,那么在整个的处理过程之中,消息的发送就属于一个核心的业务主题,真正的需求是将消息发出去但是如果要想进行消息的发送,就必须建立网络的通道,而在消息发送完成之后也需要关闭通道以释放资源。

案例
/**
 * 代理设计模式
 *
 * @author wangdx
 */
public interface IMessage {
    public void send(String msg);
}

class MessageImpl implements IMessage {

    @Override
    public void send(String msg) {
        System.out.println("【核心业务-send】" + msg);
    }
}

class MessageProxy implements IMessage {
    private IMessage message;

    public MessageProxy(IMessage message) {
        this.message = message;
    }

    public boolean connect() {
        System.out.println("【代理业务connect】连接远程服务器,准备发送消息");
        return true;
    }

    public void close() {
        System.out.println("【代理业务close】断开远程服务器,释放资源");
    }

    @Override
    public void send(String msg) {
        if (this.connect()) {
            this.message.send(msg);
            this.close();
        }
    }
}
class Test{
    public static void main(String[] args) {
        IMessage message = new MessageProxy(new MessageImpl());
        message.send("一祥科技:www.yix.com");
    }
}
【代理业务connect】连接远程服务器,准备发送消息
【核心业务-send】一祥科技:www.yix.com
【代理业务close】断开远程服务器,释放资源

接口开发标准

在电脑中一般都会提供有 USB 接口,利用 USB 接口可以连接任何符合于 USB 接口标准的设备,,例如:U 盘、、打印机、手机等,这样同一台电脑上的 USB 接口就可以根据需要动态的连接不同设备相当于电脑和设备这两个完全无关的结构通过 USB 接口实现了关联

案例
public interface IUSB {
    public void install(); //安装usb驱动

    public void use();//使用usb设备
}

class Computer {
    private String brand; //电脑品牌

    public Computer() {
        this("一祥电脑");
    }

    public Computer(String brand) {
        this.brand = brand;
    }

    public void plugin(IUSB usb) {
        usb.install();
        usb.use();
    }
}

class Flash implements IUSB {

    @Override
    public void install() {
        System.out.println("【U盘】进行U盘驱动安装");
    }

    @Override
    public void use() {
        System.out.println("【U盘】向U盘拷贝一些重要的种子");
    }
}
class Phone implements IUSB {

    @Override
    public void install() {
        System.out.println("【手机】电脑启动手机的连接");
    }

    @Override
    public void use() {
        System.out.println("【手机】通过电脑备份手机文件");
    }
}
class Test{
    public static void main(String[] args) {
        Computer computer = new Computer("一项电脑");
        computer.plugin(new Phone());
        computer.plugin(new Flash());
    }
}

【手机】电脑启动手机的连接
【手机】通过电脑备份手机文件
【U盘】进行U盘驱动安装
【U盘】向U盘拷贝一些重要的种子

接口定义加强

案例
/**
 *
 * 接口中定义普通方法
 * @author wangdx
 */
public interface IBook {
    public void read();
    public default void create(){
        System.out.println("图书创造,为了人生积累和感悟");
    }
}
class ProgramBook implements IBook{
    public void read(){

    }
}
class Test{
    public static void main(String[] args) {
        IBook book = new ProgramBook();
        book.create();
    }
}
public interface IBook {
    public void read();
    //普通方法
    public default void create(){
        System.out.println("图书创造,为了人生积累和感悟");
    }
    //接口名称直接调用
    public static IBook getInstance(){
        return new ProgramBook();
    }
}
class ProgramBook implements IBook{
    public void read(){

    }
}

class TestDemo{
    public static void main(String[] args) {
        IBook book = IBook.getInstance();
        book.create();
    }
}

抽象类与接口区别

泛型

泛型问题的引出

案例
public class Message {
    private Object content;

    public Object getContent() {
        return content;
    }

    public void setContent(Object content) {
        this.content = content;
    }

    public void send() {
        System.out.println("【消息发送】" + this.content);
    }
}
class Test{
    public static void main(String[] args) {
        Message message = new Message();
        message.setContent(99.99);
        message.send();
        String value = (String) message.getContent();
        System.out.println(value);
    }
}

【消息发送】99.99
Exception in thread "main" java.lang.ClassCastException: class java.lang.Double cannot be cast to class java.lang.String (java.lang.Double and java.lang.String are in module java.base of loader 'bootstrap')
	at abstact.w14.Test.main(Message.java:26)

泛型基本定义

在一个类定义中如果要想使用泛型,那么首先就需要定义泛型标记,而后该泛型标记可以代替数据类型出现在类的成员属性、方法参数或方法返回值之中,在进行该类对象实例化时需要明确的为该泛型标记设置具体的类型,这样类中的相关标记就会自动进行类型的动态更换。

案例
/**
 * 如果要定义泛型类,则采用“<泛型标记>”的方式来定义。泛型标记可以随便写,现在T描述的就是Type
 * 若要定义多个泛型,可以使用“,”分割,例如class Message<T,A,C>{}
 *
 * @author wangdx
 */
public class Message<T> {
    private T content;

    public T getContent() {
        return content;
    }

    public void setContent(T content) {
        this.content = content;
    }

    public void send() {
        System.out.println("【消息发送】" + this.content);
    }
}
class Test{
    public static void main(String[] args) {
        Message<Integer> message = new Message<Integer>();
        message.setContent(15);
        int value = message.getContent();
        System.out.println(value);
    }
}

泛型通配符

泛型对象与引用传递问题
利用泛型技术解决了对象转型中可能存在的“ClassCastException”异常,但是随之而来也带来了一个引用传递的新问题,在进行方法参数传递时不同泛型类型的参数是无法互相接收的,即:如果创建的对象泛型类型为“Message<String>”,则只能通过“Message<String>"方法参数进行接收,如果再创建一个“Message<Integer>"对象则就无法进行对象引用传递
案例
class Test1 {
    public static void main(String[] args) {
        Message<Integer> messageA = new Message<>(99);
        fun(messageA);
        Message<String> messageB = new Message<>("www.yix.com");
        fun(messageB);
    }

    public static void fun(Message<?> temp) {
        System.out.println("获取content内容:" + temp.getContent());
    }
}

获取content内容:99
获取content内容:www.yix.com

泛型接口

案例
/*
 * 实现方式一:定义子类时继续进行反省定义
 * */
interface IMessage1<T> {
    public void send(T t);
}

class MessageImpl1<C> implements IMessage1<C> {

    @Override
    public void send(C c) {
        System.out.println("【消息发送】" + c);
    }
}
class TestM1{
    public static void main(String[] args) {
        IMessage1<String> message1 = new MessageImpl1<>();
        message1.send("一祥集团:www.yix.xom");
    }
}
/*
 * 实现方式二:在子类实现接口的时候不定义泛型,而是明确的为实现的父接口设置一个具体的泛型类型。
 * */
interface IMessage2<T> {
    public void send(T t);
}

class MessageImpl2 implements IMessage2<String> {

    @Override
    public void send(String c) {
        System.out.println("【消息发送】" + c);
    }
}
class TestM2{
    public static void main(String[] args) {
        IMessage2<String> message2 = new MessageImpl2();
        message2.send("一祥集团:www.yix.xom");
    }
}

泛型方法

案例
class Test3 {
    public static void main(String[] args) {
        String data[] = fun3("一祥", "网站", "冬瓜");
        for (String str : data) {
            System.out.print(str + "、");
        }
    }

    public static <T> T[] fun3(T... params) {
        return params;
    }
}

一祥、网站、冬瓜、
上次编辑于: