Go入门

因工作需要,正在了解Go的相关知识…

goroutinechannel

go语言的goroutinechannel

所谓goroutine是程序中与其他goroutine完全独立而并发执行的函数或者方法调用。类似于轻量级的线程 ;不同的goroutine之间的通讯使用channel

go语言的运行:

  • 运行源代码:
go run hello.go
  • 编译链接为可执行程序:
go build hello.go

goroutine

goroutine的创建:

  • go function(arguments)
  • go func(parameters) { block } (arguments)

channel

goroutine之间的相互协作:通过channel来交换数据:

  • make(chan Type)

  • make(chan Type, capacity)

    capacity指定缓冲区容量

通道支持的操作:

语法含义
channel <- value发送value到通道中,有可能阻塞
<-channel从通道中接收数据
x := <-channel接收数据并赋值给x
x, ok := <-channel功能同上,同时检查通道是否已关闭或者是否为空

匿名函数

示例

分组求和:

package main

import "fmt"

func sum(a []int, result chan int) {
    sum := 0
    for _, v := range a {
        sum += v
    }
    result <- sum
}

func main() {
    a := []int{2, 3, 5, 6, 10, -5, 1, 0}
    result := make(chan int)
    go sum(a[:len(a)/2], result)
    go sum(a[len(a)/2:], result)
    x, y := <-result, <-result

    fmt.Println(x, y, x+y)
}

格式化go代码

使用go fmt <文件>.go,将代码格式化,否则编译不通过

标示符(变量名,函数名)

第一个字符必须是字母

空标示符_是一个占位符,它用于在赋值操作的时候将某个值赋值给空标示符号,从而达到丢弃该值的目的。

不能单独将’_'放于‘:=’操作符号的左边

count, err = fmt.Println(x)     // 获取打印的字节数以及相应的error值
count, _ = fmt.Println(x)       // 获取打印的字节数,并且丢弃error值

常量和变量

在Go语言中,声明变量的时候类型名总是在变量名的后面

变量可以使用关键字var声明,Go语言可以自动推断出声明变量的类型。

const limit = 512
const top uint16 = 1421
last := 1.5
var a int
var debug = false

当声明多个常量时:

const (
	Cyan = 0
	Black = 1
	White = 2
)

类型转换

type(value),只要合法就能转换成功。

整型

类型说明
byte等同于uint8
int依赖于不同平台下的实现,可以是int32或者int64
int8[-128, 127]
int16[-32768, 32767]
int32[-2147483648, 2147483647]
int64[-9223372036854775808, 9223372036854775807]
rune等同于uint32
uint依赖于不同平台下的实现,可以是uint32或者uint64
uint8[0, 255]
uint16[0, 65535]
uint32[0, 4294967295]
uint64[0, 18446744073709551615]
uintptr一个可以恰好容纳指针值的无符号整型(对32位平台是uint32, 对64位平台是uint64)

类似于c语言中的sizeof,在Go语言中可以使用unsafe包中的unsafe.Sizeof()函数达到同样的效果。

浮点类型

类型说明
float32±3.402 823 466 385 288 598 117 041 834 845 169 254 40x1038计算精度大概是小数点后7个十进制数
float64±1.797 693 134 862 315 708 145 274 237 317 043 567 981x1038计算精度大概是小数点后15个十进制数
complex32复数,实部和虚部都是float32
complex64复数,实部和虚部都是float64

字符串

语法描述
s += t将字符串t追加到s末尾
s + t将字符串s和t级联
s[n]从字符串s中索引位置为n处的原始字节
s[n:m]从位置n到位置m-1处取得的字符(字节)串
s[n:]从位置n到位置len(s)-1处取得的字符(字节)串
s[:m]从位置0到位置m-1处取得的字符(字节)串
len(s)字符串s中的字节数
len([]rune(s))字符串s中字符的个数,可以使用更快的方法utf8.RuneCountInString()
[ ]rune(s)将字符串s转换为一个unicode值组成的串
string(chars)chars类型是[]rune或者[]int32, 将之转换为字符串
[ ]byte(s)无副本的将字符串s转换为一个原始的字节的切片数组,不保证转换的字节是合法的UTF-8编码字节

若要遍历字符串中的每一个字符(非字节),使用range

处理字符串官方包

strings包提供了如查找字符串,分割字符串,判断前后缀,判断字符串包含,字符串替换,统计字符串出现的次数等常用操作;详细的参考官方:https://golang.org/pkg/strings/。

strconv包提供了许多可以在字符串和其他类型的数据之间进行转换的函数。可以将数字转换为字符串,将数字样式的字符串转换为数值(将转字符串“12345”转换为int类型的整数)。

值、指针和引用类型

package main

import (
    "fmt"
)

func swap1(x, y, p *int) {
    if *x > *y {
        *x, *y = *y, *x	// 该句的底层操作流程
    }
    *p = *x * *y
}

func swap2(x, y int) (int, int, int) {
    if x > y {
        x, y = y, x
    }
    return x, y, x * y
}

func main() {
    i := 9
    j := 5
    product := 0
    swap1(&i, &j, &product)
    fmt.Println(i, j, product)

    a := 64
    b := 23
    a, b, p := swap2(a, b)
    fmt.Println(a, b, p)
}

数组和切片

Go中的数组的长度固定且不可修改,创建数组的语法:

[length]Type
[N]Type{value1, value2, ..., valueN}
[...]Type{value1, value2, ..., valueN}	// 当使用'...'时,Go语言会自动计算数组的长度

切片

创建切片的语法:

make([ ]Type, length, capacity)
make([ ]Type, length)
[ ]Type{}
[ ]Type{value1, value2, ..., valueN}

长度和容量,是不同的概念,当执行append之后,切片的capacity的增加问题。

映射(map)

创建map的语法:

  • make(map[KeyType]VauleType, initialCapacity)
  • make(map[KeyType]ValueType)
  • map[KeyType]ValueType{ }
  • map[KeyType]ValueType{key1: value1, key2: value2, …, keyN: valueN}

map支持的操作:

m[k] = v用键k来将值赋值给映射m。如果映射m中的k已存在,则将之前的值舍弃
delete(m, k)将键k及其相关的值从映射m中删除,如果k不存在则不执行任何操作
v := m[k]从映射m中取得键k相对应的值并赋值给v。如果k不存在,则将映射类型的0值赋值v
v, found := m[k]从映射m中取得键k相对应的值并赋值给v, 并将found的值赋值为true。如果k不存在,则found为false
len(m)返回映射m中的项
k := range m遍历映射m中的键
k, v := range m同时遍历映射中的键和值

Go语言语句基础

Go语言也支持多重赋值,a, b = b, a

快速声明操作符:=,它的作用是同时在一个语句中声明赋值一个变量。

:=位于一个作用域的起始处时,Go语言会创建一个新的变量。

此外,Go语言必须使用;的地方:

// 当在一行中放入一条或者多条语句时,需要使用;
a, b, c := 2, 3, 5 
for a := 7; a < 8; a++ {
    fmt.Println(a)
}

类型转换

resultOfType := Type(expression)

也可以使用type关键字声明类型:

type StringsSlice []string

类型断言

接口:interface{}可以用于表示任意类型,类似于object,因此有时候需要将interface{}类型转换为我们需要的类型,这个操作称为类型断言。

resultOfType, boolean := expression.(Type) // 安全的类型断言
resultOfType := expression.(Type) // 非安全的类型断言,失败时程序会产生异常
if s, ok := s.([]string); ok { // 创建了影子变量,if的作用域中覆盖了外部的变量s
	fmt.Printf("%T -> %q\n", s, s)
}

分支语句

if optionalStatement1; booleanExpression1 {
    block1
} else if optionalStatement2; booleanExpression2 {
    block2
} else {
    block3
}

其中optionalStatement是可选的表达式,真正决定分支走向的是booleanExpression1的值。

函数

main函数是特殊的函数,main函数必须出现在main包中,main函数不可接收任何参数:

func functionName(optionalParameters) optionalReturnType { 
    block // func 函数名(参数列表) 单个返回值类型
}

func functionName(optionalParameters) (optionalReturnValues) { 
    block // func 函数名(参数列表) (返回值1 类型,返回值2 类型)
}

函数可以有任意多个参数,也可以有任意多个返回值:

func func1(first int, rest ...int) int { 
    return first // func1 函数可以接收多个int类型的参数,并且返回一个int类型的值
}

func func2(first int, second string) (int, string) { 
    return first, second // func2 接收两个参数,并且返回一个int和string类型的值
}

func func3(first int, second string) (a, b int) {
    a, b = 1, 2 // func3 接收两个参数,并返回两个int类型的值,因为返回值是命名的,所以这里可以缩写
    return
}

deferpanicrecover

defer

当存在多条defer时,以后进先出的顺序执行。

defer语句用于延迟执行一个函数或者方法或者是当前创建的匿名函数,它会在外部函数或者方法返回之前但是其返回值计算之后执行,类似于python语言中的with

panicrecover

recover捕捉到panic引发的异常的时候,将会停止panic的传播;

(练习)打印n以内的素数
package main

import (
	"fmt"
	"math"
)

func exec(n int) (result []int) {

	if n == 2 || n == 3 {
		return append(result, n)
	}

	for a := 2; a <= n; a++ {

		if ok := isexec(a); ok {
			result = append(result, a)
		}

	}
	return result

}

func isexec(n int) (ok bool) {

	if n == 2 || n == 3 {
		return true
	}
	for a := 2; a <= int(math.Sqrt(float64(n))); a++ {
		if n%a == 0 {
			return false
		}
	}
	return true
}

func main() {

	result := exec(100)

	fmt.Printf("%d", result)

}

GO语言的面向对象

Go语言以包结构来组织代码,Go语言不支持继承,只支持聚合;当标示符以大写字母开头,那么这些标示符可以导出,可以导入到其他包中使用。

自定义类型

自定义类型中的匿名字段:

type Part struct { // 基于结构体创建自定义类型 Part
    stat  string
    Count // 匿名字段
}
方法

定义方法和定义函数几乎相同,但是需要在func关键字和方法名之间写上接收者,也就是能够调用该方法的对象的类型指针或者值类型

type Count int

func (count *Count) Increment() { *count++ }  // 接受者是一个`Count`类型的指针
func (count *Count) Decrement() { *count-- }
func (count Count) IsZero() bool { return count == 0 }
  • 一个指向自定义类型的值的指针,它的方法集由该类型定义的所有方法组成,无论这些方法接受的是一个值还是一个指针。如果在指针上调用一个接受值的方法,Go语言会聪明的将该指针解引用。
  • 一个自定义类型值的方法集合则由该类型定义的接收者为值类型的方法组成,但不包括那些接收者类型为指针的方法。(如果使用去调用接收者为指针类型的方法,仍然可以成功,这是因为Go语言会自动获取值的地址传递给该方法,前提是该值是可寻址的)

接口

接口的实现和方法的重载:

package main

import "fmt"

type Human struct { // 结构体
    name  string
    age   int
    phone string
}

//Human实现SayHi方法
func (h Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

//Human实现Sing方法
func (h Human) Sing(lyrics string) {
    fmt.Println("La la la la...", lyrics)
}

type Student struct {
    Human  //匿名字段
    school string
    loan   float32
}

type Employee struct {
    Human   //匿名字段
    company string
    money   float32
}

// Employee重载Human的SayHi方法
func (e Employee) SayHi() {
    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
        e.company, e.phone)
}

//--------------------------------------------------------------
// Interface Men被Human,Student和Employee实现
// 因为这三个类型都实现了这两个方法
type Men interface {
    SayHi()
    Sing(lyrics string)
}

此实现非彼实现

接口变量值的断言

当需要获得接口变量中的值的具体类型,使用类型断言:

var men Men

	men = Human{"Li Ming", 18, "10010"}

	switch value := men.(type) {

	default:
		fmt.Print("default")

	case Men:
		fmt.Print("Men")

	case Human:
		fmt.Print("Human name:", value.name)

	}
嵌入interface
type Interface1 interface {
    Send()
    Receive()
}

type Interface2 interface {
    Interface1
    Close()
}

interface2中包含了interface1中的所有方法;

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值