数组的声明
当声明一个数组的时候 但是没有声明这个数组的大小 后续是可以自己通过append来往数组里加入新的元素的(其实那个就是切片slice)
1 | func main() { |
结构体
go的结构体跟c的结构体基本一样 但是比较有意思的是 go的结构体指针调用这个结构体的元素跟这个结构体调用元素都是一样的 都是以.一点
例子1
2
3
4
5
6
7
8
9
10
11
12
13func 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
5t := []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
3type Phone interface {
call()
}
声明一个结构体1
2
3type iphone struct {
name string
}
这个结构体实现这个接口的一些函数(不用全部实现都可以 go的接口并不是implement整个接口 而是implement这个接口里的一些函数)
实现这个接口的一个函数1
2
3func (_iphone iphone) call() {
fmt.Println("this is iphone call")
}
调用1
2
3
4func main() {
myIphone := iphone{}
myIphone.call()
}
接口中的方法理所当然是可以有返回类型的 上面那个例子是void类型的返回值 下面的例子是string类型返回值
1 | type Phone interface { |
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
27package 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)
}
输出
有一个编码之后可读性很低的问题 因此可以用
json.MarshalIndent(u, "", " ")
这个函数进行编码 第一个函数是结构体 第二个参数每一行输出的前缀 第三个参数是每一个级层的缩进
输出
byte和rune
其实byte和rune其实只是unint8和int32的别称
byte用来强调数据是raw data,而不是数字;而rune用来表示Unicode的code point。
方法
方法是面向对象编程中对对象或者结构体进行参数的一种方式 避免了直接对对象或者结构体的成员进行操作
go的结构体的方法声明和定义比较简单 只要在一个函数中有这个结构体作为参数传入 其后声明这个方法的名字和返回值类型 没有返回值类型则返回值类型为void
声明一个结构体1
2
3
4type MyUser struct {
Name string
Login bool
}
声明这个结构体的一个方法 名字为show 返回值类型为string1
2
3func (u MyUser) show() string {
return "name " + u.Name + " login " + strconv.FormatBool(u.Login)
}
调用这个方法1
2
3
4func main() {
u := MyUser{"gzm", false}
fmt.Println(u.show())
}
输出
要注意这个方法的参数列表的位置 上面那个u MyUser并不是这个参数列表 这个参数只是将结构体传进来 真正在参数列表在方法名字的后面
接收器 具有方法的结构体也叫作接收器
一般约定一个结构体或者类具有一个指针作为接收器的方法 但是go语言很人性化 传入结构体指针的方法 一般也可以直接使用这个结构体的变量去调用这个方法 编译器会隐式地转为指针类型
基本文件IO
一般文件的读写都是使用ioutil这个自带的库 写进去的内容和读出来的结果一般都是[]byte
讲byte数组写入文件1
2
3s := "lalla"
b := []byte(s)
err := ioutil.WriteFile("test.txt", b, 0600)
0600的意思是文件应该按照只有当前用户可以读写的权限进行创建这个文件
从文件中读内容为[]byte1
2
3
4
5rb, 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执行方式
- 深度优先执行import
- 执行const
- 执行var
- 自行init函数
有点出乎意料 init函数竟然是在最后执行的