1、变量声明
package main
import (
"fmt"
"mypro/user"
)
// 如果我们接收到多个变量,有一些变量使用不到,可以使用下划线_表示变 名称,这种变量叫做匿名变量。例
func getNameAndAge() (name string, age int) {
return "abiao", 23
}
func getNameAndAge1() (string, int) {
return "abiao", 23
}
func main() {
s := user.Hello()
fmt.Printf("s: %v\n", s)
//变量初始化 var 变量名 类型 = 表达式
/*
Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类型的默认
值,例如:整型和浮点型变量的默认值为0。字符串变量的默认值为空字符串“”。布尔型变量默认为false。
片、函数、指针变量的默认为nil。
*/
var myName string = "abiao"
var myAge int = 23
var isMarried bool = false
fmt.Printf("myAge: %v\t", myAge)
fmt.Printf("myName: %v\t", myName)
fmt.Printf("isMarried: %v\n", isMarried)
//批量声明
var (
name string = "tom"
age int = 20
b bool = true
)
fmt.Printf("name: %v \t", name)
fmt.Printf("age: %v \t", age)
fmt.Printf("b: %v \n", b)
//类型推导
var (
name1 = "tom"
site = "www.baiu.com"
)
fmt.Printf("name = %v\t", name1)
fmt.Printf("name = %v\n", site)
//批量初始化多个变量
var name2, site2, age2 = "abiao", "www.ali.com", 23
fmt.Printf("name2: %v\t", name2)
fmt.Printf("site2: %v\t", site2)
fmt.Printf("age2: %v\n", age2)
//短变量声明--在函数内部,可以使用:= 运算符对变量进行声明和初始化。
//注意:这种方法只适合在函数内部,函数外面不能使用。
name3 := "abiaobiao"
age3 := 24
fmt.Printf("name3: %v\t", name3)
fmt.Printf("age3: %v\n", age3)
name4, age4 := getNameAndAge()
fmt.Printf("name4: %v\t", name4)
fmt.Printf("age4: %v\n", age4)
//如果我们接收到多个变量,有一些变量使用不到,可以使用下划线_表示变量名称, 这种变量叫做匿名变量。例如:
_, age5 := getNameAndAge()
fmt.Printf("age5: %v\n", age5)
}
2、常量
常量,就是在程序编译阶段 就确定下来的值,而程序在运行时则无法改变该值。在Go程序中,常量可以是数值类型(包括整型、浮点型和复数类型)、布尔类型、 字符串类型等。
定义一个常量使用const关键字,语法格式如下:
const constantName [type ]= value
const :定义常量关键字
constantName :常名称
type :常量类型
value:常量的值
const定义常量
package main
import "fmt"
func main() {
const PI float64 = 3.1415926
const PI2 = 3.141 //类型推断
//批量声明
const (
width = 100
height = 200
)
fmt.Printf("width: %v\t", width)
fmt.Printf("height: %v\n", height)
//多重赋值
const i, j = 1, 2
const a, b, c = 1, 2, "foo"
}
iota
iota比较特殊,可以被认为是一个可被编译器修改的常量, 它默认开始值是0,每调用一次加1。遇到const 关键字时被重置为0,
const (
a1 = iota // 0
a2 = iota // 1
a3 = iota // 2
)
fmt.Printf("a1: %v\t", a1)
fmt.Printf("a2: %v\t", a2)
fmt.Printf("a3: %v\n", a3)
const (
a4 = iota // 0
_ //跳过
a6 = iota // 2
)
fmt.Printf("a4: %v\t", a4)
fmt.Printf("a6: %v\n", a6)
const (
a7 = iota // 0
a9 = 1000 //中间插队
a8 = iota // 2
)
fmt.Printf("a7: %v\t", a7)
fmt.Printf("a8: %v\n", a8)
3、go数据类型
package main
import "fmt"
func myFunc() {
}
func main() {
var name string = "abiao"
age := 20
bo := true
p := &age
fmt.Printf("name: %T\t", name)
fmt.Printf("age: %T\t", age)
fmt.Printf("bo: %T\t", bo)
fmt.Printf("p: %T\n", p) //name: string age: int bo: bool p: *int
arr := [2]int{1, 2}
fmt.Printf("%T\n", arr) //[2]int
slice := []int{1, 2, 3}
fmt.Printf("slice: %T\n", slice) //slice: []int
fmt.Printf("%T", myFunc) //func()
}
go 语言布尔类型
func main() {
var b1 bool = true
var b2 bool = false
var b3 = true //自动推导类型
var b4 = false
b5 := true //短变量形式
b6 := false
fmt.Printf("b1: %v\n", b1)
fmt.Printf("b2: %v\n", b2)
fmt.Printf("b3: %v\n", b3)
fmt.Printf("b4: %v\n", b4)
fmt.Printf("b5: %v\n", b5)
fmt.Printf("b6: %v\n", b6)
age := 12
if age >= 18 {
fmt.Printf("你已经成年了\n")
} else {
fmt.Printf("还未成年\n")
}
count := 10
for i := 0; i < count; i++ {
fmt.Printf("i = %v\t", i)
}
fmt.Println("")
age1 := 18
gender := "man"
if age1 >= 18 && gender == "man" {
fmt.Println("你是成年男子")
}
}
go 语言数字类型
Go语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码。
Go也有基于架构的类型,例如: int、 uint 和uintptr 。
这些类型的长度都是根据运行程序所在的操作系统类型所决定的:
●int和uint 在32位操作系统上,它们均使用32位(4个字节),在64位操作系统上,
(8个字节)。
●uintptr的长度被设定为足够存放一个指针即可。
Go语言中没有float类型。(Go语言中只有 float32和float64 )没有double类型。与操作系统架构无关的类型都有固定的大小,并在类型的名称中就可以看出来:
整数:
●int8 (-128-> 127)
●int16 (-32768 -> 32767)
●int32 (-2,147,483,648 -> 2,147,483,647)
●int64 (-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)
无符号整数:
●uint8 (0 > 255)
●uint16 (0 -> 65,535)
uint32 (0 -> 4,294,967,295)
●uint64 (0 > 18,446,744,073,709,551,615)
浮点型(EE-754 标准) : .
●float32 (± 1e-45->±3.4* 1e38)
●float64 (± 5 * 1e-324-> 107 * 1e308)
int型是计算最快的一种类型。
整型的零值为0,浮点型的零值为0.0。
golang字符串
package main
import (
"bytes"
"fmt"
"strings"
)
func main() {
var s string = "hello golang"
var s1 = "hello"
s2 := "world"
s3 := `
hello,
biao
baba
`
msg := s1 + s2
fmt.Printf("s: %v\n", s)
fmt.Printf("s1: %v\n", s1)
fmt.Printf("s2: %v\n", s2)
fmt.Printf("s3: %v\n", s3)
fmt.Printf("msg: %v\n", msg)
//字符串拼接
var name = "abiao"
age := 23
message := fmt.Sprintf("name=%s,age=%d", name, age)
fmt.Printf("message: %v\n", message)
name1 := "tom"
age1 := "25"
message1 := strings.Join([]string{name1, age1}, ",")
fmt.Printf("message1: %v\n", message1)
var buffer bytes.Buffer
buffer.WriteString("tom")
buffer.WriteString(",")
buffer.WriteString("28")
fmt.Printf("buffer.String(): %v\n", buffer.String())
//字符串切片
str := "hello world"
a := 2
b := 5
fmt.Printf("s[a]: %c\n", str[a])
fmt.Printf("str[a:b]: %v\n", str[a:b])
fmt.Printf("str[a:]: %v\n", str[a:])
fmt.Printf("str[:b]: %v\n", str[:b])
//长度
fmt.Printf("len(str): %v\n", len(str))
//分割
fmt.Printf("strings.Split(s, \" \"): %v\n", strings.Split(s, " "))
//包含
fmt.Printf("strings.Contains(s, \"hello\"): %v\n", strings.Contains(s, "hello"))
//转大写
fmt.Printf("strings.ToUpper(str): %v\n", strings.ToUpper(str))
fmt.Printf("strings.HasPrefix(\"hello\"): %v\n", strings.HasPrefix(str, "hello"))
fmt.Printf("strings.HasSuffix(\"world\", str): %v\n", strings.HasSuffix(str, "World"))
fmt.Printf("strings.Index(str, \"ll\"): %v\n", strings.Index(str, "ll"))
fmt.Printf("strings.Trim(\" 444 22 \", \" \"): %v\n", strings.Trim(" 444 22 ", " "))
}
4、格式化输出
package main
import "fmt"
type WebSite struct {
Name string
}
func main() {
// %v var
fmt.Printf("\"hello\": %v\n", "hello")
site := WebSite{Name: "abiao"}
fmt.Printf("site: %#v\n", site)
fmt.Printf("site: %v\n", site)
fmt.Printf("site: %T\n", site)
fmt.Printf("%%")
i := 97
fmt.Printf("i: %v\n", i)
fmt.Printf("i: %b\n", i)
fmt.Printf("i: %c\n", i)
x := 100
p := &x
fmt.Printf("p: %p\n", p)
/*
"hello": hello
site: main.WebSite{Name:"abiao"}
site: {abiao}
site: main.WebSite
%i: 97
i: 1100001
i: a
p: 0xc00001a0d0
*/
}
5、golang 运算符
Go语言内置的运算符有:
1.算术运算符
2.关系运算符
3.逻辑运算符
4.位运算符
5.赋值运算符
package main
import "fmt"
func main() {
a := 100
b := 22
fmt.Printf("a+b: %v\n", a+b)
fmt.Printf("a-b: %v\n", a-b)
fmt.Printf("a*b: %v\n", a*b)
fmt.Printf("a/b: %v\n", a/b)
fmt.Printf("a%%b: %v\n", a%b)
a++ //c := a++ ++不可以应用到表达式
fmt.Printf("a: %v\n", a)
}
6、流程控制
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
fmt.Printf("i: %v\n", i)
}
x := [...]int{1, 2, 3}
for _, v := range x {
fmt.Printf("v: %v\n", v)
}
}
golang 中的 if语句
go语言if语句使用提示:
1.不需使用括号将条件包含起来
2. 大括号{}必须存在,即使只有一行语句
3.左括号必须在if或else的同一行.
4.在if之后,条件语句之前,可以添加变量初始化语句,使用;进行分隔
if age := 20; age > 18 {
fmt.Printf("你已经成年")
} else {
fmt.Print("你还未成年")
}
注意:不能使用0或非0表示真假
golang if else语句
golang if else if语句
switch语句
func f() {
grade := 'A'
switch grade {
case 'A':
fmt.Println("优秀")
case 'B':
fmt.Println("优秀")
default:
fmt.Println("其他")
}
}
func f2() {
day := 1
switch day {
case 1, 2, 3, 4, 5:
fmt.Printf("工作日")
case 6, 7:
fmt.Printf("休息日")
default:
fmt.Printf("非法输入")
}
}
func f3() {
score := 60
switch {
case score >= 60:
fmt.Printf("及格")
case score >= 80 && score < 90:
fmt.Printf("优秀")
default:
fmt.Printf("其他")
}
}
//fallthrough
func f4() {
num := 100
switch num {
case 100:
fmt.Println("100")
fallthrough
case 200:
fmt.Println("200")
case 300:
fmt.Println("300")
}
}
//结果 100 200
没有break
case后支持多个值
case后支持条件
fallthrough
for 循环
for 初始语句;条件表达式;结束语句{
循环体语句
}
func f1() {
for i := 1; i < 5; i++ {
fmt.Printf("i: %v\n", i)
}
}
//初始化语句放外面
func f2() {
i := 1
for ; i < 5; i++ {
fmt.Printf("i: %v\n", i)
}
}
//可以直接写条件,初始语句和结束语句放其他地方
func f3() {
i := 1
for i <= 4 {
fmt.Printf("i: %v\n", i)
i++
}
}
//死循环
func f4() {
for {
fmt.Println("running....")
}
}
for range循环
Go语言中可以使用for range 遍历数组、切片、字符串、map 及通道(channel) 。通过for range 遍历的返回值有以下规律:
- 数组、切片、字符串返回索引和值。
- map返回键和值。
3.通道. (channel)只返回通道内的值。
func f1() {
//数组
a := [...]int{1, 2, 3}
for i, v := range a {
fmt.Printf("i: %v\t", i)
fmt.Printf("v: %v\n", v)
}
for i, _ := range a {
fmt.Printf("i: %v\n", i)
}
for _, i := range a {
fmt.Printf("i: %v\n", i)
}
}
func f2() {
//切片
var s = []int{1, 2, 3, 4}
for _, v := range s {
fmt.Printf("v: %v\n", v)
}
}
func f3() {
//map
m := make(map[string]string, 0)
m["name"] = "tom"
m["age"] = "20"
for k, v := range m {
fmt.Printf("k: %v\t", k)
fmt.Printf("v: %v\n", v)
}
}
break关键字
break语句可以结束for、 switch 和select的代码块。
go语言使用break注意事项
1.单独在select中使用break和不使用break没有啥区别。
2.单独在表达式switch语询,且没有fallthough,使用break和不使用break没有啥区别。
3.单独在表达式switch语句,组有fallthough,使用break能够终止fallthough后面的case语句的执行。
4.带标签的break,可以跳出多层select/ switch 作用域。让break更加灵活,写法更加简单灵活,不需要使用控制变量一层一层跳出循环, 没有带break的只能跳出当前语句块。
package main
import "fmt"
//跳出for循环
func f1() {
for i := 0; i < 10; i++ {
if i >= 5 {
break
}
fmt.Printf("i: %v\n", i)
}
}
//跳出switch
func f2() {
i := 2 //i = 1
switch i {
case 1:
fmt.Printf("1")
break // i=1的时候,加不加都一样
case 2:
fmt.Printf("2")
break //2的时候终止穿透,没有break会穿透
fallthrough
case 3:
fmt.Println("3")
}
}
//跳转到标签处
func f3() {
MYLABEL:
for i := 0; i < 10; i++ {
fmt.Printf("i: %v\n", i)
if i >= 5 {
break MYLABEL
}
}
fmt.Println("end.....")
}
func main() {
f3()
}
continue关键字
continue只能用在循环中,在go中只能用在for循环中,它可以终止本次循环,进行下一次循环。在continue语句后添加标签时,表示开始标签对应的循环。
func f1() {
for i := 0; i < 10; i++ {
MYLABEL:
for j := 0; j < 10; j++ {
if i == 2 && j == 2 {
continue MYLABEL //不打印 2 2,不写标签也具有相同效果
}
fmt.Printf("%v, %v\n", i, j)
}
}
}
goto 关键字
goto语句通过标签进行代码间的无条件跳转。goto 语句可以在快速跳出循环、避免重复退出上有一定的帮助。Go语言中使用goto语句能简化一些代码的实现过程。例如双层嵌套的for循环要退出时:
package main
import (
"fmt"
)
func f1() {
i := 1
if i >= 2 {
fmt.Println("2")
} else {
goto END
}
END:
fmt.Println("END...")
}
func f2() {
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if i >= 2 && j >= 2 {
goto END
}
fmt.Printf("%v-%v\t", i, j)
}
}
END:
fmt.Println("END...")
}
7 、golang数组
数组初始化
package main
import "fmt"
func test() {
var a1 [2]int
var a2 [3]string
var a3 [2]bool
fmt.Printf("a1: %T\n", a1)
fmt.Printf("a2: %T\n", a2)
fmt.Printf("a1: %v\n", a1)
fmt.Printf("a2: %v\n", a2)
fmt.Printf("a3: %v\n", a3)
/*
a1: [0 0]
a2: [ ]
a3: [false false]
*/
//使用初始化列表
var a4 = [2]int32{1, 2}
fmt.Printf("a4: %v\n", a4)
var a5 = [3]string{"hello", "world", "!"}
fmt.Printf("a5: %v\n", a5)
var a6 = [2]bool{false, true}
fmt.Printf("a6: %v\n", a6)
}
func test1() {
var a = [...]int{1, 2, 3}
fmt.Printf("len(a): %v\n", len(a)) //3
}
func test2() {
//指定索引初始化
var a1 = [...]int{2: 15, 3: 27, 6: 15}
fmt.Printf("a1: %v\n", a1)
var a2 = [...]bool{1: true, 6: true}
fmt.Printf("a2: %v\n", a2)
}
func test3() {
//访问数组下标
var a1 = [...]int{1, 2, 3}
a1[2] = 100
fmt.Printf("a1: %v\n", a1)
}
func main() {
test3()
}
访问数组
func test1() {
var a1 [5]int
a1[0] = 10
a1[1] = 100
fmt.Printf("a1[0]: %v\n", a1[0])
fmt.Printf("a1[1]: %v\n", a1[1])
fmt.Println("")
a1[0] = 20
a1[4] = 2000
fmt.Printf("a1[0]: %v\n", a1[0])
fmt.Printf("a1[4]: %v\n", a1[4])
fmt.Println("")
fmt.Printf("len(a1): %v\n", len(a1))
//遍历1
for i := 0; i < len(a1); i++ {
fmt.Printf("a1[i]: %v\t", a1[i])
}
fmt.Println("")
fmt.Printf("len(a1): %v\n", len(a1))
//遍历2
for _, v := range a1 {
fmt.Printf("v: %v\t", v)
}
fmt.Println("")
}
8 、golang切片
前面我们学习了数组,数组是固定长度,可以容纳相同数据类型的元素的集合。当长度固定时,使用还是带来一些限制,比如:我们申请的长度太大浪费内存,太小又不够用。
鉴于上述原因,我们有了go语言的切片,可以把切片理解为,可变长度的数组,其实它底层就是使用数组实现的,增加了自动扩容功能。坊(Slice) 是一个拥有相同类型元素的可变长度的序列。
切片初始化
func test1() {
var a1 = [3]int{1, 2, 3} //数组
//声明切片1
var s1 []int //切片 []中没有数字也没有...
var s2 []string
fmt.Printf("a1: %v\n", a1) //a1: [1 2 3]
fmt.Printf("s1: %v\n", s1) //s1: []
fmt.Printf("s2: %v\n", s2) //s2: []
fmt.Printf("type of a1: %T\t", a1) //type of a1: [3]int
fmt.Printf("type of s1: %T\t", s1) //type of s1: []int
fmt.Printf("type of s2: %T\n", s2) //type of s2: []string
//声明切片2
var s3 = make([]int, 2) //类型,长度
fmt.Printf("s3: %v\n", s3) //s3: [0 0]
//打印容量
var s4 = []int{1, 2, 3}
fmt.Printf("len(s4): %v\n", len(s4)) //len(s4): 3
fmt.Printf("cap(s4): %v\n", cap(s4)) //cap(s4): 3
fmt.Printf("s4[0]: %v\n", s4[0]) //s4[0]: 1
}
…
func test1() {
var s1 = []int{1, 2, 3, 4, 5, 6}
var s2 = s1[0:3]
fmt.Printf("s2: %v\n", s2) //s2: [1 2 3]
s3 := s1[3:]
fmt.Printf("s3: %v\n", s3) //s3: [4 5 6]
s4 := s1[2:5]
fmt.Printf("s4: %v\n", s4) //s4: [3 4 5]
s5 := s1[:]
fmt.Printf("s5: %v\n", s5) //s5: [1 2 3 4 5 6]
}
func test2() {
var a1 = [...]int{1, 2, 3, 4, 5, 6}
a2 := a1[0:3]
fmt.Printf("a2: %v\n", a2)
a3 := a1[3:]
fmt.Printf("a3: %v\n", a3)
a4 := a1[2:5]
fmt.Printf("a4: %v\n", a4)
a5 := a1[:]
fmt.Printf("a5: %v\n", a5)
}
切片元素的添加、删除、copy
//增
func test1() {
var s1 = []int{}
s1 = append(s1, 100)
s1 = append(s1, 2200)
s1 = append(s1, 200)
fmt.Printf("s1: %v\n", s1)
}
//删
func test2() {
var s1 = []int{1, 2, 3, 4}
s1 = append(s1[:2], s1[3:]...)
fmt.Printf("s1: %v\n", s1)
}
//改
func test3() {
var s1 = []int{1, 2, 3, 4}
s1[1] = 100
fmt.Printf("s1: %v\n", s1)
}
//查
func test4() {
var s1 = []int{1, 2, 3, 4}
key := 2
for i, v := range s1 {
if v == key {
fmt.Printf("%v", i)
}
}
}
//copy
func test5() {
var s1 = []int{1, 2, 3, 4}
s2 := s1
s1[1] = 200
fmt.Printf("s1: %v\n", s1) //s1: [1 200 3 4]
fmt.Printf("s2: %v\n", s2) // s2: [1 200 3 4]
}
func test6() {
var s1 = []int{1, 2, 3, 4}
var s2 = make([]int, 4)
copy(s2, s1)
s1[1] = 200
fmt.Printf("s1: %v\n", s1) //s1: [1 200 3 4]
fmt.Printf("s2: %v\n", s2) // s2: [1 2 3 4]
}
9、 golang map
map是一 种key :value键值对的数据结构容器。map内部实现是哈希表( hash )。map最重要的一点是通过key来快速检索数据,key类似于索引,指向数据的值。
map是引用类型的。
package main
import (
"fmt"
)
func test1() {
var m1 map[string]string
m1 = make(map[string]string)
fmt.Printf("m1: %v\n", m1) //m1: map[]
fmt.Printf("m1: %T\n", m1) //m1: map[string]string
}
func test2() {
var m1 = map[string]string{"name": "abiao", "age": "18"}
fmt.Printf("m1: %v\n", m1) //m1: map[age:18 name:abiao]
m2 := make(map[string]string)
m2["name"] = "abiaobiao"
m2["age"] = "19"
fmt.Printf("m2: %v\n", m2)
}
func test3() {
m1 := map[string]string{"name": "abiao", "age": "18"}
var key = "name"
//查询key对应value
var value = m1[key]
fmt.Printf("value: %v\n", value)
fmt.Println("")
v, ok := m1["email"]
fmt.Printf("v: %v\n", v) //v:
fmt.Printf("ok: %v\n", ok) //ok: false
v, ok = m1["name"]
fmt.Printf("v: %v\n", v) //v: abiao
fmt.Printf("ok: %v\n", ok) // ok: true
}
func main() {
test3()
}
遍历map
func tets4() {
m1 := map[string]string{"name": "abiao", "email": "abiao.163.com"}
for k := range m1 {
fmt.Printf("k: %v\n", k)
}
for k, v := range m1 {
fmt.Printf("%v :: %v\n", k, v)
}
}
10、golang 函数
函数是go语言中的一级公民,我们把所有的功能单元都定义在函数中,可以重复使用。函数包含函数的名称、参数列表和返回值类型I这些构成了函数的签名(signature) 。
go语言中函数特性
1 go语言中有3种函数:普通函数、匿名函数(没有名称的函数)、方法(定义在struct上的函数)。
2. go语言中不允许函数重载(overload),也就是说不允许函数同名。
3. go语 言中的函数不能嵌套函数,但可以嵌套匿名函数。
4.函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数。
5.函数可以作为参数传递给另一个函数。
6.函数的返回值可以是一个函数。
7.函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。
8.函数参数可以没有名称。
go语言中函数的定义和调用
函数在使用之前必须先定义,可以调用函数来完成某个任务。函数可以重复调用,从而达到代码重用。
package main
import "fmt"
func sum(a int, b int) (ret int) {
ret = a + b
return ret
}
func comp(a int, b int) int {
var max int
if a > b {
max = a
} else {
max = b
}
return max
}
func main() {
res := comp(100, 20)
fmt.Printf("res: %v\n", res)
}
函数返回值
函数可以有0或多个返回值,返回值需要指定数据类型,返回值通过return关键字来指定。return可以有参数,也可以没有参数,这些返回值可以有名称,也可以没有名称。go中的函数可以有多个返回值。
1 return关键字中指定了参数时,返回值可以不用名称。如果return省略参数,则返回值部分必须带名称
2.当返回值有名称时,必须使用括号包围,逗号分隔,即使只有一个返回值
3.但即使返回值命名了,return 中也可以强制指定其它返回值的名称,也就是说return的优先级更高
4.命名的返回值是预先声明好的,在函数内部可以直接使用,无需再次声明。命名返回值的名称不能和函数参数名称相同,否则报错提示变重复定义
5. return 中可以有表达式,但不能出现赋值表达式,这和其它语言可能有所不同。例如return a+b 是正确的,但return c=a+b 是错误的。
函数参数
go语言函数可以有0或多个参数,参数需要指定数据类型。
声明函数时的参数列表叫做形参,调用时传递的参数叫做实参。
go语言是通过传值的方式传参的,意味着传递给函数的是拷后的副本,所以函数内部访问、修改的也是这个副本。
go语言可以使用变长参数,有时候并不能确定参数的个数,可以使用变长参数,可以在函数定义语句的参数部分
使用ARGS… .TYPE的方式。这时会将… 代表的参数全部保存到一个名为ARGS的slice中,注意这些参数的数据类型都是TYPE。
map、slice 、interface 、channel 这些数据类型本身就是指针类型的,所以就算是拷贝传值也是拷贝的指针,拷贝后的参数仍然指向底层数据结构,所以修改它们可能会影响外部数据结构的值。
package main
import "fmt"
func f3(args ...int) {
for _, v := range args {
fmt.Printf("v: %v\n", v)
}
}
func f4(name string, ok bool, args ...int) {
fmt.Printf("name: %v\n", name)
fmt.Printf("ok: %v\n", ok)
for _, v := range args {
fmt.Printf("v: %v\n", v)
}
}
func main() {
f4("abc", false, 3, 4, 5)
}
高阶函数
函数作为参数
package main
import (
"fmt"
)
func sayHello(name string) {
fmt.Printf("Hello, %s\n", name)
}
func sayHi(name string) {
fmt.Printf("HI,%s\n", name)
}
func test(name string, fName func(string)) {
fName(name)
}
func main() {
test("myname", sayHello)
test("myname", sayHi)
}
函数作为返回值
func add(a int, b int) int {
return a + b
}
func sub(a int, b int) int {
return a - b
}
func cal(operator string) func(int, int) int {
switch operator {
case "+":
return add
case "-":
return sub
default:
return nil
}
}
func main() {
subOperate := cal("+")
res := subOperate(10, 25)
fmt.Printf("res: %v\n", res)
addOperate := cal("-")
res = addOperate(100, 56)
fmt.Printf("res: %v\n", res)
}
匿名函数
go语言函数不能成套,但是在函数内部可以定义匿名函数,实现一下简单功能调用。
所谓匿名函数就是,没有名称的函数。
package main
import "fmt"
func main() {
//匿名函数
max := func(a int, b int) int {
if a > b {
return a
} else {
return b
}
}
res := max(10, 96)
fmt.Printf("res: %v\n", res)
//自己调用自己
r := func(a int, b int) int {
if a > b {
return a
} else {
return b
}
}(15, 56)
fmt.Printf("r: %v\n", r)
}
11、golang 闭包
闭包可以理解成定义在一个函数内部的函数。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。或者说是函数和其引用环境的组合体。
闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说,闭包=函数+引用环境。首先我们来看一个例子:
package main
import "fmt"
func add() func(y int) int {
var x int
return func(y int) int {
x += y
return x
}
}
func main() {
f := add()
res := f(10)
fmt.Printf("res: %v\n", res) //res: 10
res = f(20)
fmt.Printf("res: %v\n", res) // res: 30
res = f(30)
fmt.Printf("res: %v\n", res) // res: 60
fmt.Println("----------------------")
fmt.Println("变量f是一个函数并且它引用了其外部作用域中的x变量,此时f就是一个闭包。 在f的生命周期内,变量x也一直有效。")
f = add()//闭包的生命周期主要在重新引用
r := f(100)
fmt.Printf("r: %v\n", r) //100
r = f(200)
fmt.Printf("r: %v\n", r) //300
}
变量f是一个函数并且它引用了其外部作用域中的x变量,此时f就是一个闭包。 在f的生命周期内,变量x 也一直有效。
package main
import "fmt"
func cal(base int) (func(int) int, func(int) int) {
add := func(a int) int {
base += a
return base
}
sub := func(a int) int {
base -= a
return base
}
return add, sub
}
func main() {
add, sub := cal(100)
r := add(100)
fmt.Printf("r: %v\n", r) //200
r = sub(50)
fmt.Printf("r: %v\n", r) //150
add1, sub1 := cal(100)
r = add1(100)
fmt.Printf("r: %v\n", r) //200
r = sub1(150)
fmt.Printf("r: %v\n", r) //5
}
12、golang递归
函数内部调用函数自身的承数称为递归函数。
使用递归函数最重要的三点:
1.递归就是自己调用自己。
2.必须先定义函数的退出条件,没有退出条件,递归将成为死循环。
3. go语言递归函数很可能会产性一大堆的goroutine, 也很可能会出现栈空间内存溢出问题。
。
package main
import "fmt"
//a的阶乘
func f1(a int) int {
if a == 1 {
return 1
}
return a * f1(a-1)
}
func main() {
fmt.Printf("f1(5): %v\n", f1(5))
}
13、defer 语句
go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。
defer特性
1.关键字defer 于注册延迟调用。
2.这些调用直到return 前才被执。因此,可以用来做资源清理。
3.多个defer 语句,按先进后出的方式执行。
4. defer 语句中的变量,在defer声明时就决定了。
defer用途
1.关闭文件句柄
2.锁资源释放
3.数据库连接释放
package main
import "fmt"
func test1() {
fmt.Println("satrt...")
defer fmt.Println("step1...")
defer fmt.Println("step2...")
defer fmt.Println("step3...")
fmt.Println("end...")
}
func main() {
test1()
/*
结果:
satrt...
end...
step3...
step2...
step1...
*/
}
14、 init函数
golang有一个特殊的函数init函数,先于main函数执行,实现包级别的一些初始化操作。
init函数的主要特点
●init函数先于main函数自动执行,不能被其他函数调用;
●init函数没有输入参数、返回值;
●每个包可以有多个init函数;
●包的每个源文件也可以有多个init函数,这点比较特殊;
●同一个包的init执行顺序, golang没有明确定义,编程时要注意程序不要依赖这个执行顺序。
●不同包的init函数按照包导入的依赖关系决定执行顺序。
golang初始化顺序
初始化顺序:变量初始化->init()->main()
package main
import "fmt"
var i int = initVar()
func initVar() int {
fmt.Println("initVar")
return 100
}
func init() {
fmt.Println("init2...")
}
//init函数必须没有参数和返回值
func init() {
fmt.Println("init...")
}
func main() {
fmt.Println("main...")
}
initVar
init2…
init…
main…
init函数快捷键finit
15、指针
Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个 指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。
类型指针不能进行偏移和运算。
Go语言中的指针操作非常简单,只需要记住两个符号: & (取地址) 和*(根据地址取值)。
指针地址和指针类型
每个变量在运行时都拥有一个地址, 这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行取地址操作。Go语言中的值类型(int、 float、 bool、 string、 array、 struct)都有对应的指针类型,如: *int、 *int64、 *string等。
package main
import "fmt"
func main() {
var ip *int
fmt.Printf("ip: %v\n", ip) //ip: <nil>
fmt.Printf("ip: %T\n", ip) //ip: *int
var i int = 100
ip = &i
fmt.Printf("ip: %v\n", ip) //ip: 0xc000122090
fmt.Printf("ip: %v\n", *ip) //ip: 100
var sp *string
var s string = "hello"
sp = &s
fmt.Printf("sp: %T\n", sp) //sp: *string
fmt.Printf("sp: %v\n", *sp) //sp: hello
}
指向数组的指针
package main
import "fmt"
func main() {
a := [3]int{1, 2, 3}
var pa [3]*int
//pa: [<nil> <nil> <nil>]
fmt.Printf("pa: %v\n", pa)
for i := 0; i < len(a); i++ {
pa[i] = &a[i]
}
//pa: [0xc000018120 0xc000018128 0xc000018130]
fmt.Printf("pa: %v\n", pa)
for i := 0; i < len(pa); i++ {
fmt.Printf("%v\t", *pa[i]) // 1 2 3
}
}
16、类型定义和类型别名
类型定义的语法
type NewType Type
go语言类型定义和类型别名的区别
1.类型定义相当于定义了一个全新的类型,与之前的类型不同;但是类型别名并没有定义一个新的类型,而是使用一个别名来替换之前的类型
2.类型别名只会在代码中存在,在编译完成之后并不会存在该别名
3.因为类型别名和原来的类型是一致的, 所以原来类型所拥有的方法,类型别名中也可以调用,但是如果是重新定义的一个类型,那么不可以调用之前的任何方法
17、 结构体
go语言没有面向对象的概念了,但是可以使用结构体来实现,面向对象编程的一些特性,例如:继承、组合等特性。
结构体定义
package main
import "fmt"
//两种定义方式
type Person struct {
id int
name string
age int
email string
}
type Customer struct {
id, age int
name, email string
}
func main() {
var tom Person
fmt.Printf("tom: %v\n", tom) //tom: {0 0 }
tom.name = "tom"
tom.age = 20
tom.id = 1000001
tom.email = "tom@gmail.com"
fmt.Printf("tom: %v\n", tom) //tom: {1000001 tom 20 tom@gmail.com}
fmt.Printf("tom.age: %v\n", tom.age)
fmt.Printf("tom.email: %v\n", tom.email)
var customer Customer
fmt.Printf("customer: %v\n", customer) //customer: {0 0 }
//匿名结构体
var lucy struct {
id int
name string
age int
email string
}
lucy.id = 100002
lucy.name = "lucy"
lucy.age = 16
lucy.email = "lucy123@163.com"
fmt.Printf("lucy: %v\n", lucy) //lucy: {100002 lucy 16 lucy123@163.com}
}
golang结构体的初始化
未初始化的结构体,成员都是零值int 0 float 0.0 bool false string
nil nil
func main() {
type Person struct {
id int
name string
age int
email string
}
var tom Person
tom = Person{
id: 101,
name: "tom",
age: 25,
email: "tom@gmail.com",
}
fmt.Printf("tom: %v\n", tom) //tom: {101 tom 25 tom@gmail.com}
tom = Person{1, "tom", 21, "tom.163.com"}
fmt.Printf("tom: %v\n", tom) //tom: {1 tom 21 tom.163.com}
//部分初始化
tom = Person{
id: 1, age: 20,
}
fmt.Printf("tom: %v\n", tom) //tom: {1 20 }
}
结构体指针
回顾普通指针
package main
import "fmt"
func main() {
var name string
name = "tom"
var p_name *string
p_name = &name
fmt.Printf("name: %v\n", name)
fmt.Printf("p_name: %v\n", p_name)
fmt.Printf("* p_name: %v\n", *p_name)
/*
name: tom
p_name: 0xc000054250
* p_name: tom
*/
}
结构体指针
package main
import "fmt"
func main() {
test2()
}
func test1() {
type Person struct {
id int
age int
name string
}
tom := Person{
id: 101,
name: "mimi",
age: 2,
}
var p_person *Person
p_person = &tom
fmt.Printf("tom: %v\n", tom) //tom: {101 2 mimi}
fmt.Printf("p_person: %p\n", p_person) //p_person: 0xc0000563c0
fmt.Printf("p_person: %v\n", p_person) // p_person: &{101 2 mimi}
fmt.Printf("*p_person: %v\n", *p_person) //*p_person: {101 2 mimi}
}
func test2() {
type Person struct {
id int
age int
name string
}
var Tom = new(Person)
Tom.id = 655
(*Tom).age = 4
Tom.name = "TTTooMMM"
fmt.Printf("Tom: %v\n", Tom) //Tom: &{655 4 TTTooMMM}
fmt.Printf("Tom: %p\n", Tom) //Tom: 0xc0000a03a0
fmt.Printf("Tom: %v\n", *Tom) //Tom: {655 4 TTTooMMM}
}
golang结构体作为函数参数
go结构体可以像普通变量-样,作为函数的参数,传递给函数,这里分为两种情况:
1.直接传递结构体,这是是一个副本(拷贝), 在函数内部不会改变外面结构体内容。
2.传递结构体指针,这时在函数内部,能够改变外部结构体内容。
传结构体本身
package main
import "fmt"
type Person struct {
id int
name string
}
func showPerson(person Person) {
person.id = 123456
person.name = "abiaooo"
fmt.Printf("person: %v\n", person) //person: {123456 abiaooo}
}
func main() {
person := Person{
id: 14789,
name: "kuaile",
}
showPerson(person)
fmt.Printf("person: %v\n", person) //person: {14789 kuaile}
}
传结构体指针
package main
import "fmt"
type Person struct {
id int
name string
}
func showPerson2(person *Person) {
person.id = 123456
person.name = "abiaooo"
fmt.Printf("person: %v\n", *person) //person: {123456 abiaooo}
}
func main() {
person := Person{
id: 14789,
name: "kuaile",
}
showPerson2(&person)
fmt.Printf("person: %v\n", person) //person: {123456 abiaooo}
}
嵌套结构体
go语言没有面向对象编程思想,也没有继承关系,但是可以通过结构体嵌套来实现这种效果。
下面通过实例演示如何实现结构体嵌套,加入有一个人Person结构体,这个人还养了一个 宠物Dog结构体。
package main
import "fmt"
func main() {
type Dog struct {
name string
age int
color string
}
type Person struct {
dog Dog
name string
age int
}
dog1 := Dog{
name: "花花",
age: 5,
color: "white",
}
per := Person{
dog: dog1,
age: 20,
name: "小小",
}
fmt.Printf("per: %v\n", per) //per: {{花花 5 white} 小小 20}
fmt.Printf("per.dog.name: %v\n", per.dog.name) //per.dog.name: 花花
}
18、方法
go语言没有面向对象的特性,也没有类对象的概念。但是,可以使用结构体来模拟这些特性,我们都知道面向对
象里面有类方法等概念。我们也可以声明一些方法,属于某个结构体。|
package main
import "fmt"
type Customer struct {
name string
}
func (customer Customer) login(name string, password string) bool {
fmt.Printf("customer.name: %v\n", customer.name) //customer.name: tom
if name == "tom" && password == "abiao123" {
return true
} else {
return false
}
}
func main() {
customer := Customer{
name: "tom",
}
res := customer.login("tom", "abiao123")
fmt.Printf("res: %v\n", res) //res: true
}
go语言方法的注意事项
1.方法的receiver type并非一定要是struct类型, type定义的类型别名、 slice、 map、channel、 func类型等都可以。
2. struct结合它的方法就等价于面向对象中的类。 不过struct可以和它的方法分开,并非一定要属于同一个文件,但必须属于同一个包。
3.方法有两种接收类型: (T Type) 和(T *Type) ,它们之间有区别。
4.方法就是函数,所以Go中没有方法重载(overload)的说法,也就是说同一个类型中的所有方法名必须都唯一。
5.如果receiver是一 个指针类型, 则会自动解除引用。
6.方法和type是分开的,意味着实例的行为(behavior)和数据存储(field)是分开的,但是它们通过receiver建立起关联关系。
golang方法接收者类型
结构体实例,有值类型和指针类型,那么方法的接收者是结构体,那么也有值类型和指针类型。区别就是接收者是否复制结构体副本。值类型复制,指针类型不复制。
值类型结构体和指针类型结构体
结构体
type Person struct {
name string
}
func showPerson1(per Person) {
per.name = "tom...."
}
func showPerson2(per *Person) {
per.name = "tom...." //(*per).name = "tom...."
}
func main() {
p1 := Person{
name: "tom",
}
p2 := &Person{
name: "tom",
}
showPerson1(p1)
fmt.Printf("p1: %v\n", p1) //p1: {tom}
showPerson2(p2)
fmt.Printf("p2: %v\n", *p2) //p2: {tom....}
}
方法
type Person struct {
name string
}
func (person Person) showPerson3() {
person.name = "tom...."
}
func (person *Person) showPerson4() {
person.name = "tom...." //(*per).name = "tom...."
}
func main() {
p1 := Person{
name: "tom",
}
p2 := &Person{
name: "tom",
}
p1.showPerson3()
fmt.Printf("p1: %v\n", p1) //p1: {tom}
p2.showPerson4()
fmt.Printf("p2: %v\n", *p2) //p2: {tom....}
}
19、golang接口
接口像是一-个公司里面的领导,他会定义一些通用规范,只设计规范,而不实现规范。
go语言的接口,是-种新的类型定义,它把所有的具有共性的方法定义在一起, 任何其他类型只要实现了这些方法就是实现了这个接口。
语法格式和方法非常类似。
接口语法格式
package main
import "fmt"
//vscode快捷键 tyi--type name interface
type USB interface {
read()
write()
}
//vscode快捷键 tys--type name interface
type Computer struct {
name string
}
//vscode快捷键 tys--type name interface
type Mobile struct {
model string
}
func (c Computer) read() {
fmt.Printf("c.name: %v\n", c.name)
fmt.Println("read....")
}
func (c Computer) write() {
fmt.Printf("c.name: %v\n", c.name)
fmt.Println("write....")
}
func (m Mobile) read() {
fmt.Printf("m.model: %v\n", m.model)
fmt.Println("read....")
}
func (m Mobile) write() {
fmt.Printf("m.model: %v\n", m.model)
fmt.Println("write....")
}
func main() {
c := Computer{
name: "dell",
}
c.read()
c.write()
m := Mobile{
model: "5G",
}
m.read()
m.write()
}
c.name: dell
read....
c.name: dell
write....
m.model: 5G
read....
m.model: 5G
write....
golang接口值类型接收者和指针类型接收者
这个话题,本质上和方法的值类型接收者和指针类型接收者,的思考方法是一样的,值接收者是一个拷贝, 是一个副本,而指针接收者,传递的是指针。
package main
import "fmt"
type Pet interface {
eat(string) string
}
type Dog struct {
name string
}
// func (dog Dog) eat(name string) string {
// dog.name = "嘟嘟"
// fmt.Printf("name: %v\n", name)
// return "吃的好肥"
// }
func (dog *Dog) eat(name string) string {
dog.name = "嘟嘟肥柯基"
fmt.Printf("name: %v\n", name) //name: 大骨头
return "吃的好肥"
}
func main() {
dog := &Dog{
name: "蛋蛋小柯基",
}
ret := dog.eat("大骨头")
fmt.Printf("dog: %v\n", *dog) //dog: {嘟嘟肥柯基}
fmt.Printf("ret: %v\n", ret) //ret: 吃的好肥
}
golang接口和类型的关系
1.一个类型可以实现多个接口
2.多个类型可以实现同一个接口(多态)
一个类型实现多个接口
一个类型实现多个接口,例如:有一个Player接口可以播放音乐,有一个Video接口可以播放视频,一个手机Mobile
实现这两个接口,既可以播放音乐,又可以播放视频。
一个类型可以实现多个接口
package main
import "fmt"
type Player interface {
playMusic()
}
type Video interface {
playVideo()
}
type Mobile struct {
}
func (mobile Mobile) playMusic() {
fmt.Println("play music")
}
func (mobile Mobile) playVideo() {
fmt.Println("play video")
}
func main() {
mobile := Mobile{}
mobile.playMusic()
mobile.playVideo()
}
多个类型实现同一个接口
比如,一个宠物接口Pet,猫类型Cat和狗类型Dog都可以实现该接口,都可以把猫和狗当宠物类型对待,这在其他语言中叫做多态。
package main
import "fmt"
type Pet interface {
eat()
}
type Cat struct {
}
type Dog struct {
}
func (cat Cat) eat() {
fmt.Println("cat eat...")
}
func (dog Dog) eat() {
fmt.Println("dog eat...")
}
func main() {
// cat := Cat{}
// dog := Dog{}
// cat.eat()
// dog.eat()
var pet Pet
pet = Dog{}
pet.eat() //dog eat...
pet = Cat{}
pet.eat() //cat eat...
}
接口嵌套
接口可以通过嵌套,创建新的接口。例如:飞鱼,既可以飞。又可以游泳。我们创建一个飞Fly接口, 创建一个游泳接口Swim,"飞鱼接口由这两个接口组成。
package main
import "fmt"
type Flyer interface {
fly()
}
type Swimmer interface {
swim()
}
type FlyFish interface {
Flyer
Swimmer
}
type Fish struct{}
func (fish Fish) fly() {
fmt.Println("fly........")
}
func (fish Fish) swim() {
fmt.Println("swim........")
}
func main() {
var fish FlyFish
fish = Fish{}
fish.fly() //fly........
fish.swim() //swim........
}
通过接口实现OCP设计原则
而面向对象的可复用设计的第一块基石, 便是所谓的”开闭“原则(Open-Closed Principle,常缩写为0CP)。虽然,go不是面向对象语言,但是也可以模拟实现这个原则。
对扩展开放、对修改关闭
package main
import "fmt"
type Pet interface {
eat()
sleep()
}
type Dog struct {
}
type Cat struct {
}
type Person struct {
}
func (dog Dog) eat() {
fmt.Println("Dog,eat........")
}
func (dog Dog) sleep() {
fmt.Println("Dog,sleep........")
}
func (cat Cat) eat() {
fmt.Println("cat,eat........")
}
func (cat Cat) sleep() {
fmt.Println("cat,sleep........")
}
//Pet既可以传递Dog也可以传递Cat
func (person Person) care(pet Pet) {
pet.eat()
pet.sleep()
}
func main() {
person := Person{}
dog := Dog{}
cat := Cat{}
person.care(dog)
person.care(cat)
}
20、golang模拟OOP的属性和方法
golang没有面向对象的概念,也没有封装的概念,但是可以通过结构体struct和函数绑定来实现0OP的属性和方法等特性。接收者receiver方法。
例如,想要定义一个Person类,有name和age属性, 有eatsleep/work方法。
package main
import "fmt"
type Person struct {
name string //公共的是Name,首字母大写
age int
}
func (person Person) eat() {
fmt.Println("eat.........")
}
func (person Person) sleep() {
fmt.Println("sleep.........")
}
func (person Person) work() {
fmt.Println("work.........")
}
func main() {
tom := Person{
name: "tom",
age: 25,
}
fmt.Printf("tom: %v\n", tom)
tom.sleep()
tom.eat()
tom.work()
tom.eat()
tom.sleep()
}
21、 继承
golang本质上没有ogp的概念, 也没有继承的概念,但是可以通过结构体嵌套实现这个特性。
package main
import "fmt"
type Animal struct {
name string
age int
}
func (a Animal) eat() {
fmt.Println("eat.....")
}
func (a Animal) sleep() {
fmt.Println("sleep.....")
}
type Dog struct {
a Animal //可以理解为继承
color string
}
type Cat struct {
a Animal //可以理解为继承
breed string
}
type Dog2 struct {
Animal //可以理解为继承,匿名
color string
}
type Cat2 struct {
Animal //可以理解为继承,匿名
breed string
}
func main() {
dog := Dog{
a: Animal{name: "嘟嘟", age: 3},
color: "brown",
}
dog.a.eat()
dog.a.sleep()
//匿名————直接调用
dog2 := Dog2{
Animal{name: "嘟嘟", age: 3},
"black",
}
//匿名————直接调用
cat2 := Cat2{
Animal{"花花", 23},
"labuladuo",
}
//匿名————直接调用
dog2.eat()
dog2.sleep()
fmt.Printf("dog2.color: %v\n", dog2.color)
//匿名————直接调用
cat2.eat()
cat2.sleep()
fmt.Printf("cat2.breed: %v\n", cat2.breed)
}
22、构造函数
package main
import "fmt"
type Person struct {
name string
age int
}
func NewPerson(name string, age int) (*Person, error) {
if name == "" {
return nil, fmt.Errorf("name 不能为空")
}
if age < 0 {
return nil, fmt.Errorf("age 不能小于0")
}
return &Person{name: name, age: age}, nil
}
func main() {
person, err := NewPerson("aBiao", 15)
if err == nil {
fmt.Printf("person: %v\n", *person)
fmt.Printf("err: %v\n", err)
}
}
23、包
包可以区分命令空间(一个文件夹中不能有两个同名文件),也可以更好的管理项目。go中创建一 个包,-般
创建一 个文件夹,在该文件夹里面的go文件中,使用package关键字声明包名称,通常,文件夹名称和包名称相
同。并且,同一个文件下面只有一个包
包注意事项
●一个文件夹下只能有一个package
0 import后面的其实是‘GOPATH
开始的相对目录路径,包括最后-段。但由于一个目录下只能有一个
package,所以import一个路径就等于是import了这个路径下的包。
。注意,这里指的是“直接包含”的go文件。如果有子目录,那么子目录的父目录是完全两个包。
●比如你实现了一个计算器package,名叫calc,位于calc录下;仅想给别人一个使用范例,于是在ca
下可以建个example子眼(calc/example/) , 这个子目录里有个example.go (calc/example/example.go)。
时,example.go可以是main包, 面还可以有个main函数。
●一个package的文件不能在多个文件夹下
。如果多个文件夹下有重名的package, 它们其实是彼此无关的package。
。如果一个go文件需要同时使用不同目录下的同名package,需要在import这些目录时为每个目录指定一
个package的别名。
go module
自动编译到缓存里,然后可以搜索到
…待续