当前位置: 首页 > news >正文

TypeScript装饰器 ------- 学习笔记分享

目录

一. 简介

二. 类装饰器

1. 基本语法

2. 应用举例

3. 关于返回值

4. 关于构造类型

5. 替换被装饰的类

三. 装饰器工厂

四. 装饰器组合

五. 属性装饰器

1. 基本语法

2. 关于属性遮蔽

3. 应用举例

六. 方法装饰器

1. 基本语法

2. 应用举例

七. 访问器装饰器

1. 基本语法

2. 应用举例

八. 参数装饰器

1. 基本语法

2. 应用实例


一. 简介
  1. 装饰器本质是一种特殊的函数,它可以对 : 类 , 属性 , 方法 , 参数进行扩展,同时让代码更简洁

  2. 装饰器自2015年在ECMAScript中被提出到现在,已将近10年

  3. 截至目前,装饰器依然是实验性特性,需要开发者手动调整配置,来开启装饰器支持

  4. 装饰器有5种 :

    (1)类装饰器

    (2)属性装饰器

    (3)方法装饰器

    (4)访问器装饰器

    (5)参数装饰器

备注 : 虽然TypeScript5.0中可以直接使用类装饰器,但为了确保其他装饰器可用,现阶段使用时,仍建议使用experimentalDecorators配置来开启装饰器支持,而且不排除未来的版本中官方会进一步调整装饰器的相关语法 !

二. 类装饰器
1. 基本语法

类装饰器是一个应用再类声明上的函数,可以为类添加额外的功能,或添加额外的逻辑

/*Demo函数会在Person类定义时执行参数说明:target参数是被装饰的类,即Person
*/
function Demo(target: any) {console.log(target);
​
}
​
@Demo
class Person {name: stringage: numberconstructor(name: string, age: number) {this.name = namethis.age = age}
}
2. 应用举例

需求 : 定义一个装饰器,实现Person实例调用toString时返回JSON.stringify的执行结果

function CustomString(target:Function){target.prototype.toString = function(){return JSON.stringify(this)}Object.seal(target.prototype)
}
​
@CustomString
class Person {name: stringage: numberconstructor(name: string, age: number) {this.name = namethis.age = age}
}
const p1 = new Person('张三',18)
console.log(p1.toString())
3. 关于返回值
  • 类装饰器返回值 : 若类装饰器返回一个新的类,那这个新类将替换掉被装饰的类

  • 类装饰器返回值 : 若类装饰器无返回值或返回undefined,那被装饰的类不会被替换

function Demo(target: Function) {return class {test() {console.log(100);console.log(200);console.log(300);}}
}
​
@Demo
class Person {test() {console.log(100);}
}
​
console.log(Person);
4. 关于构造类型

在TypeScript中,Function类型所表示的范围十分广泛,包括 : 普通函数,箭头函数,方法等等,但并非Function类型的函数都可以被new关键字实例化,例如箭头函数是不能被实例化的,那么TypeScript中该如何声明一个构造类型呢?有以下两种方式:

  • 仅声明构造类型

/*
new表示:该类型是可以用new操作符调用
...args表示:构造器是可以接受[任意数量]的参数
any[]表示:构造器可以接受[任意类型]的参数
{}表示:返回类型是对象(非null,非undefined的对象)
*/
​
type Constructor = new (...args: any[]) => {}
//需求是fn得是一个类
function test(fn: Constructor) { }
​
class Person { }
​
test(Person)
  • 声明构造类型 + 指定静态属性

type Constructor = {new(...args: any[]): {}wife: string
}
//需求是fn得是一个类
function test(fn: Constructor) { }
​
class Person { static wife:string
}
​
test(Person)
5. 替换被装饰的类

对于高级一些的装饰器,不仅仅是覆盖一个原型上的方法,还要有更多功能,例如添加新的方法和状态

需求 : 设计一个LogTime装饰器,可以给实例添加一个属性,用于记录实例对象的创建时间,再添加一个方法用于读取创建时间

type Constructor = new (...args: any[]) => {}
​
interface Person {getTime(): void
}
function LogTime<T extends Constructor>(target: T) {return class extends target {createdTime: Dateconstructor(...args: any[]) {super(...args)this.createdTime = new Date()}getTime() {return `该对象的创建时间是:${this.createdTime}`}}
}
​
​
@LogTime
class Person {constructor(public name: string, public age: number) {this.name = namethis.age = age}speak() {console.log('你好呀!');}
}
​
const p1 = new Person('张三', 18)
console.log(p1);
console.log(p1.getTime());
三. 装饰器工厂

装饰器工厂是一个返回装饰器函数的函数,可以为装饰器添加参数,可以更灵活地控制装饰器的行为

需求 : 定义一个LogInfo类装饰器工厂,实现Person实例可以调用到introduce方法,且introduce中输出内容的次数,由LogInfo接收的参数决定

interface Person{introduce():void
}
​
function LogInfo(n:number) {return function(target:Function){target.prototype.introduce = function(){for(let i = 0;i < n;i++){console.log(`我的名字:${this.name},我的年龄:${this.age}`);}}}//装饰器
}//装饰器工厂
​
​
@LogInfo(3)
class Person {constructor(public name: string,public age: number) { }speak() {console.log('你好呀!');}
}
​
const p1 = new Person('张三',18)
p1.introduce()
四. 装饰器组合

装饰器可以组合使用,执行顺序为:先[由上到下]的执行所有的装饰器工厂,依次获取到装饰器,然后再[由下到上]执行所有的装饰器

  • 装饰器组合的执行顺序

//装饰器
function test1(target:Function) {console.log('test1');
}
​
//装饰器工厂
function test2() {console.log('test2工厂');return function (target:Function) {console.log('test2');}
}
​
//装饰器工厂 
function test3() {console.log('test3工厂');return function (target:Function) {console.log('test3');}
}
​
//装饰器
function test4(target:Function) {console.log('test4');
}
​
@test1
@test2()
@test3()
@test4
class Person{}

  • 装饰器组合的应用

interface Person {introduce(): voidgetTime(): void
}
function CustomString(target: Function) {//使用装饰器重写toString方法 + 封闭其原型对象target.prototype.toString = function () {return JSON.stringify(this)}Object.seal(target.prototype)
}//装饰器
​
function LogInfo(n: number) {//装饰器函数:target是被装饰的类return function (target: Function) {target.prototype.introduce = function () {for (let i = 0; i < n; i++) {console.log(`我的名字:${this.name},我的年龄:${this.age}`);}}}//装饰器
}//装饰器工厂
​
type Constructor = new (...args: any[]) => {}
function LogTime<T extends Constructor>(target: T) {return class extends target {createdTime: Dateconstructor(...args: any[]) {super(...args)this.createdTime = new Date()}getTime() {return `该对象的创建时间是:${this.createdTime}`}}
}//装饰器
​
@CustomString
@LogInfo(5)
@LogTime
class Person {constructor(public name: string,public age: number) { }speak() {console.log('你好呀!');}
}
​
const p1 = new Person('张三', 18)
p1.speak()
console.log(p1.toString());
p1.introduce()
console.log(p1.getTime());

五. 属性装饰器
1. 基本语法
function Demo(target:object,propertyKey:string){console.log(target,propertyKey) ;
}
​
/*参数说明:target : 对于静态属性来说值是类,对于实例属性来说值是类的原型对象{}propertyKey : 属性名
*/
​
class Person {@Demo name: string@Demo age: number@Demo static school: stringconstructor(name: string, age: number) {this.name = namethis.age = age}
}

2. 关于属性遮蔽

如下代码中 : 当构造器中的this.age = age试图在实例上赋值时,实际上时调用了原型上age属性的set方法

class Person {name: stringage: numberstatic school: stringconstructor(name: string, age: number) {this.name = namethis.age = age}
}
​
let value = 130
Object.defineProperty(Person.prototype, 'age', {get() {return value},set(val) {value = val},
})
​
const p1 = new Person('张三', 18)
console.log(p1.age);//18
console.log(Person.prototype.age);//18
3. 应用举例

需求 : 定义一个State属性装饰器,来监视属性的修改

function State(target: object, propertyKey: string) {let key = `__${propertyKey}`Object.defineProperty(target, propertyKey, {get() {return this[key]},set(newValue) {console.log(`${propertyKey}的最新值为:${newValue}`);this[key] = newValue},//还可以加上两个配置项enumerable: true,//可枚举性configurable: true,//可配置性})
}
​
class Person {name: string@State age: numberconstructor(name: string, age: number) {this.name = namethis.age = age}
}
​
const p1 = new Person('张三', 18)
六. 方法装饰器
1. 基本语法
/*参数说明:target:对于静态方法来说值是类,对于实例方法来说值是原型对象propertyKey:方法的名称descriptor:方法的描述对象,其中value属性是被装饰的方法
*/
​
​
function Demo(target:object,propertyKey:string,descriptor:PropertyDescriptor){console.log(target);console.log(propertyKey);console.log(descriptor);
}
​
class Person {constructor(public name: string,public age: number,) { }@Demospeak() {//实例方法console.log(`你好,我的名字:${this.name},我的年龄:${this.age}`);}@Demostatic isAdult(age: number) {//静态方法return age >= 18;}
} 
​
const p1 = new Person('张三',18)
p1.speak()
2. 应用举例

定义一个Logger方法装饰器,用于在方法执行前和执行后,均追加一些额外逻辑

function Logger(target:object,propertyKey:string,descriptor:PropertyDescriptor){//存储原始方法const originnal = descriptor.value//替换原始方法descriptor.value = function(...args:any[]){console.log(`${propertyKey}开始执行......`);const result = originnal.call(this,...args) console.log(`${propertyKey}执行完毕......`);return result}
​
}
​
class Person {constructor(public name: string,public age: number,) { }@Logger speak(str:string) {//实例方法console.log(`你好,我的名字:${this.name},我的年龄:${this.age},${str}`);}static isAdult(age: number) {//静态方法return age >= 18;}
} 
​
const p1 = new Person('tom',18)
p1.speak('hello')

定义一个Validate方法装饰器,用于验证数据

function Logger(target:object,propertyKey:string,descriptor:PropertyDescriptor){//存储原始方法const originnal = descriptor.value//替换原始方法descriptor.value = function(...args:any[]){console.log(`${propertyKey}开始执行......`);const result = originnal.call(this,...args) console.log(`${propertyKey}执行完毕......`);return result}
​
}
​
function Validate(maxValue:number){return function(target:object,propertyKey:string,descriptor:PropertyDescriptor){//保存原始方法const originnal = descriptor.value//替换原始方法descriptor.value = function(...args:any[]){//自定义的验证逻辑if(args[0] > maxValue){throw new Error('年龄非法')}//如果所有参数都符合要求,则使用原始方法return originnal.apply(this,args)}}
}
​
class Person {constructor(public name: string,public age: number,) { }@Logger speak(str:string) {//实例方法console.log(`你好,我的名字:${this.name},我的年龄:${this.age},${str}`);}@Validate(120)static isAdult(age: number) {//静态方法return age >= 18;}
} 
​
const p1 = new Person('tom',18)
p1.speak('hello')
console.log(Person.isAdult(100));
七. 访问器装饰器
1. 基本语法
/*参数说明target:1.对于实例访问器来说是[所属类的原型对象]2.对于静态访问器来说值是[所属类]propertyKey:访问器名称descriptor:描述对象
*/
function Demo(target: object, propertyKey: string, descriptor: PropertyDescriptor) {console.log(target);console.log(propertyKey);console.log(descriptor);
}
​
class Person {@Demoget address() {return '涩谷'}//实例访问器@Demostatic get country() {return 'Japan'}//静态访问器
}
2. 应用举例

需求 : 对Weather类的temp属性的set访问器进行限制,设置的最低温度-50,最高温度50

function RangeValidate(min:number,max:number){return function(target:object,propertyKey:string,descriptor:PropertyDescriptor){//保存原始的setterconst originalSetter = descriptor.set//重写setterdescriptor.set = function(value:number){//检查设置的值是否在指定的最小值和最大值之间if(value < min||value > max){//如果值不在范围内,抛出错误throw new Error(`${propertyKey}的值应该在${min}到${max}之间`);}//如果值在范围内,且原始setter方法存在,则调用原始setter方法if(originalSetter){originalSetter.call(this,value);}}}
}
​
class Weather {private _temp: number;constructor(_temp:number) {this._temp = _temp;}@RangeValidate(-50,50)set temp(value) {this._temp = value;}get temp() {return this._temp;}
}
​
const w1 = new Weather(28)
w1.temp = 9000
console.log(w1);

八. 参数装饰器
1. 基本语法
/*参数说明:target:1.如果修饰的是[实例方法]的参数,target是类的[原型对象]2.如果修饰的是[静态方法]的参数,target是[类]propertyKey:参数所在的方法的名称parameterIndex:参数在函数参数列表中的索引,从0开始
*/
function Demo(target: object, propertyKey: string, parameterIndex: number) {console.log(target);console.log(propertyKey);console.log(parameterIndex);
}
​
//类定义
class Person {constructor(public name: string) { }speak(@Demo message1: any, message2: any) {console.log(`${this.name}想对你说:${message1},${message2}`);}
}
2. 应用实例

需求 : 定义方法装饰器Validate,同时搭配参数装饰器NotNumber,来对speak方法的参数类型进行限制

function NotNumber(target: any, propertyKey: string, parameterIndex: number) {// 初始化或获取当前方法的参数索引列表let notNumberArr: number[] = target[`__notNumber_${propertyKey}`] || [];// 将当前参数索引添加到列表中notNumberArr.push(parameterIndex);// 将列表存储回目标对象target[`__notNumber_${propertyKey}`] = notNumberArr;}// 方法装饰器定义function Validate(target: any, propertyKey: string, descriptor: PropertyDescriptor) {const method = descriptor.value;descriptor.value = function (...args: any[]) {// 获取被标记为不能为空的参数索引列表const notNumberArr: number[] = target[`__notNumber_${propertyKey}`] || [];// 检查参数是否为 null 或 undefinedfor (const index of notNumberArr) {if (typeof args[index] === 'number') {throw new Error(`方法 ${propertyKey} 中索引为 ${index} 的参数不能是数字!`)}}// 调用原始方法return method.apply(this, args);};return descriptor;}// 类定义class Student {name: string;constructor(name: string) {this.name = name;}@Validatespeak(@NotNumber message1: any, message2: any) {console.log(`${this.name}想对说:${message1},${message2}`);}}// 使用const s1 = new Student("张三");s1.speak(100, 200);

相关文章:

TypeScript装饰器 ------- 学习笔记分享

目录 一. 简介 二. 类装饰器 1. 基本语法 2. 应用举例 3. 关于返回值 4. 关于构造类型 5. 替换被装饰的类 三. 装饰器工厂 四. 装饰器组合 五. 属性装饰器 1. 基本语法 2. 关于属性遮蔽 3. 应用举例 六. 方法装饰器 1. 基本语法 2. 应用举例 七. 访问器装饰器 …...

FPGA实现UltraScale GTH光口视频转USB3.0传输,基于FT601+Aurora 8b/10b编解码架构,提供2套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 GT 高速接口解决方案本博已有的FPGA驱动USB通信方案 3、工程详细设计方案工程设计原理框图输入Sensor之-->OV5640摄像头动态彩条输入视频之-->ADV…...

蓝桥杯篇---实时时钟 DS1302

文章目录 前言特点简介1.低功耗2.时钟/日历功能3.32字节的额外RAM4.串行接口 DS1302 引脚说明1.VCC12.VCC23.GND4.CE5.I/O6.SCLK DS1302 寄存器1.秒寄存器2.分钟寄存器3.小时寄存器4.日寄存器5.月寄存器6.星期寄存器7.年寄存器8.控制寄存器 DS1302 与 IAP25F2K61S2 的连接1.CE连…...

C语言蓝桥杯1003: [编程入门]密码破译

要将"China"译成密码&#xff0c;译码规律是&#xff1a;用原来字母后面的第4个字母代替原来的字母&#xff0e; 例如&#xff0c;字母"A"后面第4个字母是"E"&#xff0e;"E"代替"A"。因此&#xff0c;"China"应译…...

【MySQL在Centos 7环境安装】

文章目录 一. 卸载不必要的环境二. 检查系统安装包三. 卸载这些默认安装包四. 获取mysql官⽅yum源五. 安装mysql yum 源&#xff0c;对⽐前后yum源六. 看看能不能正常⼯作七. 安装mysql服务八. .查看配置⽂件和数据存储位置九. 启动服务并查看服务是否存在十. 登陆⽅法十一. 设…...

科技引领未来,中建海龙C-MiC 2.0技术树立模块化建筑新标杆

在建筑行业追求高效与品质的征程中&#xff0c;中建海龙科技有限公司&#xff08;简称“中建海龙”&#xff09;以其卓越的创新能力和强大的技术实力&#xff0c;不断书写着装配式建筑领域的新篇章。1 月 10 日&#xff0c;由深圳安居集团规划&#xff0c;中建海龙与中海建筑共…...

玩转观察者模式

文章目录 什么是观察者模式解决方案结构适用场景实现方式观察者模式优缺点优点:缺点:什么是观察者模式 观察者模式通俗点解释就是你在观察别人,别人有什么变化,你就做出什么调整。观察者模式是一种行为设计模式,允许你定义一种订阅机制,可在对象事件发生时通知多个“观察…...

Baklib知识中台构建企业智能运营核心架构

内容概要 在数字化转型的浪潮中&#xff0c;企业对于知识的系统化管理需求日益迫切。Baklib作为新一代的知识中台&#xff0c;通过构建智能运营核心架构&#xff0c;为企业提供了一套从知识汇聚到场景化落地的完整解决方案。其核心价值在于将分散的知识资源整合为统一的资产池…...

Anaconda +Jupyter Notebook安装(2025最新版)

Anaconda安装&#xff08;2025最新版&#xff09; Anaconda简介安装1&#xff1a;下载anaconda安装包2&#xff1a; 安装anaconda3&#xff1a;配置环境变量4&#xff1a;检查是否安装成功5&#xff1a;更改镜像源6&#xff1a;更新包7&#xff1a;检查 Jupyter Notebook一.Jup…...

正成为现代城市发展的必然趋势的智慧交通开源了

智慧交通视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。通过人流密集检测…...

手撕Transformer编码器:从Self-Attention到Positional Encoding的PyTorch逐行实现

Transformer 编码器深度解读 代码实战 1. 编码器核心作用 Transformer 编码器的核心任务是将输入序列&#xff08;如文本、语音&#xff09;转换为富含上下文语义的高维特征表示。它通过多层自注意力&#xff08;Self-Attention&#xff09;和前馈网络&#xff08;FFN&#x…...

Webpack和Vite插件的开发与使用

在现代开发中一般各公司都有自己的监控平台&#xff0c;对前端而言如果浏览器报错的话就可以通过埋点收集错误日志&#xff0c;再结合sourcemap文件可以帮助我们定位到错误代码&#xff0c;帮助我们排查问题。这里就记录一下之前在webpack和vite两个环境中的插件开发&#xff0…...

HTTP的状态码

HTTP 状态码 当浏览者访问一个网页时&#xff0c;浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前&#xff0c;此网页所在的服务器会返回一个包含 HTTP 状态码的信息头&#xff08;server header&#xff09;用以响应浏览器的请求。 常见的HTTP状态码 …...

Python函数-装饰器

装饰器 写好的函数&#xff0c;不做任何修改&#xff0c;就可以改变执行内容&#xff0c;在其头或尾部加入新的流程代码本质上就是使用函数嵌套&#xff0c;在内部嵌套定义的函数中调用原函数&#xff0c;从而可读在前或后加入新的代码使用的关键&#xff1a; 将原函数作为参数…...

【数据可视化-17】基于pyecharts的印度犯罪数据可视化分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…...

HTTP请求报文头和相应报文头

一、HTTP请求报文头 HTTP请求报文由请求行、请求头和请求体组成。请求头包含客户端向服务器发送的附加信息。 1.1 请求行 格式: 方法 请求URI HTTP/版本示例: GET /index.html HTTP/1.1   方法: 请求类型&#xff0c;如GET、POST、PUT、DELETE等。   请求URI: 请求的资源…...

19.4.9 数据库方式操作Excel

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 本节所说的操作Excel操作是讲如何把Excel作为数据库来操作。 通过COM来操作Excel操作&#xff0c;请参看第21.2节 在第19.3.4节【…...

BFS 走迷宫

#include<bits/stdc.h> using namespace std; int a[100][100],v[100][100];//访问数组 n,m<100 struct point {int x;int y;int step; }; queue<point> r;//申请队列 int dx[4]{0,1,0,-1};//四个方向 右下左上 int dy[4]{1,0,-1,0}; int main() { /* 5 4 1 …...

【Linux系统】—— 简易进度条的实现

【Linux系统】—— 简易进度条的实现 1 回车和换行2 缓冲区3 进度条的准备代码4 第一版进度条5 第二版进度条 1 回车和换行 先问大家一个问题&#xff1a;回车换行是什么&#xff0c;或者说回车和换行是同一个概念吗&#xff1f;   可能大家对回车换行有一定的误解&#xff0…...

Qt 中使用 SQLite 数据库的完整指南

SQLite 是一款轻量级、嵌入式的关系型数据库&#xff0c;无需独立的服务器进程&#xff0c;数据以文件形式存储&#xff0c;非常适合桌面和移动端应用的本地数据管理。Qt 通过 Qt SQL 模块提供了对 SQLite 的原生支持&#xff0c;开发者可以轻松实现数据库的增删改查、事务处理…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...