go语言基础学习

变量/常量

go语言的变量声明主要有两种形式,一种是通过var声明(常量也可通过const声明),如下所示:

var t bool //= true,省略后面的赋值时,变量会赋予默认值

//可单个声明,可分组为语法块
const (
    x int = 12
    f float32 = 1.2
    s string = "hello"
)

注意当声明省略了基本类型的时候,会根据赋值进行类型推导

var i = 64 //int

还有一种是短变量声明,通过:=在类型明确的地方代替var,这里也用到了类型推导(const无法这样声明)

i := 12 //int
f := 12.3 //float64
c := 12 + 3i //complex128

类型转换的方法为T(v),例如f := float64(i)

流程控制语句

for

for有三种形式的循环

var sum int = 1
//形式一,有三个条件,与Java的形式区别不大
for i := 1; i < 10; i++ {
    sum += i
}
//形式二,有一个条件,类似于Java中的while
for sum < 100 {
    sum += sum
}
//形式三,没有条件,是一个无限循环
for {
    sum += 1
}

if

if有两种形式

var x int = -3
//形式一,类似于Java中的if
if x < 0 {
    x = -x
}
//形式二,在条件表达式前执行一个简单的语句,语句的变量作用域只在if/else内
if i := 3; i > 0 {
    i = -i
} else {
    //...
}

defer

defer会将后面跟的函数推迟到外层函数返回之后执行

注意事项:

  1. 推迟调用的函数会立即求值,最后被调用
  2. 被推迟的函数被压入一个栈中,故被调用的顺序为后进先出
fmt.Println("counting")
for i := 0; i < 3; i++ {
    defer fmt.Println(i)
}
fmt.Println("done")
/* 结果为
counting
done
2
1
0
*/

更多类型

指针

指针和C++类似

var x int = 13
var y *int = &x
fmt.Println(*y)

结构体

一组字段

type Vertex struct {
    X int
    Y int
}

v := Vertex{1, 2}
v := Vertex{X: 1} //结构体文法,未初始化的字段会隐式初始化

结构体指针可以直接通过隐式间接引用(同结构体直接访问字段,如p.X

数组/切片

注意数组是固定数量

var a [10]int

想要提供动态大小,就需要切片,切片类似于数组的引用,不存储任何数据,引用了底层的数组,切片有两种声明方式

primes := [6]int{1, 2, 3, 4, 5, 6}
var s1 []int = primes[:4] //方法一,通过数组切片获得
var s2 []int = {1, 2, 3, 4} //方法二,构建一个数组,同时构建一个引用他得切片
var s3 []int = make([]int, 4[, 5]) //方法三,通过make创建len(s3) = 4, cap(s3) = 5

通过func append(s []T, vs ..T) []T向切片追加元素

var s []int
s = append(s, 0)
s = append(s, 1, 2, 3, 4)

range

for循环的range形式可遍历切片映射

var pow = []int{1, 2, 3, 4}
for i, v := range pow {
    fmt.Printf("index is %v, value is %v\n", i, v)
}
//单独获取索引
for i := range pow { 
    fmt.Printf("index is %v\n", i)
}

映射

映射就是map,将键映射到值,有两种声明方式

var m map[string]int = make(map[string]int) //方式一,通过make创建
var m map[string]int = map[string]int{
    "helllo": 1,
    "world": 2
} //方式二,文法方式,可创建空映射

修改映射

//修改或添加元素
m[key] = elem
//获得元素
elem = m[key]
elem, ok = m[key]
//删除元素
delete(m, key)

函数闭包

我个人的理解是,一个函数(如func1)的返回值也是一个函数(如func2),同时返回的函数中(func2)引用了了外边函数定义的变量,例:

//内部返回的函数使用了left和right
func fibonacci() func() int {
	left := 0
	right := 1
	return func() int {
		cur := left
		left = right
		right += cur
		return cur
	}
}

func main() {
	f := fibonacci()
    //闭包使用过程中会改变所引用的变量,从而导致结果的不同
	for i := 0; i < 10; i++ {
		fmt.Println(f())
	}
}

方法和接口

方法

方法可以当作带接收者参数的函数

type Vertex struct {
    X, Y float64
}

//接收者参数位于 func 关键字和方法名之间
func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X * v.X + v.Y * v.Y)
}

接收者参数可以分为值接收者和指针接收者,对于值接收者,方法只会对原始值的副本进行操作

type Vertex struct {
    X, Y float64
}

//不会修改原始值
func (v Vertex) Scale1(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

//会修改原始值
func (v *Vertex) Scale2(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

注:当对象为nil时,go可以通过方法优雅的处理这种情况

type Vertex struct {
	X, Y float64
}

func (v *Vertex) M() {
	if v == nil {
		fmt.Println("<nil>")
		return
	}
	fmt.Println(v)
}

func main() {
	var v *Vertex
	v.M() //打印<nil>
}

接口

接口类型是由一组方法签名定义的集合,其变量可以保存任何实现了这些方法的值(故无需使用implements关键字显式声明)

type Abser interface {
	Abs() float64
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

func main() {
    var a Abser
    a = MyFloat(-math.Sqrt2)
    fmt.Println(a.Abs())
}

在内部,接口值可以看做包含值和具体类型的元组:(value, type)

接口为nil的值既不保存值也不保存具体类型,在调用方法是会产生运行错误

type I interface {
	M()
}

func main() {
	var i I
	describe(i)
	i.M()
}

func describe(i I) {
	fmt.Printf("(%v, %T)\n", i, i)
}
/*打印
(<nil>, <nil>)
panic: runtime error: invalid memory address or nil pointer dereference
*/

指定了零个方法的接口值被称为空接口:interface{},空接口可保存任何类型的值

类型断言提供了访问接口值底层具体值的方式,有两种断言方式

type Vertex struct {
	X, Y float64
}

func main() {
	var i interface{} = Vertex{1, 2}
    //当接口未保存此类型的值,会触发恐慌
    s := i.(Vertex)
	fmt.Println(s)

    //用这种方式来判断是否保存了特定类型
	s, ok := i.(float64)
	if ok {
		fmt.Println(s)
		return
	}
}

并发

Go程

Go运行时管理的轻量级线程

//f,x,y和z的求值发生在当前的Go程中,而f的执行发生在新的Go程中
func f(x, y, z int) {
	//...
}

func main() {
	go f(1, 2, 3)
}

线程通讯/同步

信道

信道是带有类型的管道,你可以通过它用信道操作符<-来发送或者接收值

//信道的创建
ch := make(chan int)
ch2 := make(chan int, 5) //带缓冲的信道,仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞

//信道的发送
sum := 100
ch <- sum
//信道的接收
s1 := <-ch
for i := range ch {
	//...
}

发送者可通过close关闭信道,表示没有需要发送的值

ch := make(chan int, 10)
for i := 0; i < 10; i++ {
	ch <- i
}
close(ch)

通过range可以不断从信道中接收值,直到信道无可接收数据且被关闭

//接上文
//close(ch)
for i := range ch {
	fmt.Println(i)
}

注:信道与文件不同,通常情况下无需关闭它们。只有在必须告诉接收者不再有需要发送的值时才有必要关闭

当需要从多个通讯中仅等待一个通讯的执行完才能进行后续操作时,可以用select语句

func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		//三个case一趟循环只能执行其中一个
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		default: //其他分支都没准备好,就执行这个
			fmt.Println("default")
			return
		}
	}
}

sync.Mutex

go标准库中实现互斥概念的数据结构为互斥锁(mutex)

var mux sync.Mutex

mux.Lock()
//...
mux.UnLock() //可以用 defer mux.UnLock()来保证一定会被解锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值