数据类型
整型取值范围
var n int8 n=100 fmt.Println(n) //100 没有问题 //如果赋值为200 则不行 因为int8取值范围最大是127
字符串
v1 := 'A' v2 := "A"
//单引号存储的是 ASCII编码 //A的ASCII值=65 //B的ASCII值B=66 //a的ASCII值a=97
fmt.Printf("v1的类型是%T,%d,值为%sn", v1, v1) // int32 A 65
fmt.Printf("v2的类型是%T,%sn", v2, v2) //string A
//字符串的长度 //中文占3个字节
fmt.Println(len(v2)) //1
//获取某个字节
s2 := `hello go`
fmt.Println(s2[0]) //104
占位符号
字符转义
"
//字符转义空格
fmt.Println("hello t golang")//hello golang
//输出换行 n
//回车符号 r
//输出路径
fmt.Println("E:\golang\go.jpg")//E:golanggo.jpg
//双引号
fmt.Println("hello "golang"") //hello "golang"
布尔
//布尔类型作为条件比较返回结果只有true和false两种
a:=10
fmt.Println(a==2) //false
fmt.Println(a!=2) //true
浮点型
Go语言有两种精度的浮点数 float32 和 float64。浮点类型的数据取值范围可以从很小或者很巨大.
1.79E-3081.79E+308
单精度双精度两者区别
在内存中占有的字节数不同
- 单精度浮点数在机内占4个字节。
- 双精度浮点数在机内占8个字节。
有效数字位数不同
- 单精度浮点数 有效数字7位。
- 双精度浮点数 有效数字16位。
使用情况区别
- 一般用来表示美元和分的时候用单精度类型。
- 超出人类经验的数字函数,例如 sin() cos() tan() sqrt() 都使用双精度值。
数据类型的转换
var a int8 = 10 var b int16 //b=a 因为类型不同不能直接转换
b= int16(a) fmt.Println(a,b)//10,10f1:=3.12 var c int
c = int(f1) fmt.Println(f1,c)// 3.12 , 3 浮点类型转为整形的时候 只是取了整数部分
//不是所有类型都能互相转换 //数值类型的可以转换 int 和bool 不能转换
复制代码
程序控制结构
数组for 循环语句
arr := [5]int{1,2,3,4,5}
//for循环
for i:=0; i<len(arr);i++{
fmt.Println(arr[i])
}
for循环中的break 和continue
break 用来终止执行for循环语句,终止整个循环。continue 终止当前循环的迭代,重新进入下一条件,进入循环。+
goto语句
//语法
lable:func1
...
goto label
冒泡排序
package main
import "fmt"
func main() {
values := []int{4, 3, 14, 85, 34, 27, 91, 95, 26,12,32}
fmt.Println(values)
BubblingASC(values)//正序冒泡
BubblingDESC(values)//倒序冒泡
}
//冒泡排序 正序,大的靠后 小的靠前。
func BubblingASC(values []int) {
for i := 0; i < len(values)-1; i++ {
for j := i+1; j < len(values); j++ {
if values[i]>values[j]{ //左右两边数据对比
values[i],values[j] = values[j],values[i] //数据交换
}
}
}
fmt.Println(values)
}
//冒泡排序 倒序, 大的靠前 小的靠后。
func BubblingDESC(values []int) {
for i := 0; i < len(values)-1; i++ {
for j := i+1; j < len(values); j++ {
if values[i]<values[j]{ //左右两边数据对比
values[i],values[j] = values[j],values[i] //数据交换
}
}
}
fmt.Println(values)
}
复制代码
return 返回
package main
import "fmt"
func main() {
a, b, c := myfunc()
fmt.Printf("a = %d, b = %d, c = %dn", a, b, c)
}
func myfunc() (a, b, c int) {
a, b, c = 111, 222, 333
return
}
复制代码
切片Slice
切片也是一种存储相同类型的数据结构,但是不同于数组的是它的大小可以改变,如果长度不够可以自动扩充。
//声明一个切片slice
var slice []int
//使用make函数创建切片
s1:=make([]int,0,5)
fmt.Println(s1)// [] 打印空的切片
s1=append(s1,1,2)
fmt.Println(s1)// [1,2]
//因为切片可以扩容 所以定义容量为5 但是可以加无数个数值
s1=append(s1,3,4,5,6,7)
fmt.Println(s1)// [1,2,3,4,5,6,7]
//添加一组切片到另一切片中
s2:=make([]int,0,3)
s2=append(s2,s1...) //...表示将另一个切片数组完整加入到当前切片中
复制代码
make()与new() 的区别
make()是Go语言中的内置函数,主要用于创建并初始化slice切片类型,或者map字典类型,或者channel通道类型数据。他与new方法的区别是。new用于各种数据类型的内存分配,在Go语言中认为他返回的是一个指针。指向的是一个某种类型的零值。make 返回的是一个有着初始值的非零值。
//测试使用new方法新建切片
slice1 := new([]int)
fmt.Println(slice1) //输出的是一个地址 &[]
//使用make创建切片
slice2 := make([]int, 5)
fmt.Println(slice2)//输出初始值都为0的数组, [0 0 0 0 0]
fmt.Println(slice1[0])//结果出错 slice1是一个空指针 invalid operation: slice1[0] (type *[]int does not support indexing)
fmt.Println(slice2[0])//结果为 0 因为已经初始化了
复制代码
切片是如何扩容的
package main
import (
"fmt"
)
func main() {
s1 := make([]int, 0, 3)
fmt.Printf("地址%p,长度%d,容量%dn", s1, len(s1), cap(s1))
s1 = append(s1, 1, 2)
fmt.Printf("地址%p,长度%d,容量%dn", s1, len(s1), cap(s1))
s1 = append(s1, 3, 4, 5)
fmt.Printf("地址%p,长度%d,容量%dn", s1, len(s1), cap(s1))
//地址0xc000010540,长度0,容量3
//地址0xc000010540,长度2,容量3
//地址0xc00000e4b0,长度5,容量6
//如果添加的数据容量够用, 地址则不变。
//如果实现了扩容, 地址就会发生改变成新的地址,旧的则自动销毁。
}
复制代码
- 每一个切片都引用了一个底层数组。
- 切片本身不能存储任何数据,都是这底层数组存储数据,所以修改切片的时候修改的是底层数组中的数据。
- 当切片添加数据时候,如果没有超过容量,直接进行添加,如果超出容量自动扩容成倍增长。
- 切片一旦扩容,指向一个新的底层数组内存地址也就随之改变。
值传递与引用传递
int、float、string、boolarray、slice、map、struct、pointer、function、chanint、float、string、bool、array、structslice、pointer、map、chan
引用传递因为存储的是内存地址,所以传递的时候则传递是内存地址,所以会出现多个变量引用同一个内存。
//数组为值传递类型
//定义一个数组 arr1
arr1 := [4]int{1, 2, 3, 4}
arr2 := arr1 //将arr1的值赋给arr2
fmt.Println(arr1, arr2) //[1 2 3 4] [1 2 3 4] 输出结果 arr1与arr2相同,
arr1[2] = 200 //修改arr1中下标为2的值
fmt.Println(arr1, arr2) //[1 2 200 4] [1 2 3 4] 结果arr1中结果改变,arr2中不影响
//说明只是将arr1中的值给了arr2 修改arr1中的值后并不影响arr2的值
//切片是引用类型
//定义一个切片 slice1
slice1 := []int{1, 2, 3, 4}
slice2 := slice1 //将slice1的地址引用到slice2
fmt.Println(slice2, slice2) //[1 2 3 4] [1 2 3 4] slice1输出结果 slice2输出指向slice1的结果,
slice1[2] = 200 //修改slice1中下标为2的值
fmt.Println(slice1, slice2) //[1 2 200 4] [1 2 200 4] 结果slice1中结果改变,因为修改的是同一份数据
//说明只是将slice1中的值给了slice2 修改slice1中的值后引用地址用的是同一份 slice1 和slice2 同时修改
fmt.Printf("%p,%pn", slice1, slice2)//0xc000012520,0xc000012520
//切片引用的底层数组是同一个 所以值为一个地址 是引用的底层数组的地址
fmt.Printf("%p,%pn", &slice1, &slice2)//0xc0000044a0,0xc0000044c0
//切片本身的地址
复制代码
深拷贝和浅拷贝
深拷贝是指将值类型的数据进行拷贝的时候,拷贝的是数值本身,所以值类型的数据默认都是深拷贝。浅拷贝指的是拷贝的引用地址,修改拷贝过后的数据,原有的数据也被修改。 那么如何做到引用类型的深拷贝?也就是需要将引用类型的值进行拷贝。修改拷贝的值不会对原有的值造成影响。
1,使用range循环获取元素中的值 进行拷贝
//使用range循环将切片slice中的元素一个一个拷贝到切片s2中
slice := []int{1, 2, 3, 4}
s2 := make([]int, 0)
for _, v := range slice {
s2 = append(s2, v)
}
fmt.Println(slice) //结果 [1 2 3 4]
fmt.Println(s2) //结果 [1 2 3 4]
复制代码
2,使用深拷贝数据函数: copy(目标切片,数据源)
//copy(目标切片,数据源) 深拷贝数据函数
s2 := []int{1, 2, 3, 4}
s3 := []int{7, 8, 9}
copy(s2, s3) //将s3拷贝到s2中
fmt.Println(s2) //结果 [7 8 9 4]
fmt.Println(s3) //结果 [7 8 9]
copy(s3, s2[2:]) //将s2中下标为2的位置 到结束的值 拷贝到s3中
fmt.Println(s2) //结果 [1 2 3 4]
fmt.Println(s3) //结果 [3 4 9]
copy(s3, s2) //将s2拷贝到s3中
fmt.Println(s2) //结果 [1 2 3 4]
fmt.Println(s3) //结果 [1 2 3]
复制代码
切片的删除
删除切片中元素的方法
//方法一 获取切片指定位置的值 重新赋值给当前切片
slice:=[]int{1,2,3,4}
slice=slice[1:]//删除切片中开头1个元素 结果 [2,3,4]
//方法二 使用append不会改变当前切片的内存地址
slice = append(slice[:0], slice[1:]...) // 删除开头1个元素
fmt.Println(slice)
复制代码
删除指定的下标元素
slice:=[]int{1,2,3,4}
i := 2 // 要删除的下标为2
slice = append(slice[:i], slice[i+1:]...) // 删除中间1个元素
fmt.Println(slice) //结果[1 2 4]
复制代码
删除切片结尾的方法
slice := []int{1, 2, 3, 4}
slice = slice[:len(slice)-2] // 删除最后2个元素
fmt.Println(slice) //结果 [1,2]
复制代码
复合数据 map 键值对 key:value 类型的数据
var m1 map[int]string //只是声明 nil
var m2 = make(map[int]string) //创建
m3 := map[string]int{"语文": 89, "数学": 23, "英语": 90}
fmt.Println(m1 == nil) //true
fmt.Println(m2 == nil) //false
fmt.Println(m3 == nil) //false
//map 为nil的时候不能使用 所以使用之前先判断是否为nil
if m1 == nil {
m1 = make(map[int]string)
}
//1存储键值对到map中 语法:map[key]=value
m1[1]="小猪"
m1[2]="小猫"
//2获取map中的键值对 语法:map[key]
val := m1[2]
fmt.Println(val)
//3判断key是否存在 语法:value,ok:=map[key]
val, ok := m1[1]
fmt.Println(val, ok) //结果返回两个值,一个是当前获取的key对应的val值。二是当前值否存在,会返回一个true或false。
//4修改map 如果不存在则添加, 如果存在直接修改原有数据。
m1[1] = "小狗"
//5删除map中key对应的键值对数据 语法: delete(map, key)
delete(m1, 1)
//6 获取map中的总长度 len(map)
fmt.Println(len(m1))
//map的遍历
因为map是无序的 如果需要获取map中所有的键值对
可以使用 for range
map1 := make(map[int]string)
map1[1] = "张无忌"
map1[2] = "张三丰"
map1[3] = "常遇春"
map1[4] = "胡青牛"
//遍历map
for key, val := range map1 {
fmt.Println(key, val)
}
复制代码
map结合Slice
//创建一个map存储第一个人的信息
map1 := make(map[string]string)
map1["name"] = "张无忌"
map1["sex"] = "男"
map1["age"] = "21"
map1["address"] = "明教"
//如果需要存储第二个人的信息则需要重新创建map
map2 := make(map[string]string)
map2["name"] = "周芷若"
map2["sex"] = "女"
map2["age"] = "22"
map2["address"] = "峨眉山"
//将map存入切片 slice中
s1 := make([]map[string]string, 0, 2)
s1 = append(s1, map1)
s1 = append(s1, map2)
//遍历map
for key, val := range s1 {
fmt.Println(key, val)
}
复制代码
sync.map的使用
package main
import (
"fmt"
"sync"
)
//声明sync.Map
var syncmap sync.Map
func main() {
//Store方法将键值对保存到sync.Map
syncmap.Store("zhangsan", 97)
syncmap.Store("lisi", 100)
syncmap.Store("wangmazi", 200)
// Load方法获取sync.Map 键所对应的值
fmt.Println(syncmap.Load("lisi"))
// Delete方法键删除对应的键值对
syncmap.Delete("lisi")
// Range遍历所有sync.Map中的键值对
syncmap.Range(func(k, v interface{}) bool {
fmt.Println(k, v)
return true
})
}
复制代码
包管理和常用包介绍
包的概念就是我们程序中的目录,我们所写的所有代码都放在包中在定义的时候用package定义包, 然后使用 import 引入包。Go语言提供了很多内置包,例如:fmt、strings、strconv、os、io 等等。
strings包
//是否包含指定的字符串中任意一个字符 有一个出现过 就返回true
fmt.Println(strings.ContainsAny(s1,"glass"))
//返回指定字符出现的次数
fmt.Println(strings.Count(s1,"g"))
//文本的开头
fmt.Println(strings.HasPrefix(s1,"ok"))
//文本的结尾
fmt.Println(strings.HasSuffix(s1,".txt"))
//查找指定字符在字符串中存在的位置 如果不存在返回-1
fmt.Println(strings.Index(s1,"g"))
//查找字符中任意一个字符出现在字符串中的位置
fmt.Println(strings.IndexAny(s1,"s"))
//查找指定字符出现在字符串中最后一个的位置
fmt.Println(strings.LastIndex(s1,"s"))
//字符串的拼接
s2:=[]string{"123n","abc","ss"}
s3:=strings.Join(s2,"_")
fmt.Println(s3)// 123n_abc_ss
//字符串的切割
s4:=strings.Split(s3,"_")
fmt.Println(s4)// 返回切片[]string{"123n","abc","ss"}
//字符串的替换
s5 := "okoletsgo"
s6 := strings.Replace(s5, "o", "*", 1)
fmt.Println(s6)//*koletsgo
//TODO 1 只替换1次, -1 全部替换
//字符串的截取
//str[start:end]包含start 不包含end
复制代码
strconv包
主要用于字符串和基本类型的数据类型的转换
/str:="aa"+100
//字符串和整形数据不能放在一起 所以需要将100 整形转为字符串类型
//+号在字符串中表示字符串的连接 在整形中表示数据的计算
//string 转 bool类型
s1 := "true" //字符串
b, err := strconv.ParseBool(s1)
if err != nil {
fmt.Println(err) //打印错误信息
}
fmt.Printf("%T,%t", b, b) //bool,true
//string 转int
s1 := "100" //字符串
b, err := strconv.ParseInt(s1, 10, 64)
//10 表示s1要转的数据是10进制 64位
if err != nil {
fmt.Println(err) //打印错误信息
}
fmt.Printf("%T,%d", b, b) //int64,100
//整形转为字符串
s := strconv.Itoa(23)
//字符串转为整形
i, e := strconv.Atoi(s)
复制代码
time包
time包操作的都是时间,时间的单位都包括年,月,日,时,分,秒,毫秒,微妙,纳秒,皮秒。
package main
import (
"fmt"
"time"
)
func main() {
//获取当前时间
t := time.Now()
fmt.Println(t) //2020-03-31 21:26:01.7307507 +0800 CST m=+0.001999001
//获取的时间后面的信息是时区
//上面的时间看起来不是很方便 于是需要格式化时间
s := t.Format("2006年1月2日 15:04:05")
fmt.Println(s)
}
复制代码
2006-01-02 15:04:05
s := t.Format("2006-1-2 15:04:05")
fmt.Println(s) //打印出的格式就是当前的时间 2020-3-31 23:08:35
s := t.Format("2006/1/2")
fmt.Println(s) //打印出的格式就是当前的年月日 2020/3/31
//字符串类型的时间
str := "2020年3月31日"
//第一个参数是模板,第二个是要转换的时间字符串
s, _ := time.Parse("2006年1月2日", str)
fmt.Println(s) //打印出的格式就是2020-03-31 00:00:00 +0000 UTC
//获取年月日信息
year, month, day := time.Now().Date()
fmt.Println(year, month, day) //2020 March 31
//获取时分秒信息
hour, minute, second := time.Now().Clock()
fmt.Println(hour, minute, second) //23 23 54
//获取今年过了多少天了
tday := time.Now().YearDay()
fmt.Println(tday) //91 (今年已经过了91天了)
//获取今天是星期几
weekday := time.Now().Weekday()
fmt.Println(weekday) //Tuesday
复制代码
时间戳
时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数。它主要是为用户提供一份电子证据.
package main
import (
"fmt"
"time"
)
func main() {
//获取指定日期的时间戳
t := time.Date(2020, 3, 31, 23, 30, 0, 0, time.UTC)
timestamp := t.Unix()
fmt.Println(timestamp) //1585697400
//获取当前时间的时间戳
timestamp2 := time.Now().Unix()
fmt.Println(timestamp2) //1585669151
//当前时间的以纳秒为单位的时间戳
timestamp3 := time.Now().UnixNano()
fmt.Println(timestamp3) //1585669151296330900
}
//时间间隔 相加
now := time.Now()
//当前时间加上一分钟
t := now.Add(time.Minute)
fmt.Println(now) //2020-03-31 23:43:35.0004791 +0800 CST m=+0.002999201
fmt.Println(t) //2020-03-31 23:44:35.0004791 +0800 CST m=+60.002999201
//计算两个时间的间隔
d := t.Sub(now)
fmt.Println(d) //1m0s 相差一分钟
复制代码
时间戳与时间格式互转
//将指定时间转为时间戳格式
beforetime := "2020-04-08 00:00:00" //待转化为时间戳的字符串
timeLayout := "2006-01-02 15:04:05" //转化所需模板
loc := time.Now().Location() //获取时区
theTime, _ := time.ParseInLocation(timeLayout, beforetime, loc) //使用模板在对应时区转化为time.time类型
aftertime := theTime.Unix() //转化为时间戳 类型是int64
fmt.Println(theTime) //打印输出theTime 2020-04-08 00:00:00 +0800 CST
fmt.Println(aftertime) //打印输出时间戳 1586275200
//再将时间戳转换为日期
dataTimeStr := time.Unix(aftertime, 0).Format(timeLayout) //设置时间戳 使用模板格式化为日期字符串
fmt.Println(dataTimeStr)
复制代码
dep管理方案
depglidevender
安装dep
brew install dep
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
go get -u github.com/golang/dep/cmd/dep
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/64adf95bb54a40d89466c37751be51a2~tplv-k3u1fbpfcp-zoom-1.image)
dep status
dep ensuredep ensure add github.com/go-sql-driver/mysql
mod初始化
使用mod需要注意的是:
GOPROXY=https://goproxy.ioGO111MODULEGO111MODULE=autogo mod init projectName
执行上面的命令之后,就已经可以开发编译运行此项目了。他已经自动引用了项目中所有的包文件。
函数
1,大写首字母函数共用,小写私用
匿名函数
定义一个匿名函数直接加上()就相当于直接调用了,通常只能调用一次,可以将匿名函数赋值给一个变量,这个变量就代表了这个函数。则可以调用多次。
//匿名函数作为回调函数直接写入参数中 res3 := oper(2, 4, func(a, b int) int { return a + b }) fmt.Println(res3) 复制代码