Golang派生类型

  • (指针类型(Pointer)
  • 数组类型 (Arrayllist)
  • 结构化类型 (Struct)
  • 通道类型 (Channel )
  • 函数类型(Func)
  • 切片类型 (Slice)
  • 接口类型(Interface)
  • Map 类型

指针 Pointer

声明指针

1
var Name *Type
NameType
&
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main  

import "fmt"

func main() {
var a int = 10

fmt.Println(a)

var b *int

b = &a //指针b 指向 a 的地址

*b = 100 //将 b 指向的内存地址赋为 100
fmt.Println(a, *b) //输出 a 和 指针b 指向的值
fmt.Println(a == *b) //查看 a 和 *b 指向的值是否相等
fmt.Println(&a == b) //查看 a 的地址和 b的指针地址是否相等
}

输出结果:

1
2
3
4
10
100 100
true
true

图解:

数组 Arrayllist

数组的声明

数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。

1
var name [size] type
name
size
type
1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {

var Arrayllist [5] int //定义长度为 3 ,数据类型为int ,名为 Arrayllist 的数组

for i := 0; i < len(Arrayllist); i++ { //遍历数组
fmt.Println("Arrayllist[", i, "]: ", Arrayllist[i]) //输出
}

}

输出结果:

数组初始化

  • 静态初始化:

给出初始化值,由系统决定长度

1
2
3
var Arrayllist = [...]int8{1, 2, 3, 4, 5,6,7,8,9,10}
//或者
Arrayllist := [...]int8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  • 动态初始化:

只指定长度,由系统给出初始化值

1
2
3
var Arrayllist = [5] int8 {1,2,3,4,5}
//或者
Arrayllist := [5] int8 {1,2,3,4,5}

遍历数组

可以使用for循环来遍历数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {

var Arrayllist [5] int //定义长度为 3 ,数据类型为int ,名为 Arrayllist 的数组

for i := 0; i < len(Arrayllist); i++ { //遍历数组
fmt.Println("Arrayllist[", i, "]: ", Arrayllist[i]) //输出
}

}

切片 Slice

Go 语言切片(Slice)是对数组的抽象。

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

可以理解为切片是一个有着自动扩容功能的数组。

切片的声明

切片的声明和数组类似,只不过不需要定义长度。

1
var name []type  //声明切片
name
type
make
1
2
3
4
5
6
7
8
9
var name []int = make([]type, len)

//或者

var name = make([]type, len)

//或者

name := make([]type, len)
len
capacity
1
make([]type, len, capacity)

切片的初始化

直接初始化

1
var Silce = []int{1, 2, 3, 4, 5}

截取数组进行初始化

1
2
3
4
var ArrayList = [5]int{1, 2, 3, 4, 5} //声明数组 ArrayList

// ArrayList[startIndex:endIndex]
var Silce []int = ArrayList[0:4] //声明Silce,Silce的值是截取数组,第一个到第四个

startIndex : 开始下标(左包含)

endIndex : 结束下标,但不包含结束下标对应的值(右不包含)

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
var ArrayList = [5]int{1, 2, 3, 4, 5} //声明数组 ArrayList

var Silce []int = ArrayList[0:4] //声明Silce,Silce的值是截取数组,第一个到第四个

fmt.Println(Silce) //输出切片

}

输出结果:

遍历切片

和数组一样,切片也是可以使用for循环遍历的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
var ArrayList = [5]int{1, 2, 3, 4, 5} //声明数组 ArrayList

var Silce []int = ArrayList[0:4] //声明Silce,Silce的值是截取数组,第一个到第四个

for i := 0; i < len(Silce); i++ { //遍历切片
fmt.Println(Silce[i])
}

}

补充

空切片

nil

append()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
var Silce_A []int
Silce_A = append(Silce_A, []int{1, 2, 3, 4, 5}...) //使用append为空切片添加元素

if Silce_A == nil { //条件判断

fmt.Println("len(Silce_A) =", len(Silce_A)) //输出切片长度

} else {

fmt.Println("len(Silce_A) =", len(Silce_A))

}
}

输出结果:

append函数 和 copy 函数

copy 来拷贝切片

append 函数用来给切片添加新元素

copy函数进行的拷贝是深拷贝的方式,即创建新的内存地址用于存放复制的对象。
两数组相互独立,不受影响

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import "fmt"

func main() {

var Silce = []int{1, 2, 3}

Silce = append(Silce, 4) //append

fmt.Println("Silce =", Silce)

var SilceCopy = make([]int, len(Silce))

copy(SilceCopy, Silce) //copy

if SilceCopy == nil {
fmt.Println("SilceCopy is nil")
} else {
fmt.Println("SilceCopy = ", SilceCopy)
}

fmt.Println("-------------------------")

Silce = append(Silce, 5) //给切片Silce添加元素

fmt.Println("Silce =", Silce) //输出
fmt.Println("SilceCopy = ", SilceCopy)

}

输出结果:

使用append来实现删除切片元素

Go中没有提供专门删除元素的函数,而是通过切片本身的特点来删除元素。

即以被删除元素为分界点,再利用append将前后两个部分的内存重新连接起来。

1
name = append(name[:index], name[index+1:]...)

index : 要删除的元素下标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {

var Silce = []int{1, 2, 3, 4, 5}

//Silce[:3]只能取到下标为0 1 2 的元素,取不到下标为 3 的元素
//而Silce[4:]... 能取到包含下标为 4 的元素,实际效果就是下标前移
//实现删除的效果
Silce = append(Silce[:3], Silce[4:]...) //删除下标为3的元素

fmt.Println(Silce)

}

len函数和cap函数

切片是可索引的,并且可以由 len() 方法获取长度。

切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
var ArrayList = [10]int{1, 2, 3, 4, 5, 6, 7, 8} //声明数组ArrayList长度为10

var Silce []int = ArrayList[0:4] //声明Silce,Silce的值是截取数组,第一个到第四个

fmt.Println("len(Silce) = ", len(Silce)) //获取长度

fmt.Println("cap(Silce) = ", cap(Silce)) //切片最长可以达到多少

}

输出结果:

可见如果是截取数组的切片,长度和截取的原数组长度有关。

如果是声明并且初始化的数组,最大长度是初始长度,而使用append函数添加后,最大长度会自动变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import "fmt"

func main() {

var Silce = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

fmt.Println("len(Silce) = ", len(Silce)) //声明并初始化的切片长度

fmt.Println("cap(Silce) = ", cap(Silce)) //声明并初始化的切片最大长度

Silce = append(Silce, []int{11, 12, 13, 14, 15}...)

fmt.Println("len(Silce)Append = ", len(Silce)) //使用Append添加一次后的切片长度

fmt.Println("cap(Silce)Append = ", cap(Silce)) //使用Append添加一次后的切片最大长度

Silce = append(Silce, []int{16, 17, 18, 19, 20, 21}...)

fmt.Println("len(Silce)Append2 = ", len(Silce)) //使用Append添加第二次次后的切片长度

fmt.Println("cap(Silce)Append2 = ", cap(Silce)) //使用Append添加第二次后的切片最大长度

}

输出结果:

MAP

key : vlaue

map的特点就是类似与Python的字典,,按照key来找到对应的value 。key指向数据的值。

Map的声明

Go语言的map同样是可是使用make来声明的。

1
2
3
4
5
6
7
8
9
var Name [KeyType]ValueType  //声明map, 此时声明的map是nil map,不存在初始值

//或者

var Name = make(map[KeyType]ValueType)

//或者

var Name = map[KeyType]ValueType{key:value,key:value} //这种要静态初始化
Name
KeyType
ValueType

Map的初始化

map的初始化和数组类似。

var Name [KeyType]ValueType

就需要make函数来创建一个非空的map。这样才可以赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
var Map map[int]int

Map = make(map[int]int) //make函数来创建一个非空的map,赋值。

for i := 0; i < 5; i++ {
Map[i] = i
}
fmt.Println("Map: ", Map)

}

输出结果:

当然也可以利用这个来做个判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import "fmt"

func main() {

var Map = make(map[int]int)

for i := 0; i < 5; i++ {
Map[i] = i
}

Value, err := Map[5]
if err == true { //如果能取到kKey为5的值,err为true,否则为false
fmt.Println("Value: ", Value)
} else {
fmt.Println("不存在该键")
}

fmt.Println("Map: ", Map)

}

遍历Map

for range
  • 遍历Key
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func main() {

var Map = make(map[string]int)

Map["一"] = 1
Map["二"] = 2
Map["三"] = 3
Map["四"] = 4
Map["五"] = 5

for key := range Map {
// 使用for range 遍历key
fmt.Println(key)
}

}

输出结果:

由于内部实现是哈希值的原因,map不像切片或者数组有序,不存在下标。

  • 遍历Value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {

var Map = make(map[string]int)

Map["一"] = 1
Map["二"] = 2
Map["三"] = 3
Map["四"] = 4
Map["五"] = 5

for _, value := range Map { //稍微修改以下即可,key直接用匿名变量
fmt.Println(value)
}

}

输出结果:

  • 遍历Key,Value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {

var Map = make(map[string]int)

Map["一"] = 1
Map["二"] = 2
Map["三"] = 3
Map["四"] = 4
Map["五"] = 5

for key, value := range Map {
fmt.Println(key, ": ", value)
}

}

输出结果:

删除元素

elete() 函数用于删除集合的元素, 参数为 map 和其对应的 key。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func main() {

var Map = make(map[int]int)
for i := 0; i < 5; i++ {
Map[i] = i
}

delete(Map, 0) //删除key 为 0的元素
delete(Map, 1)//删除key 为 1的元素
delete(Map, 2)//删除key 为 2的元素

for key, value := range Map {
fmt.Println(key, ": ", value) //遍历输出
}

}

输出结果:

其他

不知道是不是个例,我如果把map类型改为 [int]int , 然后遍历key或者value会报错。报错原因是因为window安全中心检测到了病毒。这时候要把实时检测关掉来运行程序,或者卸载Goland重装一遍。

结构体 Struct

golang有个非常重要的关键字 struct ,也是golang的一个特色.

结构体特点

结构体是一个可以存储不同数据类型的数据类型,
声明完后,它可以和数据类型一样,也有指针值等等.

定义结构体

1
2
3
4
5
6
7
type Person struct {  //Person结构体
Age int //参数
Name string
Job string
Sex bool
}

声明结构体

结构体可以作为变量声明使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main  

import "fmt"

type Person struct {
Age int
Name string
Job string
Sex bool
}

func main() {
var person Person //创建Person对象
person.Age = 20 //各参数赋值
person.Name = "LiMing"
person.Job = "Student"
person.Sex = true

fmt.Println(person) //输出变量

}

输出结果:

1
{20 LiMing Student true}

当然结构体也是可以隐式声明的,类似JAVA语言的实例化

隐式声明有两种方式

1
2
3
4
5
6
person2 := Person{ //隐式声明  
Age: 40,
Name: "Danny",
Job: "Worker",
Sex: false,
}
1
person3 := Person{50, "Jenny", "Teacher", true} //隐式声明

注意第二个种声明必须按照顺序给定参数.

完整代码 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main  

import "fmt"

type Person struct {
Age int
Name string
Job string
Sex bool
}

func main() {
var person Person //创建Person对象
person.Age = 20 //各参数赋值
person.Name = "LiMing"
person.Job = "Student"
person.Sex = true

fmt.Printf("person: %v\n", person) //输出变量

person2 := Person{ //隐式声明
Age: 40,
Name: "Danny",
Job: "Worker",
Sex: false,
}

fmt.Printf("person2: %v\n", person2)

person3 := Person{50, "Jenny", "Teacher", true} //隐式声明2

fmt.Printf("person3: %v\n", person3)

}

输出结果:

1
2
3
person: {20 LiMing Student true}
person2: {40 Danny Worker false}
person3: {50 Jenny Teacher true}

默认声明会给定初始值

1
2
3
4
5
6
def := Person{  
Age: 0,
Name: "",
Job: "",
Sex: false,
}

以及一种特殊的声明方法:

1
nul := new(Person) 
1
nul: &{0   false}

不过这个出来的直接就是带有地址的.

访问结构体

由于 struct 是一种特殊的数据类型,他也可以当做一个数据类型用在函数中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main  

import "fmt"

type Person struct {
Age int
Name string
Job string
Sex bool
}

func main() {

person2 := Person{ //隐式声明
Age: 40,
Name: "Danny",
Job: "Worker",
Sex: false,
}

Test(person2)
}

func Test(person Person) {
fmt.Println(person.Name)
}

输出结果:

1
Danny

接口 Interface

接口的定义: 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。

接口的使用

声明接口

1
2
3
4
5
6

type Person interface { //声明接口
Say() //定义方法
Listen()
}

实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type Person interface { //声明接口  
Say()
Listen()
}

type person1 struct { //声明新结构体
Name string
Age int
}

func (p person1) Say() { //实现接口Say
fmt.Println(p.Name, "说了一句话")
}

func (p person1) Listen() { //实现接口Listen
fmt.Println(p.Name, "在听")
}

简单使用

通过接口实现实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main  

import "fmt"

type Person interface { //声明接口
Say()
Listen()
}

type person1 struct {
Name string
Age int
}

func (p person1) Say() { //实现接口
fmt.Println(p.Name, "说了一句话")
}

func (p person1) Listen() {
fmt.Println(p.Name, "在听")
}

func main() {
var P Person //接口类型

p2 := person1{ //实例
Name: "person2",
Age: 10,
}

P = p2

P.Say() //通过接口实现实例内部的函数
P.Listen()

}

输出结果:

1
2
person2 说了一句话
person2 在听

注意的是,只要实现了接口的结构体,均可使用此方法来调用实例的函数,但是通过接口的话无法调用到结构体的内容.

通过接口实现泛型效果

1
2
3
func Fun(inf interface{}) { //定义函数,将类型设为空接口,即可实现  

}

在方法中使用接口

这样的话,只用将符合接口的实例传入即可调用接口的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

package main

import "fmt"

type Person interface { //声明接口
Say()
Listen()
}

type person1 struct {
Name string
Age int
}

func (p person1) Say() { //实现接口
fmt.Println(p.Name, "说了一句话")
}

func (p person1) Listen() {
fmt.Println(p.Name, "在听")
}

func main() {
var P Person

p2 := person1{
Name: "person2",
Age: 10,
}

P = p2

P.Say()
P.Listen()

p3 := person1{
Name: "person3",
Age: 11,
}

Fun(p3)

}

func Fun(per Person) { //定义函数,将类型设为空接口,即可实现
per.Say()
per.Listen()
}

输出结果:

1
2
3
4
person2 说了一句话
person2 在听
person3 说了一句话
person3 在听

其他

Go语言接口 和 JAVA接口 的区别
java 实现结果需要implement 关键词来指定实现具体接口,
而golang的话只需要与某个接口中的某个方法名字相同即可,golang更加松散.

通道 Channel

Channel的主要使用是在Golang 的并发中:
相关笔记:Golang并发编程

函数 Func

何为函数

函数是基本的代码块,用于执行一个任务。

Go 语言最少有个 main() 函数。

你可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务。

函数声明告诉了编译器函数的名称,返回类型,和参数。

函数的定义和调用

函数定义

Go语言定义函数的语法:

1
2
3
4
5
func function_name( [parameter list] ) [return_types] {
函数体

[return return_Numbers]
}
func
function_name
parameter list
return_types

函数体:函数定义的代码集合。

1
2
3
4
5
6
func PrintHello() { //打印Hello的函数

var String = "Hello"
fmt.Println(String)

}
函数调用

当创建函数时,你定义了函数需要做什么,通过调用该函数来执行指定任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import "fmt"

func main() {
PrintHello() //调用PrintHello函数

fmt.Println(PrintHelloWorld()) //调用PrintHelloWorld函数


fmt.Println(Add(10, 10)) //调用求和函数
}

func PrintHello() { //PrinrHello函数,无参,无返回值

var String = "Hello"
fmt.Println(String)


}

func PrintHelloWorld() string { //PrintHelloWorld函数,无参,有返回值

var String = "Hello,World!"
return String

}

func Add(X int, Y int) int { //求和函数,有参,有返回值
Res := X + Y
return Res
}

运行结果:

函数类型

使用Type关键字可以定义函数类型。

在Go语言中,type可以定义任何自定义的类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import "fmt"

func main() {
type T1 func(int, int) int //使用type关键字,定义T1类型
var F T1 = Add //声明T1类型的变量F ,将Add函数赋给F
f1 := F(1, 4)
fmt.Println(f1)

var F2 T1 = subtract
f2 := F2(10, 40)
fmt.Println(f2)

fmt.Println(f1 + f2)

}

func Add(X int, Y int) int { //Add函数
Res := X + Y
return Res
}

func subtract(X int, Y int) int { //subtract函数
Res := X - Y
return Res
}

输出结果:

高阶函数

函数作为参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
PrintHello("Sam", SayHello)

}

func SayHello(name string) string {
var Hello string = name + " Hello"
fmt.Println("func SayHello: ", Hello)
return Hello
}

func PrintHello(name string, f func(string2 string) string) {
fmt.Println("func PrintHello: ", name, f(name))
}

输出结果:

函数作为返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"strconv"
)

func main() {
fmt.Println(Add(10, 20))

}

func Add(x int, y int) string {
var Res = x + y
return ToString(Res)
}

func ToString(z int) string {
var Res = strconv.Itoa(z) //使用 strconv.Itoa()将整型数字转为字符串数字
return Res
}

匿名函数

Go语言中的函数无法实现函数的嵌套,但是可以通过匿名函数来实现相似的效果。

匿名函数效果类似Python中的lamda

格式:

1
2
3
func(参数列表) 返回值 { 
函数体
}
1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
Add := func(X int, Y int) int { //匿名函数
return X + Y
}

A := Add(1, 2) //调用匿名函数
fmt.Println("var A =", A) //输出
}

输出结果:

var A = 3

其他

strconv.Itoa() 和 string()的区别

string()是直接整数类型的数字转为ASCII码值等于该整形数字的字符。

而strconv.Itoa() 是转换成对应的字符串类型的数字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
"strconv"
)

func main() {

var Number = 40

fmt.Println(Number)
fmt.Printf("%T\n", Number)
fmt.Println("------------------")

fmt.Println(string(Number))
fmt.Printf("%T\n", string(Number))

fmt.Println("------------------")

fmt.Println(strconv.Itoa(Number))
fmt.Printf("%T\n", strconv.Itoa(Number))

}

输出结果: