Go语言基础语法 | 青训营

121 阅读10分钟

1.变量声明以及赋值的格式:

a. var 变量名 = 所赋的值(var a = "inital")(会自动推导出变量的类型) b. var 变量名 变量类型 = 所赋的值 (var a,b int = 1,2) c. 变量名 := 所赋的值 (f := 1)

2.常量的声明

a.const 变量名 变量类型 =所赋的值 b.const 变量名 =所赋的值(系统会根据上下文自动匹配类型)

3.if ... else...语句

a.if后面的条件不加括号且if执行语句必须用大括号 b.if的多个条件之间要用;隔开

4.go循环语句只有for且使用格式与c类似

5.swith...case...语句

a.swith后面的常数或字符串不需要加括号 b.case语句中不需要加default c.可以代替多个if ... else ...语句(swith后面不加条件在后面的 case中每个case后添加一个条件并在下面的执行语句后面添加default语句来结束swith语句

6.数组

定义: a.var 数组名 [数组长度] 数组类型(var a [2] int
b.数组名 := [数组长度] 数组类型{赋值}(f := [5] int {1,2,3,4,5}

7.切片(可变长度的数组)(slice)

a.可以用make来声明( c:= make([] string, 3)
b.可以用append来扩容数组但是扩容后的新数组必须赋给原数组(s=append(s,"e","f")
c.fmt.println(s[:5])表示打印数组的前五个元素

8.map(哈希表)

a.定义:
变量名 := make(map[key]value)
变量名 :=map[key]value{所赋的值}
var 变量名 = map[key]value{所赋的值}
b.哈希表的实际长度和key的个数有关
c.一个key对应有一个value,打印时会成对出现
d. r,ok可以用来判断哈希表中是否存在这个key,若不存在
则会打印出 0 false,且该key对应的数值为0(r(value),ok(true or false) :=m(map名)["unknow"])
e.若要删掉哈希表中的某一key用delete函数(delete (变量名,key名))

9.rang

用来快速遍历数组和哈希表(和for语句类似但更简洁) 两者内部都可以和if连用

数组:for 变量1(i) ,变量2(num) :=range 数组名(str){ fmt.Println(i,num }
(i中接收到的是是数组下标 num中接收到的是对应的元素值 就会把下标和元素对应全部输出 )
map:for 变量1(k),变量2(v) :=range 哈希表名{fmt.Println(k,v) }
(k中接收到的是map的key,v中接收到的是map的value 就会把key和value对应全部输出)

10.函数

a.go中的函数变量名在类型前
b.可以返回多个变量(return v,ok)
c.go返回一个值只需将返回的变量类型写在最后即可若想返回多个值则必须在函数形参列表后加一个括号将所有要返回的变量都写在里面(变量名和变量类型都要)(如(v int ,ok bool) 则此时这两个变量不能再在函数内声明,应该直接赋值(不然会报错)

11.指针

a.定义:*变量类型(*int (指向int类型的指针变量))
b.多用于函数传值修改
c.*n取内容 &n取地址

12.结构体

a.结构体是带类型的字段(不同的变量)的集合
b.type 结构体名 struct{}
c.结构体的赋值 :(a := user{赋值} 的。结构体内数据的修改: (a.name=...)
d.结构体当函数的形参列表 可以传结构体的值也可以传地址值

13.结构体方法

a.在一般的函数前面加上你想要操作的结构体的声明(u user) (例:(u user) checkPassword(password string) bool{return};) 原函数要实现这一功能必须在函数列表中添加该结构体 但是这样修改后可直接让某一结构体变量像调用属性一样调用该方法更简洁
b.若在方法前只添则结构体在调用时其本质是用一个副本在操作,如果想改变结构体中元素的值则在开头要用指针的方法来(u *user)

14.错误处理

在一个函数中设置多个返回值其中一个是 error类型(和字符串类似)(v int,....,err error),在最后返回时加上if判断语句 若最后没有错误则返回nil(null),若有错误则返回errors.New("提示词")

15.字符串格式化

a.fmt.Println 可以同时打印出多个数字或字符串
b.fmt.Printf一次只能打印出一个字符串(且%v就是打印出字符串,或结构体的成员
%+v就是打印出结构体的变量名和变量值 %#v就是进一步详细的输出) c.%.2f就是保留两位小数
d.字符串操作:Contains(判断是否有重复)
Join(字符串的拼接)
index(所找的字符串所在位置)
Field(将字符串中空格全部去掉并存放到一切片中)
Repeat(判断是否有重复)
Replace(替代)
Split(将字符串按某一字符分开)
Trim(将某一字符串某一段的两边去掉某一字符)
e.字符串的转换
Append 将整数等转换为字符串后,添加到现有的字节数组中
Format 把其它类型的转换为字符串
Parse把字符串转换为其他类型

16.Json处理

a.一种主流的数据交换格式(用于数据的传输,可以加快数据传输的速度)
b.任何一种数据都可以被ison表示
c.是键值对的形式:key-val—({"key":val1}
d.若一个key对应有多个值,则多个值用[]括起来·
e.若有连着的多个键值对则外部用中括号括起来
f.json的序列化:将有key-value结构的数据类型(比如 结构体,map,切片)序列化成json字符串的操作
ecoding/json中有将任意一种数据序列化的函数Marshal(所要序列化的变量名)([]byte,error)
(对结构体的序列化:data,err := json.marshal(&结构体变量名) fmt.Printf("%v",string(data))(且变量名开头必须大写))
g.若想结构体中key在被序列化后在反序列化时key变为我们想要的就需要在结构体定义变量时在该变量后面加上(json:"你想要的名字")tap标签
h.反序列化:json.Unmarshal([]byte(str(json字符串)(要用"来转义)),&monster(某一结构体的变量的地址))(err error)

map:map在反序列化时不需要make因为已经封装到unmarshal函数中了 转义前后的数据类型必须相等,且若字符串是程序得到则不需转义"符号

17.接口

a.type 接口名 interface
b.接口中只有方法的声明,实现需要通过其他类型(自定义类型)
c.只要是实现了此接口方法的类型,则这个类型的变量就可以给接口类型的变量赋值
(例:type humaner interface{saihi()}(定义一接口,并给出该接口的方法声明)
type teacher struct{ name string age int }
func (tmp *teacher)saihi(){fmt,Printf("teacher[%d,%s]",tmp.age,tmp.name)}(teacher结构体的结构体方法)
var i humaner
t := &teacher{30,"李华"}
i = t
i.saihi()
)
d.若有多个自定义的类型都符合这一特点,则此接口变量可调用所有这些类型的该方法,这就是接口的多态性
e.接口的继承:在另一个接口中把一个接口名写入,则此接口被另一接口继承(type humaner interface( maner) humaner继承maner接口

18.文件操作

a.流:数据在数据源(文件)和程序(内存)之间经历的路径
b.输入流:从文件输入到程序(内存)的路径(读文件)
c.输出流:从程序(内存)输入到文件的路径(写文件)
d.file封装了所有的文件相关操作(结构体方法),file是一个结构体
e.file在os包内,且file中有些函数不是结构体方法,因此要通过包名直接调用
f.读取文件的方式
reader.ReaderString(某一符号)循环读取
ioutil.ReaderFile(文件地址)一次性全部读出
g.编写文件内容 os.OpenFile((文件指针)name string,(打开方式)flag int,(控制权限windows无效)FileMode)(file *File ,err error)

20.bufio

a.包里面是关于缓存区的函数(如bufio.NewReader(file)就是打开一个缓存区返回一个*Reader(缓冲区)(Reader有 自己的结构体方法 例如 reader.ReadString()就是读取文件内容,到所标记点停止 )
b.向文件中写数据时也是先writer :=bufio.NewWriter(file)再writer.WriteSring(字符串)需要注意 的是最后要writer.flush()来将缓存区中的数据全部放入有文件

21.goroutine(协程和并行)

a.进程和线程
进程:程序在操作系统中的一次执行过程,是系统进行资源分配的基本单位
线程:进程的一个执行实例,是程序执行的最小单元,他是比进程更小的能独立运行的基本单位(更好的使用CPU)
(一个进程可以创建销毁多个线程,同一个进程可以并发执行多个线程 一个程序至少有一个进程,一个进程至少有一个线程)
b.程序或文件一执行就代表着一具体进程开始执行,一个进程可以有多个线程同时执行
c.并发:多线程程序在单核上运行(轮询进行)

多个任务作用在一个cpu上
从微观的角度上来看,一个时间点只有一个任务在执行
从宏观上来看几个任务同时进行

d.并行:多线程程序在多核上运行

多个任务在多个cpu上进行
并行速度大于并发

e.go协程和go主线程
协程相当于是外部函数,只有在main函数下才能执行。 主线程相当于是main函数,可以调用外部函数,也可以阻止外部函数的执行。

go主线程(类似于进程):一个GO线程上,可以起多个协程(轻量的线程)
GO协程的特点:
有独立的栈空间,共享程序堆空间,调度由用户控制,协程是轻量的线程 主线程是写在main函数中的 ,协程是写在外部某一函数中的 外部某一函数在调用时,前加上go就能使主线程和协程同时运行

f.线程和协程的流程图:
程序开始主线程开始执行-->执行过程中遇到协程-->主线程运行的同时协程也开始运行-->主线程运行结束程序结束(此时不管协程执行结束与否都停止 )
g.MPG模式
M:主线程 P:上下文(协程运行的环境) G:协程 h.go设置运行CPU数目

runtime包下有关于cpu的操作函数(runtime.NUMCPU()(查找逻辑CPU的个数),runtime.GOMAXPROCS()(自己设置使用多少的CPU)

22.channel

a.channel的本质是数据结构-队列
b.数据是先进先出的顺序(FIFO)
管道有管道长度和管道
c.channel是协程安全的,多个协程操作同一管道时,不会发生资源竞争问题
d.channel有类型的,一个string的channel只能存放string类型数据
e.channel的声明: var 变量名 chan(关键字) 数据类型(存放的元素的数据类型)
f.channel声明后必须先make之后才能使用,make的作用是确定管道长度(make(chan int,3))
g.channel是引用数据类型,该变量中存放的是管道的地址
h.管道的赋值:变量名<-所赋值 管道的取值:(例如 var get int get <-- intchar )(也可以直接取出)
i.若想在管道中存放不同的数据类型,则在管道的声明时,存放变量类型声明为interface{}但需要注意的是此时编译器 认为该管道中所有的变量都是interface{}类型的,因此再取出数据是要进行类型断言(如:a :=newcat.(cat)(断言这个newcat是cat类型的)
g.channel的遍历和关闭

关闭:close(变量名)
管道的遍历:
用for循环,注意循环条件:不能直接为 i<len(intchan)因为每一次取出数据,管道长度都会实时减少,这样最终会取少, 因此若要使用for循环就需要把这一循环条件设为定值,且管道在拿出数据赋给变量是要加一个=表示已经存好 (get =<- intchan)
用for ...range: (for v :=range intchan2{})对管道用for...range时结果返回一个值,及管道最前面的值, 同时要注意的是遍历前要关闭管道,不然最后会因为无法取值而报错

k.管道只要在流动就不会报错
l:管道的注意事项和细节

管道可以声明为只读或只写
只写:var chan1 <- int(因为int型数据可传进去)(由符号可知)
只读:var chan2 <-chan int
解决管道堵塞问题
当我们不确定什么时候关闭管道时,我们可以用select函数
(不管管道是否堵塞都会执行case后面的语句,最后的default语句后面可以加上程序员自己想要加的指令)
select{ case v:= <- intChan: 执行语句
case v:= <- intChan: 执行语句
default: 执行语句
}