当前位置:   article > 正文

Golang(一):基础、数组、map、struct

Golang(一):基础、数组、map、struct

目录

hello world

变量

常量,iota

函数

init函数和导包过程 

指针

defer

数组和动态数组

固定长度数组

遍历数组

动态数组

len 和 cap

截取

切片的追加

map

四种声明方式

遍历map

删除

查看键是否存在

结构体

声明

作为形参

方法

封装

 继承

多态

类型断言


配置:

一文轻松实现在VSCode中编写Go代码_vscode 开发go-CSDN博客

hello world

  1. package main
  2. //导入包
  3. import "fmt"
  4. //主函数,入口函数
  5. func main() { //大括号必须和函数在同一行
  6. //打印输出
  7. fmt.Println("hello go")
  8. }

导入多个包:

  1. import "fmt"
  2. import "time"

or

  1. import(
  2. "fmt"
  3. "time"
  4. )

变量

四种定义方式

  1. func main() {
  2. var a int
  3. fmt.Print(a) //默认为0
  4. var b int = 3
  5. fmt.Print(b) //3
  6. var c = 10
  7. fmt.Print(c) //10
  8. fmt.Printf("c is %T", c) //c is int
  9. d := 3.45
  10. fmt.Printf("d = %f", d) //d=3.450000
  11. }

第四种方法只能使用于局部变量,例如下面这张方法是不可以的:

  1. a :=200
  2. //non-declaration statement outside function body
  3. func main() {
  4. fmt.print(a)
  5. }

声明多个变量

  1. func main() {
  2. var a, b = 3, "asdad"
  3. fmt.Print(a, b)
  4. var c, d int = 3, 4
  5. fmt.Print(c, d)
  6. var (
  7. e int = 4
  8. f = "aeasd"
  9. )
  10. fmt.Print(e, f)
  11. g, h := 9, false
  12. fmt.Println(g, h)
  13. }

数据类型转换

一、强制类型转换 

        但是只有相同底层类型的变量之间可以进行相互转换(如将 int16 和 int32 相互转换,float 类型和 int 类型相互转换),不同底层类型的变量相互转换时会引发编译错误(如 bool 类型和 int 类型,string 与 int 类型之间的转换):

  

二、 strconv包:字符串和数值类型的相互转换 

    1. Itoa():整型转字符串  integer to alphanumeric 

    2. Atoi():字符串转整型 alphanumeric to integer 

    3. Parse 系列函数(str转其他): ParseBool()、ParseInt()、ParseUint()、ParseFloat() 

    4.Format 系列函数(其他转str): FormatBool()、FormatInt()、FormatUint()、FormatFloat() 

    5.Append 系列函数(其他转str后加到切片中): AppendBool()、AppendFloat()、AppendInt()、AppendUint() 

  

 golang的数据类型

golang 的数据类型_golang数据类型-CSDN博客

常量,iota

常量不允许重复赋值

  1. func main() {
  2. const PI float32 = 3.14
  3. fmt.Printf("PI: %v\n", PI)
  4. const a = 90
  5. fmt.Printf("a: %v\n", a)
  6. a = 99 //cannot assign to a (neither addressable nor a map index expression)
  7. }

常量可以枚举

  1. const (
  2. Unknown = 0
  3. Female = 1
  4. Male = 2
  5. )

可以使用iota实现自增赋值,例如下面这种一个一个赋值很繁琐

  1. const (
  2. CategoryBooks = 0
  3. CategoryHealth = 1
  4. CategoryClothing = 2
  5. )

于是:

  1. const (
  2. CategoryBooks = iota // 0
  3. CategoryHealth // 1
  4. CategoryClothing // 2
  5. )

在每个枚举中,iota从0开始,每行递增

也可以在第一行使用公式,下面所有行都会使用同样的公式

  1. const (
  2. CategoryBooks = 10 * iota //0
  3. CategoryHealth //10
  4. CategoryClothing //20
  5. )

公式在中途也可以变化,iota的值不会中断

  1. const (
  2. CategoryBooks = 10 * iota //0
  3. CategoryHealth //10
  4. CategoryClothing //20
  5. a = 100 * iota //300
  6. b //400
  7. c //500
  8. )

注意,iota只能在const中使用

函数

  1. // 普通函数
  2. func foo1(x int, y int) int {
  3. return x + y
  4. }
  5. func foo2() {
  6. fmt.Printf("\"hello\": %v\n", "hello")
  7. }
  8. // 多返回值函数
  9. func foo3(x, y int) (int, string) {
  10. return 9, "asdad"
  11. }
  12. // 给返回值命名
  13. func foo4() (r1 int, r2 string) {
  14. fmt.Printf("r1: %v\n", r1) //0
  15. fmt.Printf("r2: %v\n", r2) //""
  16. //说明已经命名的返回值就是一个局部变量,具有默认值
  17. r1 = 9
  18. r2 = "qwqw"
  19. return
  20. }
  21. func main() {
  22. a := foo1(1, 2)
  23. fmt.Printf("a: %v\n", a)
  24. foo2()
  25. c, d := foo3(1, 2)
  26. fmt.Printf("c: %v\n", c)
  27. fmt.Printf("d: %v\n", d)
  28. e, f := foo4()
  29. fmt.Printf("e: %v\n", e)
  30. fmt.Printf("f: %v\n", f)
  31. }

init函数和导包过程 

 lib1:

  1. package lib1
  2. import (
  3. "fmt"
  4. "lib2"
  5. )
  6. func init() {
  7. fmt.Println("lib1 init")
  8. lib2.Lib2_test()
  9. }
  10. func Lib1_test() {
  11. fmt.Println("lib1_test")
  12. }

lib2:

  1. package lib2
  2. import "fmt"
  3. func init() {
  4. fmt.Println("lib2 init")
  5. }
  6. func Lib2_test() {
  7. fmt.Println("lib2_test")
  8. }

main:

  1. package main
  2. import (
  3. "fmt"
  4. "lib1"
  5. )
  6. func main() {
  7. fmt.Print("main")
  8. lib1.Lib1_test()
  9. // lib2 init
  10. // lib1 init
  11. // lib2_test
  12. // mainlib1_test
  13. }

值得注意的是,包中的函数必须是大写字母开头的,这样这个函数才能在包外被调用,可以看成是java中public的一种形式

 在golang中,如果导入了包,却不调用包的方法,会报错

但是如果我只想执行包的init函数却不使用包中的方法,可以使用匿名包,这样不能调用包的方法,但是会执行init

  1. import (
  2. "fmt"
  3. _ "lib1"
  4. )
  5. func main() {
  6. fmt.Print("main")
  7. }

可以给包起别名

  1. import (
  2. "fmt"
  3. mylib1 "lib1"
  4. )
  5. func main() {
  6. fmt.Print("main")
  7. mylib1.Lib1_test()
  8. }

指针

跟c语言一样  

  1. package main
  2. //导入包
  3. import "fmt"
  4. func changevalue(p *int) { //申明 p 为指针型变量,指向一个int型的变量,p本身存储的是这个int变量的地址
  5. *p = *p * 10 //*p 表示指向的这个变量本身
  6. }
  7. func main() {
  8. a := 10
  9. changevalue(&a) //&a表示a的地址
  10. fmt.Printf("a: %v\n", a) //100
  11. }
  1. package main
  2. //导入包
  3. import "fmt"
  4. func changevalue(a *int, b *int) {
  5. temp := *a
  6. *a = *b
  7. *b = temp
  8. }
  9. func main() { //大括号必须和函数在同一行
  10. a := 10
  11. b := 20
  12. changevalue(&a, &b)
  13. fmt.Printf("a: %v\n", a) //20
  14. fmt.Printf("b: %v\n", b) //10
  15. }

二级指针,指向指针的指针

  1. package main
  2. //导入包
  3. import (
  4. "fmt"
  5. )
  6. // 普通函数
  7. func main() {
  8. a := 10
  9. var p *int = &a
  10. var p1 **int = &p
  11. fmt.Println(&p) //0xc000070030
  12. fmt.Println(p1) //0xc000070030
  13. }

defer

在当前函数体的最后运行

  1. func main() {
  2. defer fmt.Println("main end")
  3. fmt.Printf("\"alalal\": %v\n", "alalal")
  4. // "alalal": alalal
  5. // main end
  6. }

如果存在多个defer,则遵循 栈 的原则

  1. func main() {
  2. defer fmt.Println("main end1")
  3. defer fmt.Println("main end2")
  4. fmt.Printf("\"alalal\": %v\n", "alalal")
  5. // "alalal": alalal
  6. // main end2
  7. // main end1
  8. }

在函数调用中,defer在return之后,这个很好理解,因为defer应该在函数体最后执行,最先入栈,在return之后

  1. func returnfunc() int {
  2. fmt.Println("return end")
  3. return 0
  4. }
  5. func testfunc() int {
  6. defer fmt.Println("func end")
  7. return returnfunc()
  8. }
  9. func main() {
  10. testfunc()
  11. // return end
  12. // func end
  13. }

数组和动态数组

固定长度数组

  1. func main() {
  2. var myarray [10]int
  3. myarray2 := [10]int{1, 2, 3, 4}
  4. }

固定长度数组需要指定数组长度,且有默认值

当固定长度数组作为形参时,进行的是值传递,而不是引用传递

  1. func change_array(myarry [4]int) {
  2. myarry[0] = 100
  3. }
  4. func main() {
  5. myarray2 := [4]int{1, 2, 3, 4}
  6. change_array(myarray2)
  7. //myarray2的值不改变
  8. }

而且当函数形参是固定长度数组时,传入其他长度的数组也不可以,非常的不方便

遍历数组

  1. func main() {
  2. var myarray [10]int
  3. myarray2 := [10]int{1, 2, 3, 4}
  4. for i := 0; i < len(myarray); i++ {
  5. fmt.Printf("myarray[i]: %v\n", myarray[i])
  6. }
  7. for index, value := range myarray2 {
  8. fmt.Printf("index:%d,value:%d\n", index, value)
  9. }
  10. }

 也可以只遍历下标:

  1. myarray2 := [10]int{1, 2, 3, 4}
  2. for index := range myarray2 {
  3. fmt.Printf("index:%d\n", index)
  4. }

动态数组

也叫  切片

  1. func change_array(myarry []int) {
  2. myarry[0] = 100
  3. }
  4. func main() {
  5. myarray2 := []int{1, 2, 3, 4}
  6. fmt.Printf("%T \n", myarray2) //[]int
  7. change_array(myarray2)
  8. for index, value := range myarray2 {
  9. fmt.Printf("index:%d,value:%d\n", index, value)
  10. } //myarray2的值改变
  11. }

多种声明方式

  1. func main() {
  2. slice1 := []int{}
  3. slice1 = make([]int, 3) //开辟三个空间
  4. fmt.Printf("len(slice): %d, slice1: %v\n", len(slice1), slice1)
  5. // len(slice): 3, slice1: [0 0 0]
  6. slice2 := make([]int, 4)
  7. fmt.Printf("len(slice): %d, slice2: %v\n", len(slice2), slice2)
  8. // len(slice): 4, slice2: [0 0 0 0]
  9. }

追加元素:

  1. func main() {
  2. slice1 := []int{}
  3. fmt.Printf("len(slice): %d, cap: %d, slice1: %v\n", len(slice1), cap(slice1), slice1)
  4. // len(slice): 0, cap: 0, slice1: []
  5. slice1 = append(slice1, 2)
  6. fmt.Printf("len(slice): %d, cap: %d, slice1: %v\n", len(slice1), cap(slice1), slice1)
  7. // len(slice): 1, cap: 1, slice1: [2]
  8. }

len 和 cap

这里需要说一下 len 和cap

  1. func main() {
  2. slice1 := make([]int, 3)
  3. fmt.Printf("len(slice): %d, cap: %d, slice1: %v\n", len(slice1), cap(slice1), slice1)
  4. // len(slice): 3, cap: 3, slice1: [0 0 0]
  5. slice1 = append(slice1, 2)
  6. fmt.Printf("len(slice): %d, cap: %d, slice1: %v\n", len(slice1), cap(slice1), slice1)
  7. // len(slice): 4, cap: 6, slice1: [0 0 0 2]
  8. }

当我们为其开辟是空间3时,其默认的cap也是3,cap是系统为数组一次性开辟空间的大小

当我再次加入元素时,长度变为了4,但是cap变成了6,也就是说在加入时自动增加了三个大小的空间

当然我们也可以指定cap

  1. func main() {
  2. slice1 := make([]int, 3, 5)
  3. fmt.Printf("len(slice): %d, cap: %d, slice1: %v\n", len(slice1), cap(slice1), slice1)
  4. // len(slice): 3, cap: 3, slice1: [0 0 0]
  5. slice1 = append(slice1, 2)
  6. fmt.Printf("len(slice): %d, cap: %d, slice1: %v\n", len(slice1), cap(slice1), slice1)
  7. // len(slice): 4, cap: 6, slice1: [0 0 0 2]
  8. slice1 = append(slice1, 2)
  9. fmt.Printf("len(slice): %d, cap: %d, slice1: %v\n", len(slice1), cap(slice1), slice1)
  10. // len(slice): 5, cap: 5, slice1: [0 0 0 2 2]
  11. slice1 = append(slice1, 2)
  12. fmt.Printf("len(slice): %d, cap: %d, slice1: %v\n", len(slice1), cap(slice1), slice1)
  13. // len(slice): 5, cap: 5, slice1: [0 0 0 2 2]
  14. }

当然只有在增加元素超过了len时,才会使用cap增加空间

截取

截取方法和python一样,值得注意的是,这里的截取都是浅拷贝

  1. func main() {
  2. s := []int{1, 2, 3}
  3. s1 := s[0:2]
  4. s1[0] = 100
  5. fmt.Printf("s1: %v\n", s1)
  6. fmt.Printf("s: %v\n", s)
  7. // s1: [100 2]
  8. // s: [100 2 3]
  9. }

如果想实现深拷贝:

  1. func main() {
  2. s := []int{1, 2, 3}
  3. s1 := make([]int, 2)
  4. copy(s1, s)
  5. s1[0] = 100
  6. fmt.Printf("s1: %v\n", s1)
  7. fmt.Printf("s: %v\n", s)
  8. // s1: [100 2]
  9. // s: [1 2 3]
  10. }

切片的追加

  1. slice := make([]int, 2, 4)
  2. append_slice := []int{1, 2}
  3. fmt.Printf("slice addr:%p len:%d cap:%d slice:%v\n", slice, len(slice), cap(slice), slice)
  4. slice = append(slice, append_slice...)
  5. fmt.Printf("slice addr:%p len:%d cap:%d slice:%v\n", slice, len(slice), cap(slice), slice)
  6. slice = append(slice, 10, 20)
  7. fmt.Printf("slice addr:%p len:%d cap:%d slice:%v\n", slice, len(slice), cap(slice), slice)
  8. slice = append(slice, 30, 40, 50)
  9. fmt.Printf("slice addr:%p len:%d cap:%d slice:%v\n", slice, len(slice), cap(slice), slice)
  10. //output
  11. slice addr:0xc0420540a0 len:2 cap:4 slice:[0 0]
  12. slice addr:0xc0420540a0 len:4 cap:4 slice:[0 0 1 2]
  13. slice addr:0xc04207a080 len:6 cap:8 slice:[0 0 1 2 10 20]
  14. slice addr:0xc04208a000 len:9 cap:16 slice:[0 0 1 2 10 20 30 40 50]

 删除等操作可以查看:

初识go语言之 数组与切片(创建,遍历,删除,插入,复制)_golang创建数组-CSDN博客

map

四种声明方式

声明方式1:

  1. var mymap map[int]string
  2. if mymap == nil {
  3. fmt.Print("mymap is empty\n")
  4. }
  5. // mymap is empty

这种情况下给map增加键值对是非法的,因为此时mymap的容量为0

但是如果是下面这种是可以的,因为默认分配了空间:

  1. var mymap1 = map[int]string{}
  2. mymap1[0] = "awew"

声明方式2:

可以使用make申请空间,且这个空间是动态可以扩展的

  1. mymap2 := make(map[int]string, 1)
  2. mymap2[1] = "aa"
  3. mymap2[2] = "aa"
  4. fmt.Printf("mymap2: %v\n", mymap2)

声明方式3:

不主动申请空间,但是其默认是有空间的

  1. mymap3 := make(map[int]string)
  2. mymap3[2] = "sad"
  3. fmt.Printf("mymap3: %v\n", mymap3)

声明方式4:

  1. mymap4 := map[int]string{
  2. 1: "aa",
  3. 2: "bb",
  4. 3: "cc",
  5. }
  6. mymap4[4] = "ee"
  7. fmt.Printf("mymap4: %v\n", mymap4)

遍历map

  1. mymap4 := map[int]string{
  2. 1: "aa",
  3. 2: "bb",
  4. 3: "cc",
  5. }
  6. mymap4[4] = "ee"
  7. for key, value := range mymap4 {
  8. fmt.Printf("key: %v\n", key)
  9. fmt.Printf("value: %v\n", value)
  10. }
  11. // key: 1
  12. // value: aa
  13. // key: 2
  14. // value: bb
  15. // key: 3
  16. // value: cc
  17. // key: 4
  18. // value: ee

删除

  1. mymap4 := map[int]string{
  2. 1: "aa",
  3. 2: "bb",
  4. 3: "cc",
  5. }
  6. mymap4[4] = "ee"
  7. fmt.Printf("mymap4: %v\n", mymap4)
  8. delete(mymap4, 4)
  9. fmt.Printf("mymap4: %v\n", mymap4)
  10. // mymap4: map[1:aa 2:bb 3:cc 4:ee]
  11. // mymap4: map[1:aa 2:bb 3:cc]

查看键是否存在

  1. func main() {
  2. m := make(map[string]int)
  3. m["张三"] = 10
  4. m["李四"] = 20
  5. value, ok := m["张四"]
  6. if ok {
  7. fmt.Println(value)
  8. } else {
  9. fmt.Println("查无此人")
  10. }
  11. }

当map作为形参时,也是引用传递 

golang的map-CSDN博客

Golang底层原理剖析之map_golang map底层-CSDN博客

结构体

这里的结构体可以看出是  类 

声明

  1. type Book struct {
  2. name string
  3. price int
  4. }
  5. func main() {
  6. var mathbook Book
  7. mathbook.name = "lalal"
  8. mathbook.price = 12
  9. fmt.Printf("mathbook: %v\n", mathbook)
  10. // mathbook: {lalal 12}
  11. }

作为形参

  1. type Book struct {
  2. name string
  3. price int
  4. }
  5. func changebook(book Book) {
  6. book.price = 100
  7. }
  8. func changebook2(book *Book) {
  9. book.price = 100
  10. }
  11. func main() {
  12. var mathbook Book
  13. mathbook.name = "lalal"
  14. mathbook.price = 12
  15. changebook(mathbook)
  16. fmt.Printf("mathbook: %v\n", mathbook)
  17. // mathbook: {lalal 12}
  18. changebook2(&mathbook)
  19. fmt.Printf("mathbook: %v\n", mathbook)
  20. // mathbook: {lalal 100}
  21. }

这说明作为形参传递是 值传递,而不是引用传递

方法

结构体方法的定义:

  1. type Book struct {
  2. name string
  3. price int
  4. }
  5. func (this Book) show() {
  6. fmt.Printf("this.name: %v\n", this.name)
  7. fmt.Printf("this.price: %v\n", this.price)
  8. }
  9. func (this Book) changeprice(new_price int) {
  10. this.price = new_price
  11. }
  12. func (this *Book) changeprice1(new_price int) {
  13. this.price = new_price
  14. }
  15. func main() {
  16. var mathbook Book
  17. mathbook.name = "lalal"
  18. mathbook.price = 12
  19. mathbook.show()
  20. // mathbook: {lalal 12}
  21. mathbook.changeprice(112)
  22. mathbook.show()
  23. mathbook.changeprice1(112)
  24. mathbook.show()
  25. // this.name: lalal
  26. // this.price: 12
  27. // this.name: lalal
  28. // this.price: 12
  29. // this.name: lalal
  30. // this.price: 112
  31. }

当调用mathbook.changeprice()时相当于changeprice(mathbook),实参和行参都是类型 Book,可以接受。此时在changeprice()中的t只是参数t的值拷贝,所以method1()的修改影响不到main中的t变量。

当调用mathbook.changeprice1()=>changeprice1(mathbook),这是将 Book 类型传给了 *Book 类型,go可能会取 mathbook 的地址传进去:changeprice1(&mathbook)。所以 changeprice1() 的修改可以影响 mathbook

由于结构体作为形参是指传递,所以在改变参数时务必使用 地址传参

封装

在go中的struct中,类名、属性名、方法名大写表示对外可以访问,也就是java中的public

 继承

  1. type Person struct {
  2. name string
  3. sex string
  4. }
  5. func (this *Person) Eat() {
  6. fmt.Print("person eat")
  7. }
  8. type Super_man struct {
  9. Person //继承Person类
  10. level int
  11. }
  12. func (this *Super_man) Eat() { //重写父类方法
  13. fmt.Print("super_man eat")
  14. }
  15. func (this *Super_man) Fly() { //定义新的方法
  16. fmt.Print("super_man fly")
  17. }
  18. func main() {
  19. //两种声明对象的方式
  20. super_man1 := Super_man{Person{"lee", "male"}, 2}
  21. var super_man2 Super_man
  22. super_man2.name = "lee"
  23. super_man2.sex = "male"
  24. super_man2.level = 2
  25. super_man1.Fly()
  26. super_man2.Fly()
  27. }

多态

  1. type Animal interface {
  2. Sleep()
  3. GetColor() string
  4. }
  5. type Cat struct {
  6. color string
  7. }
  8. func (this *Cat) Sleep() {
  9. fmt.Print("CAT IS SLEEPING\n")
  10. }
  11. func (this *Cat) GetColor() string {
  12. return this.color
  13. }
  14. type Dog struct {
  15. color string
  16. }
  17. func (this *Dog) Sleep() {
  18. fmt.Print("Dog IS SLEEPING\n")
  19. }
  20. func (this *Dog) GetColor() string {
  21. return this.color
  22. }
  23. func main() {
  24. var animal Animal
  25. animal = &Cat{"blue"}
  26. fmt.Printf("animal.GetColor(): %v\n", animal.GetColor())
  27. animal.Sleep()
  28. // animal.GetColor(): blue
  29. animal = &Dog{"black"}
  30. fmt.Printf("animal.GetColor(): %v\n", animal.GetColor())
  31. animal.Sleep()
  32. // animal.GetColor(): blue
  33. // CAT IS SLEEPING
  34. // animal.GetColor(): black
  35. // Dog IS SLEEPING
  36. }

上面这部分代码定义了一个接口  Animal,其中定义了2个方法

Cat和Dog类只需要完全实现这2个方法(也可以有接口中没有定义的方法), 就可以实现和Animal接口的多态,可以用鸭子类型理解

为了更加直观的实现多态:

  1. func animal_is_sleeping(animal Animal) {
  2. animal.Sleep()
  3. }
  4. func main() {
  5. mycat := Cat{"blue"}
  6. animal_is_sleeping(&mycat)
  7. // CAT IS SLEEPING
  8. }

在golang中,只能借助接口实现多态,使用父类是无法实现多态的,当然,如果B的父类是A,而A实现了接口C的方法,那么B也可以与C形成多态

interface{}

interface接口可以跟所有的类形成多态:

  1. func myshow(arg interface{}) {
  2. fmt.Println(arg)
  3. }
  4. func main() {
  5. myshow(12)
  6. myshow("asdas")
  7. myshow([]int{1, 2, 3})
  8. myshow(map[int]int{1: 2, 2: 3})
  9. // 12
  10. // asdas
  11. // [1 2 3]
  12. // map[1:2 2:3]
  13. }

类型断言

可以判断接口现在对应的类是哪个:

  1. var myanimal Animal
  2. myanimal = &Cat{"blue"}
  3. value, ok := myanimal.(*Cat)
  4. fmt.Printf("ok: %v\n", ok)
  5. fmt.Printf("value: %v\n", value)
  6. value2, ok2 := myanimal.(*Dog)
  7. fmt.Printf("ok: %v\n", ok2)
  8. fmt.Printf("value: %v\n", value2)
  9. // ok: true
  10. // value: &{blue}
  11. // ok: false
  12. // value: <nil>

Go Struct超详细讲解 - 掘金

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/453737
推荐阅读
相关标签
  

闽ICP备14008679号