Java中的抽象类和接口

目录

1. 抽象类

1.1 抽象类概念

1.2 抽象类语法 

1.3 抽象类需要注意的点

1.4 抽象类的作用

2. 接口 

2.1 接口的概念

2.2 语法规则

2.3 接口使用 

2.4 接口特性 

2.5 实现多个接口 

2.6 接口间的继承 

2.7 接口使用实例

2.8 Clonable接口,浅拷贝和深拷贝

2.9 抽象类和接口的区别 

3. Object类

3.1 获取对象信息 

3.2 对象比较equals方法 

3.3 hashcode方法 


1. 抽象类

1.1 抽象类概念

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

abstract class Shape {public abstract void draw();//抽象方法
}class Cycle extends Shape {@Overridepublic void draw() {System.out.println("🟢");}
}class Rect extends Shape {@Overridepublic void draw() {System.out.println("🖼️");}
}
public class Test2 {public static void drawMap(Shape shape) {shape.draw();}public static void main(String[] args) {drawMap(new Rect());drawMap(new Cycle());}
}

在打印图形例子中, 我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由 Shape 的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstract method), 包含抽象方法的类我们称为抽象类(abstract class).

1.2 抽象类语法 

在Java中,一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用 给出具体的实现体。

// 抽象类:被abstract修饰的类
public abstract class Shape {// 抽象方法:被abstract修饰的方法,没有方法体abstract public void draw();abstract void calcArea();// 抽象类也是类,也可以增加普通方法和属性public double getArea(){return area;}protected double area; // 面积
}

注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法

1.3 抽象类需要注意的点

1. 抽象类和抽象方法都是使用abstract修饰的

2. 抽象类不能实例化,但是普通类可以

3. 抽象类中不一定包含抽象方法,但包含抽象方法的类一定是抽象类

4. 抽象类中可以定义成员变量和成员方法

5. 当一个普通类继承了抽象类,那么普通类中一定要重写抽象类中的抽象方法

6. 抽象类存在的最大的意义就是被继承

7. 当一个抽象类A继承了抽象类B,此时抽象类A不需要重写抽象类B中的抽象方法,但是当一个普通类C继承了抽象类A,此时就要重写所有的抽象方法

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

9. final关键字与abstract关键字不可能同时作用在一个方法或者类上

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

1.4 抽象类的作用

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

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

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

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

2. 接口 

2.1 接口的概念

接口其实就是一种公共的行为规范标准,可以算抽象类的进一步抽象,大家在实现时,只要符合这个标准的,就都可以用这个接口.

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

2.2 语法规则

接口的定义格式与定义类的格式基本相同,将class关键字换成 interface 关键字,就定义了一个接口。

public interface 接口名称{// 抽象方法public abstract void method1(); // public abstract 是固定搭配,可以不写public void method2();abstract void method3();void method4();
// 注意:在接口中上述写法都是抽象方法,跟推荐方式4,代码更简洁
}

提示:

1. 创建接口时, 接口的命名一般以大写字母 I 开头.

2. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性 

2.3 接口使用 

接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。

public class 类名称 implements 接口名称{
// ...
}

注意:子类和父类之间是 extends 继承关系,类与接口之间是 implements 实现关系。 

2.4 接口特性 

1. 接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)

public interface IShape {// Error:(4, 18) java: 此处不允许使用修饰符privateprivate void darw();void draw1();
}

2. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现 

public interface IShape {void darw();// 编译失败:因为接口中的方式默认为抽象方法// Error:(5, 23) java: 接口抽象方法不能带有主体void draw1() {System.out.println("画图形!");}
}

3. 但是在接口中的方法有两个特例

3.1 在接口中,静态方法可以有具体的实现 

interface IShape {// int age3 = 1;void draw();public static void draw1() {System.out.println("1234");}
}

3.2 被default关键字修饰的方法可以有具体的实现(从1.8开始Java引入了这个特性)

interface IShape {// int age3 = 1;void draw();default void draw4() {System.out.println("1234");}public static void draw1() {System.out.println("1234");}
}

4. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量

interface IShape {int age3 = 1;void draw();
}public class Test {public static void main(String[] args) {System.out.println(IShape.age3);  // 可以直接通过接口名访问,说明是静态的IShape.age3 = 3; // 编译报错:Error:(12, 12) java: 无法为最终变量brand分配值// 说明age3具有final属性}
}

5. 接口类型是一种引用类型,但是不能直接new接口的对象 

public class Test {public static void main(String[] args) {IShape shape = new IShape();}
}// Error:(10, 19) java: day20210915.USB是抽象的; 无法实例化

6. 当一个类实现了一个接口后,这个类必须重写这个接口当中的抽象方法

7. 当接口中,存在default方法,可以选择重写,也可以选择不重写,具体看需求

interface IShape {void draw();default void draw4() {System.out.println("1234");}
}
class Cycle implements IShape {@Overridepublic void draw() {System.out.println("🟢");}@Overridepublic void draw4() {System.out.println("我觉得父接口的这个默认方法不好,我自己重写!");}
}

8. 不管是接口还是抽象类,它们仍是可以发生向上转型. 

(接口)

interface IShape {void draw();/*default void draw4() {System.out.println("1234");}*/
}
class Cycle implements IShape {@Overridepublic void draw() {System.out.println("🟢");}/*@Overridepublic void draw4() {System.out.println("我觉得父接口的这个默认方法不好,我自己重写!");}*/
}
class Rect implements IShape {@Overridepublic void draw() {System.out.println("🖼️");}
}public class Test {public static void drawMap(IShape shape) {shape.draw();}public static void main(String[] args) {IShape iShape = new Cycle();IShape iShape1 = new Rect();drawMap(iShape);drawMap(iShape1);}}

(抽象类) 

abstract class Shape {public abstract void draw();//抽象方法
}class Cycle extends Shape {@Overridepublic void draw() {System.out.println("🟢");}
}class Rect extends Shape {@Overridepublic void draw() {System.out.println("🖼️");}
}
public class Test {public static void drawMap(Shape shape) {shape.draw();}public static void main(String[] args) {Shape shape = new Cycle();Shape shape1 = new Rect();drawMap(shape);drawMap(shape1);}
}

9. 子类重写方法的时候,这个方法一定要是public修饰的

interface IShape {void draw();
}class Rect implements IShape {// 编译报错,重写IShape中draw方法时,不能使用默认修饰符// 必须要是public@Overridevoid draw() {System.out.println("🖼️");}
}

10. 接口中不能有代码块和构造方法 

interface IShape {void draw();// 编译失败{}// 编译失败public IShape() {}}

11. 一个类如果不想实现接口中的方法,这个类可以被定义为抽象类

12. 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class 

 

2.5 实现多个接口 

一个类实现多个接口,可以解决Java中多继承的问题

语法是一定要先继承类在实现接口

interface IFlying {void fly();
}interface ISwimming {void swim();
}interface IRunning {void run();
}abstract class Animal {private String name;private int age;public Animal(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public abstract void eat();
}class Dog extends Animal implements IRunning, ISwimming {public Dog(String name, int age) {super(name, age);}@Overridepublic void swim() {System.out.println(this.getName() + "正在狗刨!");}@Overridepublic void run() {System.out.println(this.getName() + "正在跑步!");}@Overridepublic void eat() {System.out.println(this.getName() + "正在吃狗粮!");}
}class Bird extends Animal implements IFlying {public Bird(String name, int age) {super(name, age);}@Overridepublic void fly() {System.out.println(this.getName() + "正在用翅膀飞!");}@Overridepublic void eat() {System.out.println(this.getName() + "正在吃鸟量!");}
}class Robot implements IRunning {@Overridepublic void run() {System.out.println("机器人在跑!");}
}public class Test {public static void test1(Animal animal) {animal.eat();}public static void testFly(IFlying iFlying) {iFlying.fly();}public static void testRun(IRunning running) {running.run();}public static void testSwim(ISwimming iSwimming) {iSwimming.swim();}public static void main(String[] args) {testFly(new Bird("小鸟", 2));testRun(new Dog("小狗", 1));testSwim(new Dog("小狗", 1));testRun(new Robot());}public static void main1(String[] args) {test1(new Dog("小狗", 1));test1(new Bird("小鸟", 2));}
}

通过上述代码,我们可能会有如下疑惑

为什么不能将三个接口都写成在Animal类的方法呢?

因为有的动物不会飞

为什么不能把三个接口写成三个类呢?

因为Java当中,只支持单继承. 

继承表达的含义是 is - a 语义, 而接口表达的含义是具有 xxx 特性.

时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型, 而只关注某个类是否具备某种能力.比如在上述代码中,不需要是动物,机器人也可以跑.

2.6 接口间的继承 

接口和接口之间也是存在关系的,用extends关键字来关联,此时认为这个意思是扩展接口功能的意思

interface A {void testA();
}interface B {void testB();
}
//扩展功能!
interface C extends A,B {void testC();
}class D implements C {@Overridepublic void testA() {}@Overridepublic void testB() {}@Overridepublic void testC() {}
}

可以看到在D类当中要重写A,B接口中的抽象方法.

接口间的继承相当于把多个接口合并在一起.

2.7 接口使用实例

给对象数组排序

class Student {public String name;public int age;public double score;public Student(String name, int age, double score) {this.name = name;this.age = age;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}}public class Test {public static void main(String[] args) {Student[] array = new Student[3];array[0] = new Student("zhangsan",89,39.9);array[1] = new Student("lisi",69,59.9);array[2] = new Student("wangwu",39,68.4);System.out.println("排序前:" + Arrays.toString(array));Arrays.sort(array);System.out.println("排序后:" + Arrays.toString(array));}
}

我们知道Arrays类中的sort这个方法是可以给数组进行排序的,但是给自定义类型排序时则不行了.

我们可以点进ComparableTimeSort错误,去看源代码.

可以看出数组的内容被强转成了Comparable类型,那Comparable又是什么呢?是一个接口,这个接口当中还有一个compareTo方法,那么我们的学生对象怎么能和这个Comparable接口产生联系呢?

用学生类实现这个接口即可,然后再重写接口中的compareTo方法就可以达到给对象数组排序的效果了(代码是按照年龄排序的).

class Student implements Comparable<Student>{public String name;public int age;public double score;public Student(String name, int age, double score) {this.name = name;this.age = age;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}@Overridepublic int compareTo(Student o) {/*if(this.age - o.age > 0) {return 1;}else if(this.age == o.age) {return 0;}else {return -1;}*/return this.age - o.age;}
}public class Test {public static void main(String[] args) {Student[] array = new Student[3];array[0] = new Student("zhangsan",89,39.9);array[1] = new Student("lisi",69,59.9);array[2] = new Student("wangwu",39,68.4);System.out.println("排序前:" + Arrays.toString(array));Arrays.sort(array);System.out.println("排序后:" + Arrays.toString(array));}}

为了进一步加深对接口的理解, 我们可以尝试自己实现一个 sort 方法来完成刚才的排序过程(使用冒泡排序)

class Student implements Comparable<Student>{public String name;public int age;public double score;public Student(String name, int age, double score) {this.name = name;this.age = age;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}@Overridepublic int compareTo(Student o) {/*if(this.age - o.age > 0) {return 1;}else if(this.age == o.age) {return 0;}else {return -1;}*/return this.age - o.age;}
}public class Test {public static void main(String[] args) {Student[] array = new Student[3];array[0] = new Student("zhangsan",89,39.9);array[1] = new Student("lisi",69,59.9);array[2] = new Student("wangwu",39,68.4);System.out.println("排序前:" + Arrays.toString(array));//Arrays.sort(array);bubbleSort(array);System.out.println("排序后:" + Arrays.toString(array));}public static void bubbleSort(Comparable[] comparables) {for (int i = 0; i < comparables.length-1; i++) {for (int j = 0; j < comparables.length-1-i; j++) {if(comparables[j].compareTo(comparables[j+1]) > 0){Comparable tmp = comparables[j];comparables[j] = comparables[j+1];comparables[j+1] = tmp;}}}}}

但是我们会发现 Comparable接口中的compareTo方法被写死了,如果我不按照年龄来排序,那该怎么办呢?我现在来按照成绩排序,还有一个Comparator接口,这个接口更加的灵活.

class Student {public String name;public int age;public double score;public Student(String name, int age, double score) {this.name = name;this.age = age;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}
}
class AgeComparator implements Comparator<Student> {@Overridepublic int compare(Student o1, Student o2) {return o1.age - o2.age;}
}
class ScoreComparator implements Comparator<Student> {public int compare(Student o1, Student o2) {return (int)(o1.score - o2.score);}
}
public class Test {public static void main(String[] args) {Student student1 = new Student("zhangsan",19,30.9);Student student2 = new Student("lisi",9,80.9);AgeComparator ageComparator = new AgeComparator();int ret = ageComparator.compare(student1,student2);System.out.println(ret);System.out.println("=====================");ScoreComparator scoreComparator = new ScoreComparator();int ret2 = scoreComparator.compare(student1,student2);System.out.println(ret2);}
}

2.8 Clonable接口,浅拷贝和深拷贝

class Student {public int age;public Student(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"age=" + age +'}';}}public class Test {public static void main(String[] args) {Student student1 = new Student(10);Student student2 = (Student) student1.clone(); // 编译报错}
}

这个clone方法student1这个引用没有访问权限 

class Student implements Cloneable{public int age;public Student(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"age=" + age +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}public class Test {public static void main(String[] args) {Student student1 = new Student(10);Student student2 = (Student) student1.clone(); // 编译失败}
}

此时,出现了异常,我们只需要处理这个异常就ok了 

这个Cloneable接口,里面什么都没有,它的作用是什么呢?

这个接口被叫做标记接口,只要实现了该接口,就说明当前的类是可以被克隆的.

class Student implements Cloneable{public int age;public Student(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"age=" + age +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}public class Test {public static void main(String[] args) throws CloneNotSupportedException{Student student1 = new Student(10);Student student2 = (Student) student1.clone();System.out.println(student1);System.out.println(student2);}
}

此时证明克隆成功了.

接下来我们在Student类中新增一个对象

class Money {public double money;
}class Student implements Cloneable{public int age;public Money m = new Money();public Student(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"age=" + age +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}public class Test {public static void main(String[] args) {Student student1 = new Student(10);student1.m.money = 19.9;Student student2 = (Student) student1.clone(); // 此时还是编译报错  还按照上面的步骤 // 就可以解决}
}

改正后的代码 

class Money implements Cloneable{public double money;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}class Student implements Cloneable{public int age;public Money m = new Money();public Student(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"age=" + age +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}public class Test {public static void main(String[] args) throws CloneNotSupportedException{Student student1 = new Student(10);student1.m.money = 19.9;Student student2 = (Student) student1.clone();System.out.println(student1.m.money);System.out.println(student2.m.money);}
}

class Money implements Cloneable{public double money;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}class Student implements Cloneable{public int age;public Money m = new Money();public Student(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"age=" + age +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}public class Test {public static void main(String[] args) throws CloneNotSupportedException{Student student1 = new Student(10);student1.m.money = 19.9;Student student2 = (Student) student1.clone();System.out.println(student1.m.money);System.out.println(student2.m.money);System.out.println("=================");student1.m.money = 29.9;System.out.println(student1.m.money);System.out.println(student2.m.money);}
}

 

为什么我只动改了student1引用指向的对象中的属性,怎么student2引用指向的对象中的属性也跟着改了呢?

首先,我们的代码,先是将student1引用指向的对象拷贝一份,student2这个引用指向了这个拷贝的对象,但是在new Student()对象中,我们还有一个m引用指向的对象没有被拷贝,此时,m引用在student2这个引用指向的对象中,m引用里面的地址值没有改变,所以对student1引用指向的对象中的money属性改动,student2中的也没有变.这种没有彻底将所有对象拷贝的方式就叫做浅拷贝

那么,深拷贝就是将所有对象都拷贝了.

把上面的代码改成深拷贝

只要将上面Student类中的clone方法内容重写就行了.

2.9 抽象类和接口的区别 

1. 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中 不能包含普通方法, 子类必须重写所有的抽象方法.

2. 使用extends关键字继承抽象类,使用关键字implements来实现接口

3. 一个子类只能继承一个抽象类,但是一个子类可以实现多个接口

4. 一个抽象类可以实现多个接口,但是接口不能继承抽象类,接口只能继承接口.

3. Object类

Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类

class Person{}
class Student{}
public class Test {public static void main(String[] args) {function(new Person());function(new Student());}public static void function(Object obj) {System.out.println(obj);}
}

所以在开发之中,Object类是参数的最高统一类型。但是Object类也存在有定义好的一些方法。如下:

3.1 获取对象信息 

如果要打印对象中的内容,可以直接重写Object类中的toString()方法,之前已经讲过了,此处不再累赘。

// Object类中的toString()方法实现:public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}

3.2 对象比较equals方法 

在Java中,==进行比较时:

a.如果==左右两侧是基本类型变量,比较的是变量中值是否相同

b.如果==左右两侧是引用类型变量,比较的是引用变量地址是否相同

c.如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的:

// Object类中的equals方法public boolean equals(Object obj) {return (this == obj); // 使用引用中的地址直接来进行比较}
class Person{private String name ;private int age ;public Person(String name, int age) {this.age = age ;this.name = name ;}
}
public class Test {public static void main(String[] args) {Person p1 = new Person("gaobo", 20) ;Person p2 = new Person("gaobo", 20) ;int a = 10;int b = 10;System.out.println(a == b); // 输出trueSystem.out.println(p1 == p2); // 输出falseSystem.out.println(p1.equals(p2)); // 输出false}
}

Person类重写equals方法后,然后比较:

class Person{
...@Overridepublic boolean equals(Object obj) {if (obj == null) {return false ;}if(this == obj) {return true ;}
// 不是Person类对象if (!(obj instanceof Person)) {return false ;}Person person = (Person) obj ; // 向下转型,比较属性值return this.name.equals(person.name) && this.age==person.age ;}
}

结论:比较对象中内容是否相同的时候,一定要重写equals方法。

3.3 hashcode方法 

我们看到了hashCode()这个方法,他帮我算了一个具体的对象位置,

 

该方法是一个native方法,底层是由C/C++代码写的。我们看不到。 我们认为两个名字相同,年龄相同的对象,将存储在同一个位置,如果不重写hashcode()方法,我们可以来看示例 代码: 

class Person {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;}
}
public class TestDemo4 {public static void main(String[] args) {Person per1 = new Person("gaobo", 20) ;Person per2 = new Person("gaobo", 20) ;System.out.println(per1.hashCode());System.out.println(per2.hashCode());}
}
//执行结果
460141958
1163157884

注意事项:两个对象的hash值不一样.

像重写equals方法一样,我们也可以重写hashcode()方法。此时我们再来看看。 

class Person {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}
public class TestDemo4 {public static void main(String[] args) {Person per1 = new Person("gaobo", 20) ;Person per2 = new Person("gaobo", 20) ;System.out.println(per1.hashCode());System.out.println(per2.hashCode());}
}
//执行结果
460141958
460141958

注意事项:哈希值一样。

结论:

1、hashcode方法用来确定对象在内存中存储的位置是否相同

2、事实上hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的 散列码,进而确定该对象在散列表中的位置。 

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

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

相关文章

算法分析与设计课后练习23

求下面的0-1背包问题 &#xff08;1&#xff09;N5,M12,(p1,p2,…,p5)(10,15,6,8,4),(w1,w2,…,w5)(4,6,3,4,2) &#xff08;2&#xff09;N5,M15,(p1,p2,…,p5)(w1,w2,…,w5)(4,4,5,8,9)

Keka v1.3.5(mac压缩解压工具)

Keka是一款功能强大的文件压缩和解压缩软件&#xff0c;为Mac系统用户提供便捷、高效的文件管理工具。以下是Keka的主要特点和功能&#xff1a; 多种压缩格式支持&#xff1a;Keka支持多种常见的压缩格式&#xff0c;包括ZIP、7Z、RAR、TAR、GZIP等。它能够方便地创建和提取这些…

react antd下拉选择框选项内容换行

下拉框选项字太多&#xff0c;默认样式是超出就省略号&#xff0c;需求要换行全展示&#xff0c;选完在选择框里还是要省略的 .less: .aaaDropdown {:global {.ant-select-dropdown-menu-item {white-space: pre-line !important;word-break: break-all !important;}} } html…

mongodb——原理简介,docker单机部署

MongoDB noSQL数据库 特点 数据文件存储格式为 BSON &#xff08;JSON 的扩展&#xff09; &#xff5b;“name”&#xff1a;“joe”&#xff5d;这是 BSON 的例子&#xff0c;其中"name"是键&#xff0c;"joe"是值。键值对组成了 BSON 格式。面向集合…

ROS2中Executors对比和优化

目录 SingleThreadExecutorEventExecutor SingleThreadExecutor 执行流程 EventExecutor 通信图

Linux学习第43天:Linux 多点电容触摸屏实验:难忘记第一次牵你手的温存

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 人都是性情中人&#xff0c;如果把学习当做自己的女朋友&#xff0c;对她细致入微、掏心掏肺、有耐心有恒心&#xff0c;终会修成正果。 而我们本节需要学习的电…

(swjtu西南交大)数据库实验(概念数据库设计及逻辑关系转换):音乐软件数据管理系统

一、实体型及属性 &#xff08;20分&#xff09; 用户&#xff08;账号&#xff0c;用户名&#xff0c;密码&#xff0c;性别&#xff0c;生日&#xff0c;地区&#xff0c;手机号&#xff0c;个性签名&#xff0c;信息修改审核客服&#xff09; 歌手&#xff08;歌手号&#…

C++模拟实现——红黑树封装set和map

一、红黑树迭代器的实现 基本的框架和实现链表的迭代器思路是一样的&#xff0c;都是对指针进行封装处理&#xff0c;然后实现一些基本的运算符重载&#xff0c;最重要的是operator&#xff0c;需要不递归的实现走中序的规则&#xff0c;这里只实现那最核心的几个基本功能&…

第一次参加算法比赛是什么感受?

大家好&#xff0c;我是怒码少年小码。 冬日暖阳&#xff0c;好日常在。今天中午在食堂干饭的时候&#xff0c;我的手机&#x1f4f1;收到了一条收货信息。 阿&#xff1f;什么玩意儿&#xff1f;我又买啥了&#xff1f; 个败家玩意&#xff0c;我都准备好叨叨我自己&#x…

msvcp120.dll缺失的解决方法与作用介绍

大家好&#xff01;我是小编。今天&#xff0c;我想和大家分享一下关于“找不到msvcp120.dll无法继续执行代码的5个解决方法”的话题。 首先&#xff0c;让我们来了解一下msvcp120.dll的作用。msvcp120.dll是Microsoft Visual C Redistributable Package的一部分&#xff0c;它…

高防CDN如何预防攻击?

现在网络攻击事件越来越多&#xff0c;而且愈发凶猛&#xff0c;为了保障互联网业务能稳定正常的运行&#xff0c;市场上出现了很多高防产品&#xff0c;例如高防服务器、高防IP、高防CDN等等。其中究竟高防CDN怎么防攻击&#xff0c;能防哪些攻击&#xff1f;高防CDN如何实现防…

matlab-BP神经网络的训练参数大全

本文部分图文来自《老饼讲解-BP神经网络》bp.bbbdata.com 本文列兴趣MATLAB神经网络工具箱中&#xff0c;训练参数trainParam的各个参数与意义 以方便在使用matlab工具箱时&#xff0c;用于查阅 一、matlab神经网络工具箱trainParam的参数列表 trainParam中的各个具体参数如下…

4.2 Windows驱动开发:内核中进程线程与模块

内核进程线程和模块是操作系统内核中非常重要的概念。它们是操作系统的核心部分&#xff0c;用于管理系统资源和处理系统请求。在驱动安全开发中&#xff0c;理解内核进程线程和模块的概念对于编写安全的内核驱动程序至关重要。 内核进程是在操作系统内核中运行的程序。每个进…

对象分配规则

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一份大厂面试资料《史上最全大厂面试题》&#xff0c;Springboot、微服务、算法、数据结构、Zookeeper、Mybatis、Dubbo、linux、Kafka、Elasticsearch、数据库等等 …

函数有返回类型,但函数体未返回类型,程序崩溃问题记录

问题 使用类指针调用函数时&#xff0c;程序崩溃。 问题定位&#xff1a; name new nameSetting;name->setName("helloworld");qDebug().noquote() << name->getName();原因 class nameSetting { public:nameSetting();QString setName(const QStri…

GB28181视频监控国标平台EasyGBS如何进行服务迁移?

视频流媒体安防监控国标GB28181平台EasyGBS视频能力丰富&#xff0c;部署灵活&#xff0c;既能作为业务平台使用&#xff0c;也能作为安防监控视频能力层被业务管理平台调用。国标GB28181视频EasyGBS平台可提供流媒体接入、处理、转发等服务&#xff0c;支持内网、公网的安防视…

golang学习笔记——接口和继承比较1

继承 Go 语言的设计之初&#xff0c;就不打算支持面向对象的编程特性&#xff0c;因此 Go 不支持面向对象的三大特性之一——继承。但是 Go 可以通过组合的思想去实现 “继承”。继承是面向对象的三大特性之一&#xff0c;继承是从已有的类中派生出新的类&#xff0c;新的类能…

【Django使用】4大模块50页md文档,第4篇:Django请求与响应和cookie与session

当你考虑开发现代化、高效且可扩展的网站和Web应用时&#xff0c;Django是一个强大的选择。Django是一个流行的开源Python Web框架&#xff0c;它提供了一个坚实的基础&#xff0c;帮助开发者快速构建功能丰富且高度定制的Web应用 Django全套笔记地址&#xff1a; 请移步这里 …

Unity中Shader的PBR的基础知识与理论

文章目录 前言一、什么是PBR二、什么是PBS在这里插入图片描述 三、PBS的核心理论1、物质的光学特性&#xff08;Substance Optical Properties&#xff09;2、微平面理论&#xff08;Microfacet Theory&#xff09;3、能量守恒&#xff08;Energy Conservation&#xff09;4、菲…

7 Redis的PipeLine

PipeLine的作用是批量执行命令 redis的性能瓶颈基本上是网络 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.…