go 语言基本语法
一、go语言初探 1. 基本数据类型 整型(int)、浮点型(float)、布尔型(bool)、字符串(string) 2. 复合数据类型 数组、切片、结构体、函数、map、通道(channel)、接口等 值类型: 基本数据类型和数组都是值类型 引用类型: 切片 3. 安装Bee go install github.com/beego/bee/v2@master 4. 命令: go version #查看go的安装版本 go env #查看go运行环境 go run main.go #运行go脚本 go build main.go #生成main.exe main.exe #执行生成的文件 go fmt main.go #格式化代码 一般不用了, 编辑器可以格式化 go get github.com/shopspring/decimal 下载go插件 二、语法: 1. fmt.Print #可以接收多个参数 2. fmt.Println #可以接收多个参数 3. fmt.Printf #格式化输出 类似c语言 %v 相应默认格式输出 %T 输出数据类型 int .... 4. fmt.Sprintf # 格式化输出到变量 4.定义变量 定义变量后必须使用否则会报错 建议用驼峰方式定义 小驼峰表示私有/大驼峰表示公有 var a = 'aaa' var a int = 10 a := 10 #类型推导方式定义变量 短变量声明法 不能声明全局变量 #变量初始化的方式 同一作用域变量不能重复声明 var name string name = "张三" #先定义再赋值的方式 必须用双引号 或 var name string = "张三" #直接赋值方式 或 var name = "张三" #类型推导方式 name := "张三" 短变量声明法 #定义多个变量 var a1, a2 string #同类型变量 a1 = "张三" a2 = "李四" 或 var ( #也可以使用类型推导方式 username string age int sex string ) username = "张三" age = 18 sex = "男" 或 a, b, c := 12, 13, "C" # 短变量声明多个变量 #匿名变量 _ var username string #变量不设置值 默认值为空 var num int #变量不设置值 默认值为0 5. 常量 const pi = 3.14195 #定义常量必须赋值 # 一次定义多个常量 const ( A = "A" B = "B" ) const ( # 如果后面的常量不赋值, 则值等于前面的常量值 且 常量定义后不使用也不会报错 name = "abc" name2 ) const ( # iota 常量自动累加 类似计数器 a = iota # 0 b # 1 c # 2 ) const ( # iota 常量自动累加 类似计数器 a = iota # 0 _ # 跳过某个值 c # 2 ) const ( # iota 常量自动累加 类似计数器 a = iota # 0 b = 100 # 100 c # 2 d # 3 ) const ( # 矩阵? a, b = iota + 1, iota + 2 #1 2 c, d # 2 3 e, f # 3 4 ) 6. 数据类型 int类型 unsafe.Sizeof(a) 变量占用的存储空间(占用空间的位数) int类型尽量用具体的类型不要直接使用int, 因为int占用8字节会浪费内存空间 有符号整型: int8 int16 int32 int64(默认) 无符号整型: uint8 uint16 uint32 uint64 不同位数不能进行加减运算, 运算前需进行类型转换如int64(a) 注意高位转低位溢出问题 char字符: 属于int类型 值为字符对应的ascii码 用单引号设置值 fmt.Printf("%c",'a') 原样输出 float浮点型 float32 float64(默认) 科学计数法: var a = 3.14e2 #314 bool布尔型 默认值是false, bool无法转换成整型, 不能参与运算 string字符串 要用双引号 字符用单引号 str1 := `agsd` #原样字符串/多行字符串使用反引号, 类似js的模板字符串 len(str) # 计算字符串占用存储空间, 一个中文占三个字节 + # 拼接字符串 strings.Split(str,"-") # 字符串分割成切片 strings.Join(str,"-") # 切片连接成字符串 strings.Contains(str1, "123") # 字符串是否包含另一个字符串 true/false strings.HasPrefix(str1, "123")/strings.HasSuffix(str1, "123") # 前辍/后辍是否包含某个字符串 true/false strings.Index(str1, "456") # 查找字符串第一次出现的位置(从前往后找) 找不到返回-1 strings.LastIndex(str1, "456") # 查找字符串第一次出现的位置( )从后往前找) 找不到返回-1 #循环输出字符串 s := "你好 golang" for _, v := range s { fmt.Printf("%v(%c)", v, v) } #修改字符串 s1 := "big" byteStr := []byte(s1) //转换成byte byteStr[0] = 'p' //修改字符串 fmt.Println(string(byteStr)) //转回string pig s1 := "你好" byteStr := []rune(s1) //转换成rune byteStr[0] = '大' //修改字符串 fmt.Println(string(byteStr)) //转回string 大好 7. 基本数据类型转换: int8()、int16()、float32()、float64() str := fmt.Sprintf("%d",num) #可以使用Sprintf 把任意类型转换成字符串类型 strconv.FormatInt(int64(a),10) # int转字符串 strconv.FormatFloat(float64(a),'f',2,64) #浮点转字符串 strconv.ParseInt # 字符串转int # 字符串转int str := "abc" num, err := strconv.ParseInt(str, 10, 64) # 字符串转float strconv.ParseFloat # 不建议把strng 转 bool 8. 运算符 # int 相除 只保留整数部分 # ++ -- 无前置运算符 9. 流程控制语句 if: 条件不加括号, 大括号不能省略且不能换行 # 没有while语句 for: 条件不加括号 # for rang 循环字符串 str := "你好 golang" for _, v := range str { fmt.Printf("%c", v) } #for rang 循环切片 也可以用len计数循环 arr := []string{"php", "java", "golang"} for k, v := range arr { fmt.Println(k, v) } switch case # case 1,2,3,5 可以这样写 可以不写break fallthrough 穿透一层 注: 其他语言不加break会自动穿透 break/continue/goto: 使用 break label 可以跳出多个循环 continue label 可以跳出多个循环,但是还会继续执行循环 goto label 可以跳到指定位置 10. 数组(固定长度的切片) 数组类型为: [n]int [n]string go无array类型, 切片的类型为 []int []string, 切片是没有固定元素个数的数组吗? # 定义数组 var arr [3]int # 定义数组 默认值为 [0 0 0] arr[0] = 100 //数组赋值 arr[1] = 80 //数组赋值 arr[2] = 90 //数组赋值 #初始化数组 var arr = [3]int{12, 23, 56} arr := [3]int{12, 23, 56} arr := [...]int{12, 23, 56} //数组类型仍为[3]int 只是编译器会自动推导数组元素的个数(建议用法) 虽然长度可以让编译器推导, 但是数组长度不可改变 arr := [...]int{0: 2, 3: 3} //指定数组下标方式, 可以略过不指定的下标, 默认值为基础数据类型的默认值 #数组遍历 for len循环/for range循环两种 #二维数组/多维数组 数组的数据类型必须要一致 var arr = [3][2]string{ {"北京", "上海"} {"广州", "深圳"} {"成都", "重庆"} } #取数组中的元素 arr1 := arr[:] # 取数组中的所有值赋值到变量 此时变量为切片 arr1 := arr[1:4] # 取数组中第一个到第三个元素的值 左包右不包 arr1 := arr[1:] # 取数组第一个元素到最后一个元素 左包右不包 arr1 := arr[:3] #取数组前三个元素 左包右不包 11. 切片(可变长度的数组, 数据类型为引用数据类型) 切片的类型为 []int []string 默认值为[] 也是nil # 声明切片 把数组的长度去掉 var arr []int # 定义切片 默认值为 []也是nil, 长度为0, 容量为0 var arr = []int{1,23,45,8} # 声明并赋值 #切片遍历 for len循环/for range循环两种 #数组转切片 var arr = [3]int{12, 23, 56} var slice = arr[:] # 取数组中的所有值 #取切片中的数据(与数组相同) #切片长度len: 切片中元素的个数 容量cap: 切片第一个元素到底层数组的最后一个元素的个数 # 使用make声明切片 var sliceA = make([]int, 4, 8) # 声明长度为4 容量为8的切片 # 修改切片 sliceA[0] = 10 # sliceA[4] = 20 会报错, golang中给切片扩容要用append方法 # 切片操作 # append插入数据 sliceA = append(sliceA, 12, 12, 22) # 给切片插入数据, 不会因为扩容越界问题 # append 合并切片 sliceA := []string{"php", "java"} sliceB := []string{"nodejs", "go"} sliceC := append(sliceA, sliceB...) #合并切片 注意... # append 删除切片元素 cliceC := append(slice[:2], slice[3:]...) # 删除index为2的元素 # copy 复制切片 sliceA := []string{"php", "java"} sliceB := make([]string, 2, 2) copy(sliceB, sliceA) # 把sliceA复制到sliceB # sort包可以对数组和切片进行排序 12. map 数据类型 属于引用数据类型 key并不是按照一定顺序排序的 #创建map类型 var userInfo = make(map[string]string) # 默认值为map[] 类型为map[string]string var userInfo = map[string]string{ "username":"张三" } #map类型赋值 userInfo["username"] = "张三" # for range循环map v, ifExist := userInfo["username"] # ifExist bool true/false 判断map中的key是否存在 delete(userInfo,"username") # 删除map中的key 13. 切掉与map类型结合使用 # 切片的值是map (这种类型的数据结构和mysql表的结构基本一致) sliceMap := []map[string]string{ //数据类型为 []map[string]string {"username": "张三"}, {"username": "李四"}, } # map的值是切片 mapSlice := map[string][]string{ // []string是个切片类型 "hobby": {"吃饭", "睡觉"}, } # 综合应用: 统计文字出现的个数 str := "你好呀,这是一个好玩的开发语言好一" byteStr := []rune(str) //字符串转rune类型 var countNum = make(map[string]int) //map类型计数 for _, v := range byteStr { char := fmt.Sprintf("%c", v) //rune 转文字 countNum[char]++ } fmt.Println(countNum) 14. 函数 func sumInt(x ...int) # 参数可变函数 x类型是切片 如果不传参数为nil return a,b #返回值可以是多个 func sumInt(x, y int) (sum int, sub int) { // 返回值可以提前定义 sum = x + y sub = x - y return // 返回2值 sum 和 sub } # 定义函数类型(类似c语言的.h) type cal func(x, y int) int //定义一个函数类型 func add(x, y int) int { return x + y } func main() { c := add fmt.Println(c(1, 2)) } # 函数作为函数的参数进行传递 func add(x, y int) int { /*加法函数*/ return x + y } func sub(x, y int) int { /*减法函数*/ return x - y } type calType func(int, int) int /*定义函数类型*/ func cal(x, y int, cal calType) int { /*计算器方法 第三个参数为函数 也可以传个匿名函数*/ return cal(x, y) } func main() { add := cal(2, 1, add) /*加法操作*/ sub := cal(2, 1, sub) /*减法操作*/ fmt.Println(add, sub) } # 函数作为函数返回值 func add(x, y int) int { /*加法函数*/ return x + y } func sub(x, y int) int { /*减法函数*/ return x - y } type cal func(int, int) int func do(x string) cal { /*计算器方法 第三个参数为函数*/ if x == "+" { return add } else if x == "-" { return sub } else { return nil } } func main() { a := do("+")(1, 2) fmt.Println(a) } # 匿名函数 func () { //匿名自执行函数 //... }() var fn = func () { //匿名函数赋值到变量 //... } # 闭包 # defer 延迟执行语句 先defer的语句最后执行 # panic 抛出异常 recover 接收异常 15. 指针: p := &a # 取变量a的地址赋值到p, p的类型为*int 指针变量 a := *p # 根据指针变量取值赋值到a, a的类型为int 16. 自定义数据类型 type cal func(x, y int) int //自定义函数类型 数据类型为cal type myInt int //自定义类型 数据类型为myInt var a myInt = 10 此时的数据类型为myInt type myInt = int // 给类型起个别名 var a myInt = 10 此时的数据类型为int #结构体 (值类型) type Person struct { #定义结构体 name string age int sex string } var p1 Person 或 var p2 = new(Person) 或 var p3 = &Person{} #实例化结构体 p1.name = "张三" #赋值 # 结构体自定义方法: 方法在结构体外面 还可以给自定义类型添加方法 # 结构体嵌套(也叫结构体继承) 结构体里面数据可以是切片或map或结构体 # json和结构体相互转换 jsonByte, _ := json.Marshal(p) fmt.Println(string(jsonByte)) # 结构体转json var jsonStr = `{"Name":"张三","Age":12}` var p Person _ = json.Unmarshal([]byte(jsonStr), &p) # json转结构体 # 结构体标签 type Person struct { Name string `json:"id"` # 结构体转json的时候key的名字 Age int `json:"age"` } 17. 包管理工具 go mod 其他语言包管理工具: php composer / java maven / nodejs npm go mod init demo #生成go mod 配置文件 # 第三方包 https://pkg.go.dev go get github.com/shopspring/decimal # 下载第三方包 go mod download # 代码中加入引入包的路径 会自动下载第三方包到GOPATH目录下 GOPATH目录可以通过go env来查看 go clean -modcache # 清除 go mod download 下载的第三方包 go mod vendor # 复制第三方包到项目vendor中 18. 接口interface 一组函数的集合 空接口表示任意类型/函数参数可以使用空接口/map类型的值可以是空接口/切片的值也可以是空接口 !!!这不就是弱类型语言吗 结构体可以实现多个接口, 接口也可以嵌套 type Usber interface { start() stop() } type Computer struct { } func (c Computer) work(usb Usber) { usb.start() usb.stop() } type Phone struct { Name string } func (p Phone) start() { fmt.Println(p.Name, "启动") } func (p Phone) stop() { fmt.Println(p.Name, "关机") } func main() { var computer = Computer{} var phone = Phone{Name:"小米"} computer.work(phone) } 19. 断言: v, ok := a.(tring) # 是括号中的数据类型 20. 并发 goroutine channel 1. 进程: 操作系统一次执行过程, 资源分配和调度的基本单位 2. 线程: 进程的一个执行实例, 比进程更小能够独立运行的基本单位, 一个进程中可以有多个线程 3. 并发: 多个线程竞争同一个位置 4. 并行: 多个线程同时执行 5. 协程: 用户线程 go func() # go 关键字 后面执行方法表示开启一个协程 主线程结束, go 协程会强制结束 协程等主线程, 但是主线程不会等协程 6. 协程实例 var wg sync.WaitGroup func test() { for i := 0; i < 10; i++ { fmt.Println("test() 你好golang", i) time.Sleep(time.Millisecond * 100) } wg.Done() /*协程计数器减1*/ } func main() { wg.Add(1) /*协程计数器加1*/ go test() for i := 0; i < 10; i++ { fmt.Println("main() 你好golang", i) time.Sleep(time.Millisecond * 50) } wg.Wait() /*等待协程执行完毕*/ } 7.多个协程中的消息传递: channel管道 for range循环管道的时候必须要关闭管道 close(ch) ch := make(chan int, 3) # 创建管道 可读可写 ch := make(chan<- int,3) # 只写管道 ch := make(<-chan int,3) # 只读管道 ch <- 11 # 管道写入数据 num := <-ch # 管道取出数据 8. select 多路复用 不需要关闭通道 21. 反射: reflect.TypeOf(x) # 获取x的数据类型 reflect.ValueOf(x) # 获取x的值 22. 框架beego go get github.com/astaxie/beego # 下载beego bee new beegodemo01 # 创建项目 bee run # 启动运行项目
If the copyright belongs to the longfei, please indicate the source!!!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?