主要参考对象
https://docs.microsoft.com/zh-cn/learn/paths/go-first-steps/
https://tour.go-zh.org/

安装

https://golang.google.cn/
下载安装即可

语言

工作区

Go 在组织项目文件方面与其他编程语言不同。 首先,Go 是在工作区的概念下工作的。工作区就是应用程序源代码所在的位置。 所有 Go 项目共享同一工作区,可以通过环境变量$GOPATH 修改。go 1.11 引入了 Go Modules模块,环境变量GO111MODULE = on时(不同版本含义有点不一样)可以将所有的代码仓库均不存储在 GOPATH 下。

在任意一个目录下执行 go mod init 会生成go.mod文件,之后就可以直接执行这个目录下的文件(类似这个目录就成了工作区)

常见目录结构

  1. bin
  2. src
  3. pkg

变量

使用var 声明变量

在没有值的情况下需要标注类型

var firstName string
var firstName, lastName string
var (firstName, lastName stringage int
)

初始化变量

如果你决定初始化某个变量,则不需要指定其类型,因为当你使用具体值初始化该变量时,Go 会推断出其类型

使用冒号等于号时,要声明的变量必须是新变量,在声明函数外的变量时,必须使用 var 关键字执行此操作,即:=只能在函数内部使用,如果声明全局变量需要使用var关键字

如果声明了变量但未使用,Go 会抛出错误, 使用特殊字符 _ 可以占位,不使用,让编译顺利通过(一些函数会返回多个值,必须接收,但又没用)

var (firstName = "John"lastName  = "Doe"age       = 32
)var (firstName, lastName, age = "John", "Doe", 32
)func main() {firstName, lastName := "John", "Doe"age := 32fmt.println(firstName, lastName, age)
}

常量

const HTTPStatusOK = 200
const (StatusOK              = 0StatusConnectionReset = 1StatusOtherError      = 2
)

数据类型

Go 是一种强类型语言。 这意味着你声明的每个变量都绑定到特定的数据类型,并且只接受与此类型匹配的值.共有四类数据类型:

  1. 基本类型:数字、字符串和布尔值
  2. 聚合类型:数组和结构
  3. 引用类型:指针、切片、映射、函数和通道
  4. 接口类型:接口

在 Go 中,如果你不对变量初始化,所有数据类型都有默认值。 此功能非常方便,因为在使用之前,你无需检查变量是否已初始化。

基本类型

  1. 数字
  2. 字符串
  3. 布尔值

数字

int8、int16、int32(rune)、int64、uint8(byte)、uint16、uint32、uint64、float32、float64

默认值: 整数数值为0,浮点数为+0.000000e+000

定义整数类型的关键字是 int,但 Go 还提供了 int8、int16、int32 和 int64 类型,其大小分别为 8、16、32 或 64 位的整数。 使用 32 位操作系统时,如果只是使用 int,则大小通常为 32 位。 在 64 位系统上,int 大小通常为 64 位。 但是,此行为可能因计算机而不同。 可以使用 uint。 但是,只有在出于某种原因需要将值表示为无符号数字的情况下,才使用此类型。 此外,Go 还提供 uint8、uint16、uint32 和 uint64 类型。

Go 语言把字符分 byte 和 rune 两种类型处理。 rune 是int32 的别名,常用于中文的对应的Unicode, byte 是uint8的别名,https://www.cnblogs.com/cheyunhua/p/16007219.html

如果尝试在不同类型之间执行数学运算,将会出现错误, 在 Go 中将值从一种类型转换为另一种类型时,需要显式声明新类型

布尔值

true、false

默认值: false

你可以使用关键字 bool 声明布尔类型。 Go 不同于其他编程语言,在 Go 中,你不能将布尔类型隐式转换为 0 或 1。 你必须显式执行此操作

字符串

string 用于表示字符串数据类型。 若要初始化字符串变量,你需要在双引号(")中定义值。 单引号(')用于单个字符

默认值: string类型空值

类型转化

使用内置函数

var integer16 int16 = 127
var integer32 int32 = 32767
fmt.Println(int32(integer16) + integer32)

使用 strconv 包

package mainimport ("fmt""strconv"
)func main() {i, _ := strconv.Atoi("-42")s := strconv.Itoa(-42)fmt.Println(i, s)
}

函数

函数包括一般的函数,以及特殊函数(方法):在函数名称之前加入一个额外的参数,此附加参数称为接收方。

方法的用处:分组函数并将其绑定到自定义类型(例如:结构)。 Go 中的这一方法类似于在其他编程语言中创建类,因为它允许你实现面向对象编程 (OOP) 模型中的某些功能,例如嵌入、重载和封装。

函数

main()

Go 中的所有可执行程序都具有此函数,因为它是程序的起点。 你的程序中只能有一个 main() 函数。 如果创建的是 Go 包,则无需编写 main() 函数。

main() 函数没有任何参数,并且不返回任何内容。 如要访问 Go 中的命令行参数,可以使用用于保存传递到程序的所有参数的 os 包 和 os.Args 变量来执行操作

自定义函数

语法: 关键字(func),自定义命名(name),参数(parameters),返回值(results:可以是函数内部的变量,这样return就不用待参数了;如果只是类型声明,则要明确返回数据;可以多个)

func name(parameters) (results) {body-content
}

方法

方法使用中的一些注意点:

  1. 使用指针:方法需要更新变量,或者,如果参数太大,则可能需要避免复制它。在遇到此类情况时,你需要使用指针传递变量的地址。另外依据 Go 的约定,如果结构的任何方法具有指针接收方,则此结构的所有方法都必须具有指针接收方,即使某个方法不需要也是如此。
  2. 声明其它类型(非结构):可以为任何类型创建方法,但不能给其它包的类型定义方法。因此无法给基本类(例如:string)定义方法。可以在上面加装一层
package mainimport ("fmt""strings"
)type upperstring stringfunc (s upperstring) Upper() string {return strings.ToUpper(string(s))
}func main() {s := upperstring("Learning Go!")fmt.Println(s)fmt.Println(s.Upper())
}
  1. 嵌入方法: 结构(A)中属性可以使用另一个结构(B),如果B存在方法(fc),则示例A可以直接调用该方法,有点类型基类、子类的继承关系。实际上Go 编译器会通过创建新的方法来实现。
func (t A) fc() int {return A.B.fc()
}
  1. 重载方法:上述3中希望A的方法C与B不同,可以直接定义方法。要继续使用B的C方法,只能显性调用A.B.fc()
func (t A) fc() int {return something
}
  1. 封装(访问权限): 对象的发送方(客户端)无法访问某个方法。 通常,在其他编程语言中,你会将 private 或 public 关键字放在方法名称之前。 在 Go 中,只需使用大写标识符,即可公开方法,使用非大写的标识符将方法设为私有方法。另外,Go 中的封装仅在程序包之间有效。 换句话说,你只能隐藏来自其他程序包的实现详细信息,而不能隐藏程序包本身。

接口

接口是一种用于表示其他类型的行为的数据类型。 接口类似于对象应满足的蓝图或协定。

接口是一种抽象类型

  1. 声明接口
type Shape interface {Perimeter() float64Area() float64
}
  1. 实现接口
type Square struct {size float64
}func (s Square) Area() float64 {return s.size * s.size
}func (s Square) Perimeter() float64 {return s.size * 4
}type Circle struct {radius float64
}func (c Circle) Area() float64 {return math.Pi * c.radius * c.radius
}func (c Circle) Perimeter() float64 {return 2 * math.Pi * c.radius
}func printInformation(s Shape) {fmt.Printf("%T\n", s)fmt.Println("Area: ", s.Area())fmt.Println("Perimeter:", s.Perimeter())fmt.Println()
}func main() {var s Shape = Square{3}printInformation(s)// 此时运行时可以自动转换类型,不报错c := Circle{6}printInformation(c)
}
  1. 实现字符串接口
  2. 扩展现有实现

Go 包与其他编程语言中的库或模块类似。 你可以打包代码,并在其他位置重复使用它。 包的源代码可以分布在多个 .go 文件中。

  1. 声明: 在文件中添加 package calculator ,声明这个go文件为calculator 包
  2. 导入: 在 Go 中,包名称需遵循约定。 包使用其导入路径的最后一部分作为名称。 例如,Go 标准库包含名为 math/cmplx 的包,该包提供用于处理复数的有用代码。 此包的导入路径为 math/cmplx
import "math/cmplx"cmplx.Inf()

main包

在 Go 中,甚至最直接的程序都是包的一部分。 通常情况下,默认包是 main 包。如果程序是 main 包的一部分,Go 会生成二进制文件。 运行该文件时,它将调用 main() 函数。
换句话说,当你使用 main 包时,程序将生成独立的可执行文件。 但当程序非是 main 包的一部分时,Go 不会生成二进制文件。 它生成包存档文件(扩展名为“.a”的文件)。

一般包

你现在可以开始编写包的函数和变量。 不同于其他编程语言,Go 不会提供 public 或 private 关键字,以指示是否可以从包的内外部调用变量或函数。 但 Go 须遵循以下两个简单规则:

  1. 如需将某些内容设为专用内容,请以小写字母开始。
  2. 如需将某些内容设为公共内容,请以大写字母开始。
    例如:
package calculatorvar logMessage = "[LOG]"// Version of the calculator
var Version = "1.0"func internalSum(number int) int {return number - 1
}// Sum two integer numbers
func Sum(number1, number2 int) int {return number1 + number2
}
  1. 只能从包内调用 logMessage 变量。
  2. 可以从任何位置访问 Version 变量。 建议你添加注释来描述此变量的用途。 (此描述适用于包的任何用户。)
  3. 只能从包内调用 internalSum 函数。
  4. 可以从任何位置访问 Sum 函数。 建议你添加注释来描述此函数的用途。

模块

可以将包放到模块中。 Go 模块通常包含可提供相关功能的包。 包的模块还指定了 Go 运行你组合在一起的代码所需的上下文。 此上下文信息包括编写代码时所用的 Go 版本。

此外,模块还有助于其他开发人员引用代码的特定版本,并更轻松地处理依赖项。 另一个优点是,我们的程序源代码无需严格存在于 $GOPATH/src 目录中。 如果释放该限制,则可以更方便地在其他项目中同时使用不同包版本。

因此,若要为 calculator 包创建模块,请在根目录 ($GOPATH/src/calculator) 中运行以下命令:go mod init github.com/myuser/calculator。运行此命令后,github.com/myuser/calculator 就会变成模块的名称。 在其他程序中,你将使用该名称进行引用。 命令还会创建一个名为 go.mod 的新文件。

导入本地模块时,可以直接使用上述mod init的名称,再去生成的go.mod中修改指向

module helloworldgo 1.14require github.com/myuser/calculator v0.0.0replace github.com/myuser/calculator => ../calculator

其它

新手错题集

struct literal uses unkeyed fields

出现在结构实例的时候

  1. 检查结构体是否真的存在该字段
  2. 检查是否在包外实例结构,但字段名是小写开头(类似私有),需改为大写

missing type in composite literal

常出现在结构实例的时候,特别是嵌套时,偷懒导致

  1. 结构不能直接{}, 得先声明,类似这样 Structname{},嵌套内的结构也一样,偷懒会忘了

too few values in struct literal

  1. 如下所示:实例的时候 t 这种写法必须参数写全,不然会报错
type triangle struct {size int
}
type coloredTriangle struct {trianglecolor string
}t := coloredTriangle{triangle{3}, "blue"}
t1 := new(coloredTriangle)
t1.triangle = triangle{3}t2 := coloredTriangle{triangle: triangle{3},color: "blue",
}

has no field or method

  1. 代码类似上面错误里的代码,这里主要是指嵌套结构的调用( Embed methods)时报错,如下所示:t.size、t.perimeter()都会报错,原因在于定义coloredTriangle的triangle给了变量名(上面那个没有),调用的时候必须显性的t.a.size这样
type triangle struct {size int
}
type coloredTriangle struct {a trianglecolor string
}func (t triangle) perimeter() int {return t.size * 3
}t := coloredTriangle{triangle{3}, "blue"}
fmt.Println("Size:", t.size)
fmt.Println("Perimeter", t.perimeter())

cannot assign 1 values to 2 variables 或者 cannot initialize 1 variables with 2 values

  1. 注意赋值的时候不要少了一个变量接收,特别是调用函数的时候,是有返回多个值的,例如 int, error , 必须两个变量接收

struct 类似类用法,但继承存在问题

如下所示,通过结构的嵌套实现类似class的效果,这种就相当于A为基类,定义一个值(很多方法需要这个值),这个值子类编写的会直接传入(好像没有直接给默认值的法子),可以在B实例后调用New_b来实现默认值

type A struct{a stringb map[string]string
}type B Struct {A
}var map_b:= {"test_key": "test_value"
}func (b B)New_b() B{b.b = map_breturn b
}test_b := B{A:{a:"test_a"}}

cannot use a (variable of type any) as string value in argument

泛型使用(any要至少1.18以上): 直接作为函数入参使用变量会报错,得转以下,顺便判断下a, ok := a.(string)

string 字符串多行

  1. 使用反引号`` , 注意不要乱打空格制表

gkb utf 互转 (enconding)

  1. 使用官方的第三方包(绝了)
// https://github.com/golang/text
// go get -u golang.org/x/textimport    "golang.org/x/text/encoding/simplifiedchinese"
mb, _ := simplifiedchinese.GBK.NewEncoder().Bytes(b)

golang与其它语言的比较

python

基于个人想法,受限水平,可能存在错误

  1. 明显的例如:golang是强类型语言,golang没有类(严格来说并不是一个面向对象的语言,但是可以实现类似的功能,例如通过结构、接口等)
  2. goruntine 是 go语言的特色,用于并发操作。在代码书写,思考逻辑上,个人感觉更类似python的线程(thread),而不是协程(coroutine)。这两者都是为了实现并发处理。这意味这goruntine更容易编写(个人感觉),而python的coroutine编写需要显性的不停的调用await、 async。
# 线程
import threadingdef do_something():do something ... do_thread = threading.Thread(target=do_something)
do_thread.start()
do other ... # 协程corountine
import asyncioasync def do_something():do something ...await ...do ...asyncio.run(do_something())    

python示例如上,咋一看thread和corountine差不多,实践中多corountine并行处理的遇上其中一个阻塞,会导致整体阻塞(协程的本质是自己主动切换执行代码,遇上等待就去执行其它代码),而thread基本不会。
以工作经验举例:开发一个tcp服务用于模拟设备行为
行为1: 发起tcp链接登录
行为2: 登录后发送心跳包
行为3: 登录后等待接收服务器数据,接收数据解析并应答
thread实现:非常简单,使用queue做线程间数据传输,event做一些流程控制。主线程实例socket执行到登录,起三个子线程,一个循环发送queue中的数据,一个定时生成心跳放置到queue中,一个循环接收数据,数据接收到后,将应答放置到queue中。主线程进行循环等待脚本退出信号。出现严重错误,如socket异常后,重新实例socket,销毁所有子线程重头再来。
corountine实现:基本也是类似,但要注意在子线程里阻塞的一些操作,不会影响其它线程,例如socket.recv是一个阻塞操作(没收到数据会一直等待),我是通过设置超时,让销毁子线程的时候能够一段时间后关闭,而协程里不能直接这么用,阻塞行为会让所有并发协程一起卡住(不太会异步写法,还好及时发现定时发送慢了),当然asyncio库已经带了socket异步化的操作(reader, writer = await asyncio.open_connection(family=socket.AF_INET,sock=mysocket)…当时找了半天)。还有socket异常后的处理,相对线程方案来说也比较麻烦,总而言之,编写的时候需要更加注意。

package mainimport ("fmt""time"
)func time_to_sleep(s int) {fmt.Printf("%d before sleep 当前日期:%s\n", s, time.Now().Format("2006-01-02 15:04:05.000"))time.Sleep(5 * time.Second)fmt.Printf("%d after sleep 当前日期:%s\n", s, time.Now().Format("2006-01-02 15:04:05.000"))}func main() {start := time.Now()for i := 1; i <= 3; i++ {go time_to_sleep(i)}elapsed := time.Since(start)fmt.Printf("Done! It took %v seconds!\n", elapsed.Seconds())time.Sleep(10 * time.Second)fmt.Printf("after all 当前日期:%s\n", time.Now().Format("2006-01-02 15:04:05.000"))
}
Done! It took 0 seconds!
1 before sleep 当前日期:2022-08-04 16:49:23.643
3 before sleep 当前日期:2022-08-04 16:49:23.643
2 before sleep 当前日期:2022-08-04 16:49:23.643
2 after sleep 当前日期:2022-08-04 16:49:28.665
1 after sleep 当前日期:2022-08-04 16:49:28.665
3 after sleep 当前日期:2022-08-04 16:49:28.665
after all 当前日期:2022-08-04 16:49:33.655

结果输出代码结果输出如上所示,整体类似python的thread效果,还有,注意主线程是不管goruntine的,一旦结束,goruntine会直接结束,所以要等待goruntine完成,这里是直接等待,实际使用可能会使用channel或者 sync 的 WaitGroup

golang的个人学习笔记以及错题集相关推荐

  1. 阿里云天池打卡笔记兼错题集

    一.python训练营 raise关键字:抛出指定异常 try:raise NameError('HiThere') except NmaeError:print('An exception flew ...

  2. Word 【域】学习笔记 - 图/表题注

    Word [域]学习笔记 - 图/表题注 插入题注 章节(标题)号 标题设置编号 引用标题级别 问题 错误!文档中没有指定样式的文字 参考资料 插入题注 引用 > 插入题注 或 右键 > ...

  3. Golang底层原理学习笔记(一)

    LCY~~Golang底层原理学习笔记 1 源码调试 go源代码地址:GitHub - golang/go: The Go programming language 1.1 源码编译 现在的go语言大 ...

  4. golang游戏开发学习笔记-开发一个简单的2D游戏(基础篇)

    此文写在golang游戏开发学习笔记-创建一个能自由探索的3D世界之后,感兴趣可以先去那篇文章了解一些基础知识,在这篇文章里我们要创建一个简单的2D游戏场景以及配套的人物,并实现人物运动和碰撞检测功能 ...

  5. golang游戏开发学习笔记-创建一个能自由探索的3D世界

    此文写在golang游戏开发学习笔记-用golang画一个随时间变化颜色的正方形之后,感兴趣可以先去那篇文章了解一些基础知识,在这篇文章里,我们将创建一个非常简单(只有三个方块)但能自由探索的的3D世 ...

  6. 基于小程序云开发的在线答题小程序源码含答题分类答题记录错题集适合学习适合毕业设计使用

    基于小程序云开发开发的在线答题小程序源码 核心功能: 1.答题分类 2.开始答题 3.答题评分 4.答题记录 5.错题集 部分界面截图: 如有疑问,可联系博主!

  7. php开发错题集,基于PHP技术数学错题集错系统的设计实现

    基于PHP技术的数学错题集错系统的设计与实现 摘要:现代信息技术的快速发展和不断更新,引发了教育教学领域的深刻变革.先进的教育理念鼓励教师把现代信息技术与其它学科课程相整合,鼓励教育技术从" ...

  8. “错题集”帮你期末考试冲向满分

    考试考不好,怎么办呢?习网的错题集可以全面的为学生解答平时的错误问题,是冲向满分的开始.考试没考好,一般分为以下几点: 1.睡眠不够,那就加强睡眠.延长睡眠的时间和提高睡眠质量,早半小时睡觉.睡觉前听 ...

  9. 一本好的“错题集”如何做?看这里

    <错题本>制作 每个人肯定都有听说,错题本对高考到底多么多么有用,对我们的复习多么多么有用.但是问题也来了,做错题本好像要用我们很多的时间,可能做完了还空看,那怎么办呢?今天本车从做错题本 ...

最新文章

  1. Oracle常用傻瓜问题1000问
  2. 链表面试题Java实现【重要】
  3. php网络相关的扩展,文章专题扩展功能组件
  4. C/C++基础面试题集锦
  5. 二维数组 类型_「初识C语言」二维数组
  6. Python发送文本邮件
  7. 丈夫博士毕业想离婚,妻子要求家务补偿!法院判了
  8. PHP RSS/Feed 生成类库(支持RSS 1.0/2.0和ATOM)
  9. 为什么本地图片都不能直接浏览器_抖音精选答疑解惑!你的视频为什么不能被下载?...
  10. 矩池云上安装yolov5并测试
  11. opencv之CmakeLists.txt配置
  12. Python2.7安装Numpy
  13. 通信-RS232、RS485、RS422
  14. oracle 恢复dmp数据,Oracle数据库使用DMP文件恢复数据
  15. 【单片机】按键消抖及原理(硬件和软件方法详解)
  16. 超市管理系统数据库设计
  17. 影片下载观看秘籍大全
  18. Mysql如何解决幻读:
  19. 从零开始编写minecraft光影包(1)基础阴影绘制
  20. 小鹤双拼鹤形简易入门-by小鹤双拼输入法QQ群用户-弧

热门文章

  1. 赠书 | 破译“金枪鱼”密码机,近代电子计算机诞生的前兆
  2. JPG、RGB、YUV像素数据保存成JPG、BMP图片
  3. 求整数的位数及各位数字之和
  4. Directsound开发指南(2)
  5. android宫格式布局,Android 自定义TextView实现宫格布局,Drawable添加图片并控制宽高...
  6. Exynos4412 移植针对Samsung的Linux-6.1(六)【已解决】SROMC寄存器的数值不正确、无法赋值的问题
  7. matlab函数:度分秒转换为度、度分秒转弧度、弧度转度
  8. 要将大数据和分析转变为竞争优势,实现业务转型,必须做到这三点!
  9. 页面载入-(dom、css、图片 等资源 加载完成) 执行
  10. 能源互联网和电力大数据下的厮杀