目录
一、Mac brew 安装go环境
1.1 安装步骤
1.2 设置GOPATH 及环境变量
1.3 编写第一个go程序
二、快速入门
2.1 快速入门需求
2.2 go学习(自用)
2.2.1 go基础程序
2.2.2 变量声明
2.2.3 常量和枚举
2.2.4 函数与多种返回值
2.2.5 init函数与import导包
2.2.6 import匿名、别名导包
2.2.7 defer调用顺序
2.2.8 数组的表示和动态数组 slice
2.2.9 map的声明使用
三、debug
3.1 go: cannot run *_test.go files
3.2 深拷贝(copy)和浅拷贝(slice切片)
3.3 引用传递
1)终端输入,也可以指定下载go版本
brew install go
2)查看golang当前版本
go version
3)查看当前golang环境 执行
go env
1)GOPATH 是工作目录,工作空间目录 go_project 就是一个目录,其中包含三个子目录:
src
目录包含Go的源文件,它们被组织成包(每个目录都对应一个包),pkg
目录包含包对象,bin
目录包含可执行命令。
如下图:
2) 终端输入 vim .zshrc
添加:
GOPATH 后面加你自己的工作空间,这里是我自己建立的go_project
export GOPATH="/Users/mima6ge0/Documents/yanhan_practice/go_project"
export PATH = $PATH:$GOPATH/bin
执行 source ~/.zshrc
生效,使用go env查看是否修改成功
1)新建hellow.go文件
package main
import "fmt"
func main() {
fmt.Println("hello, world")
}
2)进入文件所在目录,执行go run hellow.go,成功,如下图
package main //程序的包名
// main函数
//多个文件导入
import (
"fmt"
"time"
)
func main() { //函数的"{"必须和函数名在一行,换到下一行就是语法错误,强制代码风格
fmt.Println("hellow Go!")
//go语言不需要";",可有可无
time.Sleep(10 * time.Second)
}
package main
/*
变量的声明方法
*/
import "fmt"
//全局变量,冒等方法不支持全局,其他的都可以
var A int = 100
func main() {
//声明变量默认值是0
var a int
fmt.Println("a = ", a)
fmt.Printf("type of a = %T\n", a)
//声明变量,初始化
var b int = 100
fmt.Println("b = ", b)
fmt.Printf("type of v = %T\n", b)
//初始化可以省去变量类型,通过值去匹配变量的树枝类型
var c = 100
fmt.Println("c = ", c)
fmt.Printf("type of c = %T\n", c)
//字符串
var d string = "dasda"
fmt.Println("d = ", d)
fmt.Printf("type of d = %T\n", d)
//最常用方法:冒等,省略var,直接自动匹配,":="初始化加赋值,函数内部使用
e := 3213.432
fmt.Println("e = ", e)
fmt.Printf("type of e = %T\n", e)
fmt.Println("A = ", A)
fmt.Printf("type of A = %T\n", A)
//声明多个变量
var x, y int = 100, 100
fmt.Println("x = ", x, ", y = ", y)
var m, n = 100, "88090"
n = "kjuhku"
fmt.Println("m = ", m, ", n = ", n)
//多行多变量声明
var (
xx int = 121
zz bool = true
)
fmt.Println("xx = ", xx, ", zz = ", zz)
}
package main
import "fmt"
//const 可以定义枚举类型
const (
//可以在const里面添加一个关键字 iota,每行的iota都会累加1,第一行的iota的默认值是0
BEIJING = 10 * iota // iota = 0
SHANGHAI //iota = 10
SHENZHEN //iota = 20
)
const (
a, b = iota + 1, iota + 2 //iota = 0, a = 1, b = 2
c, d //iota = 1, c = 2, d = 3
e, f //iota = 2, e = 3, f = 4
g, h = iota * 2, iota * 3 //iota = 3, g = 6, h = 9
i, k //iota = 4, i = 8, k = 12
)
func main() {
//常量,只读,不允许修改
const a int = 100
fmt.Println("a = ", a)
fmt.Println("BEIJING = ", BEIJING)
fmt.Println("SHANGHAI = ", SHANGHAI)
fmt.Println("SHENZHEN = ", SHENZHEN)
fmt.Println("a = ", a, "b = ", b)
fmt.Println("c = ", c, "d = ", d)
fmt.Println("e = ", e, "f = ", f)
fmt.Println("g = ", g, "h = ", h)
fmt.Println("i = ", i, "k = ", k)
//iota只能在const里面使用,var里面不可以
}
package main
import "fmt"
//返回一个值,匿名
func foo1(a string, b int) int {
fmt.Println("a = ", a)
fmt.Println("b = ", b)
c := 100
return c
}
//返回多个值,匿名
func foo2(a string, b int) (int, int) {
fmt.Println("--------foo2----------")
fmt.Println("a = ", a)
fmt.Println("b = ", b)
return 888, 9999
}
//返回多个返回值,有形参名称
func foo3(a string, b int) (r1 int, r2 int) {
fmt.Println("--------foo3----------")
fmt.Println("a = ", a)
fmt.Println("b = ", b)
r1 = 1000
r2 = 100000
return
}
func foo4(a string, b int) (r1, r2 int) {
fmt.Println("--------foo4----------")
fmt.Println("a = ", a)
fmt.Println("b = ", b)
//r1, r2属于foo3的形参,初始化默认的值是0,作用域空间是整个foo4函数{}的整体
fmt.Println("未赋值:r1 = ", r1)
fmt.Println("未赋值:r2 = ", r2)
r1 = 1000
r2 = 100000
return
}
func main() {
c := foo1("func1", 1)
fmt.Println("c = ", c)
ret1, ret2 := foo2("func2", 2)
fmt.Println("ret1 = ", ret1)
fmt.Println("ret2 = ", ret2)
r1, r2 := foo3("func3", 3)
fmt.Println("r1 = ", r1)
fmt.Println("r2 = ", r2)
r1, r2 = foo4("func4", 4)
fmt.Println("r1 = ", r1)
fmt.Println("r2 = ", r2)
}
文件目录树状图
lib1.go 代码:
package lib1
import "fmt"
//当前lib1包提供的API
//首字母大写的话代表当前接口对外开放,首字母小写只能在该文件下使用
func Lib1Test () {
fmt.Println("Lib1Test() ...")
}
func init () {
fmt.Println("lib1 init ...")
}
lib2.go 代码:
package lib2
import "fmt"
//当前lib2包提供的API
func Lib2Test () {
fmt.Println("Lib2Test() ...")
}
func init () {
fmt.Println("lib2 init ...")
}
main.go代码:
package main
//需要在GOPATH下
import (
"GolangStudy/5_init/lib1"
"GolangStudy/5_init/lib2"
)
func main () {
lib1.Lib1Test()
lib2.Lib2Test()
}
基于2.2.5的代码
package main
//需要在GOPATH下,go语言语法比较严格,导入必须使用
import (
// _ 匿名导包,导入但是不使用,不会报错
_ "GolangStudy/5_init/lib1"
//mylib2是lib2的别名
//mylib2 "GolangStudy/5_init/lib2"
//可以不写包名直接使用Lib2Test(),把方法导入当前main包里面,不建议使用,如果有重名函数,会出现问题
. "GolangStudy/5_init/lib2"
)
func main () {
//lib1.Lib1Test()
//mylib2.Lib2Test()
Lib2Test()
}
总结:
package main
/*
defer的执行顺序是在函数体全部执行完以后和结束之前,
多个defer语句是压栈的,defer在return后面执行
*/
import "fmt"
func func1() {
fmt.Println("A")
}
func func2() {
fmt.Println("B")
}
func func3() {
fmt.Println("C")
}
func main () {
defer func1()
defer func2()
defer func3()
}
1)固定长度数组表示,遍历等(不建议,不常用)
package main
import "fmt"
//不建议使用,可以使用动态数组
func printArray (array [4]int){
//值拷贝
fmt.Println("---------------输出函数--------------")
for i := 0; i < len(array); i++ {
fmt.Println("array2_value = ", array[i])
}
//如果要对数组的值进行修改,下面这种传参是改不了的
// array[0] = 10000
}
func main () {
// 固定长度数组
var array1 [10]int
for i := 0; i < len(array1); i++ {
fmt.Println("array1_value = ", array1[i])
}
array2 := [10]int{1, 2, 4, 5}
array3 := [4]int{1, 2, 4, 5}
for index, value := range array2 {
fmt.Println("array2_index = ", index, "array2_value = ", value)
}
printArray(array3)
// 打印数组类型
fmt.Printf("array1 type : %T\n", array1)
fmt.Printf("array2 type : %T\n", array2)
fmt.Printf("array3 type : %T\n", array3)
}
2)动态数组,切片,slice
package main
import "fmt"
func printArray(array []int) {
// 引用拷贝,传递的是数组指针,所以array[0]会被修改为100
array[0] = 100
// _ 表示匿名的变量,不被使用也不会发生问题
for _, value := range array {
fmt.Println("value = ", value)
}
}
func main() {
// 动态数组,切片,slice
array := []int{1, 2, 3, 4}
//查看array的类型
fmt.Printf("array type : %T\n", array)
printArray(array)
}
3)slice四种定义声明方式
package main
import (
"fmt"
)
func main() {
// 声明slice1是一个切片,并且初始化,长度是3,默认值是1, 2, 3
// slice1 := []int{1, 2, 3}
// 声明slice1是一个切片,但是没有分配空间,len = 0,value = []
// var slice1 []int
// 可以通过make来开辟几个空间,这时候len = 3,value = [0, 0, 0]
// slice1 = make([]int, 3)
// 声明slice1是一个切片,通过make来开辟3空间,这时候len = 3,value = [0, 0, 0]
// var slice1 []int = make([]int, 3)
// 声明slice1是一个切片,通过make来开辟3空间,这时候len = 3,value = [0, 0, 0],通过:=推测出他是一个切片
slice1 := make([] int, 3)
fmt.Printf("len = %d , value = %v\n", len(slice1), slice1)
// 判断一个切片是否为空
if slice1 == nil {
fmt.Println("slice1 is null!")
} else {
fmt.Println("slice2 is not null!!")
}
}
4)slice切片追加与截取
package main
import "fmt"
func main () {
// 切片中len和cap是不同的
var numbers = make([] int, 3, 5) // len = 3, cap容量 = 5
fmt.Printf("len = %d, cap = %d, value = %v\n", len(numbers), cap(numbers), numbers)
//追加一个元素7
numbers = append(numbers, 7)
fmt.Printf("len = %d, cap = %d, value = %v\n", len(numbers), cap(numbers), numbers)
numbers = append(numbers, 8)
// 当追加元素时,容量已满,则自动扩充为原来一开始定义的cap的二倍,扩充为10
numbers = append(numbers, 9)
fmt.Printf("len = %d, cap = %d, value = %v\n", len(numbers), cap(numbers), numbers)
// 没有定义容量的情况,但是在容量已满的情况下追加元素,直接扩充为len的二倍
var numbers2 = make([] int, 3)
fmt.Printf("len = %d, cap = %d, value = %v\n", len(numbers2), cap(numbers2), numbers2)
numbers2 = append(numbers2, 4)
fmt.Printf("len = %d, cap = %d, value = %v\n", len(numbers2), cap(numbers2), numbers2)
// slice切片
s := [] int{1, 2, 3, 4}
s1 := s[0:2] //s1是s中的索引在[0, 2)区间的元素,和python类似
fmt.Println("s1 = ", s1)
s2 := s[1:] //s1是s中的索引在[1, len(s))区间的元素,和python类似
fmt.Println("s2 = ", s2)
//如若修改s1里面的元素,s也会随之改变,因为这种切片相当于指针指向了s,所以改s1值,s和s1会一起发生改变(浅拷贝)
s1[0] = 20000
fmt.Println("s = ", s)
// go提供了copy函数,切片的时候只将值复制过来,(深拷贝)
s3 := make([] int, 3) //[0, 0, 0]
// 将s2的值依次拷贝到s3中
copy(s3, s2)
fmt.Println("s3 = ", s3)
}
1)map的声明
package main
import "fmt"
func main() {
// 第一种声明方式
var map1 map[string]string
if map1 == nil {
fmt.Println("map1是一个空map!!")
}
// 使用前,需要通过make来给map分配数据空间
map1 = make(map[string]string, 3)
map1["one"] = "java"
map1["two"] = "php"
map1["three"] = "python"
fmt.Println("map1 = ", map1)
// 第二种声明方式,直接:=
map2 := make(map[int]string)
map2[1] = "baidu"
map2[2] = "tengxun"
map2[3] = "ali"
fmt.Println("map2 = ", map2)
// 第三种声明方式,一般带有初始化的map用这种方式
map3 := map[string]string{
"王八": "绿豆",
"傻子": "小丑",
}
fmt.Println("map3 = ", map3)
}
2)map的使用
package main
import (
"fmt"
)
func printMap (city_map map[string]string) {
// 遍历
for key, value := range city_map {
fmt.Println("key = ", key, ", value = ", value)
}
}
func changeMap (city_map map[string]string) {
// city_map是一个引用传递,传递过来是地址,可以修改city_map
city_map["UK"] = "london"
}
func main() {
cityMap := make(map[string]string)
cityMap["china"] = "beijing"
cityMap["USA"] = "DC"
printMap(cityMap)
// 删除
delete(cityMap, "china")
fmt.Println("-----------删除后---------------")
printMap(cityMap)
// 修改
cityMap["USA"] = "london"
fmt.Println("-----------修改后---------------")
printMap(cityMap)
// 增加
cityMap["china"] = "beijing"
fmt.Println("-----------增加后---------------")
printMap(cityMap)
changeMap(cityMap)
fmt.Println("-----------函数增加后---------------")
printMap(cityMap)
}
2.2.10 面向对象
1)结构体的使用
package main
import "fmt"
// 声明一种数据类型,myint是int的别名
type myint int
// 声明结构体
type Book struct{
title string
auth string
}
func changeBook (book Book) {
// 传递了一个book的副本
book.title = "三体"
}
func changeBook2 (book *Book) {
// 指针传递
book.title = "生死疲劳"
}
func main () {
var a myint = 232
fmt.Println("a = ", a)
fmt.Printf("type of a is %T\n", a)
var book1 Book
book1.title = "活着"
book1.auth = "余华"
fmt.Printf("%v\n", book1)
changeBook(book1)
// 使用上面changeBook,修改值并没有成功
fmt.Printf("%v\n", book1)
// 参数是地址,所以要加"&"
changeBook2(&book1)
fmt.Printf("%v\n", book1)
}
2)面向对象类的表示
背景:将go文件命名成array_test.go报错go: cannot run *_test.go files
原因:_test.go 是golang专用的测试文件
解决:改名
https://blog.csdn.net/weixin_45440484/article/details/131740125
引用传递是一种变量传递的方式,它不是直接传递变量的值,而是传递变量的内存地址(引用)。在引用传递中,被调用函数可以通过引用修改原始变量的值。