Go学习

2023-10-12  本文已影响0人  Zerek_W

《Go语言编程》 许式伟 吕桂华 著

一、语言特性

垃圾回收、更丰富内置类型、函数多返回值、错误处理、匿名函数和闭包、类型和接口、并发编程、反射、语言交互性

二、顺序编程


2.2 常量

const(
  size int 64 = 1024
  eof = -1
)
const(
  c0 = iota    //c0 == 0
  c1               //c1==1
  c2              //c2==2
)
const(
Sunday = iota
Monday
Tuesday
Wednesday
...
)

以大写字母开头的常量在包外可见

2.3类型

对于常规开发来说,用int和uint就可以了,以免导致移植困难
字符串在Go中也是基本类型,字符串的内容不能在初始化后被修改

str := "hello world"
n := len(str)
for i:=0; i<n; i++{
  ch := str[i]
  fmt.Println(ch)
}
for _,v := range myArray{
  fmt.Print(v)
}
var myArray [10]int = [10]int{1,2,3,4,5,6,7,8,9,10}
var mySlice []int = myArray[:5]
#遍历切片
for _, v := range mySlice{
  fmt.Print(v)
}

直接创建:

mySlice := make([]int, 5) //初始元素个数为5的切片,初始值为0
mySlice := make([]int, 5, 10) //初始元素个数为5的切片,初始值为0,并预留10个元素的存储空间
mySlice := []int{1,2,3,4,5}

在切片后附加元素:

mySlice = append(mySlice, 1, 2, 3) //附加1,2,3
mySlice = append(mySlice, mySlice...) //...相当于把mySlice打散了传进去

内容复制:
copy() :若两个切片不一样,就会按较小的那个数组切片的元素个数进行复制

#声明
var myMap map[string] PersonInfo   //PersonInfo是一个结构体,也是value的类型
#创建
myMap = make(map[string] PersonInfo)
# 赋值
myMap["1234"] = PersonInfo{初始化}
# 删除
delete(myMap,"1234")
#查找
value, ok := myMap["1234"]
if ok{
  处理value
}

2.4流程控制语句

2.5函数

小写字母开头的函数只在包内可见,大写字母开头的函数才能被其他包使用(前提是在导入了包的情况下)

func Add(a int, b int) (ret int, err error){
   if a<0 || b<0 {
      err = errors.New("error")
      return  //在函数中执行不带任何参数的return语句时,会返回对应的返回值变量的值
  }
   return a+b, nil
}
func myFun(args ...int){
  for _, v := range args{
    fmt.Print(v)
  }
}

不定参数的传递:

myfun(args...)
myfun(args[1:]...)  //任意的int slice都可以传进去

任意类型不定参数的传递:

func Printf(format string, args ...interface{}){
    //...
}

用interface{}传递任意类型数据是GO语言的惯例用法

2.6 错误处理

type error interface{
  Error() string
}

对于大多数函数,若要返回错误,可定义为如下模式:
将error作为返回值的最后一个

func Foo(param int) (n int, err error){}

#调用代码时:
n, err := Foo(0)

if err != nil{
  // 错误处理
} else {
   // 使用返回值n
}
type PathError struct {
  Op string
  Path string
  Err error
}

然后,实现Error()方法

func (e *PathError) Error() string{
  return e.Op + "" + e.Path + "" + e.Err.Error()
}

返回err时,只需要将其他函数返回的普通的err当作参数用于初始化PathError,然后就可以直接返回PathError变量了。

defer srcFile.Close()
# 可以加一个匿名函数处理
defer func(){
}()  

panic 用于引发运行时错误。可以调用 panic 函数来中止程序的正常执行。panic 会导致程序立即停止,并触发执行函数调用栈的逆序执行,直到所有被推迟(defer)的函数调用执行完毕,然后程序退出。

recover 用于捕获 panic 引发的异常,并在程序中恢复。它只能在延迟函数中调用,用于恢复 panic 引发的错误。

三、面向对象编程


3.1类型系统

通过为int取别名并增加了方法,他就变成了一个全新的类型,但这个新类型又完全拥有int的功能:

type Integer int
func (a Integer) Less(b Integer) bool {
  return a<b
}

只有在需要修改对象的时候,才必须使用指针

func (a *Integer) Add (b Integer){
  *a += b
}

3.2 初始化

rectPtr := new(Rect)  //使用new关键字创建结构体指针
rect2 := &Rect{}  //也是指针,加了&的都是指针,不加&就直接返回对象
rect3 := &Rect{0,0,100,200}
rect4 := &Rect{width:100, height:200}

在Go中无构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以NewXXX来命名

func NewRect(x,y,width,height float64) *Rect{
  return &Rect{x,y,width,height}
}

3.3匿名组合

Go也提供了继承,但采用了组合的文法,所以将其称为匿名组合。

// 定义一个Person结构体
type Person struct {
    Name string
    Age  int
}

// 定义一个Student结构体,匿名组合了Person结构体
type Student struct {
    Person
    Grade int
}

func main() {
    // 创建一个Student对象
    student := Student{
        Person: Person{
            Name: "Alice",
            Age:  20,
        },
        Grade: 10,
    }

    // 访问Student对象的字段
    fmt.Println("Name:", student.Name) // 输出: Name: Alice
    fmt.Println("Age:", student.Age)   // 输出: Age: 20
    fmt.Println("Grade:", student.Grade) // 输出: Grade: 10
}
//Student 结构体匿名组合了 Person 结构体。这意味着 Student 类型继承了 Person 类型的所有字段和方法。

3.5 接口

在Go语言中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口

3.5.3 接口赋值

有两种情况:将对象实例赋值给接口(赋值时对象前加&)、将一个接口赋值给另外一个接口(方法列表为其子集)

3.5.4接口查询

接口查询是指通过类型断言或类型判断来判断一个值是否实现了特定的接口,并获取其实现的接口类型或调用接口方法。

type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}

func main() {
    var shape Shape
    circle := Circle{Radius: 5.0}
    shape = circle

    // 类型断言
    if c, ok := shape.(Circle); ok {
        fmt.Printf("Type assertion successful. Radius: %.2f\n", c.Radius)
    } else {
        fmt.Println("Type assertion failed")
    }
}
func calculateArea(shape Shape) {
    switch v := shape.(type) {
    case Circle:
        fmt.Printf("Area of circle: %.2f\n", v.Area())
    default:
        fmt.Println("Unknown shape")
    }
}

func main() {
    circle := Circle{Radius: 5.0}
    calculateArea(circle)
}

3.5.6 接口组合

3.5.7 Any类型

由于Go中任何对象实例都满足空接口interface{} 所以,interface{}看起来像是可以指向任何对象的Any类型。
当函数可以接受任意的对象实例时,我们会将其声明为interface{}

func Printf(fmt string, args ...interface{})

四、并发编程


4.1 并发基础

并发的几种主流的实现模型:

4.2 协程

Go语言在语言级别支持轻量级线程,叫goroutine。其让轻量级线程的切换管理不依赖于系统的线程和进程,也不依赖于CPU的核心数量。

4.3 goroutine

goroutine是Go语言中轻量级线程实现,由Go运行时管理。
加上go关键字,这次调用就会在一个新的goroutine中并发执行,当被调用的函数返回时,这个gorontine也就结束了。如果这个函数由返回值,其返回值会被抛弃。

4.4 并发通信

Go语言以消息机制而非共享内存作为通信方式
消息机制认为每个并发单元是自包含的,独立的个体,并且都有自己的变量,但在不同的并发单元间这些变量不共享。每个并发单元的输入和输出只有一种,那就是消息。类似于进程的概念,每个进程不会被其他进程打扰。
这种消息机制被称为Channel。不要通过共享内存来通信,而应该通过通信来共享内存。

4.5 Channel

channel 是Go语言在语言级别提供的goroutine间的通信方式。channel是进程内的通信方式。如果需要跨进程通信,我们建议用分布式系统的方法来解决,比如使用Socket或者HTTP等通信协议。
channel是类型相关的,一个channel只能传递一种类型的值。

var chanName chan ElementType
var m map[string] chan bool  //声明一个map,其元素类型是bool的channel
# 声明并初始化
ch := make(chan int)

ch<-value
value := <-ch

向channel中写入数据会导致程序阻塞,直到有其他goroutine从这个channel中读取数据。如果channel之前没有写入数据,那么从channel中读数据也会导致程序阻塞。

select {
  case <-chan1:
      ... //如chan1成功读到数据,则进行该case处理语句,并直接忽略掉读到的数据
  case chan2<- 1:
    ...  //如果成功向chan2写入数据,则进行该case语句
  default:
    ...  //上面都没有成功,进入default
}

在 Go 语言的 select 语句中,如果多个 case 同时满足条件,Go 会随机选择一个 case 执行,这也称为伪随机选择。这意味着无法确定哪个 case 会被优先选择,因此我们不能依赖于特定 case 的执行顺序。

time out := make(chan bool, 1)
go func(){
  time.sleep(1e9)  //1s
  timeout <- true
}()

select{
  case <-ch:
  ...
  case <-timeout:
  ...
}

# 声明
var ch1 chan int
var ch2 chan<-float64    //单向的,只能写float64数据
var ch3 <-chan int  //单向的,只能读取int数据

#初始化
ch4 := make(chan int)
ch5 := <-chan int(ch4)
ch6 := chan<- int(ch4)

#用法
func Parse(ch <-chan int){
  for value := range ch{
    ...
  }
}

4.6 多核并行化

可以通过设置环境变量GOAMAXPROCS的值来控制使用多少给CPU核心
或者runtime.GOMAXPROCS()
runtime.NumCOU()获取核心数

4.7 让出时间片

runtime.Gosched()主动让出时间片给其他goroutine
实际上,要想精细控制goroutine行为,须要深入了解runtime包提供的具体功能。

4.8 同步

var l sync.Mutex
func foo() {
  l.Lock()
  defer l.Unlock()
}
var once sync.Once
once.Do(func)
上一篇下一篇

猜你喜欢

热点阅读