网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
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 断言和反射
断言是预判确认变量的类型
func main() {
v := "hello world"
fmt.Println(typeof(v))
}
func typeof(v interface{}) string {
switch t := v.(type) {
case int:
return "int"
case float64:
return "float64"
//... etc
default:
_ = t
return "unknown"
}
}
反射则是因为interface中实际记录的有存储对象的类型,可以提取出变量的类型
import (
"reflect"
"fmt"
)
func main() {
v := "hello world"
fmt.Println(typeof(v))
}
func typeof(v interface{}) string {
return reflect.TypeOf(v).String()
}
4.7 回收处理
在golang当中,defer代码块会在函数调用链表中增加一个函数调用。这个函数调用不是普通的函数调用,而是会在函数正常返回,也就是return之后添加一个函数调用。因此,defer通常用来释放函数内部变量。
如下示例
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
![img](https://img-blog.csdnimg.cn/img_convert/89395c3ffc5a3f22d51ff1f32069356d.png)
![img](https://img-blog.csdnimg.cn/img_convert/398fd9b069c524f84e24c2e80f500fbd.png)
![img](https://img-blog.csdnimg.cn/img_convert/6704bb8ac33a18000df91a62f065b9d9.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**
理
在golang当中,defer代码块会在函数调用链表中增加一个函数调用。这个函数调用不是普通的函数调用,而是会在函数正常返回,也就是return之后添加一个函数调用。因此,defer通常用来释放函数内部变量。
如下示例
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
[外链图片转存中…(img-IUBPr1GT-1715703183484)]
[外链图片转存中…(img-aq2SvFIT-1715703183485)]
[外链图片转存中…(img-4cdjQnMF-1715703183485)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新