面向对象修炼手册(四)(多态与空间分配)(Java宝典)
🌈 个人主页:十二月的猫-CSDN博客
🔥 系列专栏: 🏀面向对象修炼手册💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光
目录
前言
1 多态
1.1 多态的形式(四种)
1.1.1 重载(专用多态):类型签名区分
签名:
基于类型签名的重载:
1.1.2 改写/重写(包含多态):子父类中,相同类型签名,相同函数名
改写和重载的对比:
改写与遮蔽的对比:
改写机制在不同语言中的区别:
两种不同的改写形式:
1.1.3 多态变量(复制多态):声明与包含不同
延迟方法:(抽象方法)
多态变量的四种形式:
1.1.4 泛型(模板) :创建通用工具
概念:
使用:
1.2 多态运行机制
1.2.1 解释
1.2.2 方法动态绑定过程
1.2.3 小练习
2 空间分配
2.1 内存分配方法
2.1.1 静态存储分配
2.1.2 动态存储分配
2.1.3 堆式存储分配
2.2 内存分配策略
2.2.1 最小静态空间分配
2.2.2 最大静态空间分配
2.2.3 动态内存空间分配
总结
前言
前面一讲,我们重点来讲了行为和多态。面向对象修炼手册(三)(行为与多态)(Java宝典)-CSDN博客
在行为中,分为动态行为和静态行为。静态行为和动态行为实施到不同的对象中又构成不同对象,例如静态类型/动态类型;静态类/动态类(静态类是指用于声明变量的类,动态类是指运行时需要动态绑定相关数值的类);静态方法/动态方法。
在多态中,先讲了三种子类父类之间的代码复用情况(三个重),包括重载、重写、重定义。其中重载发生在一个类中,重写发生在父类和子类之间(要求函数名、函数参数都相同),重定义发生在父类和子类之间(要求函数名相同,函数参数不相同)
这一讲,我们从多态的角度再深入分析一下多态,以及内存分配
1 多态
多态、封装和继承是面向对象语言共有的三大特性,其核心思想就是代码复用。封装为了是代码复用下的安全(防止复用时修改了原代码),多态和继承就是在封装的前提下要实现代码复用的两大手段
1.1 多态的形式(四种)
1.1.1 重载(专用多态):类型签名区分
- 重载是在编译时执行的,而改写是在运行时选择的。
- 重载是多态的一种很强大的形式。
- 非面向对象语言也支持。
签名:
函数类型签名是关于函数参数类型、参数顺序、参数数目和返回值类型的描述。
函数签名经常被用在函数重载解析中,因为调用重载的方法从名字上是无法确定你调用的是哪一个方法,而要从你传入的参数和该函数的签名来进行匹配,这样才可以确定你调用的是哪一个函数。
基于类型签名的重载:
多个过程(或函数、方法)允许共享同一名称,且通过该过程所需的参数数目、顺序和类型来对它们进行区分。即使函数处于同一上下文,这也是合法的。
class Example{//same name,three different methodsint sum(int a){return a;}int sum(int a,int b){return a+b;}int sum(int a,int b,int c){return a+b+c;}
}
关于重载的解析,是在编译时基于参数值的静态类型完成的。不涉及运行时机制。
1.1.2 改写/重写(包含多态):子父类中,相同类型签名,相同函数名
如果子类的方法具有与父类的方法相同的名称和类型签名,称子类的方法改写了父类的方法。
- 语法上:子类定义一个与父类有着相同名称且类型签名相同的方法。
- 运行时:变量声明为一个类,它所包含的值来自于子类,与给定消息相对应的方法同时出现于父类和子类。
改写与替换原则同时出现是代码复用的一个重要手段。不论实际对象是父类还是子类都用父类类型,后续可以在父类类型变量中放入子类/父类,从而使得原代码将父类替换为子类时仍然符合要求。
改写可看成是重载的一种特殊情况:
- 接收器搜索并执行相应的方法以响应给定的消息。
- 如果没有找到匹配的方法,搜索就会传导到此类的父类。搜索会在父类链上一直进行下去,直到找到匹配的方法,或者父类链结束。
- 如果能在更高类层次找到相同名称的方法,所执行的方法就称为改写了继承的行为。
改写和重载的对比:
-
继承角度:对于改写来说,方法所在的类之间必须符合父类/子类继承关系,而对于简单的重载来说,并无此要求
-
类型签名角度:如果发生改写,两个方法的类型签名必须匹配
-
方法作用角度: 重载方法总是独立的,而对于改写的两个方法,有时会结合起来一起实现某种行为。
-
编译器角度: 重载通常是在编译时解析的,而改写则是一种运行时机制。对于任何给定的消息,都无法预言将会执行何种行为,而只有到程序实际运行的时候才能对其进行确定。
-
静态动态行为:重载解析是静态的,改写解析是动态的
改写与遮蔽的对比:
遮蔽
是指父类变量接收子类类型,并调用方法或者使用变量时候,使用的父类的方法和变量,而不发生多态的现象。
字段遮蔽:
class Parent {int x = 10;
}class Child extends Parent {int x = 20;
}public class Main {public static void main(String[] args) {Parent parent = new Parent();Child child = new Child();Parent parentReferenceToChild = new Child();System.out.println("parent.x = " + parent.x); // 输出10System.out.println("child.x = " + child.x); // 输出20System.out.println("parentReferenceToChild.x = " + parentReferenceToChild.x); // 输出10}
}
静态方法遮蔽:
class Parent {static void staticMethod() {System.out.println("Static method in Parent");}
}class Child extends Parent {static void staticMethod() {System.out.println("Static method in Child");}
}public class Main {public static void main(String[] args) {Parent parent = new Parent();Child child = new Child();Parent parentReferenceToChild = new Child();parent.staticMethod(); // 输出 "Static method in Parent"child.staticMethod(); // 输出 "Static method in Child"parentReferenceToChild.staticMethod(); // 输出 "Static method in Parent"}
}
改写机制在不同语言中的区别:
- Java、Smalltalk等面向对象语言,只要子类通过同一类型签名改写父类的方法,自然便会发生所期望的行为。
- C++中,需要父类中使用关键字Virtual来表明这一含义(否则会发生遮蔽)
两种不同的改写形式:
- 代替(replacement):在程序执行时,实现代替的方法完全覆盖父类的方法。即,当操作子类实例时,父类的代码完全不会执行。
- 改进(refinement):实现改进的方法将继承自父类的方法的执行作为其行为的一部分。这样父类的行为得以保留且扩充.
- java中使用super.方法名();
- c++中可以使用 _super::方法名() 或者 使用 父类名::方法名();
建议都是使用改进语义
1.1.3 多态变量(复制多态):声明与包含不同
多态变量是指可以引用多种对象类型的变量。
这种变量在程序执行过程可以包含不同类型的数值。
对于动态类型语言,所有的变量都可能是多态的。例如python中变量可以放任何对象
对于静态类型语言,多态变量则是替换原则的具体表现。使用多态变量出现了自然就有替换原则
例如:Parent variable=new Child();
延迟方法:(抽象方法)
如果方法在父类中定义,但并没有对其进行实现,那么我们称这个方法为延迟方法
优点:
可以使程序员在比实际对象的抽象层次更高的级别上考虑与之相关的活动。
实际意义:
在静态类型面向对象语言中,对于给定对象,只有当编译器可以确认与给定消息选择器相匹配的响应方法时,才允许程序员发送消息给这个对象。
延迟方法有时也称为抽象方法,并且在C++语言中通常称之为纯虚方法。
多态变量的四种形式:
1、简单多态变量(继承+替换原则)(最原始的多态)
Animal pet;
pet = new Dog();
pet.speak();
2、接收器变量(内部接口或者父类指向对象声明但未初始化)
多态变量作为一个数值,表示正在执行的方法内部的接收器。包含接收器的变量没有被正常的声明,通常被称为伪变量。
伪变量:
C++,Java,C#:this
class ThisExample{public void one(int x){value=x+4;two(x+3);}private int value;private void two(int y){System.out.println(“Value is”+(value+y));}
}
等价于:
class ThisExample{public void one(int x){this.value=x+4;this.two(x+3);}private int value;private void two(int y){System.out.println(“Value is”+(this.value+y));}
}
这个this就是在方法内部指定接收器本身的, 并且任何对象都用这个this指定接收器本身,所以this是多态变量
3、 向下造型(反多态)
向下造型这个变量本质上是取消多态赋值的过程(将父类强制转化为子类,然后赋值给子类)
Father f1 = new Son();Son s1 = (Son)f1;但有运行出错的情况:Father f2 = new Father();Son s2 = (Son)f2;//编译无错但运行会出现错误在不确定父类引用是否指向子类对象时,可以用instanceof来判断:if(f3 instanceof Son){Son s3 = (Son)f3;}
4、纯多态(我的多态依靠其他函数的多态实现)
-
多态方法支持可变参数的函数。
-
支持代码只编写一次、高级别的抽象
-
以及针对各种情况所需的代码裁剪。
-
通常是通过给方法的接收器发送延迟消息来实现这种代码裁剪的。
延迟消息:延迟实现的函数,在使用函数中并不实现。因此延迟消息一改变,使用延迟消息的函数也会发生变化。因此这个延迟消息本身就是一种多态方法,即使用延迟消息的函数本身也就实现多态。
Class Stringbuffer{String append(Object value){return append(value,toString());}…
}
方法toString在子类中得以重定义。
toString方法的各种不同版本产生不同的结果。
所以append方法也类似产生了各种不同的结果。
Append:一个定义,多种结果。
1.1.4 泛型(模板) :创建通用工具
概念:
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
进一步理解,就是我们方法的实现有延迟,这个泛型对方法实现中的参数类型也让它延迟实现。在方法实现中并不直接传入具体的参数,而是在运行时实现。从另一个角度来说,它是动态类型实现的参数。
使用:
泛型类:
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{ //key这个成员变量的类型为T,T的类型由外部指定 private T key;public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定this.key = key;}public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定return key;}
}
//泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
//传入的实参类型需与泛型的类型参数类型相同,即为Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);//传入的实参类型需与泛型的类型参数类型相同,即为String.
Generic<String> genericString = new Generic<String>("key_vlaue");
Log.d("泛型测试","key is " + genericInteger.getKey());
Log.d("泛型测试","key is " + genericString.getKey());
更重要的是,如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型(编译器自己根据传入的类型决定泛型类型)
Generic generic = new Generic("111111");
Generic generic1 = new Generic(4444);
Generic generic2 = new Generic(55.55);
Generic generic3 = new Generic(false);Log.d("泛型测试","key is " + generic.getKey());
Log.d("泛型测试","key is " + generic1.getKey());
Log.d("泛型测试","key is " + generic2.getKey());
Log.d("泛型测试","key is " + generic3.getKey());
泛型接口:
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:
//定义一个泛型接口
public interface Generator<T> {public T next();
}
/*** 传入泛型实参时:* 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>* 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。* 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型* 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。*/
public class FruitGenerator implements Generator<String> {private String[] fruits = new String[]{"Apple", "Banana", "Pear"};@Overridepublic String next() {Random rand = new Random();return fruits[rand.nextInt(3)];}
}
泛型方法:
在java中,泛型类的定义非常简单,但是泛型方法就比较复杂了。
尤其是我们见到的大多数泛型类中的成员方法也都使用了泛型,有的甚至泛型类中也包含着泛型方法,这样在初学者中非常容易将泛型方法理解错了。
泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。(重点区分:泛型类中的普通方法和泛型类中的泛型方法)
public class GenericTest {//这个类是个泛型类,在上面已经介绍过public class Generic<T>{ private T key;public Generic(T key) {this.key = key;}//我想说的其实是这个,虽然在方法中使用了泛型,但是这并不是一个泛型方法。//这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。//所以在这个方法中才可以继续使用 T 这个泛型。public T getKey(){return key;}/*** 这个方法显然是有问题的,在编译器会给我们提示这样的错误信息"cannot reslove symbol E"* 因为在类的声明中并未声明泛型E,所以在使用E做形参和返回值类型时,编译器会无法识别。public E setKey(E key){this.key = keu}*/}/** * 这才是一个真正的泛型方法。* 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T* 这个T可以出现在这个泛型方法的任意位置.* 泛型的数量也可以为任意多个 * 如:public <T,K> K showKeyName(Generic<T> container){* ...* }*/public <T> T showKeyName(Generic<T> container){System.out.println("container key :" + container.getKey());//当然这个例子举的不太合适,只是为了说明泛型方法的特性。T test = container.getKey();return test;}//这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。public void showKeyValue1(Generic<Number> obj){Log.d("泛型测试","key value is " + obj.getKey());}//这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?//同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类public void showKeyValue2(Generic<?> obj){Log.d("泛型测试","key value is " + obj.getKey());}/*** 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "* 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。* 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。public <T> T showKeyName(Generic<E> container){...} *//*** 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "* 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。* 所以这也不是一个正确的泛型方法声明。public void showkey(T genericObj){}*/public static void main(String[] args) {}
}
总之就是,不是方法中出现T就是泛型方法。必须要在public和返回值之间增加一个<T>
本质来说,泛型方法的作用就是让方法和类的返回值/参数类型独立化了,可以不同
具体使用如下:
public class GenericFruit {class Fruit{@Overridepublic String toString() {return "fruit";}}class Apple extends Fruit{@Overridepublic String toString() {return "apple";}}class Person{@Overridepublic String toString() {return "Person";}}class GenerateTest<T>{public void show_1(T t){System.out.println(t.toString());}//在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。//由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。public <E> void show_3(E t){System.out.println(t.toString());}//在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。public <T> void show_2(T t){System.out.println(t.toString());}}public static void main(String[] args) {Apple apple = new Apple();Person person = new Person();GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();//apple是Fruit的子类,所以这里可以generateTest.show_1(apple);//编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person//generateTest.show_1(person);//使用这两个方法都可以成功generateTest.show_2(apple);generateTest.show_2(person);//使用这两个方法也都可以成功generateTest.show_3(apple);generateTest.show_3(person);}
}
泛型方法能使方法独立于类而产生变化,以下是一个基本的指导原则:
无论何时,如果你能做到,你就该尽量使用泛型方法。也就是说,如果使用泛型方法将整个类泛型化,那么就应该使用泛型方法。另外对于一个static的方法而已,无法访问泛型类型的参数。所以如果static方法要使用泛型能力,就必须使其成为泛型方法。
泛型数组:
List<String>[] ls = new ArrayList<String>[10]; // x
List<?>[] ls = new ArrayList<?>[10]; //√
List<String>[] ls = new ArrayList[10];//√
1.2 多态运行机制
多态机制的运行是基于”方法绑定“ 。由于多态中的重写、重载、多态变量、泛型的存在,很多方法/变量需要延迟绑定来实现。想要实现延迟绑定就需要”方法绑定“机制来决定和哪个方法绑定(不能考编译器根据函数签名直接决定)
1.2.1 解释
- Java多态机制是基于“方法绑定(binding)”,就是建立method call(方法调用)和method body(方法本体)的关联。如果绑定动作发生于程序执行前(由编译器和连接器完成),称为“先期绑定”。对于面向过程的语言它们没有其他选择,一定是先期绑定。比如C编译器只有一种method call,就是先期绑定。(C++有先期联编和后期联编)
- 当有多态的情况时,解决方案便是所谓的后期绑定(late binding):绑定动作将在执行期才根据对象型别而进行。后期绑定也被称为执行期绑定(run-time binding)或动态绑定(dynamic binding)。
- Java的所有方法,只有final,static,private和构造方法是前期绑定,其他都使用后期绑定。 将方法声明为final型可以有效防止他人覆写该函数。但是或许更重要的是,这么做可以“关闭”动态绑定。或者说,这么做便是告诉编译器:动态绑定是不需要的。于是编译器可以产生效率较佳的程序代码。
1.2.2 方法动态绑定过程
- 编译器检查对象的声明类型和方法名。假设我们调用x.f(args)方法,并且x已经被声明为C类的变量,那么编译器会列举出C类中所有的名称为f的方法和从C类的超类继承过来的f方法
- 接下来编译器检查方法调用中提供的参数类型。如果在所有名称为f 的方法中有一个参数类型和调用提供的参数类型最为匹配,那么就确定调用这个方法( 重载解析)
- 当程序运行并且使用动态绑定调用方法时,虚拟机必须调用同x所指向的对象的实际类型相匹配的方法版本。假设实际类型为D(C的子类),如果D类定义了f(args)那么该方法被调用,否则就在D的超类中搜寻方法f(args),依次类推
1.2.3 小练习
代码如下:
public class Bird{public void fly(Bird p) {System.out.println(“Bird fly with Bird”);}
}
public class Eagle extends Bird {public void fly(Bird p){System.out.println(“Eagle fly with Bird!”);}public void fly(Eagle e) {System.out.println(“Eagle fly with Eagle!”);}
}
Bird p1 = new Bird () ;
Bird p2 = new Eagle () ;
Eagle p3 = new Eagle () ;
p1.fly( p1 ) ;
p1.fly( p2 ) ;
p1.fly( p3 ) ;
p2.fly( p1 ) ;
p2.fly( p2 ) ;
p2.fly( p3 ) ;
p3.fly( p1 ) ;
p3.fly( p2 ) ;
p3.fly( p3 ) ;
运行结果为:
关键点:
1、编译阶段根据调用函数的对象类型进行静态绑定——确定函数名字及函数签名,但是不确定函数是否在子类中被重写(也就说并没有真正绑定一个函数)(检查语法正确性也在这一阶段)
2、运行阶段动态绑定方法,这个动态绑定是基于第一步中确定的函数名和函数签名进行的。只是根据调用函数的对象实际类型(子类还是父类),来实际选择和父类还是子类绑定
2 空间分配
2.1 内存分配方法
内存分配方法是指程序是用什么方法去请求内存分配的
2.1.1 静态存储分配
静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间。
这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求。
2.1.2 动态存储分配
也被称为栈式存储分配,它是由一个类似于堆栈的运行栈来实现的。
和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存。
栈式存储分配按照先进后出的原则进行分配。
2.1.3 堆式存储分配
堆式存储分配专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例。
堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放。
2.2 内存分配策略(考试重点)
研究的是这门语言遇到类/方法等内存分配时采用的是什么策略
2.2.1 最小静态空间分配
-
C++使用最小静态空间分配策略,运行高效。
-
只分配基类所需的存储空间。
为了防止采用这种策略时因为多态而引发的程序错误(具体参照P179),C++改变了虚拟方法的调用规则:
-
对于指针 / 引用变量:当信息调用可能被改写的成员函数时,选择哪个函数取决于接收器的动态数值。
-
对于其他变量:调用虚拟成员函数的方式取决于静态类,而不取决于动态类
2.2.2 最大静态空间分配
无论基类还是派生类,都分配可用于所有合法的数值的最大的存储空间。
这一方案不合适,因为需要找到最大的对象,就需要对继承树上的所有对象都进行扫描,然后找到需要分配最大内存的对象才能
2.2.3 动态内存空间分配
- 堆栈中不保存对象值。
- 堆栈通过指针大小空间来保存标识变量,数据值保存在堆中。
- 指针变量都具有恒定不变的大小,变量赋值时,不会有任何问题。
只分配用于保存一个指针所需的存储空间。在运行时通过对来分配指针对应对象所需的存储空间,同时将指针设为相应的合适值。
总结
本系列内容均来自:山东大学-潘丽老师-面向对象开发技术-课程ppt、《设计模式》、《大话设计模式》
相关文章:

面向对象修炼手册(四)(多态与空间分配)(Java宝典)
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀面向对象修炼手册 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 目录 前言 1 多态 1.1 多态的形式&…...

基于UDP的网络聊天室(多线程实现收和发消息)
要求:1.有新用户登录,其他在线的用户可以收到登录信息 2.有用户群聊,其他在线的用户可以收到群聊信息 3.有用户退出,其他在线的用户可以收到退出信息 4.服务器可以发送系统信息 效果图: service.c #include <head…...
【脚本工具库】随机抽取数据 - 图像和标签对应(附源码)
在数据处理和机器学习任务中,我们经常需要从大规模数据集中随机抽取一定数量的图像及其对应的标签文件,以便进行模型训练、验证或测试。手动操作不仅耗时,而且容易出错。为了解决这个问题,我们可以编写一个Python脚本,…...

【python】eval函数
1.eval函数的语法及用法 (1)语法:eval(expression) 参数说明: expression:必须为字符串表达式,可为算法,也可为input函数等。 说明:表达式必需是字符串,否则会报错&a…...

实战|记一次java协同办公OA系统源码审计
前言 因为笔者也是代码审计初学者,写得不好的地方请见谅。该文章是以项目实战角度出发,希望能给大家带来启发。 审计过程 审计思路 1、拿到一个项目首先要看它使用了什么技术框架,是使用了ssh框架,还是使用了ssm框架ÿ…...

浅浅谈谈如何利用Javase+多线程+计算机网络的知识做一个爬CSDN阅读量总访问量的程序
目录 我们发现csdn的文章 首先为了印证我们的想法 我们用postman往csdn我们任意一篇文章发起post请求 发送请求 编辑获得响应结果 我们发现我们的阅读量上涨 PostRequestSender类 但是我们经过测试发现 定义一个字符串数组 把URL放进去 然后延迟启动 在线程池里面…...
Vscode 中launch.json与tasks.json文件
Vscode 中launch.json与tasks.json文件 launch.json文件基本结构主要属性示例配置PythonCNode.js 常见配置项1. Python2. C3. Node.js 使用示例 tasks.json基本结构主要属性示例配置C 编译任务Python 运行任务Node.js 运行任务 常见配置项使用示例 tasks.json与launch.json文件…...

C#基于SkiaSharp实现印章管理(2)
上一篇文章最后提到基于System.Text.Json能够序列化SKColor对象,但是反序列化时却无法解析本地json数据。换成Newtonsoft.Json进行序列化和反序列化也是类似的问题。 通过百度及查看微软的帮助文档,上述情况下需自定义转换类以处理SKColor类型数据的…...
大二C++期末复习(自用)
一、类 1.定义成员函数 输入年份判断是否是闰年,若是输出年份;若不是,输出NO #include<iostream> #include<cstring> using namespace std; class TDate{private:int month;int day;int year;public:TDate(int y,int m,int d)…...

重大进展!微信支付收款码全场景接入银联网络
据中国银联6月19日消息,近日,银联网络迎来微信支付收款码场景的全面接入,推动条码支付互联互通取得新进展,为境内外广大消费者提供更多支付选择、更好支付体验。 2024年6月,伴随微信支付经营收款码的开放,微…...

msvcr110.dll丢失的解决方法,亲测有效的几种解决方法
最近,我在启动一个程序时,系统突然弹出一个错误提示,告诉我电脑缺失了一个名为msvcr110.dll的文件。这让我感到非常困惑,因为我之前从未遇到过这样的问题。经过一番搜索和尝试,我总结了5种靠谱的解决方法。下面分享给大…...
SUSE Linux 15 sp5上Nginx安装配置升级
1.安装SUSE linux 15 SP5 图形化界面安装很简单,选择最小安装,安装好后,使用vim编辑配置文件,结果提示"bash: vim: command not found"。 最简安装把一些常用命令都整没有了,于是又重新选择了Server Applica…...

突破Web3红海,DePIN如何构建创新生态系统?
撰文:TinTinLand 本文来源香港Web3媒体Techub News专栏作者TinTinLand 2023 年 DePIN 赛道的火热成为 Web3 行业的重点关注方向,当前如何以可扩展、去中心化、安全方式推动 DePIN 赛道赋能下的 AI 版图建设,寻找更多 Web3 行业创新机遇成为…...

裸机与操做系统区别(RTOS)
声明:该系列笔记是参考韦东山老师的视频,链接放在最后!!! rtos:这种系统只实现了内核功能,比较简单,在嵌入式开发中,某些情况下我们只需要多任务,而不需要文件…...

详解 ClickHouse 的分片集群
一、简介 分片功能依赖于 Distributed 表引擎,Distributed 表引擎本身不存储数据,有点类似于 MyCat 之于 MySql,成为一种中间件,通过分布式逻辑表来写入、分发、路由来操作多台节点不同分片的分布式数据 ClickHouse 进行分片集群的…...
AI问答-医疗:什么是“手术报台”
手术报台并不是传统意义上的医疗工具或设备,而是一个与手术耗材追溯管理相关的系统或工具。以下是对手术报台的详细解释: 一、定义与功能 手术报台系统,如医迈德手术报台系统,是一款面向医院跟台人员的微信小程序。 它通过手术耗…...

S-Clustr(影子集群)V3 高并发,去中心化,多节点控制
S-Clustr 项目地址:https://github.com/MartinxMax/S-Clustr/releases/tag/S-Clustr-V3.0 Maptnh Не ограничивайте свои действия виртуальным миром. GitHub: Maptnh Jay Steinberg Man kann die Menschen, die man hasst, in d…...

支持WebDav的网盘infiniCloud(静读天下,Zotero 等挂载)
前言 WebDav是一种基于HTTP的协议,允许用户在Web上直接编辑和管理文件,如复制、移动、删除等。 尽管有一些网盘支持WebDav,但其中大部分都有较多的使用限制。这些限制可能包括:上传文件的大小限制、存储空间的限制、下载速度的限…...
Linux命令行导出MySQL数据库备份并压缩
Linux命令行导出MySQL数据库备份并压缩 导出SQL: 如果使用的是 MySQL 或者 MariaDB 可以使用mysqldump工具进行数据备份的导出; 基本命令: mysqldump -u用户名 -p密码 数据库名称 > 要导出的文件名.sql替换掉你实际的数据库“用户名”…...
二叉树的广度优先搜索(层次遍历)
目录 定义 层序遍历的数据结构 实现过程简述 具体代码 定义 层序遍历就是从左到右一层一层地遍历二叉树。 层序遍历的数据结构 层序遍历需要借用一个辅助数据结构实现,由于队列具有先进先出的特性,符合一层一层遍历的逻辑,而栈先进后出…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...