深入学习golang—new与make
golang基础-自定义类型和类型别名(type)

golang基础-⾃定义类型和类型别名(type)⾃定义类型Go语⾔通过type关键字定义⾃定义类型。
⾃定义类型是全新的类型。
⽰例:// 将newInt定义为int类型type newInt intfunc main() {var a newInta = 100fmt.Println(a) // 100fmt.Printf("%T\n", a) // main.newInt}上例中的newInt是具有int特性的新类型。
可以看到变量a的类型是main.newInt,这表⽰main包下定义的newInt类型。
类型别名语法格式:type 别名 = Type⽰例:type tempString = stringfunc main() {var s tempStrings = "我是s"fmt.Println(s) // 我是sfmt.Printf("%T\n", s) // string}例中,tempString是string的别名,其本质上与string是同⼀个类型。
类型别名只会在代码中存在,编译完成后不会有如tempString⼀样的类型别名。
所以变量s的类型是string。
字符类型中的byte和rune就是类型别名:type byte = uint8type rune = int32类型别名这个功能⾮常有⽤,鉴于go中有些类型写起来⾮常繁琐,⽐如json相关的操作中,经常⽤到map[string]interface {}这种类型,写起来是不是很繁琐,没关系,给它起个简单的别名!type strMap2Any = map[string]interface {}。
goland makefile 使用

goland makefile 使用Goland是一款非常强大的集成开发环境(IDE),专门针对Go语言开发。
它提供了一系列的功能和工具,能够帮助开发者更高效地进行Go语言项目的开发和调试。
而Makefile,则是一种用来自动化构建和编译程序的工具。
在使用Goland进行Go语言开发时,我们可以结合使用Makefile 来简化一些繁琐的操作,提高开发效率。
下面我将介绍一些在Goland中使用Makefile的方法和技巧。
我们需要创建一个名为Makefile的文件,并将其放置在项目根目录下。
Makefile是一个文本文件,其中包含一系列的规则(rules),每个规则都定义了一组命令,用于构建和编译程序。
在Makefile文件中,我们可以定义一些常用的操作和命令,比如编译程序、运行测试、清理临时文件等。
下面是一个简单的示例:```Makefile# 编译程序build:go build -o myapp# 运行测试test:go test ./...# 清理临时文件clean:rm -f myapp# 安装依赖deps:go mod download```在上面的示例中,我们定义了四个规则,分别用于编译程序、运行测试、清理临时文件和安装依赖。
其中,`build`规则使用`go build`命令来编译程序,`test`规则使用`go test`命令来运行测试,`clean`规则使用`rm -f`命令来删除生成的可执行文件,`deps`规则使用`go mod download`命令来安装依赖。
在Goland中,我们可以通过终端或者Goland的内置终端来执行Makefile中定义的规则。
首先,我们需要打开终端,然后进入到项目的根目录下。
接下来,我们可以使用`make`命令来执行Makefile 中的规则。
比如,如果我们想要编译程序,只需要在终端中执行`make build`命令即可。
如果我们想要运行测试,只需要执行`make test`命令即可。
Golang文件读取和写入

Golang⽂件读取和写⼊利⽤io/ioutil包⼀次性读取⼀个⽂件的所有内容--ReadFilepackage mainimport ("fmt""io/ioutil")func main() {//func ReadFile(filename string) ([]byte, error)data, err := ioutil.ReadFile("./connect.go")if err != nil {fmt.Println(err)} else {fmt.Println(string(data))}}分多次读,每次读取指定长度的⽂件内容--Readpackage mainimport ("fmt""os")func main() {//func Open(name string) (*File, error)f, err := os.Open("./connect.go")defer f.Close() //注意要关闭⽂件if err != nil {panic(err)} else {buffer := make([]byte, 20)//func (f *File) Read(b []byte) (n int, err error)//使⽤Read⽅法,将⽂件内容读⼊buffer切⽚中length, err := f.Read(buffer)if err != nil {panic(err)} else {fmt.Println("读取了", length, "字节内容")fmt.Println(string(buffer))}//第⼆次读取,会接着上⼀次的位置继续读length, err = f.Read(buffer)if err != nil {panic(err)} else {fmt.Println("读取了", length, "字节内容")fmt.Println(string(buffer))}}} 注意,使⽤os的Read时, 1、如果⽂件的内容长度⼤于buffer切⽚的长度,那么,只会读取⽂件buffer切⽚长度的内容,返回的长度就是切⽚的长度。
Golang技巧之默认值的设置

Golang技巧之默认值的设置在Golang中,默认值的设置是一种常见的需求。
在一些情况下,我们可能需要为变量设置一个默认值,以防止其未初始化或未被赋值的情况发生。
下面是一些设置默认值的技巧。
1.使用具体的默认值:最简单的设置默认值的方法是使用具体的默认值。
例如,如果一个整数变量需要默认值为0,只需将其初始化为0即可。
````govar num int = 0 // 设置默认值为0```2.使用零值:在Golang中,未初始化的变量将会被赋予其类型的零值。
因此,我们可以直接声明变量而不进行初始化,使其默认为零值。
````govar num int // 默认值为0```3.使用指针类型:可以使用指针类型来设置默认值。
通过将变量声明为指针类型,并初始化为nil,可以在需要时检查是否为nil,并在必要时分配内存并设置默认值。
````govar ptr *int // 默认值为nil// 检查是否为nilif ptr == nilptr = new(int) // 分配内存*ptr = 0 // 设置默认值为0}```4.使用结构体:如果需要设置多个变量的默认值,可以使用结构体来管理这些变量,并在结构体中定义默认值。
````gotype Config structName stringValue int}var conf Config // 默认值为零值//设置默认值conf = ConfigName: "default",Value: 0,}```5.使用函数:如果需要根据一些条件来设置默认值,可以使用函数来设置默认值并返回一个初始化后的值。
````gofunc getDefaultNum( int//根据一些条件返回默认值return 0}var num int = getDefaultNum( // 设置默认值为0```总结:在Golang中,设置默认值有多种方式,可以根据具体的需求选择适合的方法。
golang bufio的newreader方法

golang bufio的newreader方法在 Go 语言中,`bufio.NewReader`方法是`bufio`包中的一个函数,用于创建一个`bufio.Reader`类型的缓冲读取器。
该读取器会将文件内容逐行读取,并缓存到内存中以供随后的读取。
`bufio.NewReader`方法的语法如下:```gofunc NewReader(file *os.File) *Reader```其中,`file *os.File`是要读取的文件对象。
该方法返回一个`*Reader`对象,即一个指向`Reader`类型的指针。
下面是一个简单的使用示例:```gopackage mainimport ("bufio""fmt""os")func main() {// 打开文件file, err := os.Open("file.txt")if err != nil {panic(err)}defer file.Close()// 创建一个 bufio.Reader 实例reader := bufio.NewReader(file)// 创建一个缓冲区buffer := make([]byte, 4)// 循环从缓冲区中读取数据for {n, err := reader.Read(buffer)if err != nil {break}fmt.Print(string(buffer[:n]))}}```在上述示例中,首先使用`os.Open`函数打开一个文件,并将文件对象赋值给变量`file`。
然后使用`defer`语句确保在程序结束时关闭文件。
接下来,调用`bufio.NewReader`函数创建一个`bufio.Reader`实例,并将文件对象作为参数传递给该函数。
最后,创建一个大小为4的字节缓冲区`buffer`,并使用一个循环来不断从`reader`中读取数据,并将数据打印输出。
Go语言官方指南

Go 指南Hello, 世界Go 编程语言指南。
该指南被分为三个部分:基础概念、方法和接口,以及并发。
在指南后有若干个练习需要读者完成。
该指南可以进行交互。
点击“运行”按钮(或按Shift + Enter)可以在远程服务器上编译并执行程序。
结果展示在代码的下面。
这些例子程序展示了Go 的各个方面。
在指南中的程序可以成为你积累经验的开始。
编辑程序并且再次执行它。
当你准备好继续了,点击“向后”按钮或按PageDown 键。
Go 本地化该指南也有其他语言版本:∙Brazilian Portuguese — Português do Brasil∙Catalan — Català∙Hebrew —תיִרְבִע∙Japanese —日本語∙Romanian - Română∙Chinese —普通话点击“向后”按钮或者按PageDown 继续。
包每个Go 程序都是由包组成的。
程序运行的入口是包`main`。
这个程序使用并导入了包"fmt" 和`"math"`。
按照惯例,包名与导入路径的最后一个目录一致。
导入这个代码用圆括号组合了导入,这是“factored”导入语句。
同样可以编写多个导入语句,例如:import "fmt"import "math"导出名在导入了一个包之后,就可以用其导出的名称来调用它。
在Go 中,首字母大写的名称是被导出的。
Foo 和FOO 都是被导出的名称。
名称foo 是不会被导出的。
执行代码。
然后将math.pi 改名为math.Pi 再试着执行一下。
函数函数可以没有参数或接受多个参数。
在这个例子中,`add` 接受两个int 类型的参数。
注意类型在变量名_之后_。
(参考这篇关于Go 语法定义的文章了解类型以这种形式出现的原因。
)函数(续)当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。
golang学习笔记
golang学习笔记语法内层词法域可以屏蔽外层词法域中的变量花括号为显式词法域for,switch,if的条件部分会创建隐式词法域rune=int32byte=uint8运算符优先级* / % << >> & &^+ - | ^== != < <= > >=&&||格式化符号功能例%[1]o使⽤第⼀个操作数fmt.Printf("%d %[1]o %#[1]o\n", o)%#o⽤%o、%x或%X输出时⽣成0、0x或0X前缀fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x)⽤Go语⾔语法打印值%q打印带单引号字符,⽆损打印%c打印字符% x在每个⼗六进制数字前加⼊⼀个空格%t打印布尔类型%T打印数据类型浮点数中,NaN、正⽆穷⼤和负⽆穷⼤都不是唯⼀的字符串处理bytes针对[]byte类型提供strings字符串查询,替换,⽐较,截断,拆分,合并strconv布尔型,整型,浮点型和对应字符串的相互转换整数转字符串Itoa(Int to ASCII)FormatInt(int, base)FormatUint(int, base)Atoi(s)ParseInt(s, base, bit)unicode字符分类,数字,⼤⼩写判断,转换常量表达式值计算在编译期,⽽⾮运⾏期代码风格正常执⾏的语句不要代码缩进娜扎铁律:注释,⽇志,测试使⽤copy()复制切⽚和map返回切⽚或map,创建新变量进⾏遍历复制并返回使⽤defer管理⽂件和锁等资源(除⾮函数执⾏时间为纳秒级) mapmap并发读写,panic⽆法recoverslice扩容容量扩容⼤⼩扩容⽐例21142184116813216164321128641256128151225611024512112802560.2516964160.32523046080.3584905630727680.33333334409610240.33333334512010240.25716820480.4921620480.28571431228830720.333333341536030720.251945640960.266666682457651200.26315793072061440.253891281920.2666666849152102400.263157961440122880.2576800153600.2596256194560.25333333120832245760.25531915151552307200.2542373189440378880.25237568481280.25405404296960593920.25371712747520.25172412464896931840.25068875816321167360.251101327270401454080.259093121822720.2507042311366402273280.2514213122846720.25045046容量扩容⼤⼩扩容⽐例17766403553280.2522210564444160.250144127770885560320.250345834713606942720.2543397128683520.2501475542515210854400.250118678195213568000.25009438847769616957440.250037761059737621196800.2500302JSONtype struct xx{Color bool `json:"color,omitempty"`}iotaiota按⾏递增封装1. 调⽤⽅只需关注少量值2. 隐藏实现细节,使开发者在不破坏api的情况下⾃由开发(bytes.Buffer预分配空间)3. 阻⽌调⽤者对对象内布置的修改CSP顺序通信进程(communicating sequential processes)channel不要通过共享内存进⾏通信,⽽应通过通信进⾏内存共享使⽤go有锁数据结构有capacity,异步⾮阻塞⽆capacity,同步通信再次关闭已关闭channel会panic使⽤for elem := range ch,在channel关闭时⾃动退出循环向已关闭的channel中发送数据会panic关闭挂在goroutine阻塞的channel会panicclose⼀个channel会唤醒所有等待该channel的其它所有G,并使其进⼊Grunnable状态,这些Goroutine发现该channel已closed,会panic可以从已关闭channel中接受数据使⽤x, ok := <-ch中ok的值判断channel是否关闭,以及x为数据或零值关闭nil channel(使⽤var a chan int创建的channel)会panicnil channel发送数据会永远阻塞happens before: 接受者接收数据发⽣在唤醒发送者goroutine之前关闭原则⼀个sender,多个receiver,由sender关闭多个sender,借助额外channel做信号⼴播不要关闭已关闭通道,或向已关闭通道发送值检查通道关闭package mainimport "fmt"type T intfunc IsClosed(ch <-chan T) bool {select {case <-ch:return truedefault:}return false}func main() {c := make(chan T)fmt.Println(IsClosed(c)) // falseclose(c)fmt.Println(IsClosed(c)) // true}发送+++++++++++++++| buf | -----> | x | x | x | x | x || sendx || recvx || sendq | -----> +++++++++| recvq | | g | ---> G1| closed | | elem | ---> 6| lock | | ... |+++++++++++++++ +++++++++buf满时,⽤sudog包裹g和要发送的数据,⼊队sendq,并将当前gorutine进⾏gopark(m解除当前的g, m重新进⼊调度循环, g没有进⼊调度队列)出现新接收⽅时,sendq出队,从buf拷贝队头,从sender拷贝到队尾,goready(放⼊调度队列,等待被调度)读取空channel时,⽤sudog包裹g和要发送的数据,⼊队recvq,gopark关闭channel1. 加锁2. closed=13. ready所有sendq和recvq4. 解锁读取已关闭channel1. sendq和recvq为nil2. buf可能不为空3. 为空则清零reader读取位置4. 不为空则继续读bufselecttype hselect struct {// 总 case 数tcase uint16 // total count of scase[]// 当前已初始化填充的 casencase uint16 // currently filled scase[]// 轮询顺序pollorder *uint16 // case poll order// case 的 channel 的加锁顺序lockorder *uint16 // channel lock order// case 数组,为了节省⼀个指针的 8 个字节搞成这样的结构// 实际上要访问后⾯的值,还是需要进⾏指针移动// 指针移动使⽤ runtime 内部的 add 函数scase [1]scase // one per case (in order of appearance)}如果 select 中实际只操作⼀个 channel,写成 for range 形式:// do some happy things with d}如果 select 中需要监听多个 channel,并且这些 channel 可能被关闭,写双返回值的 channel 取值表达式: for {select {case d, ok := <-ch1:if !ok {break outer}case d, ok := <-ch2:if !ok {break outer}}}select不⽀持throughfall,随机选择case使⽤堆实现panictype _panic struct {argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblinkarg interface{} // argument to paniclink *_panic // 指向上⼀个panicrecovered bool // recover标志aborted bool // the panic was aborted}map⾼并发读写时,程序直接崩溃,会panic,但⽆法⽤recover捕获panic先执⾏当前goroutine上已有的derfer任务(仅限当前goroutine),然后检查recovered标记如果为true,退出panic流程,程序恢复正常;如果为false,则退出程序panic退出时会打印调⽤栈,最终调⽤exit(-2)退出整个进程recover只在defer上下⽂中⽣效不在defer上下⽂中调⽤,直接返回nilrecover流程检查current gorountine是否在panic流程中,如果不在直接返回nil执⾏完recove后,调⽤recovery在recovery中将调⽤recover的defer函数设置到当前goroutine中的sched中,并将ret设置为1接⼝interface结构// iface描述接⼝包含⽅法type iface struct {tab *itab // 接⼝类型data unsafe.Pointer // 具体值}type itab struct {inter *interfacetype // 接⼝类型_type *_type // 实体类型,内存对齐⽅式fun [1]uintptr // 数组,保存实际⽅法地址(第⼀个⽅法)}type interfacetype struct {typ _type // 描述 Go 语⾔中各种数据类型的结构体pkgpath name // 接⼝包名mhdr []imethod // 接⼝定义函数列表}// eface不包含任何⽅法的空接⼝type eface struct {data unsafe.Pointer}_typetype _type struct {// 类型⼤⼩size uintptrptrdata uintptr// 类型的 hash 值hash uint32// 类型的 flag,和反射相关tflag tflag// 内存对齐相关align uint8fieldalign uint8// 类型的编号,有bool, slice, struct 等等等等kind uint8alg *typeAlg// gc 相关gcdata *bytestr nameOffptrToThis typeOff}type arraytype struct {typ _typeelem *_typeslice *_typelen uintptr}type chantype struct {typ _typeelem *_typedir uintptr}type slicetype struct {typ _typeelem *_type}type structtype struct {typ _typepkgPath namefields []structfield}判断myWriter是否实现io.Writer接⼝var _ io.Writer = (*myWriter)(nil)var _ io.Writer = myWriter{}interface保存⼀对数据变量实际的值变量的静态类型类型转换<结果类型> := <⽬标类型> ( <表达式> )类型断⾔<⽬标类型的值>,<布尔参数> := <表达式>.( ⽬标类型 ) // 安全类型断⾔v.(type)在switch中不⽀持fallthrough类型断⾔针对接⼝变量进⾏操作如果实现了接收者是值类型的⽅法,会隐含地也实现了接收者是指针类型的⽅法> 接⼝转换当判定⼀种类型是否满⾜某个接⼝时,Go 使⽤类型的⽅法集和接⼝所需要的⽅法集进⾏匹配,如果类型的⽅法集完全包含接⼝的⽅法集,则可认为该类型实现了该接⼝。
带你实战入门golangPPT模板
06
性能优化:采用缓存机制,减少磁盘I/O,提高响应速度。
路由处理
路由定义:将请求URL映射到处理函数
路由匹配:根据请求URL查找对应的处理函数
处理函数:处理请求并生成响应
响应发送:将处理结果发送给客户端
路由优化:使用缓存、压缩等手段提高路由处理效率
请求响应处理
接收请求:使用http.ListenAndServe()函数监听端口,接收客户端请求
defer的使用
defer的作用:延迟执行,在函数返回之前执行
A
defer的常见使用场景:资源清理、错误处理、日志记录等
C
B
D
defer的语法:defer 函数名(参数)
defer的执行顺序:后进先出,即最后定义的defer语句最先执行
错误处理
错误类型:panic、error、defer
错误处理方式:try-catch、error handling、defer
数组(array):固定长度的数组
切片和数组的声明和初始化
04
golang进阶
map和channel的使用
map:用于存储键值对,支持快速查找和插入
channel:用于线程间通信,支持阻塞和非阻塞模式
使用map进行数据存储和查找
使用channel进行线程间数据传递和同步
实战案例:使用map和channel实现一个简单的生产者-消费者模型
Gin:轻量级Web框架,支持中间件、路由分组和请求验证等功能
Echo:高性能HTTP服务器框架,支持WebSocket和HTTP/2协议
Fiber:快速、简单、高效的Web框架,支持中间件、路由分组和请求验证等功能
文件操作库
Golang编程实现生成n个从a到b不重复随机数的方法
Golang编程实现⽣成n个从a到b不重复随机数的⽅法本⽂实例讲述了Golang编程实现⽣成n个从a到b不重复随机数的⽅法。
分享给⼤家供⼤家参考,具体如下:代码很简单:复制代码代码如下:package testimport ("fmt""math/rand""time")//⽣成若⼲个不重复的随机数func RandomTestBase() {//测试5次for i := 0; i < 5; i++ {nums := generateRandomNumber(10, 30, 10)fmt.Println(nums)}}//⽣成count个[start,end)结束的不重复的随机数func generateRandomNumber(start int, end int, count int) []int {//范围检查if end < start || (end-start) < count {return nil}//存放结果的slicenums := make([]int, 0)//随机数⽣成器,加⼊时间戳保证每次⽣成的随机数不⼀样r := rand.New(rand.NewSource(time.Now().UnixNano()))for len(nums) < count {//⽣成随机数num := r.Intn((end - start)) + start//查重exist := falsefor _, v := range nums {if v == num {exist = truebreak}}if !exist {nums = append(nums, num)}}return nums}输出结果:[12 20 18 19 21 28 15 13 11 10][28 15 12 10 20 18 16 24 27 17][25 28 29 19 21 12 16 13 11 15][27 20 19 23 18 13 21 24 12 26][19 10 27 18 28 12 22 14 16 26]希望本⽂所述对⼤家Go语⾔程序设计有所帮助。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Go语言中的内建函数new和make是两个用于内存分配的原语(allocation primitives)。对于初学
者,这两者的区别也挺容易让人迷糊的。简单的说,new只分配内存,make用于slice,map,和c
hannel的初始化。
1. new
这是一个用来分配内存的内建函数,但是与C++不一样的是,它并不初始化内存,只是将其置零。
也就是说,new(T)会为T类型的新项目,分配被置零的存储,并且返回它的地址,一个类型为*T的
值。在Go的术语中,其返回一个指向新分配的类型为T的指针,这个指针指向的内容的值为零(z
ero value)。注意并不是指针为零。
Go语言中的对象没有C++中的构造函数,如果用C来描述,Go中的new大概相当于:
T *t = (T*)malloc(sizeof(T))
memset(t, 0, sizeof(T))
其实,上面的描可能也不是很准确,也许用*t=zerovalue更准确。因为对于不同的数据类型,零值的
意义是完全不一样的。比如,对于bool类型,零值为false;int的零值为0;string的零值是空字符
串:
b := new(bool)
fmt.Println(*b)
i := new(int)
fmt.Println(*i)
s := new(string)
fmt.Println(*s)
输出:
false
0
注意最后有一个空字符串。
2. 初始化
很多时候,零值并不是一个好主意,我们需要做一些初始化。考虑如下结构体:
type Rect struct {
x, y float64
width, height float64
}
零值的Rect并没有多大用处,我们以下方式进行初始化:
rect3 := &Rect{0, 0, 100, 200}
rect4 := &Rect{width: 100, height: 200}
再申明一下,Go语言中没有C++中的构造函数,对象的创建一般交给一个全局的创建函数来完成:
func NewRect(x, y, width, height float64) *Rect {
return &Rect{x, y, width, height}
}
注意,这里与C/C++不同的是,返回一个局部变量的地址在Go语言中是绝对没有问题的;变量关联
的存储在函数返回之后依然存在。
更直接的说,在Go语言中,如果一个局部变量在函数返回后仍然被使用,这个变量会从heap,而
不是stack中分配内存。详细参考How do I know whether a variable is allocated on the heap o
r the stack?。
3. make
内建函数make(T, args)与new(T)的用途不一样。它只用来创建slice,map和channel,并且返回
一个初始化的(而不是置零),类型为T的值(而不是*T)。之所以有所不同,是因为这三个类型的背
后引用了使用前必须初始化的数据结构。例如,slice是一个三元描述符,包含一个指向数据(在数
组中)的指针,长度,以及容量,在这些项被初始化之前,slice都是nil的。对于slice,map和ch
annel,make初始化这些内部数据结构,并准备好可用的值。
例如,
make([]int, 10, 100)
分配一个有100个int的数组,然后创建一个长度为10,容量为100的slice结构,该slice引用包
含前10个元素的数组。对应的,new([]int)返回一个指向新分配的,被置零的slice结构体的指针,
即指向值为nil的slice的指针。
var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful
var v []int = make([]int, 100) // the slice v now refers to a new array of 100 int
s
// Unnecessarily complex:这种做法实在是很蛋疼
var p *[]int = new([]int)
*p = make([]int, 100, 100)
// Idiomatic:习惯的做法
v := make([]int, 100)
记住make只用于map,slice和channel,并且不返回指针。要获得一个显式的指针,使用new进
行分配,或者显式地使用一个变量的地址。