十一:数组和切片
不允许混合不同类型的元素
a:slice可以向后扩展,不可以向前扩展
b:s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)
c:添加元素时如果超越cap,系统会重新分配更大的底层数组
d:由于值传递的关系,必须接收append的返回值
e:s = append(s,val)
1、数组的声明
var a [3]int 声明一个长度为3的整型数组,数组中的所有元素都被自动赋值为数组类型的零值
a := [3]int{12,13,19}
a := [...]int{12,24,25}
数组的大小是类型的一部分,数组不能调整大小
2、数组是值类型
go中的数组是值类型而不是引用类型
3、数组的长度
len(a)
4、使用range迭代数组
for i, v := range a {
}
for _, v := range a {
}
5、多维数组
a := [2][2]string{
{'dd', "dsd"},
{"sc", "xs"},
}
6、切片
a:创建一个切片
a := [5]int{23,343,43,43,43} var b []int = a[1:4]
c := []int{5,3,5}
b:切片的修改
切片所做的更改会反映在数组中
c:切片的长度和容量
切片的容量是从创建切片索引开始的底层数组中元素数
切片可以重置容量
d:使用make创建一个切片
make( []T, len, cap)[]T i := make([]int, 5, 5)
e:追加切片元素
1、切片是动态的,使用append可以将元素追加到切片上 append(s[]T,x...T)[]T
2、当新的元素被添加到切片时,会创建一个新的数组。现有的数组被复制到这个新的数组中,并返回这个新数组的新切片中。
3、切片类型的零值是nil,一个nil切片的长度和容量都是0。可以使用append函数将值追加到nil切片
4、...运算符可以将一个切片添加到另一个切片中 food := append(veggies, fruits...)
f:切片的函数传递
当切片作为参数传递给函数时,函数内所做的更改也会在函数外可见
g:切片的内存优化
切片持有对底层数组的引用。只要切片在内存中,数组就不能被垃圾回收。假设有一个非常大的数组,只要处理一小部分。
解决方法L:使用copy函数生成一个切片的副本
十二:可变函数参数
func find(num int, nums ...int){
}
1、可变参数函数的工作原理是把可变参数转换为一个新的切片 ,可以不给可变参数nums传入任何参数,
nums是一个长度和容量为0的nil切片
2、将切片直接传入可变参数函数 在切片后面加... 切片将直接传入函数,不再创建新的切片
find(89,nums...)
十三:maps
map是在go中将值与键关联的内置类型,通过相应的键可以获取到值
1、如何创建map map必须使用make函数初始化
通过向map函数传入键和值的类型,可以创建map。make(map[type of key]type of value)是创建map的语法
personSalary := make(map[string]int) 键是string类型,值是int类型
2、给map添加元素
personSalary["steve"] = 1233
personSalary := map[string]int{
"steve":1200,
"jam":1500,
}
personSalary["mike"] = 9000
3、获取map中的元素
a: 获取一个不存在的元素,会返回int类型的零值0
b: 要想知道map中到底是不是存在这个key,ok为true,表示key存在
value, ok := map[key]
c:遍历map中所有的元素需要用for range循环
for key, value := range personSarlary{
fmt.Printf(key,value)
}
使用for range遍历map时,不保证每次执行程序获取的元素顺序相同
4、删除map中的元素
删除map中key的语法是delete(map,key)
5、获取map的长度
len(personSalary)
6、map是引用类型
和slices类似,map也是引用类型。
7、map的相等性
map之间不能使用 == 操作符判断, == 只能用来检查map是否为nil
十四:字符串
字符串在go语言中有着自己特殊的实现,是一个字节切片。go中的字符串是兼容Unicode编码的,使用UTF-8进行编码
1、单独获取字符串的每一个字节
a:%x格式限定符用于指定16进制编码
for i:= 0;i < len(s);i++{
}
b:%c格式用于打印字符串中的字符 在uft-8编码中,一个代码点可能会占用超过一个字节的空间,可以使用rune解决
2、rune
rune是int32的别称,在go中,rune表示一个代码点,代码无论占用多少个字节,都可以用一个rune表示
func printChars(s string){
runes := []rune(s)
for i:= 0;i < len(runes);i++{
fmt.Printf("%c",runes[i])
}
}
3、字符串的for range循环
for index, rune := range s{
}
4、用字节切片构造字符串
byteSlice := []byte{0x43, 0x61, 0x66, 0xC3, 0xA9}
str := string(byteSlice)
fmt.Println(str)
用utf-8编码的16进制字节,暑促是Cafe 16进制换成10进制也一样
5、rune切片构造字符串 runeSlice := []rune{0x0053,0x0065} //16进制的Unicode代码点
6、字符串的长度
uft8 package包中的func RuneCountInString(s string) (n int)方法用来获取字符串的长度,这个方法传入一个字符串
然后返回字符串中的rune的数量
7、字符串是不可变的
a:go中的字符串是不可变的,一旦一个字符串被创建,那么它将无法被修改
b:为了修改字符串,可以把字符串转化为一个rune切片。然后这个切片可以进行任何想要的改变,然后转化为一个字符串
func mutate(s []rune) string {
s[0] = 'a'
return string(s)
}
func main() {
h := "hello"
fmt.Println(mutate([]rune(h)))
}
十五:指针
- 要改变内容必须使用指针接收者
- 结构过大也考虑使用指针接收者
- 一致性:如果有指针接收者,最好都是指针接收者
- 值接收者是go语言特有
- 值/指针接收者均可接收值/指针
1、指针的声明
a:指针的变量类型为 *T,该指针指向一个T类型的变量
b := 255 var a *int = &b
&操作符用于获取变量的地址
b:指针的零值是 nil
2、指针的解引用
指针的解引用可以获取指针所指向的变量的值。将a解引用的语法是 *a
3、向函数传递指针参数
change(val *int){
*val = 55
}
4、不要向函数传递数组的指针,而应该使用切片
a:把一个指向数组的指针传递给这个函数 a[x]是(*a)[x]的简写形式
b:最好使用切片修改数组
5、go不支持指针运算
十六:结构体
1、结构体的声明
type Employee struct {
firstName string
lastName string
age int
}
匿名结构体:
var employee struct {
fistName, lastName string
age int
}
2、创建匿名结构体
emp3 := struct {
firstName, lastName string
age, salary int
}{
firstName: "And",
lastName: "Nik",
age: 31,
salary: 50000,
}
3、结构体的零值
4、结构体的指针
emp8 := &Employee{"Sam", "And", 55, 6000}
访问字段:(*emp8).firstName
可以使用emp8.firstName来代替显式的解引用 (*emp8).firstName
5、匿名字段
创建结构体时,字段可以只有类型,而没有字段名。这样的字段称为匿名字段
//含有两个匿名字段
type Person struct {
string
int
}
虽然匿名字段没有名称,但其实匿名字段的名称就默认为它的类型
6、嵌套结构体
type Address struct {
city,state string
}
type Person struct {
name string
age int
address Address
}
var p = Person
person.name = "nav"
p.age = 50
p.address = Address {
city: 'Chi'
state: "s"
}
7、提升字段
如果结构体中有匿名的结构体类型字段,则结构体内的字段就称为提升字段。提升字段就像是属于
外部结构体一样,可以直接访问
8、导出结构体和字段
如果结构体名称以大写字母开头,则是其他包可以访问的导出类型。如果结构体里的字段首字母大写,
也能被其他包访问
9、结构体相等性
a: 结构体是值类型,如果它的每一个字段都是可比较的,则该结构体也是可比较的。
如果两个结构体变量的对应字段相等,则这两个变量也是相等的
b:如果结构体包含不可比较的字段,则结构体变量也不可比较
type image struct {
data map[int]int
}
十七:方法
1:方法就是一个函数,在func这个关键字和方法名中间加入里一个特殊的接受器类型。
a: 接收器可以是结构体或非结构体类型,可以在方法内部访问'
type Employee struct {
name string
}
func (e Employee) display() {
}
b:方法调用
emp1 := Employee{
name: 'Sam'
}
emp1.display()
2、为什么有了函数还需要方法
a:go不是纯粹的面向对象编程语言,而且go不支持类,基于类型的方法是一种实现和类相似行为的途径
b:相同名字的方法可以定义在不同的类型,而相同名字的函数是不被允许的
3、指针接收器和值接收器
a:指针接收器的方法内部的改变对于调用者是可见的,值接收器不是这样
b:指针接收器使用:
1:对方法内部的接收器所做的改变应该对调用者可见时
2:结构体有很多字段,在方法内部使用结构体作为值接收器需要拷贝整个结构体;使用
指针接收器,只会传递一个指针到方法的内部使用
4、匿名字段的方法
属于结构体的匿名字段的方法可以被直接调用,就像方法属于定义匿名字段的结构体一样
5、方法中使用值接收器与在函数中使用值参数
a:一个函数有一个值参数,只能接受一个值参数
b:一个方法有一个值接收器,可以接受值接收器和指针接收器
6、方法职工使用指针接收器与在函数中使用指针参数
函数中只接受指针,指针接收器的方法可以使用值接收器和指针接收器
7、在非结构体的方法上
a:为了在一个类型上定义一个方法,方法的接收器类型定义和方法的定义应该在同一个包中
func (a int) add(b int){
// 尝试把一个add方法添加到内置的类型int,不被允许,因为add方法的定义和int类型的定义不
//再一个包中
}
type myInt int
func (a myInt) add(b myInt) myInt{
//这样是可以的
}
十八:接口一
1、接口
a:在面向对象的领域里:接口定义一个对象的行为
b:在go语言中,接口就是方法签名的集合。当一个类型定义里接口中的所有方法,称它实现里接口
接口指定里一个类型应该具有的方法,并由该类型决定如何实现这些方法
2、接口的声明与实现
a:其他语言如java,要求一个类使用implement关键字,显示的声明该类实现类接口
b:在go中一个类型包含类接口中声明的所有方法,就隐式的实现类接口
3、接口的实际用途
4、接口的内部表示
5、空接口
没有包含方法的接口称为空接口,空接口表示为interface{}.空接口没有方法,所有类型都实现了空接口
6、类型断言
类型断言用于提取接口的底层值。在语法i.(T)中,接口i的具体类型是T。
使用v,ok := i.(T)不会报错
7、类型选择
a:类型选择用于将接口的具体类型与很多case语句所指定的类型进行比较
switch i.(type){
case string:
fmt.Printtf('d')
case int:
fmt.Printf('x')
}
b:可以将一个类型和接口相比较。如果一个类型实现类接口,那么该类型与其实现的接口可以互相比较
十九:接口二
1、实现接口:指针接受者与值接受者
a:使用值接受者的方法,可以用值来调用,也能用指针调用
b:对于使用指针接受者的方法,用一个指针或者一个可取得地址的值调用都是合法的。
但接口中存储的具体值不能取到地址
2、实现多个接口
3、接口的嵌套
go语言没有提供继承机制,可以通过嵌套其他接口创建一个新接口
4、接口的零值
接口的零值是nil,对于值为nil的接口。其底层值和具体类型都为nil,调用它的方法会产生异常。
二十:并发入门
1、并发是指立即处理多个任务的能力
go语言原生支持并发,go使用go协程和信道来处理并发
2、并行是指同时处理多个任务
并行并不一定会加快运行速度,因为并行运行的组件之间可能需要相互通信