这是我参与「第三届青训营 -后端场」笔记创作活动的的第二篇笔记
前言:
Go(又称Golang)是Google开发的一种静态、强类型、编译型、并发型,并具有垃圾回收功能的编程语言。
近期通过自己的学习,对go语言有了一定的认识,以下是我通过查阅资料,在学习过程中整理的一些笔记,在此与大家分享,希望对大家有所帮助。笔记内容偏向于基础语法类,文章结尾有惊喜。
一、基本语法:
1、变量:
变量声明:var 变量名 类型(如:var a,b float64)
变量初始化:a= 赋值1,b= 赋值2
自动推导类型:a:= 赋值
a,b=12,45(等价于:a=12,b=45)---声明变量的同时并赋值
a,b=b,a //直接交换a,b的值,即运行后:a=45,b=12
匿名变量:下划线"-",不想要的参数位置用"-"代替即可,用来丢弃某个值
常量(不允许修改其值):constant 变量名 类型 = 赋值
自动推导:constant c=12------直接定义常量c为int型,并赋值为12
多个常量或变量定义:
var(//同时声明变量,a,b,c,其类型由每行变量的类型决定
a int
b float64
c string
d:=34//可以自动推导,只能用":="
)
const (//同样可以一次声明多个常量,且类型标识可写可不写
j int =10//有类型标识
k float =3.14
m =34//无类型标识,只能用"="不能用":=""
n 23.45
)
2、iota 枚举:
1,iota常量自动生成器,每个一行,自动累加一,同行相等
2,iota给常量赋值使用
3,iota遇到const重置为零,iota是第几行变量其值就是:行数-1
const(
a1=iota//a1=0
a2=iota//a2=1
a3,a4,a5=iota,iota,iota//a3,a4,a5都为2
a6=iota//a6=3
)
3、变量输入与输出:
//输入:
var a,b int
var c string
fmt.Scanf("%d%d%c",&a,&b,&c)//格式化输入
fmt.Scan(&a,&b,&c)//自动检测,简便输入
//输出
fmt.Printf("%d,%d,%c",a,b,c)//格式化输出
fmt.Println(a,b,c)//直接输出a,b,c对应的值,并自动换行
格式化字符含义:
4、defer的使用:
package main
import "fmt"
func main(){
defer fmt.Println("aaaaa")//defer语句在程序结束前瞬间执行
defer fmt.Println("bbbbb")
defer fmt.Println("ccccc")//多个defer语句存在时,由下至上依次执行
}
//运行结果如下图
5、类型别名:
type bigint int64 //把int64类型改为bigint
var a bigint //声明bigint型变量a,也可以看作声明int64类型变量a
type(//一次别名多个变量
myint int//int改名为myint
mystr string//string改名为mystr
)
6、条件语句:
//if 类型
if bool1{
//待执行命令
}else if bool2{
//待执行命令
}else {
//待执行命令
}
//switch类型
//switch用于在不同条件下执行不同语句,每一个case都是唯一分支
//测试顺序由上至下,直至匹配为止
//case后面不用跟break
//switch分别有带变量和不带变量两种类型
//带变量
switch var1 {
case 0:
//待执行命令
case 1:
//待执行命令
default:
//待执行命令
}
//不带变量
switch {
case var1 > 2:
//待执行命令
case var1 > 0:
//待执行命令
default:
//待执行命令
}
//用switch判断变量类型
//可以用来检测v的类型(必须创建空接口,因为空接口可以接收任何类型的变量)
func Receiver(v interface{}) {
switch v.(type){
case string: fmt.Println("这个是string")
case int :fmt.Println("这个是int")
case bool :fmt.Println("这个是bool")
}
}
7、循环语句:
//golang中只有for循环,没有while循环
//和C的for一样
for init; condition; post {
//待执行命令
}
//和C的while一样
for condition{
//待执行命令
}
//例 for x:=0;x<5;x++{
fmt.Println(x)
}
for true{
fmt.Println("无限循环")
}
//循环中可使用break跳出循环
//循环中可使用continue跳过当前循环,进行下一次循环
//可使用goto实现条件转移,跳出循环等(强烈不建议使用)
//特殊语句:switch v.(type){
case 情况1:语句
case 情况2:语句
......
}
//可以用来检测v的类型(必须创建空接口,因为空接口可以接收任何类型的变量)
func Receiver(v interface{}) {
switch v.(type){
case string: fmt.Println("这个是string")
case int :fmt.Println("这个是int")
case bool :fmt.Println("这个是bool")
}
}
8、函数:
//格式
func 函数名(输入的变量及类型,可多个多种)(返回参数及类型,可多个多种){
//函数体
return a,b//需要返回函数值就有return,否则就不用return
}
//例
func test(B bool , I int , S string)(bool,int){
//当B为true输出i,反之输出s
//当B为true返回false和从0到I的多个正整数
//当B为false返回true和0
a:=1
b:=0
if B{
for x := 0; x < I; x++ {
fmt.Println(x)
}
return false,a
}else{
fmt.Println(S)
return true,b
}
}
//range
//用于迭代数组、切片、通道、集合
for num:=range nums{
//待执行命令
}
//例
nums := []int{2, 3, 4, 5, 6}
for num:=range nums{
fmt.Println(nums[num])
}
//注意,num是从0开始的非负整数
//不定参数函数(格式:func 函数名(变量名...数据类型)返回值{执行代码} )
func add(nums...int)int{//输入的任意个数的参数以数组的形式存入数组nums中,nums可以任意命名,后面紧跟"...类型"
var sum int
for _,date:=range nums{
sum=sum + date
}
return sum
}
func main(){
sum:=add(1,2,3,4)//实现了输入数据的相加求和,输入数据的个数任意
fmt.Println(sum)
}
9、指针:
//声明格式:var 变量名 *类型
//例:var p1 *int //声明了一个int型指针变量p1
var p2 *string //声明了一个string型指针变量p2
给指针型变量赋值时,被赋值变量要加取值符“&”
例:b:=10,a:=12
p1=&b//把变量b的地址赋值给了p1,同时p1地址对应的变量值等于b,为10,p1与b对应的地址相同
*p1=b//把变量b的值复制并储存到了地址p1,此时*p1=12,但p1!=&b
//实例
package main
import "fmt"
func main(){
var p *string
ph:="这是指针"
py:="这也是指针"
p=&ph ------>操作1
fmt.Printf("*p的值是:%v\n",*p)
fmt.Printf("p的地址是:%v\n",p)
fmt.Printf("p的地址是:%v\n",&ph)
*p=py ------>操作2
fmt.Printf("*p的值是:%v\n",*p)
fmt.Printf("p的地址是:%v\n",p)
fmt.Printf("p的地址是:%v\n",&ph)
}
//运行结果:
*p的值是:这是指针
p的地址是:0xc000088220
p的地址是:0xc000088220
*p的值是:这也是指针
p的地址是:0xc000088220 ——————可见进行操作2时并未改变p的地址
p的地址是:0xc000088220
进程 已完成,退出代码为 0
10、数组:
数组长度,容量固定
//一维数组:
var a[] int//int为数组类型,也可以定义为string型
var a[5]int = [5]int{ 1,2,3,4,5}//定义数组并赋值
b:=[5]int{ 1,2,3,4,5}//短声明并赋值
c:=[5]int{2:10,4:20}//给指定元素赋值,其结果为:a[2]=10,a[4]=20,其余元素均为0
//二维数组:
var a[3][4]int //定义数组:第一个表示行数,第二个表示列数,a[][]后边紧跟数组类型
b:=[3][4]int{
{1,2,3,4},//
{2,3,4,1},//这三行可以不换行,但","必须要有
{3,4,1,2},//
} //短定义并初始化二维数组b
11、切片:
长度,容量可变
s:= []int{}//切片[]里面为空或"...",切片的长度和容量可以不固定
s:=make([]int,n,m)//创建了一个s切片,n长度,m为容量
s=append(s,7)//给切片末尾追加一个成员
12、字典map:
//格式:map[KeyType]valueType //key---->valuey 一一对应
var m1 map=[int]string{ //也可以用短声明:m1:=map[int]string{110: "mike",111: "yoyo",112: "lily"}
110:"mike",
111:"yoyo",
112:"lily", //每行结束必须要有","
}
//也可以用make来创建
m2:=make(map[int]string)
//可以指定容量
m2:=make(map[int]string,10)//这个10是指容量,不是长度;但并不固定,可以后续追加,跟append有相似之处
m2[i]="xxxx"//向m2中添加元素
//遍历
for key,value:=range m1{
fmt.Printf("key=%v value=%v",key,value)
}
//map删除: delete(map名,key)
delete(m1,110)//把map:m1中110key对应的元素删除了
13、结构体:
package main
import "fmt"
//定义一个结构体类型
type student struct{
//结构体包含的成员及成员类型
id int
name string
sex string
age int
address string
}
func main() {
zhang:=student{1,"张","男",18,"重庆"}//定义并赋值结构体
fmt.Println(zhang)
fmt.Println("性别:",zhang.sex)
//结构体全部赋值
var s student//声明结构体s
s=student{2,"耿","男",18,"重庆"}//全部赋值
fmt.Println(s)
//结构体部分赋值
var q student//声明结构体q
q.address ="上海"//部分赋值,给结构体q中的address赋值
q.name="小宏"//部分赋值,给结构体q中的name赋值,未赋值部分默认为0
fmt.Println(q)
//结构体成员使用:指针变量
var p1 *student//定义了一个指针变量p1
p1=&zhang//指针变量用来保存了zhnag的地址
//通过指针操作成员时 p1.id与(*p1).id完全等价,并且只能用"."运算符
p1.id=5//改变原成员的值
(*p1).name="mike"
fmt.Println(*p1)
//用new()函数申请一个新的结构体
p2:=new(student)
p2.name="傻逼"
fmt.Println(p2.name)
}
二、进阶语法
1、语言可见性规则:
1.如果想使用别的包的函数、结构体类型、结构体成员,函数名、类型名、结构体变量名,首字母必须大写。如果首字母是小写,只能在同一个包里使用
2、驼峰命名法:
小驼峰命名法:第一个单词以小写字母开始,其他单词的首字母则需要大写
var sumNum int
var firstName string
var isValid bool
var printValue func()
大驼峰命名法:每个单词的首字母都采用大写
var SumNum int
var FirstName string
var IsValid bool
var PrintValue func()
3、面向对象:
4、方法:
type (接收者 类型)方法名(输入参数)返回参数{}
//类型1
package main
import "fmt"
type long int//给类型int起一个别名,后面若要使用这个方法,类型必须与其相同,为long
func (a long)Add(b long)long{
return a+b
}
func main(){
var m long=10
y:=m.Add(19)
fmt.Println(y)
}
//类型2
package main
import "fmt"
type person struct{
name string
sex string
age int
}
func (p *person)change(name string,sex string,age int){
p.name=name
p.sex=sex
p.age=age
}
func main(){
p1:=person{"mike","男",18}
fmt.Println(p1)
var p2 person
(&p2).change("yoyo","女",20)
fmt.Println("p1=",p1)
fmt.Println("p2=",p2)
}
5、方法值:
package main
import (
"fmt"
)
type person struct{
name string
sex string
age int
}
func (p *person)change(name string,sex string,age int){
p.name=name
p.sex=sex
p.age=age
fmt.Println(*p)
}
func (p person)value(){
p.name="小宏"
p.sex="男"
p.age=18
fmt.Println(p)
}
func main(){
p1:=person{"mike","男",18}
fmt.Println(p1)
var p2 person
//方法值
pFunc1:=p1.change
pFunc1("yyo","nv",17)
pFunc2:=p2.value//这个就是方法值,调用函数时,无需再传递接收着,隐藏了接收者
pFunc2()//等价于pp2.value()
//方法表达式
f1:=(*person).change
f1(&p1,"kaka","男",17)//需要输入形参类型
f2:=(person).value//不需要输入形参类型
f2(p2)//显式把接受者传过去,等价于(*p2).value()/
}
6、接口:
接口定义:
type Humaner interface{
SayHi()
}
//接口命令习惯以er结尾
//接口只有方法声明,没有实现,没有数据字段
//接口可以匿名嵌入其他接口,或嵌入到结构中
接口实现:
package main
import "fmt"
//定义接口类型
type Humaner interface {
//方法,只有函数声明,没有实现,由别的类型(自定义类型)实现
sayhi()
}
type Student struct{
name string
id int
}
type Teacher struct{
name string
age int
}
//student实现了此方法
func (temp *Student) sayhi(){
fmt.Println(temp)
}
//teacher实现了此方法
func (temp Teacher) sayhi(){
fmt.Println(temp)
}
func main(){
//定义接口类型变量
var i Humaner
//只要实现了此接口方法的类型,那么这个类型的变量就可以给i赋值
s:=&Student{"mike",666}
i= s
i.sayhi()//调用了Studebt类型
t:=&Teacher{"jeke",45}
i=t
i.sayhi()//调用了Teacher类型
}
7、接口继承(匿名字段):
package main
import "fmt"
type Student struct{
Name string
Age int
Id int
}
type Human interface {//子集
sayhi()
}
func (p Student)sayhi(){
fmt.Println(p)
}
type Human2 interface {//超集
Human//匿名字段,继承了sayhi()
Twice()
}
func (p Student)Twice(){
fmt.Println(p,p)
}
func main(){
var p Student
p=Student{
Name: "mike",
Age: 19,
Id: 666,
}
p.sayhi()//继承过来的方法
p.Twice()
}
//超集可以转化为子集,反过来不可以
var i Human2//超集
var j Human//子集
j=i//可以实现
j=i//错误,不能实现
8、空接口:
tips:可以把 interface{} 作为一种类型声明变量,其声明的变量可以接收任意类型
9、类型断言:
package main
import "fmt"
type Student struct{
Name string
Age int
}
func main(){
//if断言:
i:=make([]interface{},3)
i[0]=1 //int
i[1]="hello go" //string
i[2]=Student{"小红",18} //Student
//第一个返回下标,第二个返回下标对应的值,date分别是i[0],i[1],i[2]
for index,date:=range i {
//第一个返回的是值,第二个返回判断结果的真假
if value,ok:=date.(int);ok==true{ //date.(类型A)判断变量date是否为类型A,是:返回true;否:返回false
fmt.Printf("i[%d]类型为int,内容为%d\n",index,value)
}else if value,ok:=date.(string);ok==true{
fmt.Printf("i[%d]类型为int,内容为%s\n",index,value)
}else if value,ok:=date.(Student);ok==true{
fmt.Printf("i[%d]类型为Student,内容为%v\n",index,value)
}
}
//switch断言:
for index,date:=range i {
switch value:=date.(type){
case int :fmt.Printf("i[%d]类型为int,内容为%d\n",index,value)
case string:fmt.Printf("i[%d]类型为int,内容为%s\n",index,value)
case Student:fmt.Printf("i[%d]类型为Student,内容为%v\n",index,value)
}
}
/*运行结果:((两次)
i[0]类型为int,内容为1
i[1]类型为int,内容为hello go
i[2]类型为Student,内容为{小红 18}
进程 已完成,退出代码为 0*/
}
三、错误处理
1、error接口的使用:
1,fmt包使用:fmt.Errorf( )
格式化创建错误提示
package main
import "fmt"
func main(){
err1:=fmt.Errorf("%s","This is a normol error.")
fmt.Println("err1=",err1)
}
2,errors包使用:errors.New( )
package main
import (
"errors"
"fmt"
)
func main(){
err2:=errors.New("This is a normal eroor2.")
fmt.Println("err2=",err2)
}
error( )源码:
系统的error包:
error接口的应用:
package main
import (
"errors"
"fmt"
)
func divade(a,b int)(result int,err error){
err=nil
if b==0{
err=errors.New("分母不能为零!")
}else{
result=a/b
}
return
}
func main(){
result,err:=divade(10,0)
if err!=nil{
fmt.Println(err)
}else{
fmt.Println(result)
}
}
2、panic函数:
//系统崩溃会自主调用该函数,也可以自己显示调用该函数
package main
import "fmt"
func main(){
fmt.Println("aaaaa")
fmt.Println("bbbbb")
panic("程序崩溃")//显示调用panic函数,导致程序崩溃
fmt.Println("不会执行此语句")
}
3、recover函数:
(必须搭配defer来使用,可以跳过该错误继续执行)
package main
import "fmt"
func testa(){
fmt.Println("aaaaaa")
}
func testb(x int){
//设置recover
defer func(){
if err:=recover();err!=nil{
fmt.Println(err)
}
}()//匿名函数并调用
var a [10]int
a[x]=11//当x>9时,数组越界,产生一个panic,程序崩溃
}
func testc(){
fmt.Println("ccccc")
}
func main(){
testa()
testb(20)
recover()
testc()
}
四、字符串操作:
1、string包
package main
import (
"fmt"
"strings"
)
func main() {
//Contains:检查字符串s中是否包含substr,返回bool值,包含,返回true;不包含返回false
fmt.Println(strings.Contains("seafood", "foo")) //返回true
fmt.Println(strings.Contains("seafood", "bar")) //返回false
//Join:字符串链接,把slice a通过sep链接起来
s := []string{"one", "two", "three"}
fmt.Println(strings.Join(s, "。")) //输出:one。two。three
//Index:在字符串s中查找substr所在的位置,返回位置值,找不到返回 -1
fmt.Println(strings.Index("chicken", "ken")) //返回4:c是0,h是1,i是2,c是3,ken是4,所以返回4
fmt.Println(strings.Index("chicken", "dmr")) //chicken中没有dmr所以返回-1
//Repeat:重复s字符串count次,最后返回重复的字符串
fmt.Println(strings.Repeat("ba", 3)) //重复了”ba“三次,并返回了重复后的结果:bababa
//Replace:在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换
fmt.Println(strings.Replace("ba ba ba ba ba ba", "ba", "ma", 2)) //只替换了前面两个"ba"
fmt.Println(strings.Replace("ba ba ba ba ba ba", "ba", "ma", -1)) //”ba“被全部替换了
//Split:把s字符串按照sep分割,返回slice
fmt.Printf("%q\n", strings.Split("a,b,cd", ",")) //以”,“为分割符分割为:["a" "b" "cd"] 储存到一个新数组中
fmt.Printf("%q\n", strings.Split("a,b,cd", ""))//若sep为空,则以把每个单体分别分割开来:["a" "," "b" "," "c" "d"]
//Trim:在s字符串的头部和尾部去除cutset指定的字符串
fmt.Printf("%q\n", strings.Trim("!!!mike!!", "!")) //不管!有多少个,直到前后删完为止。最后返回:"mike"
//Fields:去除s字符串的空格符,并且按照空格分割返回slice
fmt.Printf("%q\n", strings.Fields(" one two three ")) //返回:["one" "two" "three"]
}
2、strconv包(强转)
3、正则表达式:
不得而已,一般不用
//例:
package main
import (
"fmt"
"regexp"
)
func main() {
buf:="abc agc a7c akc a8c a9c akc"
//解释规则,他会解析正则表达式,如果成功返回解释器,如果解释失败返回nil
reg1:=regexp.MustCompile(`a.c`)
//根据规则提取关键信息
result:=reg1.FindAllStringSubmatch(buf,-1)
fmt.Println(result)
}
五、json操作
1、通过结构体生成json:
package main
import (
"encoding/json"
"fmt"
)
type IT struct { //`json:"成员"`进行二次编码
Company string `json:"-"`//忽略此成员,不输出company
Subjects []string `json:"subjects"` //把首字母改为小写输出
Isok bool
Price float32 `json:",string"` //先转化为字符串再输出
}
func main() {
s:=IT{
Company: "tset",
Subjects: []string{"go语言","python","c","c++"},
Isok: true,
Price: 66.66,
}
result,err:=json.Marshal(s)//编码,根据类容生成json文本
result2,err2:=json.MarshalIndent(s,""," ")//格式化编码
fmt.Println(string(result),err)
fmt.Println(string(result2),err2)
}
2、json解码到结构体:
package main
import (
"encoding/json"
"fmt"
)
type IT struct { //`json:"成员"`进行二次编码
Company string `json:"company"`//忽略此成员,不输出company
Subjects []string `json:"subjects"` //把首字母改为小写输出
Isok bool `json:"isok"`
Price float32 `json:"price"` //先转化为字符串再输出
}
func main() {
jsonbuf:=`{
"company": "tset",
"subjects": [
"go语言",
"python",
"c",
"c++"
],
"isok": true,
"price": 66.66
}`
var temp IT//定义一个结构体变量
err:=json.Unmarshal([]byte(jsonbuf),&temp)//解码json内容,第二个参数要地址传递
if err!=nil{
fmt.Println("error")
return
}
fmt.Println(temp)
}
3、通过map生成json:
package main
import (
"encoding/json"
"fmt"
)
func main() {
m:=make(map[string]interface{},4)
m["company"]="test"
m["subjects"]=[]string{"go语言","python","c","c++"}
m["isok"]=true
m["price"]=66.66
//编码成json
result,err:=json.MarshalIndent(m,""," ")
if err!=nil{
fmt.Println("error")
return
}
fmt.Println(string(result))
}
4、json解析到map:
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonbuf := `{
"company": "tset",
"subjects": [
"go语言",
"python",
"c",
"c++"
],
"isok": true,
"price": 66.66
}`
M := make(map[string]interface{}, 4)
err := json.Unmarshal([]byte(jsonbuf), &M)
if err != nil {
fmt.Println("error")
return
}
fmt.Printf("M = %+v\n", M) //详细打印
//类型断言,先断言之后才能同类型赋值
for key, value := range M {
fmt.Printf("%v——————》%v\n", key, value)
switch date := value.(type) {
case string:
fmt.Printf("map[%s]的值类型为string,type=%v\n", key, date)
case bool:
fmt.Printf("map[%s]的值类型为bool,type=%v\n", key, date)
case float32:
fmt.Printf("map[%s]的值类型为float32,type=%v\n", key, date)
case []interface{}:
fmt.Printf("map[%s]的值类型为[]interface{},type=%v\n", key, date)
}
}
}
六、文件操作:
1、建立与打开文件
2、写文件
package main
import (
"fmt"
"os"
)
func WriteFile(path string) {
f,err:=os.Create(path)//创建文件
if err!=nil{
fmt.Println("err=",err)
return
}
defer f.Close()//函数结束前关闭文件
var buf1,buf2 string
for i:=0;i<10;i++{
buf1=fmt.Sprintf("i=****%d\n",i)
fmt.Println("buf=",buf1)
f.WriteString(buf1)
}
totallevle := 30
for i := 1; i <= totallevle; i++ {
for k := 1; k <= totallevle-i; k++ {
buf1=fmt.Sprintf(" ")//给buf1赋值
f.WriteString(buf1)//在文件中写入括号中的内容
}
for j := 1; j <= 2*i-1; j++ {
fmt.Print("*")
buf2=fmt.Sprintf("*")
f.WriteString(buf2)
}
f.WriteString("\n")
}
}
func main() {
//配置路径.当前路径下的test.txt文件
path:="test.txt"
WriteFile(path)
}
3、读文件
4、删除文件
总结:
学习不是一腔热血,而是长久坚持,善于利用工具,提高学习效率。
推荐学习方式:视频+文档+手撸代码
宝藏推荐:
www.bilibili.com/ 俗称哔哩哔哩大学,视频学习好帮手
www.runoob.com/ 菜鸟教程,新手的最爱,基础语法很nice
www.liwenzhou.com/ 李文周的博客,go语言学习的个人博客,个人比较喜欢
hao.studygolang.com/ go语言中文网站,有很多关于go语言的资源
studygolang.com/pkgdoc go语言包官方文档,中文的,很友好
pkg.go.dev/ go语言包官方文档,英文的,最原始的,更准确
#以上内容来自:推荐网站+手撸代码+个人理解
#如有不足,欢迎指正
#如有侵权,联系删除