当前位置: 首页 > news >正文

初识Java 8-1 接口和抽象类

目录

抽象类和抽象方法

接口定义

默认方法

多重继承

接口中的静态方法

作为接口的Instrument


本笔记参考自: 《On Java 中文版》


        接口和抽象类提供了一种更加结构化的方式分离接口和实现

抽象类和抽象方法

        抽象类,其介于普通类和接口之间。在构建具有字段而未实现方法的类时,抽象类是重要且必要的工具。以Instrument类为例:

package music;public class Instrument {public void play(Note n) {}
}

        由这个类可以衍生出很多子类:WindBrass等。在这里,Instrument存在的目的就是为了为它的子类创建一个公共接口。换言之,Instrument建立了一种基本形式,用于抽象出所有子类的共同之处。在这里,Instrument可以被称为抽象基类,简称抽象类

    创建抽象类的目的,就是通过一个公共接口来操作一组的类。因此,Instrument只需要提供一个接口就可以了。

        Java提供了一种抽象方法的机制,这是一个不完整的方法:只有声明,而没有方法体(类似于C++的纯虚函数)。其声明语法如下:

abstract void f();

        包含抽象方法的类就是抽象类。若一个类包含有一个或以上的抽象方法,则该类就必须被定义为抽象类,否则就会报错。

abstract class Basic {abstract void unimplemented();
}

        因为抽象类是不完整的,为了防止其被误用,当试图创建一个抽象类的对象时,就会收到报错:

        若想要继承一个抽象类,那么这个新类就必须为基类中的所有抽象方法提高方法定义。否则,认为子类也是抽象的,此时编译器会强制要求使用abstract关键字来限定这个子类:

abstract class Basic2 extends Basic {int f() {return 111;}abstract void g();// 仍然没有实现unimplemented()方法
}

        一个抽象类可以不包含任何的抽象方法。这种用法主要用于阻止对于该方法的任何实例化:

abstract class Basic3 {int f() {return 111;}// 可以不需要抽象方法
}public class AbstractWithoutAbstracts {// 实例化依旧会报错:Basic3是抽象类// Basic3 b3 = new Basic3();
}

        使用一个继承了抽象类的子类,若想要实例化这个子类,就需要为抽象类的所有抽象方法提供定义:

abstract class Uninstantiable {abstract void f();abstract void g();
}public class Instantiable extends Uninstantiable {@Overridevoid f() {System.out.println("f()");}@Overridevoid g() {System.out.println("g()");}public static void main(String[] args) {Uninstantiable ui = new Instantiable();}
}

        在上述程序中,即使@Override不标注,只要没有使用相同的方法名称或是方法签名,抽象机制仍然可以知道程序员有没有实现抽象方法。在这里,@Override主要用于提示,表示该方法已经被重写了。

        抽象类对于访问权限没有过多限制,其的默认访问权限就是包访问权限。但是,抽象方法是不允许private的:

abstract class AbstractAccess {private void m1() {}// private abstract void m1a();protected void m2() {}protected abstract void m2a();void m3() {}abstract void m3a();public void m4() {}public abstract void m4a();
}

        不允许private abstract是因为,这种抽象方法无法在子类中得到一个合法的定义

        抽象类不会要求其所有的方法都是抽象的,将需要使用的公共接口声明为abstract即可。依据这些已知的知识,以乐器(Instrument)为例:

package music4;import music.Note;abstract class Instrument {private int i; // 这一变量在每个对象中都会被分配储存public abstract void play(Note n);public String what() {return "Instrument";}public abstract void adjust();
}class Wind extends Instrument {@Overridepublic void play(Note n) {System.out.println("Wind.play(): " + n);}@Overridepublic String what() {return "Wind";}@Overridepublic void adjust() {System.out.println("对Wind进行调整");}
}class Percussion extends Instrument {@Overridepublic void play(Note n) {System.out.println("Percussion.play(): " + n);}@Overridepublic String what() {return "Percussion";}@Overridepublic void adjust() {System.out.println("对Percussion进行调整");}
}class Stringed extends Instrument {@Overridepublic void play(Note n) {System.out.println("Stringed.play(): " + n);}@Overridepublic String what() {return "Stringed";}@Overridepublic void adjust() {System.out.println("对Stringed进行调整");}
}class Brass extends Wind {@Overridepublic void play(Note n) {System.out.println("Brass.play(): " + n);}@Overridepublic void adjust() {System.out.println("对Brass进行调整");}
}class Woodwind extends Wind {@Overridepublic void play(Note n) {System.out.println("Woodwind.play(): " + n);}@Overridepublic String what() {return "Woodwind";}
}public class Music4 {static void tune(Instrument i) { // 新的类型也可以使用tune()方法// ...i.play(Note.MIDDLE_C);}static void tuneAll(Instrument[] e) {for (Instrument i : e)tune(i);}public static void main(String[] args) {Instrument[] orchestra = {new Wind(),new Percussion(),new Stringed(),new Brass(),new Woodwind()};tuneAll(orchestra);}
}

        程序执行的结果如下:

        抽象类和抽象方法明确了类的抽象性,并且告诉用户和编译器自己的预期用途,这种工具也常被用在重构之中。

接口定义

        接口通过interface进行定义。在Java 8之前,它只被运行使用抽象方法:

public interface PureInterface {int m1();void m2();double m3();
}

在接口中定义抽象方法不需要使用abstract关键字,因为在接口中不会存在方法体。

        总结一下:在Java 8之前,interface可以创建一个完全抽象的类,不代表任何实现。接口仅负责描述,它确定方法名、方法体和返回类型,但不提供方法体。接口只提供一种形式,而使用了接口的代码会知道可以为接口调用哪些方法。

        Java 8开始,接口中允许默认方法和静态方法。此时接口的基本概念依旧成立,即接口是一个类型的概念,而非实现。

        接口与抽象类的区别:

  • 接口通常暗示“类的类型”或作为形容词使用。
  • 抽象类通常是类层次结构的一部分。

    接口的方法默认是public的,而不是包访问权限。

        接口可以包含字段,这些字段是隐式的staticfinal

        使用implement关键字,可以创建一个符合特定接口(或一组接口)的类。例如:

interface Concept { // 类前没有使用public修饰,是包访问权限void idea1();void idea2();
}class ImplementingAnInterface implements Concept {@Overridepublic void idea1() {System.out.println("idea1");}@Overridepublic void idea2() {System.out.println("idea2");}
}

        注意:当实现一个接口时,来自接口的方法必须被定义为public。因为Java编译器不会允许将接口方法的访问权限设为包访问权限,这会降低继承期间方法的可访问性。

默认方法

        Java 8之后,default关键字有了一个额外的用途:在接口中,default会允许方法创建一个方法体。实现了该接口的类,可以不定义default修饰的方法而直接使用方法。

interface InterfaceWithDefault {void firstMethod();void secondMethod();default void defaultMethod() {System.out.println("这是一个由default修饰的方法");}
}

        只要实现上述接口的firstMethod方法和secondMethod方法,就可以使用这个接口了:

// 和InterfaceWithDefault.java处于同一文件夹中public class Implementation implements InterfaceWithDefault {@Overridepublic void firstMethod() {System.out.println("方法一");}@Overridepublic void secondMethod() {System.out.println("方法二");}public static void main(String[] args) {InterfaceWithDefault i = new Implementation();i.firstMethod();i.secondMethod();i.defaultMethod();}
}

        程序执行的结果是:

        之所以添加默认方法,原因之一是:这允许向现有接口中添加方法,而不会破坏已经在使用该接口的所有方法(默认方法也称防御方法或虚拟扩展方法)

    JDK 9中,接口的defaultstatic方法都可以是private的。


多重继承

        多重继承,即一个类可以从多个基类型继承特性和功能。但Java在严格意义上是一种单继承语言:Java只允许继承一个类(或抽象类)。在默认方法出现之后,Java才拥有了一些多重继承的特性。

        现在,我们可以通过把接口和默认方法结合起来,来结合多个基类型的行为

        但Java只允许结合“行为”,换句话说,接口中不允许存在字段(除非是静态字段)。字段依旧只能来自单个基类或抽象类,所以我们无法获得状态的多重继承。例如:

interface One {default void first() {System.out.println("方法One.first()");}
}interface Two {default void second() {System.out.println("方法Two.second()");}
}interface Three {default void third() {System.out.println("方法Three.third()");}
}class MI implements One, Two, Three { // 结合了多个接口
}public class MultipleInheritance {public static void main(String[] args) {MI mi = new MI();mi.first();mi.second();mi.third();}
}

        程序执行的结果如下:

        只要所有基类方法都有不同名称和参数列表,就可以组合多个来源。否则,编译器就会报错:

interface Bob1 {default void bob() {System.out.println("Bob1::bob");}
}interface Bob2 {default void bob() {System.out.println("Bob2::bob");}
}// class Bob implements Bob1, Bob2 { // 不可以,会发生报错
// }interface Sam1 {default void sam() {System.out.println("Sam1::sma");}
}interface Sam2 {default void sam(int i) {System.out.println("Sam2::sma = " + i * 2);}
}class Sam implements Sam1, Sam2 { // 可以,因为方法的参数列表不同
}interface Max1 {default void max() {System.out.println("Max1::max");}
}interface Max2 {default int max() {System.out.println("Max2::max");return 1;}
}// class Max implements Max1, Max2 { // 不可以,参数列表不足以区分方法
// }

        编译器会通过方法签名来区分不同的方法,因为方法签名具有唯一性:签名包括名称和参数类型。但是,返回类型不是方法签名的一部分

        如果发生如上注释中的冲突,就需要通过重写冲突的方法来解决问题:

interface Coco1 {default void coco() {System.out.println("Coco1::coco");}
}interface Coco2 {default void coco() {System.out.println("Coco2::coco");}
}public class Coco implements Coco1, Coco2 {@Overridepublic void coco() { // 重写存在冲突的方法Coco2.super.coco();}public static void main(String[] args) {new Coco().coco();}
}

        上述程序最终会输出:Coco2::coco 。在Coco类中进行重写方法时,通过super关键字选择了基类Coco2进行实现。除此之外,也可以通过其他任何可行的方式进行实现。


接口中的静态方法

        Java 8还允许接口包含静态方法。这种设计使得我们可以将逻辑上属于接口的方法赋予接口本身。通常,会将用来操作接口的方法,以及通用工具放入接口中:

public interface Operation {void execute();static void runOps(Operation... ops) { // 用来操作接口for (Operation op : ops)op.execute();}static void show(String msg) { // 通用方法System.out.println(msg);}
}

    其中,runOps()是一个模板方法设计模式的例子。

        借由runOps()这个方法,下面展示的是创建Operation的不同方法:

import operation.Operation;class Heat implements Operation {@Overridepublic void execute() {Operation.show("Heat");}
}public class MetaWork {public static void main(String[] args) {Operation twist = new Operation() {public void execute() { // 在使用前,必须在静态上下文中对方法进行定义Operation.show("Twist");}};Operation.runOps(new Heat(), // 【1】:按常规方式创建new Operation() { // 【2】:匿名类public void execute() {Operation.show("Hammer");}},twist::execute, // 使用方法引用() -> Operation.show("Anneal")); // Lambda表达式,需要最少的代码}
}

        总结上述程序,可以得出各种创建Operation的不同方式:

  1. 常规类Heat
  2. 匿名类
  3. 方法引用
  4. Lambda表达式,需要最少的代码

作为接口的Instrument

        使用接口更新关于乐器的Instrument

        接口一经实现,这个实现就会变成一个可以用常规方式扩展的普通类。接口中,任何方法的默认权限都是public的。Instrument中的play()adjust()都使用default关键字定义。

package music5;import music.Note;interface Instrument {int VALUR = 5; // 默认是static并且final的,即编译时常量default void play(Note n) { // 默认权限是public的System.out.println(this + ".play()" + n);}default void adjust() {System.out.println("调整:" + this);}
}class Wind implements Instrument {@Overridepublic String toString() {return "Wind";}
}class Percussion implements Instrument {@Overridepublic String toString() {return "Percussion";}
}class Stringed implements Instrument {@Overridepublic String toString() {return "Stringed";}
}class Brass extends Wind {@Overridepublic String toString() {return "Brass";}
}class Woodwind extends Wind {@Overridepublic String toString() {return "Woodwind";}
}public class Music5 {static void tune(Instrument i) {// ...i.play(Note.MIDDLE_C);}static void tuneAll(Instrument[] e) {for (Instrument i : e)tune(i);}public static void main(String[] args) {Instrument[] orchestra = {new Wind(),new Percussion(),new Stringed(),new Brass(),new Woodwind()};tuneAll(orchestra);}
}

        程序执行的结果是:

        上述程序中,使用根类Object的方法toString()替代了what()方法。

    无论是向上转型为常规类、抽象类或是接口,tune()方法的行为都是一样的。实际上tune()也无从得知Instrument到底是什么类。

相关文章:

初识Java 8-1 接口和抽象类

目录 抽象类和抽象方法 接口定义 默认方法 多重继承 接口中的静态方法 作为接口的Instrument 本笔记参考自: 《On Java 中文版》 接口和抽象类提供了一种更加结构化的方式分离接口和实现。 抽象类和抽象方法 抽象类,其介于普通类和接口之间。在构…...

微信小程序音频后台播放功能

微信小程序在手机息屏后依旧能播放音频,需要使用 wx.getBackgroundAudioManager() 方法创建后台音乐播放器,并将音乐播放任务交给这个后台播放器。 具体实现步骤如下: 小程序页面中,使用 wx.getBackgroundAudioManager() 方法创…...

NotePad——xml格式化插件xml tools在线安装+离线安装

在使用NotePad时,在某些情形下,需要格式化Xml格式内容,可以使用Xml Tools插件。 一、在线安装 1. 打开Notepad 软件 2. 选择插件,选择“插件管理” 3. 搜索 XML Tools,找到该插件后,勾选该文件&#xff…...

图书管理系统 数据结构先导课暨C语言大作业复习 | JorbanS

问题描述 读取给定的图书文件book.txt中的信息(book.txt中部分图书信息如下图所示),完成一个图书信息管理系统,该系统的各个功能模块要求利用菜单选项进行选择。 系统功能要求 图书浏览 读取book.txt中的文件信息并依次输出所…...

python 爬虫的开发环境配置

1、新建一个python项目 2、在控制台中分别安装下面三个包 pip install requests pip install beautifulsoup4 pip install selenium/ 如果安装时报以下错误: raise ReadTimeoutError(self._pool, None, "Read timed out.") pip._vendor.urllib3.exceptio…...

技术架构图是什么?和业务架构图的区别是什么?

技术架构图是什么? ​技术架构图是一种图形化工具,用于呈现软件、系统或应用程序的技术层面设计和结构。它展示了系统的各种技术组件、模块、服务以及它们之间的关系和交互方式。技术架构图关注系统内部的技术实现细节,以及各个技术组件之…...

数据增强

一、数据增强 当你训练一个机器学习模型时,你实际做工作的是调参,以便将特定的输入(一副图像)映像到输出(标签)。我们优化的目标是使模型的损失最小化, 以正确的方式调节优化参数即可实现这一目…...

【Unity】2D 对话模块的实现

对话模块主要参考 【Unity教程】剧情对话系统 实现。 在这次模块的构建将基于 unity ui 组件 和 C#代码实现一个从excel 文件中按照相应规则读取数据并展示的逻辑。这套代码不仅能实现正常的对话,也实现了对话中可以通过选择不同选项达到不同效果的分支对话功能。 …...

laravel安装初步使用学习 composer安装

一、什么是laravel框架 Laravel框架可以开发各种不同类型的项目,内容管理系统(Content Management System,CMS)是一种比较典型的项目,常见的网站类型(如门户、新闻、博客、文章等)都可以利用CM…...

【VS插件】VS code上的Remote - SSH

【VS插件】VS code上的Remote - SSH 目录 【VS插件】VS code上的Remote - SSH获得Linux服务器或者Linux系统的IP地址下载插件远程登录注意如果Linux虚拟机系统无法连接成功可能是没有开启ssh服务优势 作者:爱写代码的刚子 时间:2023.9.12 前言&#xff1…...

TensorFlow 02(张量)

一、张量 张量Tensor 张量是一个多维数组。与NumPy ndarray对象类似,tf.Tensor对象也具有数据类型和形状。如下图所示: 此外,tf.Tensors可以保留在GPU中。TensorFlow提供了丰富的操作库 (tf.add,tf.matmul,tf.linalg.inv等),它们…...

513. 找树左下角的值

代码链接: 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 思路: 万金油层次遍历,保存每一层的第一个元素返回就行了 我的代码: /*** Definition for a binary tree node.* struct TreeNode {* …...

量化:基于支持向量机的择时策略

文章目录 参考机器学习简介策略简介SVM简介整体流程收集数据准备数据建立模型训练模型测试模型调节参数 参考 Python机器学习算法与量化交易 利用机器学习模型,构建量化择时策略 机器学习简介 机器学习理论主要是设计和分析一些让计算机可以自动“学习”的算法。…...

成功解决Selenium 中116版本的chromedriver找不到问题

Selenium 中的Google(谷歌浏览器)最新版本chromedriver 文章目录 Selenium 中的Google(谷歌浏览器)最新版本chromedriver1.当前作者的谷歌浏览器版本2.当前驱动官网的最新版本3.当不想降低浏览器版本继续使用谷歌浏览器的办法 1.当…...

PYQT常用组件--方法汇总

QTimeEdit timeEdit是Qt框架中的一个时间编辑器控件,它提供了以下常用方法: setTime(QTime time): 设置时间编辑器的时间为指定的QTime对象。time(): 返回时间编辑器的当前时间,返回一个QTime对象。setDateTime(QDateTime dateTime): 设置时…...

Linux系统编程(一):文件 I/O

参考引用 UNIX 环境高级编程 (第3版)黑马程序员-Linux 系统编程 1. UNIX 基础知识 1.1 UNIX 体系结构(下图所示) 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境,通常…...

OSM+three.js打造3D城市

对于我在 Howest 的研究项目,我决定构建一个 3D 版本的 Lucas Bebber 的“交互式讲故事的动画地图路径”项目。我将使用 OSM 中的矢量轮廓来挤出建筑物的形状并将它们添加到 3js 场景中,随后我将对其进行动画处理。 一、开发环境 为了使用 Node 和 npm 包,我选择使用 Vite…...

02JVM_垃圾回收GC

二、垃圾回收GC 在堆里面存放着java的所有对象实例,当对象为“死去”,也就是不再使用的对象,就会进行垃圾回收GC 1.如何判断对象可以回收 1.1引用计数器 介绍 在对象中添加一个引用计数器,当一个对象被其他变量引用时这个对象…...

ARM Linux DIY(八)USB 调试

前言 V3s 带有一个 USB 接口,将其设置为 HOST 或 OTG 模式,这样可以用来接入键盘、鼠标等 USB 外设。 USB 简介 USB 有两种设备:HOST 和 USB 功能设备。 在 USB2.0 中又引入了一个新的概念 OTG,即设备角色可以动态切换。 切换方…...

编程小白的自学笔记十四(python办公自动化创建、复制、移动文件和文件夹)

系列文章目录 编程小白的自学笔记十三(python办公自动化读写文件) 编程小白的自学笔记十二(python爬虫入门四Selenium的使用实例二) 编程小白的自学笔记十一(python爬虫入门三Selenium的使用实例详解) …...

MySQL使用Xtrabackup备份到AWS存储桶

1.安装Xtrabackup cd /tmp wget https://downloads.percona.com/downloads/Percona-XtraBackup-8.0/Percona-XtraBackup-8.0.33-28/binary/redhat/7/x86_64/percona-xtrabackup-80-8.0.33-28.1.el7.x86_64.rpm yum -y localinstall percona-xtrabackup-80-8.0.33-28.1.el7.x86…...

(高阶)Redis 7 第11讲 BIGKEY 优化篇

面试题 问题答案如何在海量数据中查询某一固定前缀的Keyscan生产环境如何限制 keys */FLUSHDB/FLUSHALL 等危险命令,防止误删误用# 修改配置文件 rename-command keys "" rename-command flushdb "" rename-command flushall ""如何使用MEMORY U…...

一阶差分和二阶差分概念及其举例

一阶差分和二阶差分概念及其举例 目录 一阶差分和二阶差分概念及其举例1、一阶差分1.1 概念1.2 举例 2、二阶差分2.1 概念2.2 举例 1、一阶差分 1.1 概念 一阶差分是指对一个数列中的每个元素,计算其与其前一个元素之差的操作。 1.2 举例 举例来说,对…...

使用自定义注解和SpringAOP捕获Service层异常,并处理自定义异常

目录 一 自定义异常二 自定义注解三 注解切面处理类四 使用 一 自定义异常 /*** 自定义参数为null异常*/ public class NoParamsException extends Exception {//用详细信息指定一个异常public NoParamsException(String message){super(message);}//用指定的详细信息和原因构…...

Kotlin(六) 类

目录 创建类 调用类 类的继承------open 构造函数 创建类 创建类和创建java文件一样,选择需要创建的目录New→Kotlin File/Class Kotlin中也是使用class关键字来声明一个类的,这一点和Java一致。现在我们可以在这个类中加入字段和函数来丰富它的功…...

蓝桥杯官网练习题(灌溉)

题目描述 小蓝负责花园的灌溉工作。 花园可以看成一个 n 行 m 列的方格图形。中间有一部分位置上安装有出水管。 小蓝可以控制一个按钮同时打开所有的出水管,打开时,有出水管的位置可以被认为已经灌溉好。 每经过一分钟,水就会向四面扩展…...

数据结构:树的概念和结构

文章目录 1. 树的概念2. 树的结构3. 树的相关概念4. 树的表示孩子表示法双亲表示法孩子兄弟表示法 5. 树在实际中的应用5. 树在实际中的应用 1. 树的概念 树是一种非线性的数据结构,它是由 n (n > 0)个有限结点组成一个具有层次关系的. 把它叫做树是因为它看起来像一棵倒挂的…...

【GIS】栅格转面报错:ERROR 000864输入栅格: 输入不在定义的属性域内。 ERROR 000863: 无效的 GP 数据类型

问题: 栅格转面(矢量)时,ArcGIS窗口显示:ERROR 000864输入栅格: 输入不在定义的属性域内。 ERROR 000863: 无效的 GP 数据类型. 原因: 栅格转面时输入的栅格数据集的字段必须是整型. 解决办法: 使用Spatial Analyst中的转为整型工具,将栅格数据转为整型后再进行栅格转面的操作…...

32 WEB漏洞-文件操作之文件下载读取全解

目录 介绍利用获取数据库配置文件文件名,参数值,目录符号 涉及案例:Pikachu-文件下载测试-参数Zdns-文件下载真实测试-功能点小米路由器-文件读取真实测试-漏洞RoarCTF2019-文件读取真题复现-比赛百度杯2017二月-Zone真题复现-比赛拓展 下载和读取都差不…...

Linux之history、tab、alias、命令执行顺序、管道符以及exit

目录 Linux之history、tab、alias、命令执行顺序、管道符以及exit history历史命令 格式 参数 修改默认记录历史命令条数 案例 案例1 --- 显示history历史记录中出现次数最高的top10 案例2 --- 增加history显示的时间信息 命令与文件名补全 --- tab 命令别名 格式 案…...

上海有哪几家做新房的网站/谷歌网页版登录入口

随着互联网的发展,越来越多的企业和个人想要制作自己的H5网站。你真的了解它吗?又该如何制作呢?下面就跟大家解答一下关于h5网页的相关问题。1.H5到底是什么?通俗地讲,H5是一个网页,就像一个很大的容器&…...

wordpress 摘要长度/网页制作教程视频

checkboxcheckbox1checkbox2checkbox3...

深圳网站建设怎样容易/中国十大搜索引擎网站

【絮语】在进行系统发育分析过程中,建树序列的进化模型选择是至关重要的一步,尤其对进化模型敏感的ML法和BI法,更是重中之重。对于一些新手而言,经常使用默认参数而忽略了这一关键步骤,从而导致建树结果的不理想。为此…...

公司网站二维码怎么做的/百度seo怎么收费

Cocos2d-x 3.0新引擎目录结构2014年4月29日 Cocos2d-x 3.0学习作为一个Cocos2d-x的菜鸟,我倒是挺愿意关注不同版本之间的区别,Cocos2d-x 3.0自2013年7月份开始发布alpha0-pre版本,到2014年4月23日发布正式版,历经9个版本&#xf…...

域名注册空间网站/百家号关键词排名

# encoding: utf-8 import sys reload(sys) sys.setdefaultencoding(utf-8)#######################Base64加密解密(可逆)################### # Base64编码,64指A-Z、a-z、0-9、和/这64个字符,还有“”号不属于编码字符,而是填充字符 import…...

wordpress配置文件是/十大基本营销方式

time和datetime的区别 之前,我们已经介绍过了python中的datetime模块,time的功能在很大程度上和datetime比较类似,也是用于处理日期和时间的。但是如果你要处理日期的话,使用datetime要更好。因此建议不需要通过time来处理日期&a…...