十五、抽象类与接口

抽象类与接口

在这里插入图片描述

solid设计原则

五个基本设计原则的首字母,然后通过这五种基本的设计原则,衍生出很多种的设计模式

抽象类

就是五个基本设计原则当中的oopen or close

某些软性规则需要服从,不然就会给自己或者他人带来麻烦

抽象类:至少有一个成员完全没有被实现的类

抽象类具有以下功能:

  • 抽象类不能实例化。

    正因为这个抽象成员没有被实现,所以调用的时候,就调用不了。所以是不允许实例化一个抽象类的。

    在这里插入图片描述

  • 抽象类可能包含抽象方法和属性。

    也就是说,有抽象成员的类一定是被abstract所修饰,但是抽象类不一定有抽象成员

    abstract class BaseExample {public int MyProperty { get; set; }
    }
    abstract class SecondeClass {protected abstract void Run();
    }
    
  • 无法使用 sealed 修饰符来修改抽象类,因为两个修饰符的含义相反。 sealed 修饰符阻止类被继承,而 abstract 修饰符要求类被继承。

  • 派生自抽象类的非抽象类,必须包含全部已继承的抽象方法和访问器的实际实现。

    在这里插入图片描述

  • 没有被实现的抽象成员,不能用private来修饰,因为需要子类可以看到去实现这个成员

    在这里插入图片描述

  • 当抽象类从基类继承虚方法时,抽象类可以使用抽象方法重写该虚方法。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    namespace AbstractExample {class BaseExample {internal virtual void Dowork() {Console.WriteLine("I'm doing work.");}}abstract class SecondClass : BaseExample {internal abstract override void Dowork();}class ThirdClass : SecondClass {internal override void Dowork() {Console.WriteLine("Yes, I'm studying.");}}internal class Program {static void Main(string[] args) {ThirdClass thi = new ThirdClass();thi.Dowork();}}
    }
    

抽象成员

抽象方法具有以下功能:

  • 抽象方法是隐式的虚拟方法。

    纯粹的虚拟方法,连方法体都没有

  • 只有抽象类中才允许抽象方法声明。

  • 由于抽象方法声明不提供实际的实现,因此没有方法主体;方法声明仅以分号结尾,且签名后没有大括号 ({ })。 例如:

    public abstract void MyMethod();  
    

    实现由方法 override 提供,它是非抽象类的成员。

  • 在抽象方法声明中使用 static 或 virtual 修饰符是错误的。

    因为会冲突:

    virtual表示可被子类所重写,但是抽象方法是必须被子类所重写

    static表示类的静态方法,是不能被重写的

除了声明和调用语法方面不同外,抽象属性的行为与抽象方法相似。

  • 在静态属性上使用 abstract 修饰符是错误的。
  • 通过包含使用 override 修饰符的属性声明,可在派生类中重写抽象继承属性。

综合使用

抽象类的综合使用

using System;
using System.Collections.Generic;
using System.Linq;
/* 为做基类而生的抽象类 与 开放/关闭原则* 都在上面有介绍*/
namespace AbstractExample {internal class Program {static void Main(string[] args) {Vehicle v = new RaceCar();v.Run();Console.ReadLine();}}abstract class Vehicle {//交通工具公有的方法,并且不会改变的public virtual void Stop() {Console.WriteLine("Stopped!");}public virtual void Fill() {Console.WriteLine("Pay and fill...");}//公有的Run方法,但是不同的交通工具的具体行为不同//这个方法在Vehicle中用不到,就可以变为抽象方法//于是类就变成抽象类public abstract void Run();}class Car : Vehicle {public override void Run() {Console.WriteLine("Car is running...");}}class Truck : Vehicle {public override void Run() {Console.WriteLine("Truck is running...");}}class RaceCar : Vehicle {public override void Run() {Console.WriteLine("Race Car is running...");}}
}

下面是完全抽象类,一般都不会出现完全抽象类,因为这种完全抽象类被接口代替了

using System;
using System.Collections.Generic;
using System.Linq;
/* 为做基类而生的抽象类 与 开放/关闭原则* 都在上面有介绍*/
namespace AbstractExample {internal class Program {static void Main(string[] args) {Vehicle v = new RaceCar();v.Run();Console.ReadLine();}}//纯抽象类//在C#中,这种纯抽象类,就是接口,会被接口替代//一般不会这么写abstract class VehicleBase {public abstract void Stop();public abstract void Run();public abstract void Fill();}abstract class Vehicle : VehicleBase{//交通工具公有的方法,并且不会改变的public override void Stop() {Console.WriteLine("Stopped!");}public override void Fill() {Console.WriteLine("Pay and fill...");}//公有的Run方法,但是不同的交通工具的具体行为不同//这个方法在Vehicle中用不到,就可以变为抽象方法//于是类就变成抽象类//public abstract void Run();//当继承一个抽象类,但是不实现某个抽象方法时,也就不用写出来了}class Car : Vehicle {public override void Run() {Console.WriteLine("Car is running...");}}class Truck : Vehicle {public override void Run() {Console.WriteLine("Truck is running...");}}class RaceCar : Vehicle { public override void Run() {Console.WriteLine("Race Car is running...");} }
}

开闭原则

开闭原则

  • 如果不是为了修Bug,或者是添加新的功能的话,闲着没事,不要老去修改一个类的代码,特别是类当中的函数成员的代码
  • 也就是说,我们应该去封装一些,不变的、稳定的、固定的、确定的成员而把那个不确定的,有可能改变的声明为抽象成员并且留给子类去实现所以抽象类和开闭原则,天生就是一对

一个完全抽象的类的上位替代就是接口:

三个示例对比着看。

using System;
using System.Collections.Generic;
using System.Linq;
/* 为做基类而生的抽象类 与 开放/关闭原则* 抽象类,在文档中有介绍* * 开闭原则:*  如果不是为了修Bug,或者是添加新的功能的话,*  闲着没事,不要老去修改一个类的代码,*  特别是类当中的函数成员的代码*  也就是说,我们应该去封装一些,不变的、稳定的、固定的、确定的成员*  而把那个不确定的,有可能改变的声明为抽象成员*  并且留给子类去实现* 所以抽象类和开闭原则,天生就是一对*/
namespace AbstractExample {internal class Program {static void Main(string[] args) {Vehicle v = new RaceCar();v.Run();Console.ReadLine();}}/* 改成接口* 接口本身,就包含了纯抽象类的这个含义* 里面的成员是不需要用abstract 和 public来修饰的* 因为默认为public和abstract的,避免重复* 加了反而会报错*/interface IVehicle {void Stop();void Run();void Fill();}abstract class Vehicle : IVehicle{//此时就是实现接口public void Stop() {Console.WriteLine("Stopped!");}//不再是重写,而是实现public void Fill() {Console.WriteLine("Pay and fill...");}//对于想留给子类实现的接口,可以声明为abstactpublic abstract void Run();}class Car : Vehicle {public override void Run() {Console.WriteLine("Car is running...");}}class Truck : Vehicle {public override void Run() {Console.WriteLine("Truck is running...");}}class RaceCar : Vehicle { public override void Run() {Console.WriteLine("Race Car is running...");} }
}

接口

抽象类是未完全实现逻辑的类。

接口是完全未实现逻辑的类。

在这里插入图片描述

接口的成员一定是private的,这决定了接口的本质。

接口的本质就是:服务的调用者 与 服务的提供者 之间的契约,既然是契约就必须是透明的,并且同时约束供需双方。

记住:

当类实现一个接口的时候,类与接口之间的关系也是紧耦合的。

当两个类之间的耦合通过一个接口来连接的时候,实际上是把两个类之间的耦合变成了,类—接口—类,这样的三者之间的耦合关系。两个类之间的关系是松了,但是和接口之间的关系紧了。

自顶向下的是需要非常了解技术、非常了解业务逻辑的人。

更多的时候,我们是在写代码的过程当中不断的重构代码,发现某个地方需要一个抽象类,然后就抽象出来。发现某个地方需要一个接口,就把接口抽象出来。慢慢这样自底向上建立起来的。

需求场景

使用接口前:

using System;
using System.Collections;namespace InterfaceExample {/* 对一组整数进行求和、求平均值*/internal class Program {static void Main(string[] args) {//整数存放的容器还不一样int[] nums1 = new int[]{1, 2, 3, 4, 5};ArrayList nums2 = new ArrayList() {1, 2, 3, 4, 5};Console.WriteLine(Sum(nums1));Console.WriteLine(Avg(nums1));//如果此时我们想要对ArrayList类型的数组也进行求和,//那么就必须得再把方法写一遍,并且更改参数列表Console.ReadKey();}//求和static int Sum(int[] nums) {int sum = 0;foreach (int i in nums) {sum += i;}return sum;}//求平均值,返回类型得是doublestatic double Avg(int[] nums) {double sum = Sum(nums);int count = 0;foreach (int i in nums) {count++;}return sum / count;}//ArrayList类型的求和static int Sum(ArrayList nums) {int sum = 0;foreach (int i in nums) {sum += (int)i;//拆箱}return sum;}//ArrayList类型的求平均值static double Avg(ArrayList nums) {double sum = Sum(nums);int count = 0;foreach (int i in nums) {count++;}return sum / count;}}
}

使用接口后:

using System;
using System.Collections;namespace InterfaceExample {/* 对一组整数进行求和、求平均值*/internal class Program {static void Main(string[] args) {/* 改用接口* 对需方的约束就是,提供的数组必须可以被迭代*/int[] nums1 = new int[]{1, 2, 3, 4, 5};ArrayList nums2 = new ArrayList() {1, 2, 3, 4, 5};Console.WriteLine(Sum(nums1));Console.WriteLine(Avg(nums1));//如果此时我们想要对ArrayList类型的数组也进行求和,//那么就必须得再把方法写一遍,并且更改参数列表Console.ReadKey();}//接口对供方的约束,就是遵守IEnumerable接口static int Sum(IEnumerable nums) {int sum = 0;foreach (int i in nums) {sum += i;}return sum;}//求平均值,返回类型得是doublestatic double Avg(IEnumerable nums) {double sum = Sum(nums);int count = 0;foreach (int i in nums) {count++;}return sum / count;}}
}

依赖关系的产生

依赖的同时就出现了耦合

using System;
using System.Collections;namespace InterfaceExample {/* 展示依赖与耦合的关系*/internal class Program {static void Main(string[] args) {var engine = new Engine();var car = new Car(engine);car.Run(3);Console.WriteLine(car.Speed);Console.ReadLine();}}class Engine {public int RPM { get; private set; }//外界不能设置这个属性public void Work(int gas) {//控制油门大小this.RPM = 1000 * gas;}}class Car {private Engine _engine;/* 因为这个字段是一个确定的类型* 此时Car这个类就和Engine紧耦合在了一起* 如果基础类Engine类出了问题,* 不管Car类写得再正确,也不会正确工作* 紧耦合要是出了问题,不仅程序不好调试,而且还是影响团队工作*/public Car(Engine engine) {_engine = engine;}public int Speed { get; private set; }public void Run(int gas) {_engine.Work(gas);this.Speed = _engine.RPM / 100;}}
}

因为使用了具体的类型,而产生了紧耦合的关系。

所以我们可以不使用一个具体的类型,而使用一个模糊的类型(该类型,可以在调用的时候,确定自己的类型,即接口类型),这样就没有了紧密的依赖关系。

引入接口解决耦合度过高

因为传入的类型是一个模糊的类型,这个类型只有在对象实例化的时候,才会确定传入的是什么类型。

这样的话,如果其中一个类出了问题,也不会那么难才能找到问题的出处了。

所以就巧妙的将两个类紧密的依赖关系,松开了一些。

这个耦合已经相当的松了。

using System;
using System.Collections;namespace InterfaceExample {/* 展示依赖与耦合的关系*/internal class Program {static void Main(string[] args) {var user = new PhoneUser(new NokiaPhone());user.UsePhone();Console.ReadKey();}}interface IPhone {//手机的四个功能void Dail();void PickUp();void Receive();void Send();}//用户类class PhoneUser {private IPhone _phone;/* 此时字段类型就不是某一个具体的类型了* 而是一个接口的类型,这样就可以解除紧耦合*/public PhoneUser(IPhone phone){_phone = phone;}public void UsePhone() { _phone.Dail();_phone.PickUp();_phone.Send();_phone.Receive();}}class NokiaPhone : IPhone {public void Dail() {Console.WriteLine("Nokia calling...");}public void PickUp() {Console.WriteLine("Hello! This is Tim!");}public void Receive() {Console.WriteLine("Nokia message ring...");}public void Send() {Console.WriteLine("Hello!");}}class EricssonPhone : IPhone {public void Dail() {Console.WriteLine("Ericsson calling...");}public void PickUp() {Console.WriteLine("Hi! This is Max!");}public void Receive() {Console.WriteLine("Ericsson ring...");}public void Send() {Console.WriteLine("Hi!");}}
}

还有更松的耦合,就是通过反射实现的,后面会讲。

  • 只要代码中有可以替换的地方,那么就一定会有接口的存在。接口就是为了解耦而生的。

  • 松耦合最大的好处就是,可以让功能的提供方变得可以替换。

    从而降低紧耦合的时候功能的提供方所带来的 高风险与高成本

依赖反转原则

五种原则当中的d

解耦在代码当中的表现就是依赖反转

自顶向下逐步求精的思维方式:将一个大问题,分成多个小问题,然后通过解决小问题,从而最后解决大问题。

在这里插入图片描述

在紧耦合的情况下,就会形成这种金字塔形。

依赖反转原则就是给我们一种思路用来平衡这种,自顶向下逐步求精的单一的思维方式。

依赖关系变化图:

依赖反转,从这个图中来看,就是箭头的反转。

原本是Driver指向Car类,现在变成了Driver类指向了IVehicle接口,Car类也指向了Ivehicle接口

在这里插入图片描述

再深入,问号那个地方就是设计模式了

单元测试

单元测试 就是 依赖反转原则在开发当中的直接应用直接收益者

接口、解耦、依赖反转原则被单元测试所应用:

背景:生产电扇的厂商,电扇中有电源。转得快就需要高的电流,转得慢就需要低的电流,有一个电流保护的功能,电流过大就会警告或者断开。

紧耦合

using System;
using System.Collections;namespace InterfaceExample {internal class Program {static void Main(string[] args) {var fan = new DeskFan(new PowerSupply());Console.WriteLine(fan.Work());Console.ReadLine();}}class PowerSupply {public int GetPower() {return 100;}}class DeskFan {private PowerSupply _powerSupply;//这里就是紧耦合public DeskFan(PowerSupply powerSupply){_powerSupply = powerSupply;}public string Work() {int power = _powerSupply.GetPower();if (power <= 0){return "Won't work.";}else if(power < 100) {return "Slow";}else if(power < 200) {return "Work fine";} else {return "Warning!";}}}
}

使用接口松耦合:

using System;
using System.Collections;namespace InterfaceExample {internal class Program {static void Main(string[] args) {var fan = new DeskFan(new PowerSupply());Console.WriteLine(fan.Work());Console.ReadLine();}}interface IPowerSupply {int GetPower();}/* 现在就是自底向上式的* 现在发现需要一个接口来解耦*/class PowerSupply : IPowerSupply {public int GetPower() {/* 可以专门建立一个用于测试的类,* 专门输出超出范围的电流* 而这个过程,应该在单元测试中完成*/return 100;}}class DeskFan {private IPowerSupply _powerSupply;//这里就是紧耦合public DeskFan(IPowerSupply powerSupply){_powerSupply = powerSupply;}public string Work() {int power = _powerSupply.GetPower();if (power <= 0){return "Won't work.";}else if(power < 100) {return "Slow";}else if(power < 200) {return "Work fine";} else {return "Warning!";}}}
}

单元测试

给测试项目添加被测试项目的依赖

在这里插入图片描述

在这里插入图片描述

不知道为什么使用xUnit运行不成功,换了一个Unit类型的就测试成功了。

测试项目中的代码

也就是在松耦合后,创建测试项目,然后测试原项目。

using InterfaceExample;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;namespace UnitTestProject1 {[TestClass]public class UnitTest1 {[TestMethod]public void TestMethod1() {var fan = new DeskFan(new PowerSupplyLowerThanZero());var expected = "Won't work.";var actual = fan.Work();Assert.AreEqual(expected, actual);}[TestMethod]public void TestMethod2() {var fan = new DeskFan(new PowerSupplyHigherThan200());var expected = "Warning!";var actual = fan.Work();Assert.AreEqual(expected, actual);}}class PowerSupplyLowerThanZero : IPowerSupply {public int GetPower() {return 0;}}class PowerSupplyHigherThan200 : IPowerSupply {public int GetPower() {return 220;}}
}

每次测试,都要创建一个类,就不太方便。

我们可以使用moq插件来帮我们生成所需要类型的实例,这样就不用每次都去创建类了。

在这里插入图片描述

使用Moq插件后的测试代码:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using Moq;namespace MoqExample.Tests {[TestClass]public class UnitTest1 {[TestMethod]public void TestMethod1() {//使用Moq来创建实例var mock = new Mock<IPowerSupply>();mock.Setup(ps => ps.GetPower()).Returns(-1);/* 创建一个实现IPowerSupply接口的名叫ps的实例* 然后设置ps的GetPower方法的返回值为0* 最后的()=>0是一个lambda1表达式* 也支持不适用表达式赋值,可以直接赋值 */var fan = new DeskFan(mock.Object);var expected = "Won't work.";var actual = fan.Work();Assert.AreEqual(expected, actual);}[TestMethod]public void TestMethod2() {var mock = new Mock<IPowerSupply>();mock.Setup(ps => ps.GetPower()).Returns(() => 220);var fan = new DeskFan(mock.Object);var expected = "Warning!";var actual = fan.Work();Assert.AreEqual(expected, actual);}}//class PowerSupplyLowerThanZero : IPowerSupply {//    public int GetPower() {//        return 0;//    }//}//class PowerSupplyHigherThan200 : IPowerSupply {//    public int GetPower() {//        return 220;//    }//}
}

显式实现接口

显式实现接口的场景:

当一个类实现了多个接口时,在不同接口中有相同的方法需要实现,这时候就可以用到显式实现接口。显式实现接口的特点是要在实现接口方法时在方法名前面加上接口名,在调用时也不能直接使用类对象的引用,而是只能使用接口的对象引用去调用,不然根本找不到相应的方法(因为有冲突)。

如果一个类实现的两个接口包含签名相同的成员,则在该类上实现此成员会导致这两个接口都将此成员用作其实现。

using System;namespace ExplicitInterface {internal class Program {static void Main(string[] args) {Simple simple = new Simple();IControl control = simple;ISurface surface = simple;simple.paint();control.paint();surface.paint();Console.ReadLine();//这就是对接口的实现//这就是对接口的实现//这就是对接口的实现}}public interface IControl {void paint();}public interface ISurface {void paint();}class Simple : IControl, ISurface {public void paint() {Console.WriteLine("这就是对接口的实现");}}
}

若要调用不同的实现,根据所使用的接口,可以显式实现接口成员。 显式接口实现是一个类成员,只通过指定接口进行调用。 通过在类成员前面加上接口名称和句点可命名该类成员。

类成员 IControl.Paint 仅通过 IControl 接口可用,ISurface.Paint 仅通过 ISurface 可用。 这两个方法实现相互独立,两者均不可直接在类上使用。

using System;namespace ExplicitInterface {internal class Program {static void Main(string[] args) {Simple simple = new Simple();IControl control = simple;ISurface surface = simple;//simple.paint();/* 类成员 IControl.Paint 仅通过 IControl 接口可用,* ISurface.Paint 仅通过 ISurface 可用。 * 这两个方法实现相互独立,两者均不可直接在类上使用。*/control.paint();surface.paint();Console.ReadLine();}}public interface IControl {void paint();}public interface ISurface {void paint();}class Simple : IControl, ISurface {void IControl.paint() {Console.WriteLine("这就是对控制接口的实现");}void ISurface.paint() {Console.WriteLine("这就是对表面接口的实现");}}
}
  • 显式接口声明中不允许使用该关键字。在这种情况下,请从显式接口声明中删除关键字。public public

    显式接口实现没有访问修饰符,因为它不能作为其定义类型的成员进行访问。 而只能在通过接口实例调用时访问。 如果为显式接口实现指定访问修饰符,将收到编译器错误 CS0106。

  • 显式接口声明中不允许使用 abstract 关键字,因为显式接口实现永远无法重写。

注:显式实现还用于处理两个接口分别声明名称相同的不同成员(例如属性和方法)的情况。 若要实现两个接口,类必须对属性 P 或方法 P 使用显式实现,或对二者同时使用,从而避免编译器错误。

在接口中声明的成员定义一个实现。 如果类从接口继承方法实现,则只能通过接口类型的引用访问该方法。 继承的成员不会显示为公共接口的一部分。 (这个功能好像是新功能,在某些版本不能使用,就不展示了,反正在微软的文档上面抄的 显式接口实现 - C# 编程指南 - C# | Microsoft Learn)

另外,显式实现接口还有一种用法,就是隐藏方法。当一个类在实现接口的过程中不想实现其中的某个方法时,可以使用显式实现的方式实现此方法,并提供一个空实现,这样类的实例在调用方法时就看不到这个不想被使用的方法了。

using System;namespace ExplicitInterface {internal class Program {static void Main(string[] args) {var simple = new SampleClass();simple.Run();Console.ReadLine();}}public interface IControl {void Paint();void Run();}public class SampleClass : IControl {void IControl.Paint() {}//隐式实现接口必须,带public修饰符//不然会报错public void Run() {Console.WriteLine("韩跑跑");}}
}

总结

这一张中,比较重要的就是抽象类与开闭原则,还有接口与依赖反转原则了,还有单元测试。

抓主要作用,显式接口这种,不是特别重要的,可以粗略看看就行了。
承的成员不会显示为公共接口的一部分。 (这个功能好像是新功能,在某些版本不能使用,就不展示了,反正在微软的文档上面抄的 显式接口实现 - C# 编程指南 - C# | Microsoft Learn)

另外,显式实现接口还有一种用法,就是隐藏方法。当一个类在实现接口的过程中不想实现其中的某个方法时,可以使用显式实现的方式实现此方法,并提供一个空实现,这样类的实例在调用方法时就看不到这个不想被使用的方法了。

using System;namespace ExplicitInterface {internal class Program {static void Main(string[] args) {var simple = new SampleClass();simple.Run();Console.ReadLine();}}public interface IControl {void Paint();void Run();}public class SampleClass : IControl {void IControl.Paint() {}//隐式实现接口必须,带public修饰符//不然会报错public void Run() {Console.WriteLine("韩跑跑");}}
}

总结

这一张中,比较重要的就是抽象类与开闭原则,还有接口与依赖反转原则了,还有单元测试。

抓主要作用,显式接口这种,不是特别重要的,可以粗略看看就行了。

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

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

相关文章

动态代理详解

动态代理 一、JDK动态代理二、CGLIB动态代理三、Javassist动态代理技术 在程序运行阶段&#xff0c;在内存中动态生成代理类&#xff0c;被称为动态代理&#xff0c;目的是为了减少代理类的数量。解决代码复用的问题。 一、JDK动态代理 DK动态代理技术&#xff1a;只能代理接口…

如何做代币分析:以 ARB 币为例

作者&#xff1a;lesleyfootprint.network 编译&#xff1a;mingfootprint.network 数据源&#xff1a;ARB 代币仪表板 &#xff08;仅包括以太坊数据&#xff09; 在加密货币和数字资产领域&#xff0c;代币分析起着至关重要的作用。代币分析指的是深入研究与代币相关的数据…

代码随想录算法训练营第三天|203.移除链表元素

题目&#xff1a; 203. 移除链表元素 已解答 简单 相关标签 相关企业 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val …

MySQL 存储过程(超详细)

一、什么是存储过程&#xff1f; 存储过程可称为过程化SQL语言&#xff0c;是在普通SQL语句的基础上增加了编程语言的特点&#xff0c;把数据操作语句(DML)和查询语句(DQL)组织在过程化代码中&#xff0c;通过逻辑判断、循环等操作实现复杂计算的程序语言。换句话说&#xff0c…

K倍区间(蓝桥杯)

文章目录 K倍区间题目描述前缀和数学优化代码部分解释 K倍区间 题目描述 给定一个长度为 N的数列&#xff0c;A1,A2,…AN&#xff0c;如果其中一段连续的子序列 Ai,Ai1,…Aj 之和是 K的倍数&#xff0c;我们就称这个区间 [i,j]是 K倍区间。 你能求出数列中总共有多少个 K倍区…

上海雷卯可以解决YPbPr/ YCbCr接口 ESD/EOS静电浪涌问题

YPbPr /YCbCr 接口传输的是视频信号&#xff0c;不传输音频信号。YPbPr 和 YCbCr 都是视频信号的颜色编码格式&#xff0c;多应用于机顶盒&#xff08;Set-top box&#xff09;,TV电视&#xff0c;投影仪&#xff0c;游戏机和DVD播放器。 YPbPr&#xff1a;是一种模拟视频接口…

05-调用API

上一篇&#xff1a; 04-JNI函数 调用 API 允许软件供应商将 Java VM 加载到任意本地应用程序中。供应商可以提供支持 Java 的应用程序&#xff0c;而无需链接 Java VM 源代码。 5.1 概述 下面的代码示例说明了如何使用调用 API 中的函数。在这个示例中&#xff0c;C 代码创建了…

Discuz论坛网站报错Discuz!Database Error(0)notconnect的解决办法

运营服务器大本营有段时间了&#xff0c;在运营期间遇到两次Discuz&#xff01;Database Error&#xff08;0&#xff09;notconnect报错&#xff0c;和你们分享遇到Discuz报错的解决办法&#xff0c;希望可以帮助到你。 首先网站报错&#xff08;0&#xff09;notconnect&…

【Web】浅聊Java反序列化之C3P0——JNDI注入利用

目录 简介 原理分析 EXP 前文&#xff1a;【Web】浅聊Java反序列化之C3P0——URLClassLoader利用 【Web】浅聊Java反序列化之C3P0——不出网Hex字节码加载利用 简介 出网的情况下&#xff0c;这个C3P0的Gadget可以和fastjson&#xff0c;Snake YAML , JYAML,Yamlbeans , …

如何防御udp攻击

UDP Flood是互联网上最经典的DDoS&#xff08;Distributed Denial of Service&#xff09;攻击之一。攻击者在短时间内向目标设备发送大量的UDP报文&#xff0c;导致链路拥塞甚至网络瘫痪。一般的UDP报文由攻击工具伪造&#xff0c;通常在数据段具备相同的特征&#xff0c;另一…

【嵌入式高级C语言】9:万能型链表懒人手册

文章目录 序言单向不循环链表拼图框架搭建 - Necessary功能拼图块1 创建链表头信息结构体 - Necessary2 链表头部插入 - Optional3 链表的遍历 - Optional4 链表的销毁 - Necessary5 链表头信息结构体销毁 - Necessary6 获取链表中节点的个数 - Optional7 链表尾部插入 - Optio…

网站开发之旅:从概念到实现

在我成为一名专业的网站开发者的过程中&#xff0c;我有幸参与了多个激动人心的项目。其中&#xff0c;一个我印象尤为深刻的经历是&#xff0c;开发一个名为“文案推荐网”的主题网站&#xff08;www.zimeiti.love&#xff09;。这个项目不仅让我深入了解了网站开发的各个方面…

excel常用操作备忘

目录 快捷键基础数据透视图统计某列的值出现的频数 数据有效性数据分列运算符顺序文本匹配中的通配符错误的类型&#xff08;常与IF嵌套使用&#xff09;函数RANK(num, ref, [order])MID(str, start, len)逻辑函数混合函数选取整列AVERAGEIF(range, criteria, average_range)TR…

【软件测试面试】银行项目测试面试题+答案(一)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 面试题&#xff1…

Unity类银河恶魔城学习记录9-2 P83 Explosive crystal源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Crystal_Skill_Controller using System.Collections; using System.Colle…

【Linux】--- Linux编译器-gcc/g++、调试器-gdb、项目自动化构建工具-make/Makefile 使用

目录 一、Linux编译器-gcc/g1.1 gcc/g 使用方法1.2 程序的翻译过程1.3 链接 -- 动静态链接特点及区别 二、Linux调试器-gdb2.1 背景2.2 使用方法 三、Linux项目自动化构建工具-make/Makefile3.1 背景3.2 原理3.3 项目清理 一、Linux编译器-gcc/g 1.1 gcc/g 使用方法 格式&…

如何在Windows系统部署Jellyfin Server并实现公网访问内网影音文件

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

用BIO实现tomcat

一、前言 本课程的难度较高&#xff0c;需要将Servlet原理和IO课程全部学完。 二、当前项目使用方式 (1).自定义servlet 自定义servlet需要实现WebServlet并且实现name和urlMapping 重启进行访问 http://localhost:8090/myServlet (2).自定义html 重启进行访问 http://loc…

项目的搭建与配置

vue create calendar_pro 选择如下配置选项 安装 vue3 支持 vue add vue-next package.json 关闭 eslint 检测。 vue.config.js 配置跨域同源策略。 const { defineConfig } require(vue/cli-service) module.exports defineConfig({transpileDependencies: true,devServe…

微服务:Docker篇

1. 初识Docker 1.1. 什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。 在数百上千台服务中重复部署&#xff0c;环境不一定一…