OC类与对象

OC类与对象

本篇是对上一篇的内容的继续学习。从单例模式开始继续学习

文章目录

    • 单例模式
      • 定义
      • 应用场景
      • 特点
      • 单例模式的创建
    • 隐藏与封装
      • 理解什么是封装
      • 目的
      • 访问控制符
      • 合成存取方法
      • 特性的指示符
      • 点语法访问属性
    • 对象初始化
      • 便利的初始化方法
    • 类的继承
        • 特点
        • 语法格式
        • 重写父类方法
        • super关键字
    • 类的多态
      • 指针变量的强制类型转化
        • 判断实际指针的类型
    • 小结

单例模式

单例模式是因为在某些时候,程序多次创建这一个类的对象没有实际上的意义,那么我们就只用在程序运行的时候只初始化一次,自然就产生了我们的一个单例模式。

定义

如果一个类始终只能创建一个实例,则这个类被称为单例类。在程序中,单例类只在程序中初始化一次,所以单例类是储存在去全局区域,在编译时分配内存,只要程序还在运行就会一直占用内存,只有在程序结束的时候释放这部分内存。

应用场景

我们只要保证全局有且只有一份就可以,一般在程序中,经常调用的类,如工具类、公共跳转类等都会采用单例模式。

特点

  1. 单例是一个类,创建出来的对象就是单例对象
  2. 单例对象使用类方法创建
  3. 单例一旦被创建出来,一直到程序结束才释放,就是只初始化一次
  4. 不用程序员管理,直到程序结束就自动释放

单例模式的创建

因为单例模式只能创建一次,那么我们就把它存储在我们的一个静态区,那么就要用到我们的一个关键字static,这个关键字可以让我们的一个变量存储到静态区,下面将给出代码实现。

接口部分

@interface FKSingletion : NSObject {
  ;
}
+ (id) instance; //设计一个类方法来创建一个单例模式
@end

实现部分

static id instance = nil;
@implementation FKSingletion
+ (id) instance {
    if(!instance) {
				instance = [[super alloc] init];
    }
  return instance;
}
@end

请添加图片描述

这样我们就实现了一个单例模式的创建,但是实际上这个方法还有问题就是我们在通过[[FKSingletion alloc] init];这个语句创建的时候,还是会出现一个问题,就是我们还是能创建一个新的对象,这样就不符合我们对于单例模式的一个定义。这时候我们可能需要对之前的进行一个改进。

笔者查阅博客发现这和我们的alloc会执行一个allocWithZone,如果我们只想分配一次内存就要重写这个方法allocWithZone

接口部分

+ (id) allocWithZone:(struct _NSZone *)zone;
+ (id) instance;

实现部分

+ (id) instance {
    return [[self alloc] init];
}
+ (id) allocWithZone:(struct _NSZone *)zone {
    if (!instance) {
        instance =  [super allocWithZone:zone]; //重写这个方法
    }
    return  instance;
}

我们这时候在主函数中执行如下代码

FKSingletion* person = [FKSingletion instance];
FKSingletion* p2 = [[FKSingletion alloc] init];
NSLog(@"%p %p", person, p2); //打印出两个地址

我们发现两个地址是一样的,这里我们就实现了一个意义上的一个单例。(实际上还要修改一系列函数保证一个单例例如:copyWithZone,mutableCopyWithZone)但笔者对于这部分还没有学习,之后还会进行一个补充。

隐藏与封装

前面我们讲了直接通过对象访问实例变量的情况,但是这样实际上会引发一系列问题,比如说我们如果直接执行这条语句FKpreson->_age = 1000 语法上没有问题,但是绝对不符合逻辑,因此我们需要把这个东西隐藏在对象内部,不允许外部程序访问对象的内部信息。

理解什么是封装

封装是面向对象语言编程对客观世界的模拟,不允许外部程序访问对象的一个内部信息,而是通过这个类的方法来实现对于内部信息的操作和访问。

目的

  1. 隐藏类的实现细节
  2. 使用者只能通过预先的方法来访问数据,从而可以在方法内计入逻辑,防止不合理访问。
  3. 可进行数据检查,保证信息的完整性
  4. 便于修改。

封装的实际上的含义就是:把该隐藏的隐藏起来,把该暴露的暴露出来。

访问控制符

OC有四个访问控制符:@private, @package,@protected,@public。

  • @private:这个访问控制符下的成员变量只能在这个类的内部访问。这个用来彻底隐藏成员变量
  • @package:这个访问控制符,可以在当前类和类的同一映像中访问(就是编译后的同一框架或同一个执行文件),这一般就是考虑到,在开发一个框架中,其他类或者其他函数也要用到这个成员变量,我们就要考虑使用这个访问控制符。这些类、函数就都在一个映像中(注意这里的函数包括我们的主函数)
  • @protect:这个访问控制符号只允许类和子类进行一个访问
  • @public:这个访问控制符号可以让成员变量在任何地方进行一个访问。

接口部分

@interface Fkbaskerball : NSObject {
    @private
    int _value;
    int _time;
}
- (void) setValue : (int) value;
- (void) setTime : (int) time;
- (int) value;
- (int) time;
@end

实现部分

@implementation Fkbaskerball {
    
}
- (void) setTime: (int)time {
    self->_time = time;
}
- (void) setValue:(int)value {
    self->_value = value;
}
- (int) value {
    return self->_value;
}
-(int) time {
    return self->_time;
}
@end

在定义上面这个类的时候我们要注意我们通过一个setter方法和一个getter方法来实现了对于这个类中成员变量的一个操作和访问,在这个类之外只能通过这两个方法来访问这两个成员变量。

当我们在主函数中想访问这两个变量的时候,因为他是@private的性质所以他会报错。

请添加图片描述

tips

请添加图片描述

这里面讲了我们对于使用这访问控制符的一个使用的基本原则

合成存取方法

  1. 在新的OC中间可以自己生成一个settergetter方法,我们则需要使用**@property指令来定义属性。使用这个指令的时候我们无须将他直接放在@interface@end**之间进行一个定义,这个指示符放在属性定义的前面。
  2. 在类的实现部分使用一个**@synthesize**指令来声明该属性。

注意:我们还是可以重写setName方法。

接口部分

@interface FKSingletion : NSObject {
}
@property int age;
@end

这个部分的代码实际上就是

@interface FKSingletion : NSObject {
}
- (void) setAge: (int) newAge;
- (int) age;
@end

实现部分

@implementation FKSingletion
@synthesize age = _age; //将成员变量写成_age
@end

这部分代码实际上就是

- (void)setAge:(int)age
 {
    _age = age;
 }
 - (int)age
 {
    return _age
 }

特性的指示符

  • assign:该指示符指定对属性只是进行一个简单的赋值,不更改对所赋的值的引用计数。
  • atmoic(nonatomic)指定合成的存取方法是否为一个原子操作。
  • copy:如果使用copy指示符,当调用一个setter方法对成员变量赋值的时候,会将被赋值的对象复制一个副本,再将副本值赋给成员变量。

我们来重点理解一下copy这一个指示符,我们通过一个代码进行一个分析。

下面是接口部分与实现部分:

@interface Fkbaskerball : NSObject {
    ;
}
@property (nonatomic) NSString* name;
@end
@implementation Fkbaskerball {
    
}
@synthesize name;
@end

然后我们通过调用主函数使用这个方法。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        Fkbaskerball* f1 = [[Fkbaskerball alloc] init];
        NSMutableString* str = [NSMutableString stringWithString:@"blackman"]; //NSMutableString是子类可以被修改的字符串类型。
        [f1 setName: str];
        NSLog(@"他叫:%@", [f1 name]);
        [str appendString:@" is good"];
        NSLog(@"%@", [f1 name]);
    }
    return 0;
}

打印出来的结果是

请添加图片描述

我们这里就可以发现我们在修改str值的时候同时修改了FKbasketball的_name值。

如果我们这一次将@property (nonatomic) NSString* name; 修改为@property (nonatomic, copy) NSString* name;时候,发现结果变成了这样。这就意味着我们name值没有因为str的修改而修改。

请添加图片描述

strongweak这两个指示符表示对于被赋值对象持有强引用还是弱引用,他们两个决定对象是否被自动回收

点语法访问属性

如果每一次设置属性都要用setter或者是getter方法的话就会非常麻烦,所以现在设计了点语法的形式来访问我们的属性,或者对我们的属性进行一个赋值,或者通过一个点语法访问。主要取决于我们的点语法的一个位置。(在左侧就是一个赋值,在右侧就是一个访问)。

f1.name = str; //这个是赋值语句
NSLog(@"他叫:%@", f1.name); //这个是访问语句

对象初始化

对象初始化调用alloc方法的时候,系统会为我们所有的实例变量分配内存空间,将每一个实例变量的内存空间变成0,同时对应类型为对应的一个空值,仅仅分配内存空间对象还是不能使用的,还需要进行一个初始化,需要init方法才可以使用它。

我们在对象初始化的过程中如果使用了[类名 alloc]语句来创建一个对象的话,那没我们就没有对对象进行一个初始化,因此调用这个方法会出问题,在实际开发中间我们可以自己设计一个自己的init方法

- (id) init {
    if (self = [super init]) { //这一步的意思是我们先对self进行一个初始化,如果初始化失败那么self就是nil会退出,如果初始化成功那么就一位置我们可以按下面的步骤进行一个自定义的初始化
        self.name = @"messi";
        self.time = 30;
    }
    return self;
}

便利的初始化方法

因为我们从上面的方法认识到这并不可以根据参数进行一个动态初始化,我们可以自定义一些初始化方法。我们以initXXX的方式进行一个传入自己参数进行一个初始化。

- (id) init {
    if (self = [super init]) {
        self.name = @"messi";
        self.time = 35;
        self.value = 1000;
    }
    return self;
}
- (id) initWithname: (NSString*) name andTime : (int) time {
    if (self = [super init]) {
        self.name = name;
        self.time = time;
    }
    return self;
}
- (id) initWithname: (NSString *) name andTime : (int) time andValue : (int) value {
    if (self = [self initWithname: name andTime: value]) {
        self.value = value;
    }
    return self;
}

这里我们有三种不同的调用方式去调用这些代码,我们这里包含了多个初始化方法,我们让其中一个初始化方法执行体中完全包含另一个初始化方法的执行体,我们在第三个方法中间调用初始化方法2,这样可以让我们更好的实现一个代码复用。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        Fkbaskerball* f1 = [[Fkbaskerball alloc] init];
        NSLog(@"%@ %d %d", f1.name, f1.value, f1.time);
        Fkbaskerball* f2 = [[Fkbaskerball alloc] initWithname:@"neyal" andTime: 30];
        NSLog(@"%@ %d %d", f2.name, f2.value, f2.time);
        Fkbaskerball* f3 = [[Fkbaskerball alloc] initWithname:@"manba" andTime: 40 andValue: 1500];
        NSLog(@"%@ %d %d", f3.name, f3.value, f3.time);
    }
    return 0;
}

打印结果为:

请添加图片描述

可见它实现了一个自定义的初始化。

类的继承

继承是面向对象的三大特性之一,也是实现一个软件复用的重要手段,OC的继承具有单继承的特点,每一个子类只有一个直接父类

特点

父类和子类是一种一般和特殊的关系,因为子类是一种特殊的父类,就好比水果和苹果一样,两个都是抽象的概念但是还是有很大的差异,苹果是属于水果的。

语法格式
@interface SubClass : SuperClass {
  
}
@end

这表明我们的subClass继承了SuperClass类,继承的含义可能更适合用扩展来描述。我们可以说是Apple类拓展了Fruit类。当子类扩展父类的时候,子类可以继承父类的如下东西:

  • 全部成员变量
  • 全部方法(包括初始化方法)

下面我会给出一个例子

@interface Fruit : NSObject {
}
@property (nonatomic) double weight;
@end
@interface Apple : Fruit {
    ;
}
@end
//上面是接口部分下面是实现部分
@implementation Fruit {
    ;
}
@synthesize weight;
@end
@implementation Apple {
    ;
}
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        Apple* greenApple = [[Apple alloc] init]; 
        greenApple.weight = 15.87; //调用setter方法
        NSLog(@"%lg", greenApple.weight);
    }
    return 0;
}

我们可以看到实际上我们上面的Apple类实际上并没有任何成员变量和方法,但是我们发现我们主函数却可以调用setter方法。这就说明了我们的一个继承的特性。

重写父类方法

我们其实在上面就已经重写过父类的方法,因为我们之前重写过init方法,因为我们所有的类都是NSObject的一个子类,从上面我们就发现了如果重写一个方法的话,一定要做到的要求是:方法签名关键字要完全相同,也就是方法名和方法签名中的形参标签都需要完全相同,否则就不算是方法重写

super关键字

其实在之前我们就多次调用过这个关键字,super用于限定该对象调用它从父类继承得到的属性或者方法。但他正如self一样不能出现在类方法里,super也只能调用实例方法,因为类的方法的调用者是类本身。

@interface FKn : NSObject {
    @private
    int _a;
}
@end
//这里定义了一个父类一个子类
@interface FKtian : FKn {
    int _a;
}
@end 

上面的这种写法是存在问题的,因为OC有一个要求就是:无论父类接口部分的成员变量使用何种访问控制符限制,子类接口部分定义的成员变量都不允许与父类接口部分定义的成员变量重名,但是我们又要注意在类的实现部分定义的成员变量将被限制在该类的内部,子类无论是接口部分还是实现部分定义的成员变量都可以与父类实现部分定义的成员变量同名

下面我给出一个例子,这里给出了我们强制调用父类的a属性,我们可以通过这种方式来访问到父类中被隐藏的成员变量

@implementation nnnn
@synthesize a = _a;
- (id) init {
    if (self = [super init]) {
        self->_a = 6;
    }
    return self;
}
@end
@implementation nextnnnn {
    int _a;
}
- (id) init {
    if (self = [super init]) {
        self->_a = 7;
    }
    return self;
}
- (void) answer { //通过这个来打印当前类中的成员变量
    NSLog(@"父类中的变量%d", super.a);
    NSLog(@"子类中的成员变量%d", self->_a);
}
@end
  
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"Hello, World!");
        nextnnnn* p1 = [[nextnnnn alloc] init];
        [p1 answer];
    }
    return 0;
}

从上面的运行结果可以看出,程序通过super关键字强制指定调用父类的a属性,通过这种方式获取(getter方法返回值)

类的多态

OC的指针类型有两个:一个是编译时候的类型,一个是运行时的类型,编译的类型由声明该变量时使用的那类型来决定,如果编译的时候和运行的时候类型不一样,就有可能出现所谓的多态。

我们先定义一下父类和子类

下面我只给出接口部分

@implementation FKBase //父类
- (void) base {
    NSLog(@"父类的方法");
}
- (void) test {
    NSLog(@"父类将被覆盖的方法");
}
@end
  
@implementation FKsub //子类部分
- (void) test {
    NSLog(@"子类覆盖父类的方法");
}
- (void) sub {
    NSLog(@"子类自己的方法");
}
@end

然后我们通过调用主函数来分辨它所调用的方法。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        FKBase* ba = [[FKBase alloc] init];
        [ba test];
        [ba base];
        FKsub* bb = [[FKsub alloc] init];
        [bb test];
        [bb sub];
        FKBase* bc = [[FKsub alloc] init];
        [bc test];
        [bc base];  
    }
    return 0;
}

输出结果是如下图所示。

这就说明了一个问题就是将子类对象赋给父类指针对象那个,就出现了一个多态。当我们将一个子类的对象赋给父类指针变量,他的行为方法总是表现出子类方法的行为特征,而不是父类的行为特征,他也只能调用父类的方法,但是父类方法呈现的却是一个子类的行为

所以我们可以通过id来解决编译时候类型检查问题

指针变量的强制类型转化

除了id类型的变量之外,指针变量只能调用它编译的时候类型的方法,而不能调用它运行时类型的方法,如果需要让这个指针变量调用它运行的时候的类型的方法,则必须要强制类型转换成运行时候的类型,强制类型转换需要借助类型转换符。

tips:强制类型转换只改变了编译时期的类型,而没有改变它实际所指向的类型,如果调用编译时期的类型的话会报错。

请添加图片描述

子类对象赋给父类对象指针变量的时候,被称为向上转型,这种转型总是可以成功的。上面报错的主要原因是,date的编译时的编译类型虽然是NSDate,但是他实际指向的对象时NSString,因此调用date方法的方法时,虽然编译可以通过,但是运行时会引起错误。

判断实际指针的类型
  • -(BOOL) isKindOfClass:clazz:判断该对象是否为clazz或者他的子类的实例
  • -(BOOL)isSubclassOfClass:clazz:判断该对象是否为clazz的子类的实例

小结

  1. 单例模式原理在一个程序中只需要一个实例的环境下运用,那么我们在这种情况下就要采用一个单例模式来实现,我们通过一个static和一个类方法来实现我们的单例模式,核心代码部分就是if (!instance)instance = [[super alloc] init]
  2. 隐藏和封装部分要注意不同的访问控制符对应的不同范围以及一些特殊指示符
  3. 对象初始化方面我们可以重写一个初始化函数,以及初始化对象会给所有值赋值为0或者是nil。
  4. 类的继承:我们可以理解为子类是父类的一个拓展,子类可以重写父类的方法,子类的接口部分的成员变量不能和父类接口部分的成员变量相同
  5. 类的多态:我们重点理解了是编译时期的变量类型和实际运行时的编译类型不同会导致多态的出现,指针变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行其运行时类型所具有的方法。

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

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

相关文章

SimpleDateFormat类.Java

目录 1.1构造方法 1.2格式规则 1.3常用方法 1.4练习1( 出生日期) 1.5练习2(秒杀活动) java.text.SimpleDateFormat 是日期/时间格式化类,我们通过这个类可以帮我们完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来…

机器学习理论基础—集成学习(1)

机器学习理论基础—集成学习 个体与集成 集成学习通过构建并结合多个学习器来完成学习任务,有时也称为多分类系统等。 分类: 根据集成学习中的个体学习器的不同可以分为同质集成(集成的学习器相同例如全部是决策树)&#xff0c…

上市公司专利数据、专利申请、专利授权和质量指标计算面板数据(1990-2022年)

01、数据简介 专利作为企业创新能力和核心竞争力的体现,越来越受到上市公司的重视。了解上市公司的专利数据、专利申请、专利授权和质量指标计算,有助于投资者更好地评估公司的创新能力和长期发展潜力。 通过分析上市公司的专利数据、专利申请、专利授…

【国标语音对讲】EasyCVR视频汇聚平台海康/大华/宇视摄像头GB28181语音对讲配置

一、背景分析 近年来,国内视频监控应用发展迅猛,系统接入规模不断扩大,涌现了大量平台提供商,平台提供商的接入协议各不相同,终端制造商需要给每款终端维护提供各种不同平台的软件版本,造成了极大的资源浪…

[C++ QT项目实战]----系统实现双击表格某一行,表格数据不再更新,可以查看该行所有信息,选中表更新之后,数据可以继续更新

前言 在需要庞大的数据量的系统中,基于合适的功能对数据进行观察和使用至关重要,本篇在自己项目实战的基础上,基于C QT编程语言,对其中一个数据功能进行分析和代码实现,希望可以有所帮助。一些特殊原因,图片…

回溯-单词搜索

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相…

压测--混合场景设置

1、设计测试场景 性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标满足需求定义的检验活动。一般有以下场景: 基准场景:单接口少量并发用户下压测,评估单个功能点性能。负载场景:逐步增…

Python实践应用|NC文件读取

import netCDF4 as nc import numpy as np import matplotlib.pyplot as plt# 打开NC文件 nc_file E:/NC_file/air.sig995.2012.nc # 将your_file.nc替换为你的NC文件路径 nc_data nc.Dataset(nc_file, r)# 查看NC文件中包含的变量 print("Variables in the NC file:&q…

免费简单好用的内网穿透工具(ngrok、natapp),微信回调地址配置

B站视频地址 文章目录 Natapp1、登录注册账号、下载软件2、使用2-1、购买隧道、查看token2-2、端口穿透 Ngrok1、登录注册账号、下载软件2、使用2-1、获取并设置 token2-2、使用 3、隧道 微信回调配置1、注册测试公众号2、回调代码3、回调配置 在一些特殊的场景下,需…

C#基础之结构体

结构体 文章目录 1、概念2、基本语法3、示例4、结构体的使用5、访问修饰符6、结构体的构造函数思考1 描述矩形信息思考2 职业名字释放了技能思考3 小怪兽思考4 多个小怪兽思考5 奥特曼打小怪兽 1、概念 结构体是一种一定义变量类型 它是数据和函数的集合,可以在结…

PCIe总线-MPS MRRS RCB参数介绍(四)

1.概述 PCIe总线的存储器写请求、存储器读完成等TLP中含有数据负载,即Data Payload。Data Payload的长度和MPS(Max Payload Size)、MRRS(Max Read Request Size)和RCB(Read Completion Boundary&#xff0…

计算机存储原理.2

1.主存储器与CPU之间的连接 2.存储器芯片的输入输出信号 3.增加主存的存储字长 3.1位扩展 数据总线的利用成分是不充分的(单块只能读写一位),为了解决这个问题所以引出了位扩展。 使用多块存储芯片解决这个问题。 3.2字扩展 因为存储器买的是8k*8位的&am…

硬件21、接线端子XH2.54、2.54排针排母、2510接插件、PH2.0、町洋接线端子5.08、ISP接口JTAG插座

XH2.54端子的间距为2.54毫米,2.54排针排母的间距也是2.54mm,2510接插件也是2.54、而PH2.0端子的间距为2.0毫米,町洋接线端子插针间的距离是5.08mm,ISP接口JTAG插座针脚的间距一般也是2.54mm XH2.54 针脚间距为2.54mm 插头 接线…

IIS中搭建.Net Core项目,步骤详解

一、准备服务器 1)安装IIS 这个比较简单,百度一下就行 2)安装 .NET Core 运行时 下载地址:下载 .NET(Linux、macOS 和 Windows) 因为我是本地开发,所以我下载的是SDK 安装成功之后显示如下: 检查是否安装…

判断字符串由几个单词组成(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int world 0;int i 0;char c 0;char string[81] { 0 };int num 0;//提示用户&#xff…

使用Github Action实现Hexo博客自动化部署

本文参考自 Akilar&#xff0c;原文地址&#xff1a;https://akilar.top/posts/f752c86d/ 每次部署Hexo都需要运行指令三件套&#xff0c;随着文章越来越多&#xff0c;编译的时间也随之越来越长&#xff0c;通过Github Action&#xff0c;我们只需要在每次完成博客的编写或修…

OSPF路由计算

1.区域内路由计算 &#xff08;1&#xff09;LSA的基本概念 LS Age&#xff1a;当LSA被始发时&#xff0c;该字段为0&#xff0c;随着LSA在网络中被泛洪&#xff0c;该时间逐渐累加&#xff0c;当到达MaxAge&#xff08;缺省值为3600s&#xff09;时&#xff0c;LSA不再用于路…

传统过程自动化工厂的智能扩展

一 通过NOA概念&#xff0c;公开、安全地迈向未来 随着数字化转型在过程自动化工业中的不断深入&#xff0c;许多公司都面临着同一挑战——如何平衡创新和传统。放眼望去&#xff0c;过程自动化工业和信息技术似乎在以不同的速度发展。虽然过程自动化工厂通过使用传统的自动化…

基于springboot实现企业oa管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现企业oa管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了企业OA管理系统的开发全过程。通过分析企业OA管理系统管理的不足&#xff0c;创建了一个计算机管理企业OA管理系统的方案…

C语言数据结构之栈

目录 1.栈的概念及结构2.栈的实现3.栈的代码实现4.相关例题 •͈ᴗ•͈ 个人主页&#xff1a;御翮 •͈ᴗ•͈ 个人专栏&#xff1a;C语言数据结构 •͈ᴗ•͈ 欢迎大家关注和订阅!!! 1.栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插…