Go基础语法 | 青训营笔记

63 阅读6分钟
基础语法
Go语言结构
  • 包声明
  • 引入包
  • 函数
  • 变量
  • 语句 & 表达式
  • 注释
package main
​
import "fmt"func main() {
   /* 这是我的第一个简单的程序 */
   fmt.Println("Hello, World!")
}
  • Go 语言的字符串连接可以通过 + 实现:
  • //单行注释,/**/多行注释
  • 在 Go 程序中,一行代表一个语句结束
  • 加空格提升代码的阅读性
语言变量

var 标识符 数据类型

一次申请多个变量

var identifier1, identifier2 type

第一种,默认变量

数值类型为0,布尔类型为 false,字符串为 "" (空字符串)

第二种,根据值自行判定变量类型

var v_name = value

第三种,:=是一个声明语句

var intVal int 
intVal =1 
intVal := 1 相等于

常量

const identifier [type] = value
循环语句

go语言不存在while循环,实现while循环的功能直接在for后面加相同的条件就行

同样使用关键字continue,break控制循环

数组

生成的数组固定,不方便长度的修改

数组定义

var a [5]int //一维数组
var b [2][3]int //二维数组

make函数

用于灵活的创建切片,map

s := make(string(),3) //3表示初始长度,动态数组

range函数

range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素

for key, value := range oldMap {
    newMap[key] = value
}

以上代码中的 key 和 value 是可以省略

map

无序的键值对的集合,通过 key 来快速检索数据,key 类似于索引,指向数据的值。

m := make(map[string]int10) #创建一个容量为10map,可以省略不写
​
//获取元素
v1 := m["apple"]
v2, ok := m["pear"]  // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值// 遍历 Map
for k, v := range m {
    fmt.Printf("key=%s, value=%d\n", k, v)
}
​
// 删除键值对
delete(m, "banana")
函数的定义
func function_name( [parameter list] ) [return_types] {
   函数体
}
​
//返回多个值
func swap(x, y string) (string, string) {
   return y, x
}
​
//可以通过传入指针,直接修改变量
func max(x *int) int(){
    return *x += 1
}
结构体

结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。

type Books struct {
   title string
   author string
   subject string
   book_id int
}

初始化方式

 // 创建一个新的结构体
fmt.Println(Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407})
​
// 也可以使用 key => value 格式
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407})
​
// 忽略的字段为 0 或 空
fmt.Println(Books{title: "Go 语言", author: "www.runoob.com"})

如果要访问结构体成员,需要使用点号 . 操作符

结构体指针

定义指向结构体的指针类似于其他指针变量,格式如下

var struct_pointer *Books

使用结构体指针访问结构体成员,使用 "." 操作符:

struct_pointer.title

结构体方法

也可以使用指针来构造结构体函数,格式如下

func (u user) check(password string) bool  {
    return u.password == password
}
​
func (u *user) reset(password string)  {
    u.password = password
}
​
//调用方式,直接对象.方法
a.reset("jdwwi")

go语言的字符串需要使用双引号,使用单引号会报编码错误

类型转换

类型转换用于将一种数据类型的变量转换为另外一种类型的变量

type_name(expression)

类型转换

类型转换用于将一种数据类型的变量转换为另外一种类型的变量

type_name(expression)
语言接口

Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。

接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。

Go 语言中的接口是隐式实现的,也就是说,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口。因此,我们可以通过将接口作为参数来实现对不同类型的调用,从而实现多态。

/* 定义接口 */
type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
   method_name3 [return_type]
   ...
   method_namen [return_type]
}
​
/* 定义结构体 */
type struct_name struct {
   /* variables */
}
​
/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
   /* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
   /* 方法实现*/
}
错误处理

1.直接调用

可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用 errors.New 可返回一个错误信息

2.使用结构体方法,实现error 接口

type DivideError struct {
    dividee int
    divider int
}
​
// 实现 `error` 接口
func (de *DivideError) Error() string {
    strFormat := `
    Cannot proceed, the divider is zero.
    dividee: %d
    divider: 0
`
    return fmt.Sprintf(strFormat, de.dividee)
}
并发

goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的

goroutine 语法格式

go 函数名( 参数列表 )

Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。

package main
​
import (
        "fmt"
        "time"
)
​
func say(s string) {
        for i := 0; i < 5; i++ {
                time.Sleep(100 * time.Millisecond)
                fmt.Println(s)
        }
}
​
func main() {
        go say("world")
        say("hello")
}

通道

通道(channel)是用来传递数据的一个数据结构,通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

ch <- v    // 把 v 发送到通道 ch
v := <-ch  // 从 ch 接收数据
           // 并把值赋给 v

声明通道

ch := make(chan int)

以下实例通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和:

package main
​
import "fmt"func sum(s []int, c chan int) {
        sum := 0
        for _, v := range s {
                sum += v
        }
        c <- sum // 把 sum 发送到通道 c
}
​
func main() {
        s := []int{7, 2, 8, -9, 4, 0}
​
        c := make(chan int)
        go sum(s[:len(s)/2], c)
        go sum(s[len(s)/2:], c)
        x, y := <-c, <-c // 从通道 c 中接收
​
        fmt.Println(x, y, x+y)
}

通道缓冲区

通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:

ch := make(chan int, 100)

带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。

不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。

注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。