总结一下初学go语言的笔记,方便复习
参考书籍:《go语言趣学指南》
使用playground运行代码
使用main方法标识代码入口
大括号的唯一位置
在go中,大括号只有一种放置位置,左大括号与func关键字于同一层,右大括号位于单独一层,否则会报错。
使用const标识常量,使用var标识变量
变量声明的三种方式
package main
import "fmt"
func main() {
var a = 111
var b = 222
var (
a = 111
b = 222
)
var a, b = 111, 222 // 三种方式的效果都一样
fmt.Println(a, b)
}
var简短声明
var test = 33
test := 33
上述两种方式实现的效果完全一样
+=、++、–等操作符仍然有效
if分支
test := 100
if res := -1; test > 99 { // 在条件中同时使用简短声明
fmt.Println(res)
} else if test < 3 {
fmt.Println("error")
} else {
fmt.Println("error")
}
使用&&表示逻辑与,||表示逻辑或,!表示逻辑非
switch分支
test := 100
switch res := -1; test { // 在这里同时使用简短声明
case 3:
fmt.Println("error")
case 100:
fmt.Println(101)
fallthrough
default:
fmt.Println(res)
}
test := 100
switch {
case test > 99:
fmt.Println(99)
case test < -1:
fmt.Println(101)
fallthrough
default:
fmt.Println("test")
}
for循环
for i := 0; i < 3; i++ { // 使用简短声明
fmt.Println(i)
}
j := 0
for j < 3 { //不使用简短声明
fmt.Println(j)
j++
}
上述if、switch、for的条件中均可以使用简短声明,使用分号隔开即可
声明类型变量
var 变量名 类型变量 = 值
var test float64 = 39493
test := 39493
var test float32 = 39493
test := 39493
也可以不声明类型变量,默认为float64类型,go中大部分浮点数都是float64
浮点数进行比较时需要注意,精度不能太高,否则会出错。
Go提供了10种整数类型可供选择,默认为int类型,其他可选的还有uint、int8、int64等类型
int和uint没有指定位数,它们根据硬件的架构来决定(32位架构就是32位,64位架构它们就是64位)。
如果数值过大,且想要整数不会随着硬件变化,就要指定相应的位数int32、int64等。
通常使用big包和const存储异常大的整数
使用新类型同名的函数来包裹变量即可
package main
import "fmt"
func main() {
a := 41
fmt.Println("%T \n", a)
b := float64(a)
fmt.Printf("%T", b)
}
在go中,以大写字母开头(类似于public)的函数、变量以及其他标识符是对其他包可用的,反之则不然。
函数声明
func 函数名(形参名 类型, 形参名 类型, ···) (返回值类型,返回值类型) {
行为
}
eg:
func test(a int, b float64) int {
行为
}
func test(a int64) (float32, int16) {
行为
}
声明新的类型
type 新类型 基于的类型
type myInt int32
var a myInt = 22
fmt.Printf("%T", a)
注意:声明的新类型和基于的类型不是同一个类型,不能直接运算
我们可以为自己声明的类型添加方法
方法声明如下
关键字 接收者 方法名 返回值 { // 接收者有且只有一个,它的格式如下
行为 // (接收者变量名 自己声明的类型)
}
eg:
func (a myInt) test() myInt {
return a += 1
}
使用的时候直接用对应类型的变量调用即可
包括高阶函数、匿名函数等,现在用不到,略。
数组初始化
var test = [3]string{"test1", "test2", "test3"}
test := [3]string{"test1", "test2", "test3"}
test := [...]string{"test1", "test2"} // go编译器会自动计算数组元素的数量
test := []string{"test1", "test2"} // 这种方式利用的是数组切片,先在内部创建对应的数组
// 再返回的是对应数组的切片(视图)
迭代数组,使用range关键字可以取得数组中每个元素的索引和值
数组复制—无论是将数组赋值给新的变量还是将它传递给函数,都会产生一个完整的数组副本 。
数组切片—形式和python完全一样,切片后返回的数组提供的是原数组的视图,改变切片中元素的值同样会影响到原数组。
数组也有对应的方法
第十八章 数组切片的扩展,现在应该用不上,略。
映射声明
map[key类型] value类型 {
key1: value1,
key2: value2, // 注意,这里的逗号也是必须的(很奇怪)
}
结构声明
var 结构名 struct {
属性 类型
属性 类型
···
}
// 也可以通过自定义的类型来复用结构
type 类型名 struct {
属性 类型
属性 类型
···
}
结构初始化
变量名 := 类型(基于结构)名{属性: 值, 属性: 值}
eg:
type test struct {
key1 int
key2 int
}
t := test{key1: 2, key2: 5}
结构的赋值将产生相应的副本
由结构组成的切片
变量名 := []类型(基于结构)名 {
{key1: value1, ···}
{key2: value2, ···}
···
}
构造函数,名称为newTypeName/NewTypeName的函数,感觉不是很有用,就是一个普通的函数,只是名称有所规范,略。
Go没有提供class,而是通过结构和方法来满足相同的需求。
在结构里面还可以嵌套其他结构/类型,这些结构/类型有自己的方法,我们可以通过转发的方式使得顶层结构使用其内部结构/类型的方法,具体声明如下
package main
import "fmt"
type struct1 struct {
age int
}
func (s struct1) addOne() int { // 这种方式改变不了对应属性的值
s.age += 1 // 要使用指针的方式
return s.age
}
func main() {
type topStruct1 struct {
struct1 struct1 // 第一个struct1是属性名,第二个是它的类型
id int
}
type topStruct2 struct {
struct1 // 不给定属性名,直接指定类型
id int
}
t1 := topStruct1{
struct1: struct1{age: 23},
id: 55,
}
t2 := topStruct2{
struct1: struct1{age: 25},
id: 66,
}
fmt.Println(t2.addOne(), t2.age) // 打印26 25 可以直接使用嵌套类型的方法和属性
fmt.Println(t1.id) // 打印55
}
但转发的方法存在同名的情况,这个时候我们为顶层结构定义一个同名的新方法即可解决问题。
只要满足了接口所声明的方法,任何自定义类型/结构的任何值都可以被归类到相应的接口中(按照惯例,接口类型的名称通常以er为后缀)
package main
import "fmt"
type nothing float64
func (t nothing) test() int {
return int(t)
}
func main() {
type tester interface {
test() int // 对应的方法名 返回值类型
}
// var t tester = nothing(32.0)
var n nothing = 32.0
var t tester = n
fmt.Println(t.test())
}
接口有点绕,但也很重要,还需要进一步探索
使用&操作符获得指定变量的内存地址
使用获得内存地址指向的值(与&操作相反)
将放在类型前面表示要声明指针类型,将它放在变量前面则是要取该地址存储的值
package main
import "fmt"
func main() {
test := 2333
var address = &test
fmt.Println(address)
fmt.Println(*address)
var t *int = address
fmt.Println(t)
fmt.Println(*t)
}
在复合变量(结构/数组)前面可以直接放置&操作符
使用的使用不用加*号,go语言自动为它们解析
package main
import "fmt"
func main() {
type test struct {
age int
}
t := &test{age: 3}
fmt.Println(t.age) // 使用的时候不用加*号,直接引用即可
nums := &[...]int{1, 3, 7}
fmt.Println(nums[2]) // 和其他语言的方式一样
}
同样的,我们可以使用指针作为方法的接收者,这样可以对结构/数组的属性进行修改,略。
nil空指针的问题,有点深入了,略。
Go语言允许函数和方法返回多个值,按照惯例,使用最后一个返回值来表示错误。我们在使用函数/方法执行的结果前,应该先检查是否有错误信息。
defer关键字—defer可以延迟任何函数/方法,Go语言将保证这些被延迟的操作将在函数return之前执行。
参考链接:VS code golang开发环境搭建(Windows系统下 )
GOBIN—目前理解应该是Go编译器所在的位置,基本用不上;
GOPATH—Go开发时的项目路径,各种包都在这里,类似于Global Environment;
按照grpc-go官网的教程,安装protoc和两个插件
$ go install google.golang.org/protobuf/cmd/[email protected]
$ go install google.golang.org/grpc/cmd/[email protected]
如果安装出现问题就直接下载protobuf和grpc项目代码到本地,使用go build命令生成两个可执行文件放到GOBIN的bin目录下即可