Go基础笔记
1.基础类型
打印字符必须使用printf,因为println会输出字符的ascii码
1
fmt.Printf("ch1 = %c, ch2 = %c, %d\n", ch1, ch2, ch3)
字符串是双引号,字符是单引号
自动推导类型有两种写法
- var a = 1
- a := 1
%v自动匹配格式输出,但是并不是很只能比如字符型会输出其ascii码
_为匿名变量,一般用于丢弃某个函数返回值
1 | package main //每个程序都要有一个main包,没有main包则会导致build无法生成exe文件 |
2.流程控制
if和左大括号要在同一行
支持一个初始化表达式, 初始化字句和条件表达式直接需要用分号分隔
1
2
3if b := 3; b == 3 {
fmt.Println("b==3")
}switch不用写break来跳出循环,go语言自动包含
如果不想在执行了switch的case语句后跳出switch,则用fallthrough关键字
switch也支持一个初始化语句
switch有三种写法,见word
for 初始化条件;判断条件;条件变化{}
go语言只有i++没有++i
1
2
3for{
代码块....
}//这种没有条件的则会无限循环range迭代函数返回值为两个,一个是下标,另一个是下标所对应元素
3.函数
自定义函数放在main前main后都可以
函数的参数不需要写var关键字
如果形参为同类型则可以有简化写法,eg:a,b int (不推荐这种写法,会很乱)
如果不知道函数传入参数个数,则用不定参数 eg:(args …int),其中args由切片实现,要访问传入的参数可以用arg[0],arg[1]…….
不定参数要写在形参的最后面
注意函数定义最常用写法,word22页
函数定义分类:
6.1有参无返回值型
6.1.1固定参数
6.1.2不定参数
6.2无参有返回值型
6.2.1有一个返回值
6.2.2有n个返回值
6.3有参有返回值型
如何函数的返回值是int型,则可以直接可以用+运算
函数其实也是一种数据类型即函数类型
利用函数类型实现计算器函数,即实现多态(一个接口,但其有多种形态)课p52
匿名函数是写在main内的,其可以调用main中的全局变量
匿名函数内部改外部变量,则外部变量相应改变
多个defer时从后向前执行
注意局部变量的作用范围:
1
2
3
4
5
6
7if flag := 3;flag == 3{
fmt.println("flag = ",flag)
}
flag = 4//err,因为flag的作用范围只有if语句里面,是一个局部变量执行到定义局部变量那句话时才会给其分配空间,当程序执行到离开作用域时,空间就被释放
当使用变量时,采用就近原则,即采用当前作用域变量,如果未定义就用全局的那个
4.工程管理
- 一个包是由很多go文件组成,比如go/src/fmt
- import . “fmt”这样导入则后续代码不需要写fmt前缀
- 同一个目录包名必须一样,参照src/fmt,注:fmt就是一个包
- 同一个目录,调用别的文件的函数,直接调用即可,无需包名引用
- 一个包即一个工程即一个文件夹
- 一个包内的不同go文件就是实现这个工程的不同功能模块
- 当在进行一个工程之前,先设置GOPATH为该工程的src路径
- 函数想被其他包调用则要将函数名首字母大写
5.复合类型
&a与%p对应,不是%d
p=new(T),new函数就是在内存中拿出一个T类型的内存空间,然后将该内存空间的地址赋给p
注意数组的初始化形式
如果种子参数一样,则每次运行程序产生的随机数都一样
可以将种子参数设置为当地时间,这样每次执行程序种子不同从而随机数也会 不同
数组传入到函数只是传值,普通类型变量也一样都只是传值,函数内对该变量的操作不会影响函数外的该变量。要想改变则必须传地址
s := a[0:3:5],s为切片,长度为3,容量为5
切片的初始化定义方式有如下几种:
1
2s1 := []int{}//len=0,cap=0 也可以是s1:=[]int{1,2,3,4}
s4 := make([]int, 0, 0)//第一个0是切片长度,第二个0为容量,若没指定容量则容量与长度相等通过append函数给slice加元素
s := a[0:3],在进行切片时如果没有指定max则max默认为len(5),注意不要和8混淆
切片并不是数组或数组指针,它通过内部指针和相关属性引⽤数组⽚段
-
改变切片值原数组值也会改变,改变数组值那么切片值也会相应改变
不管是哪一种切片,最终切片的指针都指向底层数组!!!
总结:对切片的操作会影响底层数组
切片传入到函数是传引用,函数内对其进行改变,函数外的相应也会改变
在使用多个scanf时,前面的scanf要加\n
map作为函数参数传递的是引用,即函数内部对其改变,函数外部该值也会做出相应改变
结构体的定义写在main函数外部
在定义了结构体指针p1后可以通过.运算符直接操作成员。eg:p1.sex即不用*p1.sex
如果想使用其他包的函数,结构体类型,结构体成员,函数名,类型名,结构体成员变量名,首字母必须大写。如果首字母是小写,只能在同一个包里使用
6. 面向对象
方法:给自定义类型定义一种方法
不同类型可以拥有同名方法
接收者可以类比为对象中的类,即给类添加方法
继承某一个结构体,其方法也能够被继承
string,*p,struct都属于接口类型,可以定义一个接口类型的切片将这三个存入
如果用户定义的类型实现了某个接口类型声明的一组方法,那么这个用户定义的类型的值就可以赋给这个接口类型的值
空接口为万能类型,可以保存任意类型的值
1
2
3var i interface{} = 1
var i interface{} = "abc"
7.异常处理
接口类型即万能类型,什么都可以存
1
fmt.println(recover())//功能是出现panic时并不会让程序中断,并打印panic错误信息
error是报错但不崩溃,panic是报错并崩溃程序
recover()返回值为panic的错误信息
8.文本文件处理
常用字符串处理函数
结构体想要生成json,成员变量名首字母必须大写
如果想要json中的值首字母小写,要在比如结构体定义的后面加上’ json:”company” ‘其中’为反引号
利用’ json:”-“ ‘表示此字段不会输出到屏幕
1
2
3//用make创建切片和map
slice=make([]string,0,100)//len=0,cap=100
map1=make(map[string]interface{},4)//创建一个键为字符串型,值为任何类型且长度为4的字典json后返回的结果为字符列表,要通过string(result)转换为json格式
json需要先转换成[]byte,才能反转为结构体或者map
空接口类型值如何赋值给string等类型,看(中)p56
json解析到结构体更加简单,因为解析完之后每个字段什么类型很清楚的被定义了。而对于解析到map类型,其值全是空接口,要通过类型断言判断每个值的类型以及赋值给字符串
fmt.println()相当于os.stdout.WriteString()
Sprintf() 是把格式化字符串输出到指定的字符串中,可以用一个变量来接受,然后在打印
当文件读取完时err=io.EOF
注意file.read返回值有两个一个是读取长度n,一个是err
9.并发编程
主协程退出,其他子协程也要跟着退出
main函数即主协程
设置核数越多,时间片就越短
1
2
3
4//1.无缓冲型channel特点:
写入的时候对方未读就会阻塞任务
读的时候没有数据也会阻塞
即要保证数据放入时被立即取出可通过range遍历channel内容
channel传参,是引用传递
双向管道传参可以变为单向
1
2
3
4
5timer1 := time.NewTimer(time.Second * 2)//timer1为定时器,三秒后定时器就会向自己的C字节管道发送一个time.Time类型的元素值
//timer1.Stop()停止计时器
//ok := timer1.Reset(1*time.Second)重置计时器1s后写入
t2 := <-timer1.C //定时器可实现延时功能,类似于sleep
10.HTTP编程
socket编程中write和read函数的传参都是字节切片,比如发送hello,即conn.write([]byte(“hello”))
1
2
3
4
5
6
7
8
9
10n, err :=conn.read(buf)//接受来自对方发送的内容
if err != nil{
if err == io.EOF{
fmt.println("文件接收完毕")
}else{
fmt.println("err =",err)
}
return
}
f.write(buf[:n])//往文件写入内容定义在main函数外部的为全局变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package main //服务器端代码示例
import (
"fmt"
"net/http"
)
//服务端编写的业务逻辑处理程序myhandler
//w, 给客户端回复数据
//req为结构体, 读取客户端发送的数据,参数就是这两不能变
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello world")
}
func main() {
http.HandleFunc("/go", myHandler)// 请求127.0.0.1/go时调用myhandler
//在指定的地址进行监听,开启一个HTTP
http.ListenAndServe("127.0.0.1:8000", nil)
}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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47package main //客户端代码
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
//get方式请求一个资源
//resp, err := http.Get("http://www.baidu.com")
//resp, err := http.Get("http://www.neihan8.com/article/index.html")
resp, err := http.Get("http://127.0.0.1:8000/go")
if err != nil {
log.Println(err)
return
}
defer resp.Body.Close() //关闭
fmt.Println("header = ", resp.Header)
fmt.Printf("resp status %s\nstatusCode %d\n", resp.Status, resp.StatusCode)
fmt.Printf("body type = %T\n", resp.Body)
//以下为读取服务器回应的主体数据部分,因为回应的主体数据部分是用resp.Body来获取,该接口是一个流所以要通过read的方式将其通过for循环一点一点的读入buf里面
buf := make([]byte, 2048) //切片缓冲区
var tmp string
for {
n, err := resp.Body.Read(buf) //读取body包内容
if err != nil && err != io.EOF {
fmt.Println(err)
return
}
if n == 0 {
fmt.Println("读取内容结束")
break
}
tmp += string(buf[:n]) //累加读取的内容
}
fmt.Println("buf = ", string(tmp))
}