Go语言学习

语法基础

变量、常量

const var = 1
const (
var = 1
)

字符串

  • Go语言字符串为任意字节常量序列
  • UTF-8编码,变宽字符序列。而Java的String、C++的std::strin都是定宽的
  • 可使用 ==、!=、>=、<=、>、<比较字符串,底层会在内存中逐字节比较字符串
  • 可直接转成 []runeunicode切片(rune是int32的别名)
  • 可使用 ` 和 ” 创建

    双引号创建可解析字符串,支持转义序列,不支持多行
    反引号创建原生字符串,不支持转义序列,支持多行,可包含除了反引号之外的任意字符

  • 可使用 for range循环

    for idx, rune range str {
     ....
    }
  • Go语言字符串底层就是增强型的 []byte ,所以 []byte(string) string([]byte) 只需要O(1),无需复制
  • 对于只包含7位ASCII字符的字符串,可以直接用string[idx]索引字符;而对于包含非ASCII字符的字符串,需要转换成[]rune(string)再索引
  • strings.IndexFunc(string, indexFunc)可使用自定义方法查询index,如unicode.IsSpace来查询任意空白符的位置
  • strings.FieldsFunc(string, func(rune) bool) 遍历string中rune,如果匿名函数返回true则进行拆分

集合类型

值、指针和引用类型

  1. 指针
    • Go指针类似C、C++,区别是不能参与指针计算
    • Go不需要显示free内存,本身具备垃圾回收器
    • 对布尔、数值、字符串类型,传递给方法和函数时使用了值传递,且无论字符串长度如何,代价都很小。但是如果函数内部修改了字符串,那么将会生成一份副本,这个代价可能比较大
    • new() 函数返回的是指针 同 取址操作符 &
type test struct {
id int64
}

hello := new(test)
hello.id = 2
  • Go支持大括号直接初始化对象
  • Go语言的 . 操作符,会直接解引用指针,无需再显示使用 * 解引用

    1. 引用类型
  • 映射、切片、通道、函数、方法都是引用类型

数组

  • 长度固定,不可修改
  • 声明与初始化(未被显示初始化的元素,值会初始化位0值)
[length]Type
[N]Type{value1, value2, ...}
[...]Type{value1, value2, ...}

切片

  • 创建,二三四长度等于容量
make([]Type, length, capacity)
make([]Type, length)
[]Type{}
[]Type{value1, value2, ...}
  • 切片的重新切片 与 原切片都是对同一个底层数组的引用
  • for range循环中,直接修改range参数中的当前元素,会包 ineffectual assignment错误。
for _, amount := range amounts {
        amount = amount * 2
    }

正确修改方式如下

for i := range amounts {
amounts[i] xxxxx
}
  • append函数 返回新切片(…代表 将切片元素全部取出转成数组)
    append(slice1, slice2[idx:]...)

映射

  • 键唯一,且必须支持 == 与 != 操作
  • 基本类型、可比较的数组、结构体以及基于这些类型的自定义类型都可以作为键
  • 创建方式
 make(map[KeyType] ValueType, initialCapacity)
 make(map[KeyType] ValueType)
 map[KeyType] ValueType{}
 map[KeyType] ValueType{key1: value1, ...}
  • 支持for range遍历
  • 查询,可以通过判断found来知晓是否存在键,但通过值无法判断,因为不存在就返回零值
    value, bFound := map[key]
  • 删除
    delete(map, key)

过程式编程

快速声明

  • 快速声明操作符:=,如果声明的变量已经存在了,只有当声明语句位于块作用域的起始处,才会新创建一个变量,否则会直接覆盖原值。
  • 使用函数命名返回值时需特别注意函数中快速声明的同名变量

类型转换

  • resultOfType := Type(expression)
  • 整形、浮点型、复数互转注意精度丢失
  • []byte、[]rune可以转为string,反之亦然

类型断言

  • 安全类型断言 resultofType, bOK := expression.(Type)
  • 非安全类型断言 resultofType := expression.(Type),如果失败,panic

分支

  • if分支
if optionalStatement; booleanExpression {
} else if optionalStatement; booleanExpression {
} else {
}
  • switch分支,Go语言的case不会自动向下贯穿,需使用fallthrough关键字
switch optionalStatement; optionalExpression {
case optionalStatement; booleanExpression: blockStatements
case optionalStatement; booleanExpression: fallthrough
default: blockStatements
}
  • 类型开关,typeSwitchGuard 是一个结果为类型的表达式。如果 对开关表达式使用了 快速申明,那么获取的值为开关表达式中的变量的值,类型决定于case分支
switch optionalStatement; typeSwitchGuard {
case typeLis1: block
...
default: block
}

for循环

  • 各种循环格式
for {//无限循环
block
}

for booleanExpression {// while循环
block
}

for optionalPreStatement; booleanExpression; optionalPostStatement {// 普通for循环
block
}

for index, value := range string or array or slice {// 普通for range循环,字符串、数组、切片
block
}

for index := range string or array or slice {// 省略value的for range循环,字符串、数组、切片
block
}

for key, value := range map {// 普通for range循环,映射
block
}

for item := range channel{// 通道迭代
block
}

通信和并发

goroutine

  • 类似轻量级线程
  • 可在小机器开销的情况下大量创建
  • 所有goroutine共享地址空间
  • goroutine间推荐通过通信的方式获取共享数据

创建goroutine

go functionName(arguments) // 调用已有函数
go func(parameters){block}(arguments) // 创建新匿名函数 

channel创建

make(chan Type)
make(chan Type, capacity)

通道默认是双向的
不声明缓冲区容量的通道默认为同步的,阻塞直至发送准备好发送或者接收准备好接收
给定缓冲区容量则认为是异步的,只要缓冲区存在未使用空间或者包含可接收数据,就会是异步的

channel消息收发

channel <- value //阻塞发送
<- channel // 接收并丢弃
x := <- channel // 接收并保存
x, ok := <- channel // 接收并保存并判断通道是否已关闭或者是否为空

channel关闭

延时关闭可使用defer关键字

defer func(){ // defer函数会在外层函数返回时被调用
close(chanVar)
}

close关键字不支持关闭 只接收的通道

select语句

select会评估所有case语句中的通道操作是否阻塞,全部阻塞时,若无default则等待,若有default执行default后继续执行后续语句;通道操作有可执行的,则执行并进入对应block,然后执行后续语句

select {
case sendOrRecv1: block
case sendOrRecv2: block
default: block
}

defer、panic、recover

defer

  • 延迟在外层函数返回时执行
  • 若一个函数内有多个defer语句,则秉承LIFO原则执行
  • Go语言推荐在可释放资源创建后紧跟defer关闭语句

panic和recover

  • Go语言区分错误与异常,错误指可能出错的东西,程序需优雅地处理;异常指原本不可能发生的问题发生了,需要立即中断当前函数或程序执行
  • panic发生时,会立即终端当前函数执行,然后调用存在的defer语句,然后返回到当前函数调用者,如法炮制,中断外部函数执行,调用defer语句,直至上升到main函数,中断整个程序执行
  • 如果panic在函数栈上升的过程中,某个函数的defer中存在recover()处理,则会终止panic继续向上传播

自定义函数

  • 创建,Go语言对函数的创建顺序没有要求
func functionName(optionalParameters) optionalReturnType {
}

func functionName(optionalParameters) (optionalReturnValues){
}
  • 有返回值的函数一定需要return(或者结尾处存在panic)。如果返回值未命名,则须在return后紧跟返回值类型定义相同的返回值;如果返回值已命名,则return后可空白

  • 可变参数函数,这里...Type实际上是[]Type切片

func functionName(values ...Type){// 其实, ...的作用是展开切片的意思。 numbers[1:]... 代表展开 numbers切片的子切片
}
  • 每个程序必须有main包,main包总必须有main函数。每个包中最多可以有一个init函数。每个包的init函数会在main之前被执行。
  • 闭包,匿名函数(函数字面量)都是闭包。
  • Go语言的泛型就是使用 interface{} 来作为参数类型

面向对象编程

自定义结构体与添加的方法,首字母大写的会被导出,小写的不 导出

  • Go语言不支持继承,只支持聚合和嵌入
type ColoredPoint struct{
    color.Color // 嵌入
    x, y int    // 聚合
}
  • 自定义类型,typeSpecification可以是内置类型、接口、结构体、函数签名 type typeName typeSpecification

  • 添加方法,就是在定义函数时指明了所属,函数名称要求在所属对象中唯一

type Count int
func (count *Count) Increment() {*count++}
func (count *Count) Decrement() {*count--}
func (count Count) IsZero() {return count == 0}
  • 嵌入 与 方法重写
type Item struct {
id string
price float64
quantity int
}

func (ite *Item) Cost() float64 {
    return item.price * float64(item.quantity)
}

type LuxuryItem struct {
    Item // 嵌入成员
    cataogId int
    markup int
}

func (item* LuxuryItem ) cost (item *Luxuryitem) float64 {
    return item.Item.Cost * item.markup
}
  • 方法表达式:直接将自定义类型的方法当作变量使用。
asStringV := Part.String
sv := asStringV(part)

lowerP := (*Part).LowerCase
lowerP(&part)

接口

  • 只要自定义类型实现了同签名的全部接口方法,Go编译器就会自动将改自定义类型认为实现了这个接口
type Exchanger interface {
    Exchange()
}

type StringPair struct {
    first, second string
}

func (stringPair *StringPair) Exchange() {
    stringPair.first, stringPair.second = stringPair.second, stringPair.first
}

type PointPair [2]int

func (pointPair *PointPair) Exchange() {
    pointPair[0], pointPair[1] = pointPair[1], pointPair[0]
}

func BatchExchange(pairs ...Exchanger) {
    for _, pair := range pairs {
        pair.Exchange()
    }
}

func TestExchangeInterface() {
    stringpair := StringPair{"1", "2"}
    pointpair := PointPair{1, 2}

    fmt.Println("Before ", stringpair, pointpair)

    BatchExchange(&stringpair, &pointpair)

    fmt.Println("After ", stringpair, pointpair)
}
  • 接口嵌入,嵌入后父接口包含子接口的所有方法
type LowerCase interface {
    ToLowerCase()
}


type UpperCase interface {
    ToUpperCase()
}

type LowerUpperCase interface {
    LowerCase
    UpperCase
}

结构体

聚合与嵌入

  • 嵌入字段的访问,如果存在名称冲突,需要指定嵌入的结构体名称
type Person struct {
    Title     string
    Forenames []string
    Surname   string
}

type Author1 struct {
    Names    Person // 聚合
    Title    []string
    YearBorn int
}

type Author2 struct {
    Person   // 嵌入,匿名结构体字段
    Title    []string
    YearBorn int
}

func Test_embedAndAggregation() {
    author1 := Author1{Person{"Person", []string{"X", "U"}, "ZHE"}, []string{"XU", "ZHE"}, 19911115}
    author1.Names.Title = "Male" // 聚合结构体的字段访问
    fmt.Println(author1)

    author2 := Author2{Person{"Person", []string{"M", "A"}, "ZHUOJUN"}, []string{"MA", "ZHUO", "JUN"}, 19920227}
    author2.Surname = "ZHUOJUN2"// 聚合结构体的字段访问,不存在冲突
    author2.Person.Title = "Female"// 聚合结构体的字段访问,存在冲突
    fmt.Println(author2)
}
  • 结构体的初始化,要么依次写全参数,要么指定结构体成员名称 aStruct{field1:value1}

并发编程

runtime常用API

  • runtime.NumCPU() 返回当前机器的逻辑处理器或者核心数量
  • rumtime.GOMAXPROCS(n) n为0时,同上。n > 0时,设置Go运行时系统可以使用的处理器数

注意

  • 所有业务goroutine在主goroutine退出后都会退出
  • range chan 会一直从chan中获取数据,直到chan被显示关闭

goroutine组的join结束

sync库支持如下

waiter := &sync.WaitGroup() // 创建

go func() {
// 然后再某个routine中
waiter.Add(1)

// 该routine结束的时候
waiter.Done()
}()

// 如果要等待这一组通过Add方法收集的routine的结束
waiter.Wait()

  • go src下的包可以为本机所有工程共享,go root下的只能是本机当前用户使用
  • Go中import包的搜索机制为:先Go Root然后 GOPATH 下
  • Go推荐再GoPath下的src目录创建my_package目录,其中防止自己的代码,便于后续工程引用

反射

也叫 类型检视(introspection)

常用API

  • reflect.DeepEqual() 可用于比较== != 不能比较的对象
  • reflect.valueOf() 返回一个reflect.Value 结构,可以通过它获取真实值、获取成员、调用方法等
  • 对于无法修改的值,如string常量,可以获取其指针的值,然后使用relect.Value.Elem(),获取值,替换掉指针所指向的string值。如下
test := "test1"
value := reflect.ValueOf(&test)
value.Elem().SetString("test2")
  • reflect.Value.Call([]reflect.Value) []reflect.Value 可以调用当前Value对应的方法。
  • reflect.TypeOf(interface{}).Kind() 可以获取类型,如reflect.Array, reflect.Chan等
  • reflect.Value.MethodByName(string) reflect.Value 可以获取指定方法

你可能感兴趣的:(golang)