【C#学习笔记】C#特性的继承,封装,多态

在这里插入图片描述

文章目录

  • 封装
    • 访问修饰符
    • 静态类和静态方法
    • 静态构造函数
  • 继承
    • 继承原则
    • sealed修饰符
    • 里氏替换原则
    • 继承中的构造函数
  • 多态
    • 接口
      • 接口的实例化
    • 抽象类和抽象方法
      • 抽象类和接口的异同
    • 虚方法
    • 同名方法
      • new覆盖的父类方法
      • 继承的同名方法
    • 运行时的多态性
    • 编译时的多态性


照理继承封装多态应该是学习笔记中最先学习的。但是本系列不是面向新手的,基础的继承封装多态的概念应当是要被掌握的。而本文需要讲述C#中的一些继承封装多态的特性。

部分摘自C#学习笔记(二)— 封装、继承、多态

封装

封装,就是将类的属性和方法封闭起来,外部成员不可直接调用,只能通过预留的接口访问。

访问修饰符

在之前引用对象介绍class的时候,我们已经介绍过:
访问修饰符主要分为四种:

  • public:同一程序集中的任何其他代码或引用该程序集的其他程序集都可以访问该类型或成员。(public属性可被继承)
  • private:只有同一 class 或 struct 中的代码可以访问该类型或成员。(private属性不可被继承)
  • protected:只有同一 class 或者从该 class 派生的 class 中的代码可以访问该类型或成员。(只有该类和其子类可访问)
  • internal:同一程序集中的任何代码都可以访问该类型或成员,但其他程序集中的代码不可以。 换句话说,internal 类型或成员可以从属于同一编译的代码中访问。

我们可以用访问修饰符来定义类的访问类型
111
默认当类未定义访问修饰符时,访问级别是internal。而其内的成员的未定义访问修饰符时默认未private。

class Manager // 在命名空间中定义,默认为internal,可被同一程序集的另一个代码文件访问
{class Parent // 在类中定义的成员类,默认为private,不可被同一程序集的另一个代码文件访问{}
}

(注意,在命名空间中,类的访问修饰符只能为internal或public,因为其他访问修饰符在命名空间中访问不了,也就没有定义的必要了)

静态类和静态方法

使用static修饰符定义的类被称为静态类。静态类基本上与非静态类相同,但存在一个差异:静态类无法实例化。 换句话说,无法使用 new 运算符创建类类型的变量。 由于不存在任何实例变量,因此可以使用类名本身访问静态类的成员。

例如,如果你具有一个静态类,该类名为 UtilityClass,并且具有一个名为 MethodA 的公共静态方法,如下面的示例所示:

static class UtilityClass
{public static int a = 1;public static void MethodA() { }
}
static void main(){UtilityClass.MethodA();
}

使用静态类,我们不用实例化对象,而是直接调用类中的public方法。其内部成员只能是静态的。(而实际上其实普通类也可以通过类名访问静态成员)
无法继承,无法实例化,静态类的主要目的是提供一个容器来组织和管理相关的静态成员。

静态构造函数

当我们第一次访问静态类的时候,会调用一次静态构造函数。而第二次之后不会,因此起到了一个初始化的功能。

static class UtilityClass
{public static int a = 1;public static void MethodA() { }static UtilityClass(){Debug.Log("我是静态类的静态构造函数");}
}
class NormalClass
{public static void MethodA() { }static NormalClass(){Debug.Log("我是普通类的静态构造函数");}public NormalClass(){Debug.Log("我是普通类的实例构造函数");}
}
void Start()
{UtilityClass.MethodA(); // 我是静态类的静态构造函数NormalClass.MethodA(); // 我是普通类的静态构造函数UtilityClass.MethodA(); // 无NormalClass.MethodA(); // 无//UtilityClass u = new UtilityClass(); 错误,静态类无法实例化NormalClass n = new NormalClass(); // 我是普通类的实例构造函数
}

而如果单独实例化一次普通类:

void Start()
{NormalClass n = new NormalClass(); // n.MethodA(); 错误,实例化对象无法访问静态方法// 先后输出两行:// 我是普通类的静态构造函数// 我是普通类的实例构造函数
}

继承

继承就是在已存在的类基础上,建立一个新类,保留原有类的属性和方法,同时又可以增加新的内容。
 已存在的类称为基类或父类,继承的类称为派生类或子类。

继承原则

在C#中只有class,struct,interface可以继承,其中class可以继承唯一的父类和其他任意数量的接口类。struct和interface只能继承接口类。

在这里插入图片描述
每一层继承可以添加一些可被继承的方法给下一层。也有一些成员不可被继承。其关系请看上面写的访问修饰符。

当访问类中的成员时,就是逐级向上访问的,如果子类和父类有同名成员优先访问子类成员。

sealed修饰符

使用sealed修饰符,是为了让该类不能再被继承

sealed class Father{
}class Son:Father{  //错误,不可被继承
}

密封类的目的是不希望一些最底层的类被继承,加强面向对象的规范性,结构性和安全性。

里氏替换原则

任何基类可以出现的地方,派生类一定可以出现。从继承的角度来看,子类是在父类的基础上扩充的,自然比父类要全面。另一方面,要遵循里氏替换原则,子类中定义的一些方法也需要父类可用。

看个例子:

//基类
public class Mouse
{public void Dig(){Debug.Log("打洞");}
}
//派生类
public class MouseSon : Mouse
{public void Dig(){Debug.Log("不会打洞");}
}
static void main()
{Mouse m1 = new Mouse();m1.Dig(); // 打洞MouseSon m2 = new MouseSon();m2.Dig(); // 不会打洞,与父类方法重名时优先访问该类中的方法
}

老鼠的儿子不会打洞,就违反了里氏替换原则。这种情况下父类能用的时候子类是不能用的,子类无法替换父类。

通常里氏替换原则有两种基本要求:

  1. 可以用子类对象代替父类对象
  2. 父类容器包含子类对象时与1实现相同
//基类
public class Mouse
{public void Dig(){Debug.Log("打洞");}
}
//派生类,变异老鼠儿子,可以飞
public class MouseSon : Mouse
{public void Fly(){Debug.Log("飞");}
}
static void main()
{Mouse m1 = new Mouse();m1.Dig(); // 打洞MouseSon m2 = new MouseSon();m2.Dig(); // 打洞m2.Fly(); // 飞Mouse m3 = new MouseSon();m3.Dig(); // 打洞// m3.Fly(); 老鼠爸爸不能飞
}

继承中的构造函数

当子类继承父类之后,如果要在子类定义构造方法,要么父类中不定义任何构造方法,要么父类定义一个无参数的构造方法:

class Parent
{// 父类无构造方法或定义下列无参数构造方法//public Parent()//{//Debug.Log("我是Parent");//}
}
class Son : Parent
{public Son(){Debug.Log("我是Son");}
}

但是如果父类中定义了带参数构造方法且没有定义无参数构造方法,则子类中定义构造方法(无论是否带参数)都报错。除非使用base关键字定义传回对应参数:

class Parent
{public Parent(int i){Debug.Log("我是Parent");}
}
class Son : Parent
{public Son(int i) : base(i){Debug.Log("我是Son");}
}

总的来说,子类的构造方法和父类的构造方法参数数量必须是对应的。如果父类构造方法至少接受一个参数,那么相应的子类构造方法需要接受参数并调用父类构造方法。


多态

接口

定义接口时我们需要用interface关键字定义,标准的接口命名格式需要在开头加上大写的I代表interface。 接口可以包含方法、属性、索引器、事件。不能包含任何其他的成员,例如:常量、字段、域、构造函数、析构函数、静态成员。

interface IHuman
{// 接口中不允许任何显式实现void Name();void Age();public int Make { get; set; }public string this[int index]{get;set;}
}

当继承接口时,也需要实现接口的所有方法,一个接口同样可以继承其他的接口,当一个接口继承多个其他接口后,这个接口被称为派生接口,其他接口被称为基接口。一个继承了派生接口的类,不仅需要实现派生接口中的所有方法,也需要实现基接口中的所有方法:

interface IBaseInterface
{void BaseMethod();
}
interface IEquatable : IBaseInterface
{bool Equals();
}
public class TestManager : IEquatable
{bool IEquatable.Equals(){throw new NotImplementedException();}void IBaseInterface.BaseMethod(){throw new NotImplementedException();}
}

接口的定义一般用于通用的行为,当多个类都需要实现这些行为,并且每个类实现该行为的方法都需要重写时,接口是十分必要的。

接口的实例化

在学习过程中,我本以为接口是无法实例化的,但事实上网络上很多博客的说法是错误的,C#中的接口是可以实例化的!

接口不能被直接实例化。它的成员通过实现该接口的任何类或者结构来实现。(MSDN)

public class SampleClass : IControl, ISurface
{void IControl.Paint(){System.Console.WriteLine("IControl.Paint");}void ISurface.Paint(){System.Console.WriteLine("ISurface.Paint");}
}SampleClass sample = new SampleClass();
IControl control = sample;
ISurface surface = sample;// The following lines all call the same method.
//sample.Paint(); // Compiler error.
control.Paint();  // Calls IControl.Paint on SampleClass.
surface.Paint();  // Calls ISurface.Paint on SampleClass.// Output:
// IControl.Paint
// ISurface.Paint

如上所示,当一个类继承了某接口,我们可以将这个类的实例隐式转换为改接口的实例。这个接口实例包含了object基类的四大方法和自身接口的方法,如果我们调用接口方法会发现接口实例的方法指向的是类重写的方法。这种方法的好处在于当一个类继承的接口拥有同名方法时,我们可以通过实例化接口来区分同名方法的调用。
当然也可以对未实现该接口的类显式转换为该接口,当然这并没有什么用,运行时是会报错的。


抽象类和抽象方法

抽象类和抽象方法的定义关键字是abstract,抽象方法必须要在抽象类中定义,并且抽象方法必须是public的。但是抽象类和接口一样不能被实例化,需要实现抽象类中所有的方法,重写抽象方法的关键词是override,抽象类也无法使用sealed进行修饰,因为它就是用于继承的目的才会被创建。

abstract class Parent
{protected int age = 1; public Parent() { }abstract public void callName();abstract public void callAge();
}
class Child : Parent
{public Child():base(){}public override void callName(){throw new NotImplementedException();}public override void callAge(){Debug.Log(age);}
}

抽象类毕竟也是类,比较特殊的是,虽然抽象类不可实例化,但它可以拥有一些实例化特性,例如属性或者构造方法等是可以不用abstract修饰的。子类也可以像继承了正常类一般使用。
如果一个抽象类继承了接口,那么它也需要实现接口中的方法,但是抽象类可以以抽象方法的形式来实现它:

    abstract class Parent:IHuman{protected int age = 1;public Parent() { }abstract public void callName();abstract public void callAge();public void Name(){throw new NotImplementedException();}abstract public void Age();}

抽象类可以实现一些具体的方法,也可以拥有具体的属性,除了可实现抽象方法外,其他的和普通的类也没有什么区别。

抽象类和接口的异同

相同点:抽象类和接口都需要继承来实现,都不可以被直接实例化,继承了它们的子类都需要实现其中的抽象(接口)方法,继承了接口的还需要实现其中的属性和索引器等
不同点:抽象类不可实例化,接口可以通过继承类的类型转换来间接实例化,抽象类可以有具体方法,接口只能定义函数头
网络上许多博客都把接口当做一个特殊的类,但我看来,接口就是接口,类就是类,这两个是完全不同的东西。甚至有些博客说什么“接口与抽象类的区别还有接口不能继承其他类,抽象类可以。”接口本来就不是类,只能继承接口,怎么继承类?

虚方法

使用virtual关键字来修改方法法、属性、索引器或事件声明,并使它们可以在派生类中被重写。 (静态属性不可使用virtual修饰符)

    class Animal{public virtual void Eat(){Debug.Log("吃素");}}class Lion:Animal{public override void Eat(){Debug.Log("吃肉");}}class Sheep:Animal{}

虚方法与使用抽象方法的区别在于:虚方法可以在任何类中使用,无论是普通类还是抽象类。而使用虚方法,我们可以灵活地决定是否需要在父类定义虚方法,而子类是否需要重写这个虚方法,这些都是自行决定的,不会有强制要求。正如同上述的例子,当需要重写时就用override重写虚方法,不需要就直接继承即可。此外,virtual关键字必须声明函数主体,这也意味着它无法用在接口或者抽象方法中。

        Lion lion = new Lion();lion.Eat(); // 吃肉Sheep sheep = new Sheep();sheep.Eat(); // 吃素Animal Cow = new Animal();Cow.Eat(); // 吃素Animal Tiger = new Lion();Tiger.Eat(); // 吃肉

同名方法

在子类中,可以使用一些同名的方法,主要是两种情形下:

  1. 覆盖父类方法时
  2. 继承的类和接口存在相同的方法名定义时

new覆盖的父类方法

举个例子:

    class Animal{public void Eat(){Debug.Log("吃素");}}class Lion:Animal{public new void Eat() // 是否使用new关键字都会覆盖,使用new来指示这是我们有意覆盖父类方法{Debug.Log("吃肉");}}class Sheep:Animal{}

根据上面继承的结构图,运行时会从子类到父类逐级查找方法,而子类new的同名方法覆盖了父类的方法,则会直接执行子类的方法。如果子类无定义则使用父类的方法:

        Lion lion = new Lion();lion.Eat(); // 吃肉Sheep sheep = new Sheep();sheep.Eat(); // 吃素Animal Cow = new Animal();Cow.Eat(); // 吃素Animal Tiger = new Lion();Tiger.Eat(); // 吃素

继承的同名方法

现在有一个如下的接口:

interface IEat
{void Eat();
}

现在我们有一个sheep类,它同时继承了AnimalIEat,定义如下:

    class Wolf : Animal, IEat{public void Eat(){Debug.Log("吃肉");}}void Start(){Wolf wolf = new Wolf();wolf.Eat(); // 吃肉IEat eat = wolf;eat.Eat(); // 吃肉}

我们发现上述情况下Eat()方法被同时重写了,Wolf类中的Eat方法覆盖了Animal类中的Eat方法,而同时也重写了接口的Eat方法。当出现同名方法时说明会同时覆写这些方法。而如果我们指定准确的方法名的话:

    class Wolf : Animal, IEat{public void Eat(){Debug.Log("吃肉");}void IEat.Eat(){base.Eat();}}void Start(){Wolf wolf = new Wolf();wolf.Eat(); // 吃肉IEat eat = wolf;eat.Eat(); // 吃素}

通过指明接口名可以准确定义不同方法。


运行时的多态性

细心的读者可能发现了:当我们用override重写虚方法时:

        Animal Tiger = new Lion();Tiger.Eat(); // 吃肉

而当我们覆盖原来的方法时:

        Animal Tiger = new Lion();Tiger.Eat(); // 吃素

在运行时,除了逐级向上查找要执行的方法(属性)之外,还会检查它是否是重写了virtual关键字,使用父类容器装子类对象时,如果虚方法已经被子类重写了,那么则会执行重写的方法。对于虚方法的执行只会执行第第一个查找到的overrider方法,talk is cheap,看看下面的例子:

    class Animal{public virtual void Eat(){Debug.Log("吃素");}}class Lion:Animal{public override void Eat(){Debug.Log("吃肉");}}class Tiger : Lion{public new void Eat(){Debug.Log("吃兔子");}}void Start(){Animal tiger = new Tiger();tiger.Eat(); // 吃肉}

上述例子中Eat()执行的是吃肉而非吃兔子,如果逐级上查的话第一个查找到的应当输出吃兔子。但是由于基类AniamlEat()是一个虚方法,所以执行的是逐级查找到的第一个被override重写的Eat()方法,所以是输出吃肉。

    class Tiger : Lion{public override void Eat() // 如果使用override再次重写{Debug.Log("吃兔子");}}void Start(){Animal tiger = new Tiger();tiger.Eat(); // 吃兔子Lion tiger = new Tiger();tiger.Eat(); // 吃兔子}

如果再次使用override重写,才会显示吃兔子,有意思的是尽管Lion类中的方法并不是virtual,我们还是重写了基类Animal的虚方法。

    class Lion:Animal{public sealed override void Eat(){Debug.Log("吃肉");}}class Tiger : Lion{public override void Eat() // 重写报错,Eat方法已经被密封了{Debug.Log("吃兔子");}}

如果我们不希望Tiger类在继承Lion后还重写Eat()方法,我们应当用sealed关键字来密封这个基类方法,保证它不会在继承Lion了之后再重写


编译时的多态性

在程序中,我们也需要用到一些同名但是不同参数的方法,这是为了方便使用同一个方法在不同情况下进行处理。我们称为重载(overload),例如:

    void Start(){HumanEat(吃点啥呢 ?);}string HumanEat(Animal meat) { return ""; }int HumanEat(Animal meat, Vegetable fruit) { return 1; }void HumanEat(Vegetable vegetable) { }

同样是人,同样是吃东西,但是不同的人吃的东西会不一样,素食主义者只吃素,大部分人荤素都吃,肉食主义者只吃肉。甚至有的时候我们需要函数的返回值也要做出区分。当我们需要调用HumanEat方法,又需要具体区分,重载是最好的选择,这样同一个函数就可以有灵活的调用,而不是把全部情况放在一个函数里,每多出一个情况,就重写原来函数,或是定义n多个方法,搞得光记个函数名都头大。


总结:本节的概念有点多,但每一个知识点都有其存在的意义,例如如果我们想要一个可以不用实例化,直接从命名空间调用的工具类,就应该使用静态类。使用静态类的静态构造函数保证初始化时的唯一调用。当使用不同的父类考虑继承的模式。当使用多态时考虑什么情况下应当使用什么样的多态?如果我们想要一个只有结构定义而没有具体实现,且不被实例化的基类就需要抽象类,当我们需要灵活的可继承的功能重写就需要接口。当我们需要多种情况下同种方法的实现就需要重载。如果希望多态性的体现,虚方法会优于覆盖方法。所有的面向对象特性都应当在实际使用时慎重考虑,设计时细心思考,根据经验来进行程序的设计。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/48318.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++笔记之std::move和右值引用的关系、以及移动语义

C笔记之std::move和右值引用的关系、以及移动语义 code review! 文章目录 C笔记之std::move和右值引用的关系、以及移动语义1.一个使用std::move的最简单C例子2.std::move 和 T&& reference_name expression;对比3.右值引用和常规引用的经典对比——移动语义和拷贝语…

Go语言入门指南:基础语法和常用特性解析(上)

一、Go语言前言 Go是一种静态类型的编译语言,常常被称作是21世纪的C语言。Go语言是一个开源项目,可以免费获取编译器、库、配套工具的源代码,也是高性能服务器和应用程序的热门选择。 Go语言可以运行在类UNIX系统——比如Linux、OpenBSD、M…

Red Hat Enterprise Linux (RHEL) 6.4 安装、redhat6.4安装

1、下载地址 Red Hat Enterprise Linux (RHEL) 6.4 DVD ISO 迅雷下载地址http://rhel.ieesee.net/uingei/rhel-server-6.4-x86_64-dvd.iso 2、创建虚拟机 3、redhat安装 选择第一个安装 Skip跳过检查 语言选择简体中文 键盘选择默认 选择基本存储设备 忽略所有数据 设置root密…

【ECCV2022】Swin-Unet: Unet-like Pure Transformer for Medical Image Segmentation

Swin-Unet: Unet-like Pure Transformer for Medical Image Segmentation 论文:https://arxiv.org/abs/2105.05537 代码:https://github.com/HuCaoFighting/Swin-Unet 解读:Swin-UNet:基于纯 Transformer 结构的语义分割网络 -…

并查集及其简单应用

文章目录 一.并查集二.并查集的实现三.并查集的基本应用 一.并查集 并查集的逻辑结构:由多颗不相连通的多叉树构成的森林(一个这样的多叉树就是森林的一个连通分量) 并查集的元素(树节点)用0~9的整数表示,并查集可以表示如下: 并查集的物理存储结构:并查集一般采用顺序结构实…

Qt与电脑管家4

折线图&#xff1a; #ifndef LINE_CHART_H #define LINE_CHART_H#include <QWidget> #include <QPainter> #include "circle.h" class line_chart : public QWidget {Q_OBJECT public:explicit line_chart(QWidget *parent nullptr); protected:void pa…

手机直播源码开发,协议讨论篇(三):RTMP实时消息传输协议

实时消息传输协议RTMP简介 RTMP又称实时消息传输协议&#xff0c;是一种实时通信协议。在当今数字化时代&#xff0c;手机直播源码平台为全球用户进行服务&#xff0c;如何才能增加用户&#xff0c;提升用户黏性&#xff1f;就需要让一对一直播平台能够为用户提供优质的体验。…

【私有GPT】CHATGLM-6B部署教程

【私有GPT】CHATGLM-6B部署教程 CHATGLM-6B是什么&#xff1f; ChatGLM-6B是清华大学知识工程和数据挖掘小组&#xff08;Knowledge Engineering Group (KEG) & Data Mining at Tsinghua University&#xff09;发布的一个开源的对话机器人。根据官方介绍&#xff0c;这是…

打开软件提示mfc100u.dll缺失是什么意思?要怎么处理?

当你打开某个软件或者运行游戏&#xff0c;系统提示mfc100u.dll丢失&#xff0c;此时这个软件或者游戏根本无法运行。其实&#xff0c;mfc100u.dll是动态库文件&#xff0c;它是VS2010编译的软件所产生的&#xff0c;如果电脑运行程序时提示缺少mfc100u.dll文件&#xff0c;程序…

【Linux】网络层协议:IP

我们必须接受批评&#xff0c;因为它可以帮助我们走出自恋的幻象&#xff0c;不至于长久在道德和智识上自我陶醉&#xff0c;在自恋中走向毁灭&#xff0c;事实上我们远比自己想象的更伪善和幽暗。 文章目录 一、IP和TCP之间的关系&#xff08;提供策略 和 提供能力&#xff09…

中英双语对话大语言模型:ChatGLM-6B

介绍 ChatGLM-6B 是一个开源的、支持中英双语的对话语言模型&#xff0c;基于 General Language Model (GLM) 架构&#xff0c;具有 62 亿参数。结合模型量化技术&#xff0c;用户可以在消费级的显卡上进行本地部署&#xff08;INT4 量化级别下最低只需 6GB 显存&#xff09;。…

罗勇军 →《算法竞赛·快冲300题》每日一题:“超级骑士” ← DFS

【题目来源】http://oj.ecustacm.cn/problem.php?id1810http://oj.ecustacm.cn/viewnews.php?id1023https://www.acwing.com/problem/content/3887/【题目描述】 现在在一个无限大的平面上&#xff0c;给你一个超级骑士。 超级骑士有N种走法&#xff0c;请问这个超级骑士能否…

【Liunx】冯诺伊曼体系结构

冯诺伊曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺伊曼体系。 到目前为止&#xff0c;我们所认识的计算机&#xff0c;都是由一个个硬件所组成的。 输入单元&#xff1a;键盘&#xff0c;鼠标&am…

情报与GPT技术大幅降低鱼叉攻击成本

邮件鱼叉攻击&#xff08;spear phishing attack&#xff09;是一种高度定制化的网络诈骗手段&#xff0c;攻击者通常假装是受害人所熟知的公司或组织发送电子邮件&#xff0c;以骗取受害人的个人信息或企业机密。 以往邮件鱼叉攻击需要花费较多的时间去采集情报、深入了解受…

Java【HTTP】什么是 Cookie 和 Session? 如何理解这两种机制的区别和作用?

文章目录 前言一、Cookie1, 什么是 Cookie2, Cookie 从哪里来3, Cookie 到哪里去4, Cookie 有什么用 二、Session1, 什么是 Session2, 理解 Session 三、Cookie 和 Session 的区别总结 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4d5; …

2023国赛数学建模A题B题C题D题资料思路汇总 高教社杯

本次比赛我们将会全程更新思路模型及代码&#xff0c;大家查看文末名片获取 之前国赛相关的资料和助攻可以查看 2022数学建模国赛C题思路分析_2022年数学建模c题思路_UST数模社_的博客-CSDN博客 2022国赛数学建模A题B题C题D题资料思路汇总 高教社杯_2022国赛a题题目_UST数模…

[保研/考研机试] KY212 二叉树遍历 华中科技大学复试上机题 C++实现

题目链接&#xff1a; 二叉树遍历_牛客题霸_牛客网二叉树的前序、中序、后序遍历的定义&#xff1a; 前序遍历&#xff1a;对任一子树&#xff0c;先访问根&#xff0c;然后遍历其左子树&#xff0c;最。题目来自【牛客题霸】https://www.nowcoder.com/share/jump/43719512169…

Apipost数据模型功能详解

在API设计和开发过程中&#xff0c;存在许多瓶颈&#xff0c;其中一个主要问题是在遇到相似数据结构的API时会产生重复性较多的工作&#xff1a;在每个API中都编写相同的数据&#xff0c;这不仅浪费时间和精力&#xff0c;还容易出错并降低API的可维护性。 为了解决这个问题&a…

注册中心/配置管理 —— SpringCloud Consul

Consul 概述 Consul 是一个可以提供服务发现&#xff0c;健康检查&#xff0c;多数据中心&#xff0c;key/Value 存储的分布式服务框架&#xff0c;用于实现分布式系统的发现与配置。Cousul 使用 Go 语言实现&#xff0c;因此天然具有可移植性&#xff0c;安装包仅包含一个可执…

MySql014——分组的GROUP BY子句和排序ORDER BYSELECT子句顺序

前提&#xff1a;使用《MySql006——检索数据&#xff1a;基础select语句》中创建的products表 一、GROUP BY子句基础用法 SELECT vend_id, COUNT(*) AS num_prods FROMstudy.products GROUP BY vend_id;上面的SELECT语句指定了两个列&#xff0c;vend_id包含产品供应商的ID&…