【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多个方法,搞得光记个函数名都头大。


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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CSS 字体修饰属性

前言 字体修饰属性 属性说明font-family指定文本显示字体font-size设置字体的大小font-weight设置字体的粗细程度font-style设置字体的倾斜样式text-decoration给文本添加装饰线text-indent设置文本的缩进text-align设置文本的对齐方式line-height设置行高color设置文本的颜色…

Shell脚本基础教程

Shell脚本基础教程 Shell参数定义 定义变量 想要定义变量,只需要使用如下命令即可。 variable_namevariable_valuevariable_name表示变量名,variable_value表示变量值。注意,等号与变量名和变量值之间不能有空格。 变量名的命名需要遵循…

C语言入门_Day7 逻辑运算

目录: 前言 1.逻辑运算 2.优先级 3.易错点 4.思维导图 前言 算术运算用来进行数据的计算和处理;比较运算是用来比较不同的数据,进而来决定下一步怎么做;除此以外还有一种运算叫做逻辑运算,它的应用场景也是用来影…

电脑远程接入软件可以进行文件传输吗?快解析内网穿透

电脑远程接入软件的出现,让我们可以在两台电脑之间进行交互和操作。但是,很多人对于这些软件能否进行文件传输还存在一些疑问。下面的文章将解答这个问题。 1.电脑远程接入软件可以进行文件传输。传统上,我们可能会通过传输线或者移动存储设…

Redis在Java中的基本使用

本片将介绍 Redis 在 Java 中的基本使用 文章目录 1、使用jedis操作redis1.1、Jedis简介1.2、引入jedis的Maven依赖1.2、获取连接1.3、使用实例 2、对于JedisPooled的使用2.1、使用JedisPooled2.2、关于连接池 3、SpringBoot下使用Redis3.1、引入Maven依赖3.2、配置Redis连接3.…

RabbitMq:Topic exchange(主题交换机)的理解和使用

RabbitMq:Topic exchange(主题交换机)的理解和使用 在RabbitMq中,生产者的消息都是通过交换机来接收,然后再从交换机分发到不同的队列中去,在分发的过程中交换机类型会影响分发的逻辑,下面主要讲解一下主题交换机。 ​ 主题交换…

pycharm上传项目到github,版本管理

前提:下载git 设置Git路径 登录Github 此时自动打开浏览器,并打开连接页面,点击 Authorize GitHub。登录: 创建本地仓库 提交到Github 填写初始提交相关信息 origin,它们只是远程服务器的一个别名,否则你就…

NPM 创建和管理组织

目录 1、创建一个组织 2、将用户帐户转换为组织 3、组织中开启双因素身份验证 3.1 关于组织的双因素身份验证 3.2 先决条件 3.3 在您的组织中要求双因素身份验证 3.4 帮助已删除的成员和外部协作者重新加入您的组织 4、重命名组织 5、删除组织 1、创建一个组织 任何n…

“Spring管理JavaBean的过程及Bean的生命周期“

目录 引言1.弹簧容器2. Bean的生命周期2.1 配置javaBean2.2. 解析Bean的定义2.3 检查是否需要添加自己的功能2.4 初始化2.5 实现Aware接口2.6 扩展2.7. 销毁 3. 单例模式和原型模式3.1. 单例模式3.2. 原型模式 4. 总结 引言 Spring框架是一个非常流行的Java应用程序框架&#…

【【verilog典型电路设计之流水线结构】】

verilog典型电路设计之流水线结构 下图是一个4位的乘法器结构,用verilog HDL 设计一个两级流水线加法器树4位乘法器 对于流水线结构 其实需要做的是在每级之间增加一个暂存的数据用来存储 我们得到的东西 我们一般来说会通过在每一级之间插入D触发器来保证数据的联…

【AIGC】 快速体验Stable Diffusion

快速体验Stable Diffusion 引言一、安装二、简单使用2.1 一句话文生图2.2 详细文生图 三、进阶使用 引言 stable Diffusion是一款高性能的AI绘画生成工具,相比之前的AI绘画工具,它生成的图像质量更高、运行速度更快,是AI图像生成领域的里程碑…

Linux/Ubuntu 的日常更新,如何操作?

我安装的是Ubuntu 20.04.6 LTS的Windows上Linux子系统版本,启动完成后显示: Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.15.90.4-microsoft-standard-WSL2 x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.c…

Azure不可变Blob存储

文章目录 Azure不可变Blob存储介绍Azure不可变性策略实战演练 Azure不可变Blob存储介绍 不可变的存储是一种用于存储业务关键型 Blob 数据的存储方式。与可变存储相反,不可变存储的特点是一旦数据被写入后,便无法再对其进行修改或删除。这种存储方式提供…

【2023最新爬虫】爬取知乎任意问题下的全部回答

老规矩,先上结果: 爬取了前200多页,每页5条数据,共1000多条回答。(程序设置的自动判断结束页,我是手动break的) 共爬到13个字段,包含: 问题id,页码,答主昵称,答主性别,…

Ubuntu一直卡死的问题(20.04)

Ubuntu一直卡死的问题(18.04)_ubuntu频繁死机_Mr.Yi的博客-CSDN博客 我自己的解决方法: 1、首先强制关机重启后,直接打开命令行查看磁盘的使用: df -h发现/dev/loop都沾满了,我们能需要做的就是把他们清理干净 sud…

【模拟集成电路】反馈系统——基础到进阶(二)

【模拟集成电路】反馈系统——基础到进阶(二) ------------------------------------------------文末附往期文章链接-------------------------------------- 1反馈结构2反馈系统分析2.1环路增益求解方法2.1二端口网络2.2电压-电压反馈2.2.1闭环增益2.2…

ant-design-vue在ios使用AUpload组件唤起了相机,HTML的 `capture` 属性

在使用ant design vue组件的上传组件AUpload的时候有一个问题&#xff0c;直接按照demo写&#xff0c;在ios上会唤起相机&#xff0c;但是实际上我们的需求是弹出选择相册/相机这个弹框。 解决办法是加一个 cupture"null"这个属性即可 <a-upload:capture"nu…

人工智能学习框架—飞桨Paddle人工智能

1.人工智能框架 机器学习的三要素&#xff1a;模型、学习策略、优化算法。 当我们用机器学习来解决一些模式识别任务时&#xff0c;一般的流程包含以下几个步骤&#xff1a; 1.1.浅层学习和深度学习 浅层学习(Shallow Learning)&#xff1a;不涉及特征学习&#xff0c;其特征…

【云原生】【k8s】Kubernetes+EFK构建日志分析安装部署

目录 EFK安装部署 一、环境准备&#xff08;所有主机&#xff09; 1、主机初始化配置 2、配置主机名并绑定hosts&#xff0c;不同主机名称不同 3、主机配置初始化 4、部署docker环境 二、部署kubernetes集群 1、组件介绍 2、配置阿里云yum源 3、安装kubelet kubeadm …

【无线点对点网络时延分析和可视化】模拟无线点对点网络中的延迟以及物理层和数据链路层之间的相互作用(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…