学习仪式
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
/* 我是注释 */
// 我也是注释
}
- 包声明:第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包
- 引入包:下一行
import "fmt"
告诉 Go 编译器这个程序需要使用 fmt 包 - 函数:下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数
- // 单行注释 ,/* */ 多行注释
- Go 语言一行则是一个语句,不需要结尾写分号
标识符
就是给变量之类取名的东西,标识符可以由多个字母(A - Z,a-z)数字(1 - 9),下划线 _ 组成,但是第一个字符必须是字母或下划线而不能是数字。
例如无效标识符
- 1ab (数字开头)
- case (关键字)
- a + b (表达式)
字符串连接
通过 +
进行连接
package main
import "fmt"
func main() {
fmt.Println("你今天" + "真好看")
}
输出结果
你今天真好看
关键字或保留字
break | default | func | interface | select |
---|---|---|---|---|
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
除了以上介绍的这些关键字,Go 语言还有 36 个预定义标识符:
append | bool | byte | cap | close | complex | complex64 | complex128 | uint16 |
---|---|---|---|---|---|---|---|---|
copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 |
int32 | int64 | iota | len | make | new | nil | panic | uint64 |
println | real | recover | string | true | uint | uint8 | uintptr |
程序一般由关键字、常量、变量、运算符、类型和函数组成。
空格
在 Go 的世界中,变量声明需要空格隔开
var name string
var age int
加入空格 也可以让代码更美观
result = a + b
格式化字符串
- Sprintf 根据格式化参数生成格式化的字符串并返回该字符串。
- Printf 根据格式化参数生成格式化的字符串并写入标准输出。
package main
import "fmt"
func main() {
var money = 100
var day = "2023-1-13"
var str = "在%s这天我捡到了%d块钱"
var target = fmt.Sprintf(str, day, money)
fmt.Println(target)
}
输出结果
在2023-1-13这天我捡到了100块钱
数据类型
- 布尔类型
- true
- false
- 数字类型
- int
- float32
- float64
- 字符串类型
- 派生类型
- 指针类型
- 数组类型
- 结构化类型
- Channel 类型
- 函数类型
- 切片类型
- 接口类型
- Map 类型
数字类型
序号 | 类型和描述 |
---|---|
1 | uint8 无符号 8 位整型 (0 到 255) |
2 | uint16 无符号 16 位整型 (0 到 65535) |
3 | uint32 无符号 32 位整型 (0 到 4294967295) |
4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
5 | int8 有符号 8 位整型 (-128 到 127) |
6 | int16 有符号 16 位整型 (-32768 到 32767) |
7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
浮点型
序号 | 类型和描述 |
---|---|
1 | float32 IEEE-754 32位浮点型数 |
2 | float64 IEEE-754 64位浮点型数 |
3 | complex64 32 位实数和虚数 |
4 | complex128 64 位实数和虚数 |
其他数字类型
序号 | 类型和描述 |
---|---|
1 | byte 类似 uint8 |
2 | rune 类似 int32 |
3 | uint 32 或 64 位 |
4 | int 与 uint 一样大小 |
5 | uintptr 无符号整型,用于存放一个指针 |
变量
声明变量一般使用 var 关键字:
var name string
var 变量名 类型
也可以一次声明多个变量
var name,phone string
package main
import "fmt"
func main() {
var name, phone string
name = "小明"
phone = "8208208820"
fmt.Println(name + "的电话号码是" + phone)
}
输出结果是
小明的电话号码是8208208820
如果变量声明时不赋值,那么就是默认的零值。
- 数值类型(包括complex64/128)为 0
- 布尔类型为 false
- 字符串为 “”(空字符串)
-
以下几种类型为 nil:
var a *int var a []int var a map[string] int var a chan int var a func(string) int var a error // error 是接口
声明方式
- var
- 自行判定
- :=
package main
import "fmt"
func main() {
// 手动指定类型
var a int = 10
// 自行判断
var b = 10
// :=
c := 10
fmt.Println(a, b, c)
}
结果
10 10 10
多变量声明
var 变量名1, 变量名2, 变量名3 类型
变量名1, 变量名2, 变量名3 = 值1, 值2, 值3
var 变量名1, 变量名2, 变量名3 = 值1, 值2, 值3
变量名1, 变量名2, 变量名3 := 值1, 值2, 值3
// 这种写法一般是全局变量
var (
变量名1 类型1
变量名2 类型2
)
package main
import "fmt"
/**
多变量声明
*/
var (
x int
y bool
)
var a, b int = 1, 2
var c, d = 3, 4
func main() {
// := 只能在函数体内使用
e, f := 5, 6
fmt.Println(x, y, a, b, c, d, e, f)
}
输出结果
0 false 1 2 3 4 5 6
常量
常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式:
const 常量名 类型 = 值
const LENGTH int = 100
package main
import "fmt"
// 常量
func main() {
const LENGTH int = 100
const WIDTH = 50
var area int
area = LENGTH * WIDTH
fmt.Println("area is %d", area)
}
结果
area is %d 5000
还可以用作枚举
const (
MAN = 0
WOMAN = 1
)
也可以当做函数表达式
const (
a1 = "hahah"
b1 = len(a1)
c1 = unsafe.Sizeof(a1)
)
数组
var 数组名[大小] 类型
var numbers [10] int
条件
if (条件) {
表达式
}
循环
// 经典 for
for init; condition; post {
}
// 类似while
for condition {
}
Range
这种格式的循环可以对字符串、数组、切片等进行迭代输出元素。
for i, value := range values {
}
例
numbers := []int{1, 2, 3, 4, 5, 6}
for i, x := range numbers {
fmt.Println(i, x)
}
函数
func function_name( [parameter list] ) [return_types] {
}
// 求两数最大值
func max(num1, num2 int) int {
var result int
if num1 > num2 {
result = num1
} else {
result = num2
}
return result
}
// 返回多值
func swap(val1, val2 string) (string, string) {
return val2, val1
}
指针
Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。
func main() {
a := 10
fmt.Println("变量地址%x", &a)
}
什么是指针
一个指针变量指向了一个值的内存地址。
类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:
var 名称 *类型
package main
import "fmt"
func main() {
var name string = "karl"
var address *string // 声明指针变量
address = &name
fmt.Printf("address 指向的地址为 %x\n\n", address)
fmt.Printf("name 地址为 %x\n\n", &name)
fmt.Printf("adress指向的值为 %s\n\n", *address)
}
空指针
package main
import "fmt"
func main() {
var ptr *int
fmt.Printf("ptr value is : %x\n", ptr)
if (ptr != nil) {
fmt.Println("ptr 不是 空指针")
} else {
fmt.Println("ptr 是空指针")
}
}
結果
ptr value is : 0
ptr 是空指针
结构体
声明:
type 结构体名字 struct {
成员1 类型
成员2 类型
...
}
使用
变量名 := 结构体名字{值1, 值2, 值3...}
或者
变量名 := 结构体名字{成员1: 值1, 成员2: 值2, 成员3: 值3...}
访问
变量名.成员名
例:
type Person struct {
name string
age int
address string
}
func main() {
karl := Person{"karl", 18, "中国"}
fmt.Println(karl)
}
// 访问成员
println(karl.age)
切片
// 声明
切片名 := make([]类型, 长度)
或者
var 切片名 []类型
// 长度
len(切片名)
// 最长容量
cap(切片名)
// nil 切片
var numbers []int // 不初始化 长度为0
if (number == nil) -> true
// 截取
切片名[startIndex: endIndex]
// 追加
s = append(切片名, 追加元素)
// copy
append(目标切片, 原切片)
map
// 创建
var map名 map[key类型] value类型 // 默认是nil
or
map名 := make(map[key类型] value类型)
// 增
map名[key1] = 值1
//删
delete(map名, key)
// 遍历
for key := range map名 {
value = map[key]
}
类型转换
转换类型(原值)
接口
// 定义接口
type 接口名 interface {
方法名1 返回类型1
...
}
// 定义结构体
type 结构体名 struct {
}
// 实现方法
func (结构体名 结构体) 接口方法名 {
}
// 使用
var 变量 接口名
变量.new(结构体)
变量.方法名
type Phone interface {
call()
}
type HuaweiPhone struct {
}
type MiPhone struct {
}
func (huaweiPhone HuaweiPhone) call() {
fmt.Println("我是华为手机")
}
func (miPhone MiPhone) call() {
fmt.Println("我是小米手机")
}
func main() {
var phone Phone
phone = new(HuaweiPhone)
phone.call()
var miPhone Phone
miPhone = new(MiPhone)
miPhone.call()
}
错误处理
return errors.New(“错误信息”)
// a ÷ b
func divide(a, b float64) (float64, error) {
if (b < 0) {
return 0,errors.New("除 0 错误")
}
// code
}
Go并发
goroutine
go 函数名(参数列表)
// eg.
go f1(a, b, c)
// 开启一个新的goroutine
f1(a, b, c)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(10 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("hi")
go say("xi")
say("aa")
}
// 结果
hi
xi
aa
aa
xi
hi
xi
aa
hi
aa
xi
hi
xi
aa
channel
通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <-
用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据
// 并把值赋给 v
func main() {
s := []int{1, 2, 3, 4, 5, 6, 8, 9, 10}
c := make(chan int)
fmt.Println(len(s))
go getSum(s[len(s)/2:], c)
go getSum(s[:len(s)/2+1], c)
a, b := <-c, <-c
fmt.Println(a, b, a+b)
}
// 求和
func getSum(s []int, c chan int) {
sum := 0
for _, i := range s {
sum += i
}
c <- sum
}
设置缓冲区大小
ch := make(chan int, 10)
// 遍历
ch := make(chan int, 2)
ch <- 1
ch <- 2
println(<-ch)
println(<-ch)
// 关闭 在读数据时,如果管道中没有内容,将会发送堵塞。
close(ch)