Go
文章平均质量分 76
Go语言基础
吴声子夜歌
个人学习记录
展开
-
Go——log包详解
参数列表:out 输出目标prefix 输出前缀flag 格式配置标识值返回值:自定义的logger功能说明:这个方法用来自定义logger,指定输出目标、格式等。原创 2023-05-18 14:43:58 · 278 阅读 · 0 评论 -
Go——os包详解(二)
【代码】Go——os包详解(二)原创 2023-05-18 00:07:40 · 610 阅读 · 0 评论 -
Go——os包详解(一)
Go语言的os包中提供了操作系统函数的接口,是一个比较重要的包。顾名思义,os包的作用主要是在服务器上进行系统的基本操作,如文件操作、目录操作、执行命令、信号与中断、进程、系统状态等等。原创 2023-05-17 23:13:03 · 1381 阅读 · 1 评论 -
Go——fmt包详解
Go 语言中的fmt包含有格式化输入输出的函数,类似于C语言的printf和scanf。格式字符串的规则来源于C但更简单更好用。fmt包实现了格式化I/O。主要分为向外输出内容和获取输入内容两大部分。原创 2023-05-16 11:53:08 · 2481 阅读 · 1 评论 -
Go——strings包详解
【代码】Go——strings包详解。原创 2023-05-16 00:13:45 · 525 阅读 · 0 评论 -
Go——strconv包详解
【代码】Go——strconv包详解。原创 2023-05-14 21:58:56 · 245 阅读 · 0 评论 -
Go——container包(list、ring、heap)
List:双向链表Ring:循环链表,也就是环Heap:堆。原创 2023-05-13 23:39:28 · 350 阅读 · 0 评论 -
Go——习惯用法
大部分情况下我们不需要两个同名且只是首字母大小写不同的函数,只有在函数逻辑很复杂,而且函数在包的内、外部都被调用的情况下,才考虑拆分为两个函数进行实现。Go标准库的写法也遵循这样的规则。当绝大多数人都遵照这种写法时,如果不遵循这种“潜规则”,则写出的代码让别人读起来很别扭。很多包的开发者会在内部实现两个“同名”的函数或方法,一个首字母大写,用于导出API供外部调用;一般首字母大写的函数调用首字母小写的函数,同时包装一些功能;Go对代码的干净和整洁要求到了强迫症的程度,但这是一种好的约束,虽然很多人难以接。原创 2023-05-06 01:39:09 · 103 阅读 · 0 评论 -
Go——值、指针和引用
C++里面的引用的含义就是别名,Go语言规范中并没有引用的概念,但为了论述方便,闭包对外部变量的引用,我们可认为是建立了一个和外部变量同名的“引用”,该引用和外部变量指向相同的地址。还有一种解释就是Go语言针对闭包,显式地扩大了形参的可见域,使其在函数返回的闭包中仍然可见。至于是“同名引用”,还是“扩大作用域”,这些只是对闭包这个语言特性的规范表述。Go复合类型中chan、map、slice、interface内部都是通过指针指向具体的数据,这些类型的变量在作为函数参数传递时,实际上相当于指针的副本。原创 2023-05-06 00:53:28 · 226 阅读 · 0 评论 -
Go——切片困惑
切片可以由数组创建,一个底层数组可以创建多个切片,这些切片共享底层数组,使用append扩展切片过程中可能修改底层数组的元素,间接地影响其他切片的值,也可能发生数组复制重建,共用底层数组的切片,由于其行为不明朗,不推荐使用。可以看到切片的数据结构有三个成员,分别是指向底层数组的指针、切片的当前大小和底层数组的大小。注意:Go中的数组是一种基本类型,数组的类型不仅包括其元素类型,也包括其大小,[2]int和[5]int是两个完全不同的数组类型。创建的切片是有区别的。前者的切片指针有分配,后者的内部指针为0。原创 2023-05-06 00:39:17 · 96 阅读 · 0 评论 -
Go——defer陷阱
如果对函数调用是值拷贝、函数闭包及dfr的特性有了解,以及对上面的三条规则熟悉,那么对于这类函数就不应该再有疑惑了。当然在defer中修改函数返回值不是一种明智的编程方法,在实际编程中应尽可能避免此种情况。还有一种彻底解决该问题的方法是,在定义函数时使用不带返回值名的格式。通过这种方式,defer就不能直接引用返回值的栈区,也就避免了返回值被修改的问题,看一下下面的代码。1、2、3三个函数的共同点就是它们都是带命名返回值的函数,返回值都是变量r。综上所述,对于带defer的函数返回整体上有三个步骤。原创 2023-05-05 23:58:24 · 140 阅读 · 0 评论 -
Go——range复用临时变量
可以看到新程序的运行结果符合预期。这个不能说是缺陷,而是Go语言设计者为了性能而选择的一种设计方案,因为大多情况下for循环块里的代码是在同一个goroutine里运行的,为了避免空间的浪费和GC的压力,复用了range迭代临时变量。语言使用者明白这个规约,在for循环下调用并发时要复制迭代变量后再使用,不要直接引用for迭代变量。可以看到Goroutine 5和main goroutine存在数据竞争,更进一步证实了range共享临时变量。range在迭代写的过程中,多个goroutine并发地去读。原创 2023-05-05 20:52:53 · 190 阅读 · 0 评论 -
Go——多值赋值和短变量声明
短变量的声明和赋值中的最容易产生歧义的是多值短变量的声明和赋值,这个问题的根源是Go语言的语法允许多值短变量声明和赋值的多个变量中,只要有一个是新变量就可以使用":="进行赋值。也就是说,在多值段变量的声明和赋值时,至少有一个变量是新创建的局部变量,其他的变量可以复用以前的变量,不是新创建的变量执行的仅仅就是赋值。如果在赋值语句a,b:=va,vb所在的代码块中已经存在一个局部变量a,则赋值语句a,b:=va,vb不会创建新变量a,而是直接使用va赋值给已经声明的局部变量a,但是会创建新变。原创 2023-05-05 20:40:32 · 1086 阅读 · 1 评论 -
Go——Inject库
但是软件开发没有银弹,当问题足够复杂时,应该考虑的是服务拆分,而不是把复杂的逻辑用一个“大盒子”装起来,看起来干净了,但也只是看起来干净,实现还是很复杂,这也是使用框架带来的副作用。一般情况下,使用库的程序是程序主动地调用库的功能,但使用框架的程序常常由框架驱动整个程序,在框架下写的业务代码是被框架驱动的,这种模式就是“控制反转”。“依赖注入”是实现“控制反转”的一种方法,如果说“控制反转”是一种设计思想,那么“依赖注入”就是这种思想的一种实现,通过注入的参数或实例的方式实现控制反转。原创 2023-05-05 17:46:10 · 403 阅读 · 0 评论 -
Go——反射规则
Vlue值在什么情况下可以修改?我们知道实例对象传递给接口的是一个完全的值拷贝,如果调用反射的方法reflect.ValueOf()传进去的是一个值类型变量,则获得的Value实际上是原对象的一个副本,这个Vlue是无论如何也不能被修改的。如果传进去的是一个指针,虽然接口。内部转换的也是指针的副本,但通过指针还是可以访问到最原始的对象,所以此种情况获得的Value是可以修改的。下面来看一个简单的示例。指针类型Type到值类型Type。值类型Type到指针类型Type。原创 2023-05-05 16:44:46 · 70 阅读 · 0 评论 -
Go——反射基本概念
基础类型是根据编译器、运行时构建类型的内部数据结构不同来划分的,不同的基础类型,其构建的最终内部数据结构不一样。接口变量,则函数的返回结果又分两种情况:如果a绑定了具体类型实例,则返回的是接口的动态类型,也就是a绑定的具体实例类型的信息,如果没有绑定具体类型实例,则返回的是接口自身的静态类型信息。底层类型和基础类型的区别在于,基础类型是抽象的类型划分,底层类型是针对每一个具体的类型来定义的,比如不同的struct类型在基础类型上都划归为sturct类型,但不同的struct底层类型是不一样的。原创 2023-05-05 01:14:14 · 120 阅读 · 0 评论 -
Go——context标准库
实际编程中goroutine会拉起新的goroutine,新的goroutine又会拉起另一个新的goroutine,最终形成一个树状的结构,由于goroutine里并没有父子的概念,这个树状的结构只是在程序员头脑中抽象出来的,程序的执行模型并没有维护这么一个树状结构。因为context包,的使用思路就是不停地调用context包提供的包装函数来创建具有特殊功能的Context实例,每一个Context实例的创建都以上一个Context对象为参数,最终形成一个树状的结构。context包中的具体类型。原创 2023-05-04 23:45:19 · 129 阅读 · 0 评论 -
Go——flag包
flag包提供了一系列解析命令行参数的功能接口。原创 2023-05-04 15:17:32 · 578 阅读 · 0 评论 -
Go——并发范式(future模式)
future最大的好处是将函数的同步调用转换为异步调用,适用于一个交易需要多个子调用且这些子调用没有依赖的场景。实际情况可能比上面示例复杂得多,要考虑错误和异常的处理,编程中经常遇到在一个流程中需要调用多个子调用的情况,这些子调用相互之间没有依赖,如果串行地调用,则耗时会很长,此时可以使用Go并发编程中的future模式。原创 2023-05-03 22:49:32 · 163 阅读 · 0 评论 -
Go——并发范式(固定worker工作池)
服务器编程中使用最多的就是通过线程池来提升服务的并发处理能力。在Go语言编程中,一样可以轻松地构建固定数目的goroutines作为工作线程池。下面还是以计算多个整数的和为例来说明这种并发范式。main函数负责拉起上述goroutine,并从结果通道获取最终的结果。原创 2023-05-03 22:16:38 · 255 阅读 · 0 评论 -
Go——并发范式(每个请求一个goroutine)
这种并发模式相对比较简单,就是来一个请求或任务就启动一个goroutine去处理,典型的就是Go中的HTTP server服务。下面看一下Go语言http标准库处理请求的方式,代码如下。这几个函数分别在不同的goroutine中运行,它们通过通道和sync.WaitGroup进行通信和同步。原创 2023-05-03 21:45:40 · 262 阅读 · 0 评论 -
Go——并发范式(管道)
通道可以分为两个方向,一个是读,另一个是写,假如一个函数的输入参数和输出参数都是相同的chan类型,则该函数可以调用自己,最终形成一个调用链。当然多个具有相同参数类型的函数也能组成一个调用链,这很像UNIX系统的管道,是一个有类型的管道。下面通过具体的示例演示G0程序这种链式处理能力。原创 2023-05-03 21:14:35 · 65 阅读 · 0 评论 -
Go——并发范式(生成器)
生成器原创 2023-05-03 21:04:03 · 488 阅读 · 0 评论 -
Go——并发基础
并行是硬件和操作系统开发者重点考虑的问题,作为应用层的程序员,唯一可以选择的就是充分借助操作系统提供的API和程序语言特性,结合实际需求设计出具有良好并发结构的程序,提升程序的并发处理能力。通道分为无缓冲的通道和有缓冲的通道,Go提供内置函数len和cap,无缓冲的通道的len和cap都是0,有缓冲的通道的len代表没有被读取的元素数,cap代表整个通道的容量。通道是有类型的,可以简单地把它理解为有类型的管道。并发是在规定的时间内多个请求都得到执行和处理,强调的是给外界的感觉,实际上内部可能是分时操作的。原创 2023-05-03 17:43:54 · 206 阅读 · 0 评论 -
Go——空接口
Go的类型系统里面没有类的概念,所有的类型都是一样的身份,没有Java里面对基本类型的开箱和装箱操作,所有的类型都是统一的。Go语言的空接口有点像C语言中的void*,只不过void*是指针,而Go语言的空接口内部封装了指针而已。空接口有两个字段,一个是实例类型,另一个是指向绑定实例的指针,只有两个都为nil时,空接口才为nil。Go语言没有泛型,如果一个函数需要接收任意类型的参数,则参数类型可以使用空接口类型,这是弥补没有泛型的一种手段。空接口不是真的为空,接口有类型和值两个概念。原创 2023-05-03 14:30:24 · 472 阅读 · 0 评论 -
Go——接口运算(类型断言、接口查询)
这种使用方式存在争议:首先在switch语句块内新声明局部变量i覆盖原有的同名变量i不是一种好的编程方式,其次如果类型匹配成功,则ⅰ的类型就发生了变化,如果没有匹配成功,则ⅰ还是原来的接口类型。i必须是接口变量,如果是具体类型变量,则编译器会报non-interface type xxx on left,TypeName可以是接口类型名,也可以是具体类型名。接口查询有两层语义,一是查询一个接口变量底层绑定的底层变量的具体类型是什么,二是查询接口变量绑定的底层变量是否还实现了其他接口。原创 2023-05-03 01:49:53 · 291 阅读 · 0 评论 -
Go——接口基本概念
a接口的法集为A,b接口的法集为B,如果B是A的子集合,则a的接口变量可以直接赋值给B的接口变量。如果具体类型实例的方法集是某个接口的方法集的超集,则称该具体类型实现了接口,可以将该具体类型的实例直接赋值给接口类型的变量,此时编译器会进行静态的类型检查。接口被初始化后,调用接口的方法就相当于调用接口绑定的具体类型的方法,这就是接口调用的语义。接口方法调用的最终地址是在运行期决定的,将具体类型变量赋值给接口后,会使用具体类型的方法指针初始化接口变量,当调用接口变量的方法时,实际上是间接地调用实例的方法。原创 2023-05-02 22:38:52 · 123 阅读 · 0 评论 -
Go——函数类型
通常说的函数类型就是指有名函数类型,“函数签名”是指函数的字面量类型,在很多地方把函数类型和函数签名等价使用,这是不严谨的。从Go类型系统的角度来看,“有名函数”和“匿名函数”都是函数字面量类型的实例。Go语言没有C语言中函数声明的语义,准确地说,Go代码调用G0编写的函数不需要声明,可以直接调用,但Go调用汇编语言编写的函数还是要使用函数声明语句,示例如下。使用type NewType 0ldType语法定义一种新类型,这种类型都是命名类型,同理可以使用该方法定义一种新类型:函数命名类型,简称函数类型。原创 2023-05-02 21:05:28 · 284 阅读 · 0 评论 -
Go——组合和方法集
Go函数的调用实参都是值拷贝,方法调用参数传递也是一样的机制,具体类型变量传递给接口时也是值拷贝,如果传递给接口变量的是值类型,但调用方法的接收者是指针类型,则程序运行时虽然能够将接收者转换为指针,但这个指针是副本的指针,并不是我们期望的原变量的指针。具体类型传递给接口时编译器会进行严格的方法集校验。使用type定义的新类型不会继承原有类型的方法,有个特例就是命名结构类型,命名结构类型可以嵌套其他的命名类型的字段,外层的结构类型是可以调用嵌入字段类型的方法,这种调用既可以是显式的调用,也可以是隐式的调用。原创 2023-05-02 20:24:53 · 372 阅读 · 1 评论 -
Go——方法调用
命名类型方法接收者有两种类型,一个是值类型,另一个是指针类型,这个和函数是一样的,前者的形参是值类型,后者的形参是指针类型。下面定义一个类型T,增加两个方法,方法Gt的接收者为T,方法Set的接收者类型为*T。上面示例定义了一个新类型Int,新类型的底层类型是int,Int虽然不能继承int的方法,但底层类型支持的操作(算术运算和赋值运算)可以被上层类型继承,这是Go类型系统的一个特点。变量x的静态类型是T,M是类型T的一个方法,x.T被称为方法值(method value)。T类型的方法集是S和。原创 2023-05-02 15:34:37 · 654 阅读 · 0 评论 -
Go——类型方法
用户自定义类型使用关键字ype,其语法格式是。oldtype可以是自定义类型、预声明类型、未命名类型中的任意一种。newtype是新类型的标识符,与oldtype具有相同的底层类型,并且都继承了底层类型的操作集合(这里的操作不是方法,比如底层类型是map,支持range迭代访问,则新类型也可以使用range迭代访问).除此之外,newtype和oldtype是两个完全不同的类型,newtype不会继承oldtype的方法。原创 2023-05-02 14:55:41 · 278 阅读 · 0 评论 -
Go——类型简介
Go语言的基本类型中的复合类型:数组(array)、切片(slice)、字典(map)、通道(channel)、指针(pointer)、函数字面量(function)、结构(struct)和接口(interface)都属于类型字面量,也都是未命名类型。T1和T2的底层类型都是string,T3和T4的底层类型都是string,T6和T5的底层类型都是[]T1。类型可以通过识符来表示,这种类型称为命名类型。Go语言的基本类型中有20个预声明简单类型都是命名类型,Go语言还有一种命名类型——用户自定义类型。原创 2023-05-02 13:56:56 · 495 阅读 · 0 评论 -
Go——错误处理(error)
Go语言内置错误接口类型error。Go是一门类型安全的语言,其运行时不会出现这种编译器和运行时都无法捕获的错误,也就是说,不会出现untrapped error,所以从这个角度来说,Go语言不存在所谓的异常,出现的“异常”全是错误。程序虽然发生错误,但是程序能够容错继续执行,此时应该使用错误返回值的方式处理错误,或者在可能发生运行时错误的非关键分支上使用recover捕获panic。所以对错误的处理也有两种方法,一种是通过返回一个错误类型值来处理错误,另一种是直接调用panic抛出错误,退出程序。原创 2023-05-02 00:58:59 · 383 阅读 · 0 评论 -
Go——panic和recover
发生panic后,程序会从调用panic的函数位置或发生panic的地方立即返回,逐层向上执行函数的defer语句,然后逐层打印函数调用堆栈,直到被recover捕获或运行到最外层函数而退出。可以有连续多个panic被抛出,连续多个panic的场景只能出现在延迟调用里面,否则不会出现多个panic被抛出的场景。但只有最后一次panic能被捕获。panic的参数是一个空接口类型interface{},所以任意类型的变量都可以传递给panic调用panic的方法非常简单:panic(xxx)。原创 2023-05-01 23:05:46 · 140 阅读 · 0 评论 -
Go——闭包
如果函数返回的闭包引用的是全局变量,则多次调用该函数返回的多个闭包引用的都是同一个a。对象是附有行为的数据,而闭包是附有数据的行为,类在定义时已经显式地集中定义了行为,但是闭包中的数据没有显式地集中声明的地方,这种数据和行为耦合的模型不是一种推荐的编程模型,闭包仅仅是锦上添花的东西,不是不可缺少的。但是这种隐秘的共享变量的方式带来的坏处是不够直接,不够清晰,除非是非常有价值的地方,一般不建议使用闭包。闭包对闭包外的环境引入是直接引用,编译器检测到闭包,会将闭包引用的外部变量分配到堆上。原创 2023-05-01 22:44:39 · 74 阅读 · 0 评论 -
Go——defer
defer也有明显的副作用:defer会推迟资源的释放,defer尽量不要放到循环语句里面,将大函数内部的defer语句单独拆分成一个小函数是一种很好的实践方式。下面示例代码中,实参a的值在defer注册时通过值拷贝传递进去,后续语句a++并不会影像defer语句最后的输出结果。defer的好处是可以在一定程度上避免资源泄露,特别是在有很多return语句,有多个资源需要关闭的场景中,很容易漏掉资源的关闭操作。defer语句的位置不当,有可能导致panic,一般defer语句放在错误检查语句之后。原创 2023-05-01 21:46:31 · 101 阅读 · 0 评论 -
Go——函数签名和匿名函数
G0提供两种函数:有名函数和匿名函数。匿名函数可以看作函数字面量,所有直接使用函数类型变量的地方都可以由匿名函数代替。函数类型和map、slice、chan一样,实际函数类型变量和函数名都可以当作指针变量,该指针指向函数代码的开始位置。有名函数的函数名可以看作函数类型的常量,可以直接使用函数名调用函数,也可以直接赋值给函数类型变量,后续通过该变量来调用该函数。函数类型又叫函数签名,一个函数的类型就是函数定义首行去掉函数名、参数名和{,可以使用fmt.Printf的"%T"格式化参数打印函数的类型。原创 2023-05-01 21:10:07 · 279 阅读 · 0 评论 -
Go——函数基本概念
函数是Go程序源代码的基本构造单位,一个函数的定义包括如下几个部分:函数声明关键字func、函数名、参数列表、返回列表和函数体。函数名遵循标识符的命名规则,首字母的大小写决定该函数在其他包的可见性:大写时其他包可见,小写时只有相同的包可以访问;函数的参数和返回值需要使用“()”包裹,如果只有一个返回值,而且使用的是非命名的参数,则返回参数的()”可以省略。函数体使用“}”包裹,并且“{”必须位于函数返回值同行的行尾。原创 2023-05-01 18:14:32 · 185 阅读 · 0 评论 -
Go——控制结构(if、switch、for、Lable、goto、break)
现代计算机存储结构无论“普林斯顿结构”,还是“哈佛结构”,程序指令都是线性地存放在存储器上。程序执行从本质上来说就是两种模式:顺序和跳转。·顺序就是按照程序指令在存储器上的存放顺序逐条执行。跳转就是遇到跳转指令就跳转到某处继续线性执行。Go是一门高级语言,其源程序虽然经过了高度的抽象并封装了很多语法糖,但还是跳不出这个模式(这里暂时不考虑goroutine引入并发后的执行视图变化)。顺序在Go里面体现在从main函数开始逐条向下执行,就像我们的程序源代码顺序一样;原创 2023-04-30 19:10:30 · 261 阅读 · 0 评论 -
Go——复合数据类型
Go中的suct类型和C类似,中文翻译为结构,由多个不同类型元素组合而成。实际使用struct字面量的场景不多,更多的时候是捅咕otype自定义一个新的类型来实现的。type是自定义类型的关键字,不但支持struct类型的创建,还支持任意其他子定义类型的创建。Go语言的数组的定长性和值拷贝限制了其使用场景,Go提供了另一种数据类型slice(中文为切片),这是一种变长数组,其数据结构中有指向数组的指针,所以是一种引用类型。map的类型格式是:map[K]T,其中K可以是任意可以进行比较的类型,T是值类型。原创 2023-04-30 15:01:04 · 200 阅读 · 0 评论