既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
操作数组值的方法同样适用于切片值。还有一种操作数组的方式叫做“切片”,实施切片操作的方式就是切片表达式。例:
var number3 = [5]int{1,2,3,4,5}
var slice1 = numbers3[1:4]
上例中切片表达式numbers3[1:4]的结果为[]int{2,3,4}很明显被切下的部分不包含元素上界索引指向的元素。实际上slice1这个切片值的底层数组正是number3的值。
我们也可以在切片值上实施切片操作:
var slice2 = slice1[1:3]
除了长度切片值以及数组值还有另外一个属性–容量。数组的容量总是等于其长度,而切片值的容量往往与其长度不同。
个切片值的容量即为它的第一个元素值在其底层数组中的索引值与该数组长度的差值的绝对值。可以使用cap()内建函数获取数组、切片、通道类型的值的容量:
var capacity2 int = cap(slice2)
切片类型属于引用类型,它的零值即为nil,即空值。如果我们只声明了一个切片类型而不为它赋值,则它的默认值 nil。
切片的更多操作方法有些时候我们可以在方括号中放入第三个正整数。如下图所示:
numbers3[1:4:4]
第三个正整数为容量上界索引,它意义在于可以把作为结果的切片值的容量设置的更小。它可以限制我们通过这个切片值对其底层数组中的更多元素的访问。上节中numbers3和slice的赋值语句如下:
var numbers3 = [5]int{1,2,3,4,5}
var slice1 = numbers3[1:4]
这时,变量slice1的值是[]int{2,3,4}。但是我们可以通过如下操作将其长度延展与其容量相同:
slice1 = slice1[:cap(slice1)]
通过此操作,变量slice1的值变为了[]int{2,3,4,5},且其长度和容量均为4。现在number3的值中的索引值在(1,5)范围内的元素都被体现在了slice1的值中。这是以number3的值是slice1的值的底层数组为前提的。这意味着我们可以轻而易举地通过切片访问其底层数组中对应索引值更大的更多元素。如果我们编写的函数返回了这样一个切片值,那么得到它的程序很可能会通过这种技巧访问到本不应该暴露给它的元素。
如果我们在切片中加入了第三个索引(即容量上限索引),如:
var slice1 = numbers3[1:4:4]
那么在此之后,我们将无法通过slice1访问到number3的值中的第五个元素。
虽然切片值在上述方面受到了其容量的限制。但是我们可以通过另外一种手段对其进行不受限制的扩展。这需要用到内建函数append。append会对切片值进行扩展并返回一个新的切片值,使用方法如下:
slice1 = append(slice1, 6, 7)
通过上述操作,slice1的值变为了[]int{2,3,4,6,7}。一旦扩展操作超出了被操作的切片值的容量,那么该切片的底层数组就会被替换 最后一种操作切片的方式是“复制”。该操作的实施方法是调用copy函数。该函数接收两个类型相同的切片值作为参数,并把第二个参数值中的元素复制到第一个参数值中的相应位置(索引值相同)上。这里有两点需要注意:
- 这种复制遵循最小复制原则,即:被复制的元素的个数总是等于长度较短的那个参值的长度。
- 与append函数不同,copy函数会直接对其第一个参数值进行修改。
例:
var slice4 = []int{0,0,0,0,0,0}
copy(slice4, slice1)
通过上述复制操作,slice4会变成[]int{2,3,4,6,7,0,0}。
4.1.10 字典类型
Go语言的字典(Map)类型是一个哈希表的实现。字典类型的字面量如下:
map[K]T
其中,"K"为键的类型,而"T"则代表元素(值)的类型。如果我们描述一个键类型为int,值类型为string的字典类型的话:
map[int]string
字典的键类型必须是可比较的,否则会引起错误,即键不能是切片、字典、函数类型
字典值的字面量表示法实际上与数组的切片的字面量表示法很相似。最左边仍然是类型字面量,右边紧挨着由花括号包裹且有英文逗号分隔的键值对。每个键值对的键和值之间由冒号分隔。以字典类型map[int]string为例。他的值的字面量可以是这样的:
map[int]string{1:"a",2:"b"m,3:"c"}
我们可以把这个值赋给一个变量
mm := map[int]string{1:"a",2:"b",3:"c"}
可用索引表达式取出字典中的值:
b := mm[2]
可以用索引表达式赋值:
mm[2] = b + "2"
这样mm中键为2的值变为了"b2"。可以用如下方式向字典中添加一个键值对:
mm[4] = ""
对于字典值来说,如果指定键没有对应的值则默认为该类型的空值。所以mm[5]会返回一个"“。但是这样的话我们就不知道mm[5]到底是”"还是mm[5]没有这个值。所以go提供了另外一种写法:
e, ok := mm[5]
针对字典的索引表达式可以有两个求职结果,第二个求职结果是bool类型的。它用于表明字典值中是否存在指定的键值对。 从字典中删除键值对的方法非常简单,仅仅是调用内建函数delete:
delete(mm, 4)
无论mm中是否存在以4为键的键值对,delete都删除。 字典类型属于引用类型,它的零值即为nil
4.1.11 通道类型
通道(Channel)是Go语言中一种非常独特的数据结构。它可用于在不同Goroutine之间传递类型化的数据。并且是并发安全的。相比之下,之前几种数据类型都不是并发安全的。
Goroutine可以被看作是承载可被并发执行的代码块的载体。它们由Go语言的运行时系统调度,并依托操作系统线程(又称内核线程)来并发地执行其中的代码块。
通道类型的表示方法很简单,仅由两部分组成:
chan T
在这个类型字面量中,左边是代表通道类型的关键字chan,而右边则是一个可变的部分,即代表该通道类型允许传递的数据的类型(或称通道的元素类型)。
与其他的数据类型不同,我们无法表示一个通道类型的值,因此,我们无法用字面量来为通道类型的变量赋值。只能通过调用内建函数make来达到目的。make参数可接受两个参数,第一个参数是代表了将被初始化的值的类型的字面量(例: chan int),而第二个参数则是值的长度,例如,若我们想要初始化一个长度为5且元素类型为int的通道值,则需要这样写:
make(chan int, 5)
make函数也可以被用来初始化切片类型或字典类型的值。
暂存在通道值中的数据是先进先出。下面,我们声明一个通道类型的变量,并为其赋值:
ch1 := make(chan string, 5)
这样一来,我们就可以使用接受操作符<-向通道值发送数据了。当然,也可以使用它从通道值接收数据,例如,如果我们要向通道ch1 发送字符串"value1",那么应该这样做:
ch1 <- “value1"
如果我们从ch1那里接收字符串,则要这样:
<- ch1
我们可以把接受到字符串赋给一个变量,如:
value := <- ch1
与针对字典值的索引表达式一样,针对通道值的接受操作也可以有第二个结果值:
value, ok := <- ch1
这里的ok的值是bool类型的。它代表了通道值的状态,true代表通道值有效,而false则代表通道值已无效(或称已关闭),更深层次的原因是,如果在接受操作进行之前或过程中通道值被关闭了,则接收操作会立即结束并返回一个该通道值的元素类型的零值。
可以通过函数close来关闭通道:
close(ch1)
对通道值的重复关闭会引发运行时异常,会使程序崩溃。在通道值有效的前提下,针对它的发送操作会在通道值已满(其中缓存的数据的个数已等于它的长度)时被阻塞。而向一个已被关闭的通道值发送数据会引发运行时异常。针对有效通道值的接收操作会在它已经为空时被阻塞。通道类型属于引用类型,它的零值为nil。
4.2 流程控制
4.2.1 条件控制
对应的关键字为if、 else和else if;
if a := 1; a >= 1 {
fmt.Println("OK")
}
4.2.2 选择语句
对应的关键字为switch、 case和select
以下是switch、case的例子:
switch i {
case 0:
fmt.Printf("0")
case 1:
fmt.Printf("1")
case 2:
fallthrough
case 3:
fmt.Printf("3")
case 4, 5, 6:
fmt.Printf("4, 5, 6")
default:
fmt.Printf("Default")
}
Go语言中的switch、case中的case后面不需要专门写break就会停止向下执行。
这里在swithc、case中还有一个关键字是fallthrough,如果执行了当前的case时还想执行与当前case紧邻的下条的case语句,则可以在上条的case语句末尾中添加fallthrough,如下示例:
var a = 2
switch a {
case 1:
fmt.Println("this is case 1")
case 2:
fmt.Println("this is case 2")
fallthrough
default:
fmt.Println("this is default")
}
// 输出结果如下:
// this is case 2
// this is default
以下是select、case的例子:
select {
case communication clause :
statement(s);
case communication clause :
statement(s);
/* 你可以定义任意数量的 case */
default : /* 可选 */
statement(s);
}
以下描述了 select 语句的语法:
- 每个 case 都必须是一个通信
- 所有 channel 表达式都会被求值
- 所有被发送的表达式都会被求值
- 如果任意某个通信可以进行,它就执行,其他被忽略。
- 如果有多个 case 都可以运行,Select 会随机公平地选出一个执行。其他不会执行。
否则:- 如果有 default 子句,则执行该语句。
- 如果没有 default 子句,select 将阻塞,直到某个通信可以运行;Go 不会重新对 channel 或值进行求值。
4.2.3 循环语句
对应的关键字为for和range;
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
4.2.4 跳转语句
func myfunc() {
i := 0
HERE:
fmt.Println(i)
i++
if i < 10 {
goto HERE
}
}
4.3 函数
4.3.1 概述
首先函数的格式是固定的,func+函数名+ 参数 + 返回值(可选) + 函数体。例 :
func main()
{
fmt.Println("Hello go")
}
在golang中有两个特殊的函数,main函数和init函数,main函数不用介绍在所有语言中都一样,它作为一个程序的入口,只能有一个。init函数在每个package是可选的,可有可无,甚至可以有多个(但是强烈建议一个package中一个init函数),init函数在你导入该package时程序会自动调用init函数,所以init函数不用我们手动调用,另外它只会被调用一次,因为当一个package被多次引用时,它只会被导入一次。
4.3.2 参数传递
- 普通变量
使用普通变量作为函数参数的时候,在传递参数时只是对变量值得拷贝,即将实参的值复制给变参,当函数对变参进行处理时,并不会影响原来实参的值。 - 指针
函数的变量不仅可以使用普通变量,还可以使用指针变量,使用指针变量作为函数的参数时,在进行参数传递时将是一个地址看呗,即将实参的内存地址复制给变参,这时对变参的修改也将会影响到实参的值。 - 数组
和其他语言不同的是,go语言在将数组名作为函数参数的时候,参数传递即是对数组的复制。在形参中对数组元素的修改都不会影响到数组元素原来的值。 - slice, map, chan
在使用slice, map, chan 作为函数参数时,进行参数传递将是一个地址拷贝,即将底层数组的内存地址复制给参数slice, map, chan 。这时,对slice, map, chan 元素的操作就是对底层数组元素的操作。 - 函数名字
在go语言中,函数也作为一种数据类型,所以函数也可以作为函数的参数来使用。
4.3.3 返回值
go语言可以返回局部变量的指针,因为go语言的回收机制是发现有被引用的栈上临时变量时,会自动存在堆上。
4.3.4 闭包
和其他语言类似,golang也支持闭包函数
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
4.4 类
4.4.1 概述
type Poem struct {
Title string
Author string
intro string
}
这样就声明了一个类,其中没有public、protected、private的的声明。golang用另外一种做法来实现属性的访问权限:属性的开头字母是大写的则在其它包中可以被访问,否则只能在本包中访问。类的声明和方法亦是如此。
4.4.2 方法
func (poem *Poem) publish() {
fmt.Println("poem publish")
}
或者
func (poem Poem) publish() {
fmt.Println("poem publish")
}
和其它语言不一样,golang声明方法和普通方法一致,只是在func后增加了poem _Poem这样的声明。加_和没有加*的区别在于一个是传递指针对象,一个是传递值对象。
注意当创建了方法1后将会默认创建出方法2,反之则不会。
4.4.3 类的实例化
实例化对象有好几种方式
poem := &Poem{}
poem.Author = "Heine"
poem2 := &Poem{Author: "Heine"}
poem3 := new(Poem)
poem3.Author = "Heine"
poem4 := Poem{}
poem4.Author = "Heine"
poem5 := Poem{Author: "Heine"}
实例化的时候可以初始化属性值,如果没有指明则默认为系统默认值。
4.4.4 伪继承
golang中不存在继承,但可以使用组合的方式达到类似的效果.
func (e *Poem) ShowTitle() {
fmt.Printf(e.Title)
}
type Poem struct {
Title string
Author string
intro string
}
type ProsePoem struct {
Poem
Author string
}
ProsePoem属性中声明了Poem,表示组合了Poem的属性和方法**(属性和方法都会被继承)**。
prosePoem := &ProsePoem{
Poem: Poem{
Title: "Jack",
Author: "slow",
intro: "simple",
},
Author: "test",
}
如果其中属性有冲突,则以外围的为主,也就是说会被覆盖。
type ProsePoem struct {
Poem
Author string
}
当访问Author的时候默认为ProsePoem的Author,如果需要访问Poem的Author属性可以使用
prosePoem.Poem.Author来访问方法同理。
prosePoem := &ProsePoem{}
prosePoem.Author = "Shelley"
prosePoem.Poem.Author = "Heine"
fmt.Println(prosePoem)
从输出中可以很直观看到这一点。
&{{ Heine } Shelley}
4.5 接口
Golang的interface是方法的集合也是一种类型
package main
import "fmt"
type Animal interface {
Speak() string
}
type Cat struct{}
func (c Cat) Speak() string {
return "cat"
}
type Dog struct{}
func (d Dog) Speak() string {
return "dog"
}
func Test(params interface{}) {
fmt.Println(params)
}
func main() {
animals := []Animal{Cat{}, Dog{}}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
Test("string")
Test(123)
Test(true)
}
如上所示,将Struct 向 Interface变量赋值,实际作用是Interface获取了Struct中方法的实现集合。
4.6 断言和反射
断言是预判确认变量的类型
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
Speak() string {
return “dog”
}
func Test(params interface{}) {
fmt.Println(params)
}
func main() {
animals := []Animal{Cat{}, Dog{}}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
Test("string")
Test(123)
Test(true)
}
如上所示,将Struct 向 Interface变量赋值,实际作用是Interface获取了Struct中方法的实现集合。
### 4.6 断言和反射
断言是预判确认变量的类型
[外链图片转存中...(img-tdOlCRVk-1715815208723)]
[外链图片转存中...(img-bohPARyT-1715815208724)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.youkuaiyun.com/topics/618658159)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**