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))
}
输出结果: