【Go】超详细Go入门

环境配置
1 go语言安装
https://go-zh.org/doc/install#windows

2 goland ide 安装与配置
http://8000.woa.com/v2/Software/Detail/106

3 go 语言基础快速入门
https://tour.go-zh.org/welcome/1

1.包

单个:
import "fmt"
多个:
import (
       "fmt"
       "math/rand"
)

导出名
在 Go 中,如果一个名字以大写字母开头,那么它就是已导出的。任何未导出的名字在该包外均无法访问。

2. 函数

2.1 类型在变量名之后:

func add(x int, y int) int

2.2 两个变量相同可省略:

func add(x, y int) int

2.3 函数可以返回任意数量的返回值:

func swap(x, y string) (string, string)

2.4 Go 的返回值可被命名,它们会被视作定义在函数顶部的变量:

func split(sum int) (x, y int)

3. 变量

3.1 var语句声明类型在最后:

var c, python, java bool

3.2 变量声明可以包含初始值,每个变量对应一个,变量会从初始值获得类型:

var c,python, java = true, false, "no!"

3.3 简洁赋值语句 := 可在类型明确的地方代替 var 声明, := 结构不能在函数外使用:

c, python, java := true, false, "no!"

3.4 基本类型Go 的基本类型有

bool
string
int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 的别名
rune // int32 的别名 表示一个 Unicode 码点
float32 float64
complex64 complex128

int, uint 和 uintptr 在 32 位系统上通常为 32 位宽,在 64 位系统上则为 64 位宽

3.5 语法块

var (
       ToBe   bool       = false
       MaxInt uint64     = 1<<64 - 1
       z      complex128 = cmplx.Sqrt(-5 + 12i)
)

3.6 零值
数值类型为 0,
布尔类型为 false,
字符串为 “”(空字符串)。

3.7 类型转换

var f float64 = float64(i)
f := float64(i)

3.8 类型推导
在声明一个变量而不指定其类型时(即使用不带类型的 := 语法或 var = 表达式语法),变量的类型由右值推导得出。不过当右边包含未指明类型的数值常量时,新变量的类型就可能是 int, float64 或 complex128 了,这取决于常量的精度:

3.9 常量声明

const Pi = 3.14

常量不能用 := 语法声明。

3.10 数值常量
一个未指定类型的常量由上下文来决定其类型。

4. For

4.1

for i := 0; i < 10; i++ {
	sum += i
}

初始化语句通常为一句短变量声明,该变量声明仅在 for 语句的作用域中可见。
后面的三个构成部分外没有小括号, 大括号 { } 则是必须的。
4.2
for 是 Go 中的 while

for sum < 1000 {
	sum += sum
}

5. If

5.1

if x < 0 {
	return sqrt(-x) + "i"
}

表达式外无需小括号 ( ) ,而大括号 { } 则是必须的。
5.2 if 的简短语句

if v := math.Pow(x, n); v < lim {
	return v
} else {
	fmt.Printf("%g >= %g\n", v, lim)
}

同 for 一样, if 语句可以在条件表达式前执行一个简单的语句。
该语句声明的变量作用域仅在 if 之内。

6. Switch

6.1 Go 自动提供了在这些语言中每个 case 后面所需的 break 语句。 除非以 fallthrough 语句结束,否则分支会自动终止。
case 无需为常量,且取值不必为整数。

switch os := runtime.GOOS; os {
	case "darwin":
		fmt.Println("OS X.")
	case "linux":
		fmt.Println("Linux.")
	default:
		// freebsd, openbsd,
		// plan9, windows...
		fmt.Printf("%s.\n", os)
}

6.2 switch 的 case 语句从上到下顺次执行,直到匹配成功时停止。
6.3 没有条件的 switch 同 switch true 一样。

switch {
	case t.Hour() < 12:
		fmt.Println("Good morning!")
	case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
}

7. defer

7.1 defer 语句会将函数推迟到外层函数返回之后执行。

defer fmt.Println("world")
fmt.Println("hello")

7.2 推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用。

for i := 0; i < 10; i++ {
	defer fmt.Println(i)
}

8. 指针

指针保存了值的内存地址。

var p *int
i := 42
p = &i
与 C 不同,Go 没有指针运算。
fmt.Println(*p) // 通过指针 p 读取 i
*p = 21         // 通过指针 p 设置 i

9. 结构体

9.1

type Vertex struct {
	X int
	Y int
}
v := Vertex{1, 2}
v.X = 4

9.2 结构体指针

	v := Vertex{1, 2}
	p := &v
	p.X = 1e9

可以通过 (*p).X 来访问其字段 X。不过这么写太啰嗦了,所以语言也允许我们使用隐式间接引用,直接写 p.X 就可以。
9.3 结构体文法

v1 = Vertex{1, 2}  // 创建一个 Vertex 类型的结构体
v2 = Vertex{X: 1}  // Y:0 被隐式地赋予
v3 = Vertex{}      // X:0 Y:0
p  = &Vertex{1, 2} // 创建一个 *Vertex 类型的结构体(指针)

10. 数组

var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)

primes := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes)

11. 切片

10.1 每个数组的大小都是固定的。而切片则为数组元素提供动态大小的、灵活的视角。

a[low : high]

它会选择一个半开区间,包括第一个元素,但排除最后一个元素。
10.2 切片就像数组的引用,切片并不存储任何数据,它只是描述了底层数组中的一段。更改切片的元素会修改其底层数组中对应的元素。
10.3 下面这样则会创建一个和上面相同的数组,然后构建一个引用了它的切片:

[]bool{true, true, false}

10.4 切片下界的默认值为 0,上界则是该切片的长度。
10.5 切片的长度就是它所包含的元素个数。切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。切片 s 的长度和容量可通过表达式 len(s) 和 cap(s) 来获取。
10.6 切片的零值是 nil。if s == nil
10.7 make 函数会分配一个元素为零值的数组并返回一个引用了它的切片:

a := make([]int, 5)  // len(a)=5
b := make([]int, 0, 5) // len(b)=0, cap(b)=5

10.8 切片可包含任何类型,甚至包括其它的切片。
10.9 append 的结果是一个包含原切片所有元素加上新添加元素的切片。当 s 的底层数组太小,不足以容纳所有给定的值时,它就会分配一个更大的数组。返回的切片会指向这个新分配的数组。

12. Range

10.1 for 循环的 range 形式可遍历切片或映射。
当使用 for 循环遍历切片时,每次迭代都会返回两个值。第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本。

for i, v := range pow {
              fmt.Printf("2**%d = %d\n", i, v)
       }

12.2 可以将下标或值赋予 _ 来忽略它。

for i, _ := range pow
for _, value := range pow

若你只需要索引,忽略第二个变量即可。

for i := range pow

13. 映射

13.1 映射将键映射到值。
映射的零值为 nil 。nil 映射既没有键,也不能添加键。
make 函数会返回给定类型的映射,并将其初始化备用。

var m map[string]Vertex
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{
	40.68433, -74.39967,
}

13.2

var m = map[string]Vertex{
       "Bell Labs": Vertex{
              40.68433, -74.39967,
       },
       "Google": Vertex{
              37.42202, -122.08408,
       },
}

13.3

var m = map[string]Vertex{
       "Bell Labs": {40.68433, -74.39967},
       "Google":    {37.42202, -122.08408},
}

13.4

m := make(map[string]int)
m["Answer"] = 42
fmt.Println("The value:", m["Answer"])
m["Answer"] = 48
fmt.Println("The value:", m["Answer"])
delete(m, "Answer")
fmt.Println("The value:", m["Answer"])
v, ok := m["Answer"]
fmt.Println("The value:", v, "Present?", ok)

#14. 函数值
14.1 函数也是值。它们可以像其它值一样传递。
函数值可以用作函数的参数或返回值。

hypot := func(x, y float64) float64 {
	return math.Sqrt(xx + yy)
}
fmt.Println(hypot(5, 12))

14.2 Go 函数可以是一个闭包。闭包是一个函数值,它引用了其函数体之外的变量。该函数可以访问并赋予其引用的变量的值,换句话说,该函数被这些变量绑定在一起。
在这里插入图片描述

15.方法

15.1

type Vertex struct {
       X, Y float64
}
func (v Vertex) Abs() float64 {
       return math.Sqrt(v.Xv.X + v.Yv.Y)
}
func main() {
       v := Vertex{3, 4}
       fmt.Println(v.Abs())
}

可以为非结构体类型声明方法。接收者的类型定义和方法声明必须在同一包内;不能为内建类型声明方法。

type MyFloat float64
func (f MyFloat) Abs() float64 {
       if f < 0 {
              return float64(-f)
       }
       return float64(f)
}
func main() {
       f := MyFloat(-math.Sqrt2)
       fmt.Println(f.Abs())
}

15.2. 指针接收者
指针接收者的方法可以修改接收者指向的值(就像 Scale 在这做的)。由于方法经常需要修改它的接收者,指针接收者比值接收者更常用。
若使用值接收者,那么 Scale 方法会对原始 Vertex 值的副本进行操作。

func (v *Vertex) Scale(f float64) {
       v.X = v.X * f
       v.Y = v.Y * f
}

15.3 指针重定向
比较前两个程序,你大概会注意到带指针参数的函数必须接受一个指针:

var v Vertex
ScaleFunc(v, 5)  // 编译错误!
ScaleFunc(&v, 5) // OK

而以指针为接收者的方法被调用时,接收者既能为值又能为指针:

var v Vertex
v.Scale(5)  // OK
p := &v
p.Scale(10) // OK

由于 Scale 方法有一个指针接收者,为方便起见,Go 会将语句 v.Scale(5) 解释为 (&v).Scale(5)。
接受一个值作为参数的函数必须接受一个指定类型的值:

var v Vertex
fmt.Println(AbsFunc(v))  // OK
fmt.Println(AbsFunc(&v)) // 编译错误!

而以值为接收者的方法被调用时,接收者既能为值又能为指针:

var v Vertex
fmt.Println(v.Abs()) // OK
p := &v
fmt.Println(p.Abs()) // OK

这种情况下,方法调用 p.Abs() 会被解释为 (*p).Abs()。
使用指针接收者的原因有二:
首先,方法能够修改其接收者指向的值。
其次,这样可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,这样做会更加高效。

16. 接口

16.1 接口类型 是由一组方法签名定义的集合。
接口类型的变量可以保存任何实现了这些方法的值。
由于 Abs 方法只为 *Vertex (指针类型)定义,因此 Vertex(值类型)并未实现 Abser。

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

16.2 隐式实现
类型通过实现一个接口的所有方法来实现该接口。既然无需专门显式声明,也就没有ldquo;implementsrdquo;关键字。
隐式接口从接口的实现中解耦了定义,这样接口的实现可以出现在任何包中,无需提前准备。
16.3 接口值
接口也是值。它们可以像其它值一样传递。
接口值可以用作函数的参数或返回值。
在内部,接口值可以看做包含值和具体类型的元组:
(value, type)

func describe(i I) {
       fmt.Printf("(%v, %T)\n", i, i)
}

16.4
在这里插入图片描述

16.5 底层值为 nil 的接口值
即便接口内的具体值为 nil,方法仍然会被 nil 接收者调用。

func (t *T) M() {
       if t == nil {
              fmt.Println("<nil>")
              return
       }
       fmt.Println(t.S)
}
var t *T
i = t
describe(i)
i.M()

16.6 nil 接口值
nil 接口值既不保存值也不保存具体类型。

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

16.7 空接口
指定了零个方法的接口值被称为 空接口:
interface{}
空接口可保存任何类型的值。(因为每个类型都至少实现了零个方法。)
空接口被用来处理未知类型的值。例如,fmt.Print 可接受类型为 interface{} 的任意数量的参数。

func main() {
       var i interface{}
       describe(i)
       i = 42
       describe(i)
       i = "hello"
       describe(i)
}

16.8 类型断言
类型断言 提供了访问接口值底层具体值的方式。

t := i.(T)

该语句断言接口值 i 保存了具体类型 T,并将其底层类型为 T 的值赋予变量 t。
若 i 并未保存 T 类型的值,该语句就会触发一个恐慌。
为了 判断 一个接口值是否保存了一个特定的类型,类型断言可返回两个值:其底层值以及一个报告断言是否成功的布尔值。

t, ok := i.(T)
var i interface{} = "hello"
s := i.(string)
fmt.Println(s)
s, ok := i.(string)
fmt.Println(s, ok)
f, ok := i.(float64)
fmt.Println(f, ok)
f = i.(float64) // 报错(panic)
fmt.Println(f)

16.9 类型选择
类型选择 是一种按顺序从几个类型断言中选择分支的结构。
类型选择与一般的 switch 语句相似,不过类型选择中的 case 为类型(而非值), 它们针对给定接口值所存储的值的类型进行比较。

func do(i interface{}) {
       switch v := i.(type) {
       case int:
              fmt.Printf("Twice %v is %v\n", v, v*2)
       case string:
              fmt.Printf("%q is %v bytes long\n", v, len(v))
       default:
              fmt.Printf("I don't know about type %T!\n", v)
       }
}

17. Stringer

fmt 包中定义的 Stringer 是最普遍的接口之一。

type Stringer interface {
    String() string
}
func (p Person) String() string {
       return fmt.Sprintf("%v (%v years)", p.Name, p.Age)

18. 错误

在这里插入图片描述

19. Reader

r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8)
for {
	n, err := r.Read(b)
	fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
	fmt.Printf("b[:n] = %q\n", b[:n])
	if err == io.EOF {
		break
	}
}

19. Go 程

Go 程(goroutine)是由 Go 运行时管理的轻量级线程。

go f(x, y, z)

会启动一个新的 Go 程并执行f(x, y, z)
f, x, y 和 z 的求值发生在当前的 Go 程中,而 f 的执行发生在新的 Go 程中。

20. 信道

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

ch <- v    // 将 v 发送至信道 ch。
v := <-ch  // 从 ch 接收值并赋予 v。

(箭头就是数据流的方向。)
和映射与切片一样,信道在使用前必须创建:

ch := make(chan int)

默认情况下,发送和接收操作在另一端准备好之前都会阻塞。这使得 Go 程可以在没有显式的锁或竞态变量的情况下进行同步。
在这里插入图片描述

带缓冲的信道
信道可以是 带缓冲的。将缓冲长度作为第二个参数提供给 make 来初始化一个带缓冲的信道:

ch := make(chan int, 100)

range 和 close
发送者可通过 close 关闭一个信道来表示没有需要发送的值了。接收者可以通过为接收表达式分配第二个参数来测试信道是否被关闭:若没有值可以接收且信道已被关闭,那么在执行完

v, ok := <-ch

之后 ok 会被设置为 false。
循环 for i := range c 会不断从信道接收值,直到它被关闭。注意: 只有发送者才能关闭信道,而接收者不能。向一个已经关闭的信道发送数据会引发程序恐慌(panic)。
在这里插入图片描述

20. select 语句

select 语句使一个 Go 程可以等待多个通信操作。
select 会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行。
在这里插入图片描述

默认选择
当 select 中的其它分支都没有准备好时,default 分支就会执行。
为了在尝试发送或者接收时不发生阻塞,可使用 default 分支:

select {
case i := <-c:
    // 使用 i
default:
    // 从 c 中接收会阻塞时执行
}

21. sync.Mutex

互斥(mutualexclusion)* ,我们通常使用 互斥锁(Mutex) 这一数据结构来提供这种机制。
Go 标准库中提供了 sync.Mutex 互斥锁类型及其两个方法:

Lock
Unlock

我们可以通过在代码前调用 Lock 方法,在代码后调用 Unlock 方法来保证一段代码的互斥执行。参见 Inc 方法。
我们也可以用 defer 语句来保证互斥锁一定会被解锁。参见 Value 方法。
在这里插入图片描述

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Golang(又称为Go)是Google公司开发出的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。Go语言具有以下特点:简洁、高效、安全、并发、跨平台等。在学习Golang入门阶段,你可以了解以下几个方面: 1. Golang的特点:Go语言具有简洁、高效、安全、并发、跨平台等特点,这些特点使得Golang成为一种非常流行的编程语言。 2. Golang的变量作用域:在Golang中,变量的作用域可以分为全局作用域和局部作用域。 3. Golang的执行流程的两种方式:Golang的执行流程可以通过顺序执行和条件执行两种方式来实现。 4. 在Linux上安装Golang语言开发包:要开始学习Golang,你需要在Linux上安装Golang语言开发包。你可以在Golang官方网站上下载适合你的操作系统和架构的Golang安装包,并按照官方文档进行安装。 5. Golang变量的基本使用:在Golang中,你可以使用var关键字声明变量,并使用:=运算符进行变量的初始化和赋值操作。 6. Golang中整数的类型:在Golang中,整数类型可以分为有符号整数和无符号整数,不同的整数类型有不同的取值范围。 7. Golang基本数据类型的默认值:在Golang中,当你声明一个变量但没有进行初始化时,它的默认值将根据其数据类型而定。 8. 基本数据类型转换为String类型:在Golang中,你可以使用strconv包提供的函数将基本数据类型转换为字符串类型。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值