类和对象(5)——抽象类和接口

目录

1. 抽象类

1.1 抽象类的概念

1.2 抽象类语法:abstract关键字

1.3 抽象类的特性

1.4 抽象类的作用

2. 接口

2.1 接口的概念

2.2 接口语法:interface关键字

2.3 接口的实现:implements关键字

2.4 接口的特性

2.5 实现多个接口

2.6 接口间的继承

2.7 接口的多态性

2.8 再谈instanceof

2.8.1 检查接口的实现

2.8.2 类型可见性问题

2.8.3 易混淆的可见性

3. 抽象类与接口的区别


1. 抽象类

1.1 抽象类的概念

在面向对象的概念中,所有的对象都是通过类来描绘的;但是反过来,并不是所有的类都是用来描绘对象的,如果 一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类

  • 抽象类是一种不能被实例化,只能用作其他类的父类的类。它通常用于定义一组具有共同特征和行为的子类的基础框架,这些共同特征和行为以抽象方法的形式呈现,子类需要提供这些抽象方法的具体实现。

1.2 抽象类语法:abstract关键字

那么在java语言中,抽象类要如何表示呢?

java提供了一个abstract关键字,被该关键字修饰的类就是抽象类。

抽象类的语法:

(其他修饰词)  abstract  class 抽象类名{

        ……

}

abstract除了可以修饰类,还可以修饰成员方法使其成为抽象方法。需要注意的是,抽象方法只能存在于抽象类当中

抽象方法的语法:

(其他修饰词)  abstract  class 抽象类名{

        ……

        (其他修饰词)  abstract  返回值类型  抽象方法名 (参数表);   //抽象方法无具体实现

        ……

}

例如:

public abstract class Animal {
    private String name;
    private int age;
    //抽象方法
    abstract public void eat();
}

1.3 抽象类的特性

抽象类具有很多的特性和使用要求,我在下面一一列举出来。

1. 抽象类不能被直接实例化成对象。

以上面抽象类Animal为例:

其实这样的要求也很好理解,因为抽象类的产生就不是为了描述对象的,它也没有包含足够的信息来描绘一个具体的对象,必须由它的非抽象子类来实例化对象。


2. 抽象类被继承后,其子类要重写父类中的抽象方法否则子类也必须设为抽象类,被abstract修饰。

子类Dog没有重写抽象方法,且没有设为抽象类而报错:

package demo3;    //包名:demo3
//抽象父类Animal
public abstract class Animal {
    public String name;
    public int age;
    //抽象方法
    abstract public void eat();
}
//Dog类继承自抽象类
public class Dog extends Animal{
    public void bark(){
        System.out.println(name+"在汪汪叫");
    }
}

补充:非抽象子类需要重写的抽象方法都来自其直接父类的抽象方法

假如有A、B、C三个类,A是B的直接父类,B是C的直接父类;A和B都是抽象类,C是普通类。如果B重写了A中的部分抽象方法,那么C要重写的抽象方法有 A剩下的抽象方法 B新增加的抽象方法

例如:

​//抽象类Animal
public abstract class Animal {
    public String name;
    //2个抽象方法
    abstract public void eat();
    abstract public void sleep();
}
//抽象类Dog
public abstract class Dog extends Animal{
    //重写了抽象方法eat()
    public void eat(){
        System.out.println(name+"在吃狗粮");
    }
    //新增1个抽象方法walk()
    abstract public void walk();
}
//普通类 柯基
public class Corgi extends Dog{
    //报错:没有重写抽象方法sleep()和walk()
}

这里Dog继承自Animal,所以Dog包含成员变量name、父类方法eat和sleep 以及自己新增的抽象方法walk。

又因为Dog重写了抽象方法eat,eat方法在Dog类已经不是抽象方法,所以Dog类中只有sleep和walk两个抽象方法。

由于非抽象子类需要重写的抽象方法都来自其直接父类的抽象方法,所以柯基Corgi类必须要重写sleep和walk这两个直接来自Dog类的抽象方法。


3. 抽象方法不能被private、final和static修饰,因为抽象方法要被子类重写。


4. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量。


5. 抽象类中不一定有抽象方法,但是抽象方法只能存在于抽象类当中

无抽象方法的抽象类:

报错·含抽象方法的普通类:

1.4 抽象类的作用

抽象类本身不能被实例化,要想使用,只能创建该抽象类的子类。 然后让子类重写抽象类中的抽象方法。

有些同学可能会说了, 普通的类也可以被继承呀, 普通的方法也可以被重写呀, 为啥非得用抽象类和抽象方法呢?

确实如此. 但是使用抽象类相当于多了一重编译器的校验.

使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类 了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.

很多语法存在的意义都是为了 "预防出错", 例如我们曾经用过的 final 也是类似. 创建的变量用户不去修改, 不 就相当于常量嘛? 但是加上 final 能够在不小心误修改的时候, 让编译器及时提醒我们. 充分利用编译器的校验, 在实际开发中是非常有意义的

2. 接口

2.1 接口的概念

在现实生活中,接口的例子比比皆是,比如:笔记本上的USB口,电源插座等。

电脑侧面的USB口上,可以插:U盘、鼠标、键盘...所有符合USB协议的设备

电源插座的插孔上,可以插:电脑、电视机、电饭煲...所有符合规范的设备

通过上述例子可以看出:接口就是公共的行为规范标准。大家在实现时,只要符合规范标准,就可以通用

我们知道,在编程上我们用类来描述对象,像电脑、鼠标、U盘这些对象都可以用类来描述。自然的,我们也会有编程上的接口。

在Java中,接口可以看成是多个类的公共规范,也是一种引用数据类型。

2.2 接口语法:interface关键字

接口的语法格式 与 类的语法格式类似,将 class关键字 换成 interface关键字,就定义了一个接口。

接口的语法格式:

(其他限定词)  interface  接口名{

        ……

}

例如:写一个图形接口,规范是要实现画图形方法draw。

public interface IShape {
    void draw();    //画一个图形
}

注意:

  • 接口的命名采用大驼峰命名法。
  • 接口的命名一般以大写字母 I 开头,它的后一个字母也要大写。

2.3 接口的实现:implements关键字

接口不能直接使用,必须要有一个"实现类"来"实现"该接口。而实现指的是:重写接口中的所有抽象方法。(在接口中定义的方法都是抽象方法)

在java中,提供一个关键字implements来让我们完成对接口的实现

实现的语法:

(其他限定词) class 类名 implements 接口名{

        ……

        //重写的接口方法

}

例如:

//接口
public interface IShape {
    void draw();    //画一个图形
}

//实现类
public class Flower implements IShape{
    @Override
    public void draw() {
        System.out.println("画一朵花……");
    }
}

//测试
public class Test {
    public static void main(String[] args) {
        IShape iShape = new Flower();//向上转型
        iShape.draw();
    }
}

可以看到,实现了接口,可以通过向上转型的方式来调用接口函数,这是接口多态性的一种体现。

2.4 接口的特性

1. 与抽象类一样,接口虽是一种引用类型,但是不能直接new接口的对象

例如:


2. 接口中的成员变量都是被“ public static final ”修饰的。

也就是说,接口中的成员变量是一种静态全局常量。无论是无修饰(default),还是显式用public修饰、static修饰或final修饰,它们最终都是由“ public static final共同修饰

以下面的USB接口为例:

public interface USB {
    double brand = 10;
}


3. 接口中的方法都是由“ public abstract ”修饰。

无论是无修饰(default),还是显式用public修饰或abstract修饰,接口方法都是由public abstract共同修饰。也就是说接口方法都是公共的抽象方法,必须要求实现类进行方法重写,这体现了接口作为公共规范的作用

例如:

// 接口IShape

// Flower类

Flower类没有重写IShape接口的draw方法,由于接口方法都是抽象的,所以会报错。

补充:如果实现类没有重写接口的所有抽象方法,那么该实现类要设为抽象类


4. 由于接口的变量和方法都有固定的修饰符修饰,所以不能用其他修饰符 修饰接口的变量和方法。(比如private、protected)

错误例子:

补充:接口的变量和方法都有默认修饰符,一般建议接口的变量和方法前面都不写任何修饰符


5. 接口中不能有静态代码块动态代码块构造方法

静态代码块一般用来初始化静态变量,动态代码块和构造方法一般用来初始化成员变量,但是接口中的变量都是常量,不能修改。

错误例子:

public interface USB {
    int BRAND = 10;

    static {
         //静态代码块
    }

    {
         //实例代码块
    }

    USB(int brand){
        BRAND = brand;
    }
}


6. 实现类重写接口方法时,重写的方法的包访问权限不能比接口方法的权限要低。即:不能是无修饰词(default)、不能是protected、不能是private

例如:

【补充:其实 实现类的重写方法的访问权限在一种情况可以是无修饰词(default)的,这种情况我在第8点说】


7(小知识). 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class


8(拓展). 从Java8版本开始,接口中可以定义default方法(默认方法)。它允许在接口中为方法提供默认的实现。

default方法在接口中的定义方式:

  • 显示使用default关键字:在接口的方法签名前使用default关键字来定义一个默认方法。例如,default void methodName() {...}
  • 包含方法体:与接口中的抽象方法不同,默认方法是具体的方法实现,需要在接口中给出方法的具体代码逻辑

【注意】: 接口方法被default修饰后,就不能再被final和static修饰。

例如:

//图形·接口
public interface IShape {
    //抽象方法
    void draw1();
    //默认方法
    default  void draw2(){
        System.out.println("画两次图形");
    }
}
//圆形·类 :只重写抽象方法
public class Circle implements IShape{
    @Override
    public void draw1() {
        System.out.println("画一个圆……");
    }
}
//花形·类:抽象方法和默认方法都重写了
public class Flower implements IShape{
    @Override
    public void draw1() {
        System.out.println("画一朵花……");
    }

    @Override
    public void draw2() {
        System.out.println("画两朵花");
    }
}

public class Test {
    public static void main(String[] args) {
        Circle circle = new Circle();
        circle.draw1();
        circle.draw2();    //会使用接口的默认方法
        System.out.println("============================");
        Flower flower = new Flower();
        flower.draw1();
        flower.draw2();
    }
}

【这里的第8点特性属于拓展内容,了解即可】


2.5 实现多个接口

java中没有多继承,即一个子类不能继承自多个父类,但是一个类可以实现多个接口。

语法格式:(所有的接口都跟在implements的后面,每个接口之间用逗号隔开)

【无继承】

  class 实现类 implements 接口1,接口2,……,接口K{

          ……  //实现类的成员

  }

【有继承】(只能有一个父类) (“继承extends”要在“实现implements”前面)

  class 实现类 extends 父类名称 implements 接口1,接口2,……,接口K{

          ……  //实现类的成员

  }

下面演示如何用接口和继承来表示企鹅类:

先有父类Animal:

abstract public class Animal {
    protected String name;
    
    public Animal(String name){
         this.name = name;
    }
}

另外我们再提供一组接口, 分别表示 "会飞的", "会跑的", "会游泳的":

 interface IFlying {
    void fly();
 }
 
interface IRunning {
    void run();
 }
 
interface ISwimming {
    void swim();
 }

最后我们创建具体的动物——企鹅类(企鹅既能在路上走,也能在水中游

public class Penguin extends Animal implements IRunning,ISwimming{
    public Penguin(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(name+"用两只脚走路");
    }

    @Override
    public void swim() {
        System.out.println(name+"用两只翅膀游泳");
    }
}

tips:在IDEA中使用 ctrl + i 可快速实现接口。

2.6 接口间的继承

类与类之间可以有继承关系,那接口与接口之间可以有继承关系吗?

接口与接口之间可以有继承关系,而且接口可以多继承,即一个接口可以继承自多个接口。

语法格式:(继承语法是extends,每个父接口之间用逗号隔开)

interface 子接口 extends 父接口1,父接口2,……,父接口k{

        ……

}

青蛙是两栖动物,我们用接口间的继承可以这样表示:

interface IRunning {
    void run();
 }
 
interface ISwimming {
    void swim();
 }
 
// 两栖的动物:既能跑, 也能游
interface IAmphibious extends IRunning, ISwimming {
 
}
 
class Frog implements IAmphibious {
    ...
 }

2.7 接口的多态性

当实现类实现了接口,那么它就可以向接口类型进行向上转型。这体现了接口的多态性。

接口不仅仅是一种规范标准,它还是一种引用类型,这样的设计提高了代码的可重复性。

【实现多个接口】

实现类实现了多个接口,那么被实现的接口都可以接收实现类的向上转型。

例如:

//接口
public interface ISwimming {
    void swim();
}

public interface IRunning {
    void run();
}
//实现类
public class Penguin implements ISwimming,IRunning{
    @Override
    public void swim() {
        System.out.println("企鹅游泳");
    }

    @Override
    public void run() {
        System.out.println("企鹅走路");
    }
}

public class Dog implements IRunning{
    @Override
    public void run() {
        System.out.println("狗在奔跑");
    }
}

//多态方法
public class Test {
    public static void swim(ISwimming iSwimming){   //ISwimming接口类型作为多态类型参数
        iSwimming.swim();   //方法会根据动态绑定来使用
    }

    public static void run(IRunning iRunning){      //IRunning接口类型作为多态类型参数
        iRunning.run();
    }

    public static void main(String[] args) {
        Penguin penguin = new Penguin();
        swim(penguin);     //向上转型传参
        run(penguin);

        System.out.println("==============");
        Dog dog = new Dog();
        run(dog);
    }
}

这里的Penguin类实现了ISwimming接口和IRunning接口,所以swim方法和run方法可以完成对penguin的向上转型。


【接口间的继承】

如果子接口继承自父接口,而实现类“直接实现”的是子接口,那么父接口也可以接收实现类的向上转型

例如:

//IAmphibious接口继承自上面的ISwimming、IRunning接口
public interface IAmphibious extends ISwimming,IRunning{

}

//Penguin类直接实现IAmphibious接口
public class Penguin implements IAmphibious{
    @Override
    public void swim() {
        System.out.println("企鹅游泳");
    }

    @Override
    public void run() {
        System.out.println("企鹅走路");
    }
}

public class Test {
    public static void swim(ISwimming iSwimming){   //ISwimming接口类型作为多态类型参数
        iSwimming.swim();   
    }

    public static void run(IRunning iRunning){      //IRunning接口类型作为多态类型参数
        iRunning.run();
    }

    public static void main(String[] args) {
        Penguin penguin = new Penguin();
        swim(penguin);     
        run(penguin);
    }
}

  • Penguin类实现了Imphibious接口,所以Penguin可以向Imphibious转型。
  • 而Imphibious继承自ISwimming接口和IRunning接口,所以Imphibious可以向ISwimming接口和IRunning接口转型。
  • 于是乎Penguin可以向ISwimming接口和IRunning接口向上转型

【注意】

如果子接口继承自父接口,实现类直接实现的也是这个父接口,那么该实现类不能向子接口进行向上转型。

错误例子:

//父接口
public interface IRunning {
    void run();
}

//子接口:表示爬行类的动物
public interface ICreeping extends IRunning{
}

//实现类:蜥蜴
public class Lizard implements IRunning{
    @Override
    public void run() {
        System.out.println("蜥蜴在快速爬行");
    }
}

//用子接口ICreeping接收实现类Lizard
public class Test {
    public static void run(ICreeping iCreeping){      
        iCreeping.run();
    }

    public static void main(String[] args) {
        Lizard lizard = new Lizard();
        run(lizard);        //报错:Lizard类型与ICreeping类型不兼容
    }
}


接口多态性与继承多态性的对比:

2.8 再谈instanceof

2.8.1 检查接口的实现

我在《类和对象(4)——多态》中讲解过instanceof关键字,当时说过“该关键字不仅能判断类,还能判断接口”。但当时还没学到接口,现在让我们看看instanceof对于接口的作用。

instanceof的作用:(对象  instanceof  类名/接口名)

  • 对于类:检查对象是否指定类型的实例,或该类型的子类实例
  • 对于接口:检查对象是否实现了指定接口,或该接口的父接口

为什么instanceof检查类的时候,可以检查其子类,而在检查接口的时候,却是可以检查其父接口呢?

这就不得不说一下instanceof检查的实际用途了:

类和instanceof的用途:确保向下转型的安全,从而让子类实例变量接收向下转型后的父类实例变量,使用子类特有的功能

接口和instanceof的用途:确保对象具有某类多态行为,从而调用该多态方法

类的举例和解析:

abstract public class Animal {
    public String name;
    public Animal(String name){
        this.name = name;
    }
}

public class Dog extends Animal{
    public Dog(String name) {
        super(name);
    }

    public void eat(){
        System.out.println(super.name+"在吃狗粮");
    }
}

public class Corge extends Dog{
    public Corge(String name) {
        super(name);
    }

    public void eat(){
        System.out.println(name+"在吃科技合成肉");
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal1 = new Dog("小狗");
        if (animal1 instanceof Dog){    //检查为Dog类的实例
            Dog dog = (Dog) animal1;
            dog.eat();
        }

        Animal animal2 = new Corge("小柯基");
        if(animal2 instanceof Dog){     //检查为Dog类的子类实例
            Dog dog = (Dog) animal2;
            dog.eat();
        }
    }
}

  • Dog类和Corge类都有自己特有的eat方法
  • 在animal1中,由于对象的本质是Dog类的实例,故可以安全地将animal1向下转型成Dog类
  • 在animal2中,虽然对象的本质是Corge类的实例,但由于Corge类是Dog类的子类(即Corge类可以向上转型成Dog类),所以它也可以安全地将animal2向下转型成Dog类
  • 这就解释了:为什么instanceof能检查指定类型的子类

接口的举例和解析:

//父接口
public interface IRunning {
    void run();
}
public interface ISwimming {
    void swim();
}

//子接口
public interface IAmphibious extends ISwimming,IRunning{

}

//实现类
public class Penguin extends Animal implements IAmphibious{
    @Override
    public void swim() {
        System.out.println("企鹅游泳");
    }

    @Override
    public void run() {
        System.out.println("企鹅走路");
    }
}

//多态方法
public class Test {
    public static void run(IRunning iRunning){
        iRunning.run();
    }

    public static void main(String[] args) {
        Animal animal = new Penguin();  //向上转型

        if(animal instanceof IAmphibious){   检查到指定接口的父接口的run行为
            run( (IAmphibious)animal );         //此时调用的是Test类中的多态方法run
            //或写成((IAmphibious)animal).run();  此时调用的是自己的重写方法run
        }

        if(animal instanceof IRunning){      检查指定接口
            run((IRunning) animal);
            //或写成((IRunning)animal).run();
        }
    }
}

  • 子接口继承自多个父接口,实现类直接实现子接口 就相当于是 把所有父接口的行为都实现了一遍,那自然可以使用 以父接口类型为参数类型的多态方法
  • 这解释了:为什么instanceof能检查指定接口的父接口

小结:


2.8.2 类型可见性问题

如果我们将上面接口例子中Test类(含多态方法的那个)修改成下面这样会发生什么:

可以发现,我们把强制类型转换用的“(IAmphibious)”去掉后居然报错了。这是编译时类型对成员可见性的限制惹的祸。


要明白 “编译时类型对成员可见性的限制”,我们必需先了解编译时类型和运行时类型的概念。

编译时类型(也称 声明类型)

  • 变量在代码中声明的类型。(如" Parent parent;",此时实例变量parent的数据类型Parent就是编译时类型)(编译时类型也包括基础类型,如int、double)
  • 编译器仅根据此类型检查可访问的成员变量和方法。

运行时类型(也称 实际类型)

  • 对象实际所属的类型。(如“ new Child() ”)
  • 运行时动态决定,但编译时不可见

当用编译时类型的变量 访问 运行时类型的特有成员时,编译器检查发现该声明类型不具有访问特有成员的能力,直接触发了编译错误。这就是编译时类型对可见性的限制

  1. 强制转换的作用
    强制转换的本质是 告诉编译器,请按强转类型检查成员。编译器接受新的编译时类型,从而允许访问该类型定义的成员。

  2. 运行时验证
    强制转换可能在运行时失败(如 强转类型 与 对象实际类型 不符),因此需配合 instanceof 提前检查类型安全。


2.8.3 易混淆的可见性

上述发生的编译错误属于类型可见性的问题,而不是权限的问题。

成员可见性:

  • 在java中,权限指的就是各种访问修饰符
  • 成员可见性的控制机制是访问修饰符(比如public、protected),为每个成员变量和方法赋予权限。


类可见性:

  • 类可见性的控制机制也是访问修饰符,为类赋予访问权限。
  • 成员可见性是 成员变量和成员方法 被访问修饰符修饰,共有4种权限类可见性 被访问修饰符修饰,顶级类只有public类或默认类(default)2种
  • 类的可见性成员可见性的 前提。(如果顶级类是默认类,且内部所有成员都是public修饰的,那么不是同一个包的类就无法访问该顶级类及其所有成员。)

错误例子:

在包test1中有默认类Demo:

当我们在包test2中尝试使用Demo会报错:


包可见性:

  • 包可见性 = default 权限 = 包访问权限 = 仅包内可见。
  • 包可见性 包含于 成员可见性和类可见性当中。(包可见性是权限中的一种具体类型)

成员可见性和类的可见性通过访问修饰符定义,具体规则如下:


最终总结:

  1. 类可见性 和 成员可见性属于访问权限控制的同一维度(层级不同),且 类可见性是成员可见性的前提

  2. 包可见性是访问权限控制中的一种具体类型(默认修饰符),属于成员可见性和类可见性的一部分。

  3. 类型可见性是独立于访问控制的另一维度,属于编译时类型系统的限制。

3. 抽象类与接口的区别

核心区别: 抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不必重写),而接口中不能包含普通方法(但是可以包含default方法),子类必须重写所有的抽象方法。

下面的表格是抽象类和接口不同的地方:

抽象类和接口的相同之处:

  1. 可以没有任何成员。(此时的接口被称作空接口或标记接口)
  2. 抽象方法不能被private、static、final修饰。
  3. 子类/实现类 重写方法的包访问权限不能更低。
  4. 子类/实现类 没有重写完所有的抽象方法时,必须设置为抽象类。
  5. 字节码文件的后缀都是.class。

本期分享完毕,感谢大家的支持Thanks♪(・ω・)ノ

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

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

相关文章

kubectl exec 实现的原理

kubectl exec 是 Kubernetes 提供的一个命令,它允许你在指定的 Pod 中执行命令,类似于在容器中打开一个终端会话。这个功能对于调试、监控和管理容器化应用非常有用。kubectl exec 的实现涉及到多个 Kubernetes 组件和机制,包括 API Server、…

【ubuntu24.04】 强制重启导致大模型的磁盘挂载出错

挂载NTFS文件系统出错 各种模型放在了这个机械硬盘上,虽然速度慢,但是好在容量大。大模型在工作,但是程序看起来有问题,导致系统卡死了,然后我重启了,然后报错:wrong fs type bad option &…

【数据结构】 栈和队列

在计算机科学的世界里,数据结构是构建高效算法的基础。栈(Stack)和队列(Queue)作为两种基本且重要的数据结构,在软件开发、算法设计等众多领域都有着广泛的应用。今天,我们就来深入探讨一下栈和…

移动端测试的挑战与解决方案:兼容性、网络问题及实战策略

引言 移动应用已成为用户触达服务的核心入口,但移动端测试面临设备多样性、网络波动、用户场景复杂等多重挑战。据Statista统计,2023年全球活跃移动设备超180亿台,操作系统(Android/iOS)版本碎片化率超30%,这对测试工程师提出了极高要求。本文深度解析移动端测试的核心痛…

【设计模式】03-理解常见设计模式-行为型模式(专栏完结)

前言 前面我们介绍完创建型模式和创建型模式,这篇介绍最后的行为型模式,也是【设计模式】专栏的最后一篇。 一、概述 行为型模式主要用于处理对象之间的交互和职责分配,以实现更灵活的行为和更好的协作。 二、常见的行为型模式 1、观察者模…

matlab欠驱动船舶模型预测控制

1、内容简介 matlab135-欠驱动船舶模型预测控制 可以交流、咨询、答疑 2、内容说明 略 针对在风 、 浪 、 流时变干扰下欠驱动水面船舶的轨迹跟踪控制问题 , 设计了一种基于模型 预测控制的轨迹跟踪控制器 . 考虑到欠驱动船舶在没有横向驱动力情况下…

计算机性能与网络体系结构探讨 —— 基于《计算机网络》谢希仁第八版

(꒪ꇴ꒪ ),Hello我是祐言QAQ我的博客主页:C/C语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍快上🚘,一起学习,让我们成为一个强大的攻城狮&#xff0…

Linux上安装jdk1.8和配置环境变量

步骤一::创建jdk安装目录(该/usr/local/ ,最好把我们自己下载的放到这,容易区分) 可以省略 步骤二:查看安装程序 [rootVM_0_4_centos src]# rpm -qa | grep -i jdk 若之前安装过jdk,下次安装一定把之前的删除干净 下载地址链接…

【Spring+MyBatis】留言墙的实现

目录 1. 添加依赖 2. 配置数据库 2.1 创建数据库与数据表 2.2 创建与数据库对应的实体类 3. 后端代码 3.1 目录结构 3.2 MessageController类 3.3 MessageService类 3.4 MessageMapper接口 4. 前端代码 5. 单元测试 5.1 后端接口测试 5.2 使用前端页面测试 在Spri…

Windows环境安装部署minimind步骤

Windows环境安装部署minimind步骤 必要的软件环境 git git,可下载安装版,本机中下载绿色版,解压到本地目录下(如:c:\soft\git.win64),可将此路径添加到PATH环境变量中,供其他程序…

RocketMQ与kafka如何解决消息丢失问题?

0 前言 消息丢失基本是分布式MQ中需要解决问题,消息丢失时保证数据可靠性的范畴。如何保证消息不丢失程序员面试中几乎不可避免的问题。本文主要说明RocketMQ和Kafka在解决消息丢失问题时,在生产者、Broker和消费者之间如何解决消息丢失问题。 1.Rocket…

基于AIOHTTP、Websocket和Vue3一步步实现web部署平台,无延迟控制台输出,接近原生SSH连接

背景:笔者是一名Javaer,但是最近因为某些原因迷上了Python和它的Asyncio,至于什么原因?请往下看。在着迷”犯浑“的过程中,也接触到了一些高并发高性能的组件,通过简单的学习和了解,aiohttp这个…

Golang的代码结构规划

Golang的代码结构规划 是一种具有高效性能的开发语言,其代码结构规划对于项目的可维护性和可扩展性至关重要。在Golang中,合理的代码结构可以使代码更加清晰易懂,方便团队协作和项目维护。本文将介绍Golang代码结构规划的最佳实践&#xff0c…

【算法与数据结构】并查集详解+题目

目录 一,什么是并查集 二,并查集的结构 三,并查集的代码实现 1,并查集的大致结构和初始化 2,find操作 3,Union操作 4,优化 小结: 四,并查集的应用场景 省份…

服务器部署DeepSeek,通过Ollama+open-webui部署

1. 安装ollama 1.1. linux 安装 Ollama是目前常用的AI模式部署的第三方工具,能一键部署deepSeek Ollama官方网址https://ollama.com/ 选择Download下载对应的服务版本 服务器选择Linux,下面是下载代码 curl -fsSL https://ollama.com/install.…

(三)Axure制作转动的唱片

效果图 属性: 图标库:iconfont-阿里巴巴矢量图标库 方形图片转为圆角图片,裁剪,然后加圆角, 唱片和底图是两个图片,点击播放,唱片在旋转。 主要是播放按钮和停止按钮,两个动态面板…

5G时代的运维变革与美信监控易的深度剖析

一、5G普及后的网络运维新变化:数据驱动的挑战与机遇 (一)数据流量的爆炸式增长 在2025年,5G技术已经如同汹涌的浪潮席卷全球。据相关科技数据显示,5G网络的普及使得数据流量呈现出令人咋舌的增长态势。 这种海量的数…

BGP配置华为——RR反射器配置

实验拓扑 与之前实验同理将loop0作为routerID使用,且R1和R2上用loop1接口用于模拟用户其他网段 实验要求 1,在AS100内运行OSPF协议 2.配置路由反射器,使得从R1进入的数据能够反射到全局网络 3.在R1和R2上分别宣告自己的loop1口网段用于观…

记录第一次在windows环境编译libuvc库 踩的坑

最近遇到windows下编译libuvc库,实现经usb连接的摄像头拍摄采集。绕了一大圈,记录一下。 首先,作为新手,肯定需要参考大神资料,但是还是踩了坑。 要在windows 环境下安装libuvc的驱动并确保可用,需要经过一系列流程&a…

Mybatisplus——Mybatisplus3.5.2版本使用Page分页插件查询,records有数据但是total显示0

目录 一、问题背景 debug 执行Mybatisplus使用Page分页插件查询时,发现 Page 里面的records有数据但是total显示0。 二、问题产生的原因 未配置MybatisPlus的分页插件拦截器导致的或者因mybatis-plus版本3.4或3.5版本导致原先的分页插件paginationInterceptor无法…