go注意要点

数组的声明

当声明一个数组的时候 但是没有声明这个数组的大小 后续是可以自己通过append来往数组里加入新的元素的(其实那个就是切片slice)

1
2
3
4
5
6
7
func main()  {
a := []int{10, 100, 200}
a = append(a, 12)
for i := 0; i < 4; i++{
fmt.Println(a[i])
}
}

结构体

go的结构体跟c的结构体基本一样 但是比较有意思的是 go的结构体指针调用这个结构体的元素跟这个结构体调用元素都是一样的 都是以.一点

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
func main()  {
type test struct {
a int
b string
}
v := test{1, "123"}
fmt.Println(v.a)
fmt.Println(v.b)
v.a = 6
fmt.Println(v.a)
var structP *test = &v
fmt.Println(structP.a)
}


切片

go的切片跟Python的list切片一样 都是包含开始索引 但是不包含结束索引

例子

1
2
3
4
5
t := []int{1, 2, 3, 4, 5}
t = t[1: 3]
for i := 0; i < len(t); i++ {
fmt.Println(t[i])
}

len()函数但会切片的长度 cap()函数返回为切片分配的空间大小
copy()函数可以复制一个切片


range

之前一直被这个range弄得云里雾里 用法如下
for i, j := range sliceObject

i是下标 j是这个下标对应的元素


接口

go语言跟Java的接口不一样 go的接口定义方式就比价特别 是使用type interface两个关键字来声明的

接口一般都是搭配结构体来使用 一般都是一个结构体实现了这个接口的一些方法

例子
声明一个接口

1
2
3
type Phone interface {
call()
}

声明一个结构体

1
2
3
type iphone struct {
name string
}

这个结构体实现这个接口的一些函数(不用全部实现都可以 go的接口并不是implement整个接口 而是implement这个接口里的一些函数)

实现这个接口的一个函数

1
2
3
func (_iphone iphone) call() {
fmt.Println("this is iphone call")
}

调用

1
2
3
4
func main()  {
myIphone := iphone{}
myIphone.call()
}

接口中的方法理所当然是可以有返回类型的 上面那个例子是void类型的返回值 下面的例子是string类型返回值

1
2
3
4
5
6
7
8
9
10
11
type Phone interface {
receive() string
}

type iphone struct {
name string
}

func (_iphone iphone) receive() string {
return "this is some call from other phone in this phone " + _iphone.name
}

reflect反射

gp原因的反射是一种机制 可以在运行时更新变量和检查他们的值 调用他们的方法和他们支持的内在操作 而不需要在编译时就知道这些变量的具体类型


defer推迟

go这个defer关键字开始真的是看的我云里雾里 不知道用来干什么的 后来才知道这相当于一个延迟的”函数析构函数” 一般用来释放资源 关闭文件等等

而panic是在运行时捕捉到的类似数组访问月结 空指针引用等等问题 panic一般都会引起程序中断 是很严重的 所以一般都是使用error这个

panic引起程序中断之后 马上执行defer被延迟的函数来释放资源 以防因为程序中断而没有正确释放一些资源


json对象

讲go的结构体转为json对象的过程叫做编码 反过来叫解码

编码和解码分别使用下面两个函数

  • json.Marshal
  • json.Unmarshal

注意要点

  • 可以编码的结构体属性都是需要导出的 就是必须首字母是大写的
  • 编码之后的结果是[]uint8类型的 需要用fmt.Printf(“%s\n”, jData)这样的方法来进行打印 否则打印出来都是数字

struct tag

结构体的成员Tag可以是任意的字符串面值,但是通常是一系列用空格分隔的key:”value”键值对序列;因为值中含有双引号字符,因此成员Tag一般用原生字符串面值的形式书写。json开头键名对应的值用于控制encoding/json包的编码和解码的行为,并且encoding/…下面其它的包也遵循这个约定。

比如下面有个

Login bool `json:"login,omitempty"`

表示当结构体中的成员为空或者零值就不生成该结构体成员

果然 看下面的例子 Login这个属性 我给它的struct tag设置了omitempty 而且被编码的实例这个属性是false 编码为json之后就没有这个属性 但是比较有意思的是解码之后还是会有这个属性的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"encoding/json"
"fmt"
)

type User struct {
Name string `json:"my_name`
Login bool `json:"login,omitempty"`
}

func main() {
u := User{"gzm", false}
jData, err := json.Marshal(u)
if err != nil {
fmt.Println("convert error")
}
fmt.Println(u)
fmt.Printf("%s\n", jData)
nu := User{}
err = json.Unmarshal(jData, &nu)
if err != nil {
fmt.Println("disonvert error")
}
fmt.Println(nu)
}

输出
image_1chqecnb4ur81770451lim1pvk16.png-6.7kB

有一个编码之后可读性很低的问题 因此可以用

json.MarshalIndent(u, "", "    ")

这个函数进行编码 第一个函数是结构体 第二个参数每一行输出的前缀 第三个参数是每一个级层的缩进

输出
image_1chqecvnngqeqmlvjnh7jut21j.png-1.9kB


byte和rune

其实byte和rune其实只是unint8和int32的别称

byte用来强调数据是raw data,而不是数字;而rune用来表示Unicode的code point。


方法

方法是面向对象编程中对对象或者结构体进行参数的一种方式 避免了直接对对象或者结构体的成员进行操作

go的结构体的方法声明和定义比较简单 只要在一个函数中有这个结构体作为参数传入 其后声明这个方法的名字和返回值类型 没有返回值类型则返回值类型为void

声明一个结构体

1
2
3
4
type MyUser struct {
Name string
Login bool
}

声明这个结构体的一个方法 名字为show 返回值类型为string

1
2
3
func (u MyUser) show() string {
return "name " + u.Name + " login " + strconv.FormatBool(u.Login)
}

调用这个方法

1
2
3
4
func main()  {
u := MyUser{"gzm", false}
fmt.Println(u.show())
}

输出
image_1chrrjj84tb4ebf9vu1cng1l4d9.png-26.9kB

要注意这个方法的参数列表的位置 上面那个u MyUser并不是这个参数列表 这个参数只是将结构体传进来 真正在参数列表在方法名字的后面
image_1chrro3fqfiac827p1v3a1ejgm.png-14.1kB

接收器 具有方法的结构体也叫作接收器

一般约定一个结构体或者类具有一个指针作为接收器的方法 但是go语言很人性化 传入结构体指针的方法 一般也可以直接使用这个结构体的变量去调用这个方法 编译器会隐式地转为指针类型


基本文件IO

一般文件的读写都是使用ioutil这个自带的库 写进去的内容和读出来的结果一般都是[]byte

讲byte数组写入文件

1
2
3
s := "lalla"
b := []byte(s)
err := ioutil.WriteFile("test.txt", b, 0600)

0600的意思是文件应该按照只有当前用户可以读写的权限进行创建这个文件

从文件中读内容为[]byte

1
2
3
4
5
rb, e := ioutil.ReadFile("test.txt")
if e != nil {
fmt.Println("read file error")
}
fmt.Println("file content", string(rb))


import _

之前这里一直想不明白为什么会有些import前面带有一个下划线的

import后面跟着一个_下划线意味着我们不需要import这个包的其他内容 我们只需要它执行这个包里面的init函数 所以这个下划线只是起到一个初始化的作用


go执行方式

image_1chuhkjdu1q171r73oem1jov1mla9.png-93.6kB

  1. 深度优先执行import
  2. 执行const
  3. 执行var
  4. 自行init函数

有点出乎意料 init函数竟然是在最后执行的