执行GO程序
$ go run hello.go
Hello, World!
go语言基础语法
行分隔符
在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。
如果打算将多个语句写在同一行,它们则必须使用 ; 人为区分,但在实际开发中并不鼓励这种做法。
字符串连接
Go 语言的字符串连接可以通过 + 实现,跟其他语言的实现是一致的
GO语言的空格
1.Go 语言中变量的声明必须使用空格隔开
如:
const Pi float64 = 3.14159265358979323846
2.在变量与运算符间加入空格,程序看起来更加美观,写的代码毕竟还是要大家看的,要有格式
3.在关键字和表达式之间要使用空格
4.在函数调用时,函数名和左边等号之间要使用空格,参数之间也要使用空格
例如:
result := add(2, 3)
格式化字符串
1.Sprintf
根据格式化参数生成格式化的字符串并返回该字符串
`实例:
package main
import (
"fmt"
)
func main() {
// %d 表示整型数字,%s 表示字符串
var stockcode=123
var enddate="2020-12-31"
var url="Code=%d&endDate=%s"
var target_url=fmt.Sprintf(url,stockcode,enddate)
fmt.Println(target_url)
}
输出结果为:
Code=123&endDate=2020-12-31
2.Printf
根据格式化参数生成格式化的字符串并写入标准输出
package main
import (
"fmt"
)
func main() {
// %d 表示整型数字,%s 表示字符串
var stockcode=123
var enddate="2020-12-31"
var url="Code=%d&endDate=%s"
fmt.Printf(url,stockcode,enddate)
}
输出结果为:
Code=123&endDate=2020-12-31
GO语言变量
声明变量的一般形式是使用 var 关键字
var identifier type
可以一次声明多个变量:
var identifier1, identifier2 type
1.变量声明
第一种,指定变量类型,如果没有初始化,则变量默认为零值。
package main
import "fmt"
func main() {
// 声明一个变量并初始化
var a = "RUNOOB"
fmt.Println(a)
// 没有初始化就为零值
var b int
fmt.Println(b)
// bool 零值为 false
var c bool
fmt.Println(c)
}
数值类型(包括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 v_name = value
第三种,如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误,格式
var intVal int
intVal :=1 // 这时候会产生编译错误,因为 intVal 已经声明,不需要重新声明
多变量声明
//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断
vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误
// 这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
2.值类型和引用类型
1.所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:
当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝
3.简短形式,使用 := 赋值操作符
这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。
如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误
列如:
package main
import "fmt"
func main() {
var a string = "abc"
fmt.Println("hello, world")
}
尝试编译这段代码将得到错误** a declared but not used**
但是全局变量是允许声明但不使用的。 同一类型的多个变量可以声明在同一行
如果你想要交换两个变量的值,则可以简单地使用 a, b = b, a,两个变量的类型必须是相同
空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。
_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。
GO语言常量
以下实例演示了常量的应用
package main
import "fmt"
func main() {
const LENGTH int = 10
const WIDTH int = 5
var area int
const a, b, c = 1, false, "str" //多重赋值
area = LENGTH * WIDTH
fmt.Printf("面积为 : %d", area)
println()
println(a, b, c)
}
常量还可以用作枚举
const (
Unknown = 0
Female = 1
Male = 2
)
常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过
1.iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
const (
a = iota
b
c
)
package main
import "fmt"
func main() {
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
运行结果:
0 1 2 ha ha 100 100 7 8
Go 语言 if 语句嵌套
func main() {
/* 定义局部变量 */
var a int = 100
var b int = 200
/* 判断条件 */
if a == 100 {
/* if 条件语句为 true 执行 */
if b == 200 {
/* if 条件语句为 true 执行 */
fmt.Printf("a 的值为 100 , b 的值为 200\n" );
}
}
fmt.Printf("a 值为 : %d\n", a );
fmt.Printf("b 值为 : %d\n", b );
}
Type Switch
switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。
Type Switch 语法格式如下
package main
import "fmt"
func main() {
var x interface{}
switch i := x.(type) {
case nil:
fmt.Printf(" x 的类型 :%T",i)
case int:
fmt.Printf("x 是 int 型")
case float64:
fmt.Printf("x 是 float64 型")
case func(int) float64:
fmt.Printf("x 是 func(int) 型")
case bool, string:
fmt.Printf("x 是 bool 或 string 型" )
default:
fmt.Printf("未知型")
}
}
Go 语言 select 语句
select 是 Go 中的一个控制结构,类似于 switch 语句。
select 语句只能用于通道操作,每个 case 必须是一个通道操作,要么是发送要么是接收。
select 语句会监听所有指定的通道上的操作,一旦其中一个通道准备好就会执行相应的代码块。
如果多个通道都准备好,那么 select 语句会随机选择一个通道执行。如果所有通道都没有准备好,那么执行 default 块中的代码。
语法
select {
case <- channel1:
// 执行的代码
case value := <- channel2:
// 执行的代码
case channel3 <- value:
// 执行的代码
// 你可以定义任意数量的 case
default:
// 所有通道都没有准备好,执行的代码
}
以下描述了 select 语句的语法:
====每个 case 都必须是一个通道 所有 channel 表达式都会被求值 所有被发送的表达式都会被求值 如果任意某个通道可以进行,它就执行,其他被忽略。 如果有多个 case 都可以运行,select 会随机公平地选出一个执行,其他不会执行。 否则: 如果有 default 子句,则执行该语句。 如果没有 default 子句,select 将阻塞,直到某个通道可以运行;Go 不会重新对 channel 或值进行求值
Go 语言循环语句
** 1.For 循环有 3 种形式**
和 C 语言的 for 一样
for init; condition; post { }
和 C 的 while 一样
for condition { }
和 C 的 for(;;) 一样
for { }
init: 一般为赋值表达式,给控制变量赋初值; condition: 关系表达式或逻辑表达式,循环控制条件; post: 一般为赋值表达式,给控制变量增量或减量。
for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下
for key, value := range oldMap {
newMap[key] = value
}
如果只想读取 key,格式如下
for key := range oldMap
或者这样:
for key, _ := range oldMap
2.Go 语言循环嵌套
3.break
需要提前结束 select 语句的执行,可以使用 return 或者 goto 语句来达到相同的效果(break 语句)。
4.continue 语句
5.goto语句
Go 语言的 goto 语句可以无条件地转移到过程中指定的行。
goto 语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。
但是,在结构化程序设计中一般不主张使用 goto 语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难。
Go 语言函数
Go 语言函数定义格式如下
func function_name( [parameter list] ) [return_types] {
函数体
}
Go 函数可以返回多个值
func swap(x, y string) (string, string) {
return y, x
}
值传递
引用传递
1.Go 语言函数作为实参
Go 语言可以很灵活的创建函数,并作为另外一个函数的实参。以下实例中我们在定义的函数中初始化一个变量,该函数仅仅是为了使用内置函数 math.sqrt()
package main
import (
"fmt"
"math"
)
func main(){
/* 声明函数变量 */
getSquareRoot := func(x float64) float64 {
return math.Sqrt(x)
}
/* 使用函数 */
fmt.Println(getSquareRoot(9))
}
2.Go 语言函数闭包
Go 语言支持匿名函数,可作为闭包。匿名函数是一个"内联"语句或表达式。匿名函数的优越性在于可以直接使用函数内的变量,不必申明。
以下实例中,我们创建了函数 getSequence() ,返回另外一个函数。该函数的目的是在闭包中递增 i 变量,代码如下
package main
import "fmt"
func getSequence() func() int {
i:=0
return func() int {
i+=1
return i
}
}
3.Go 语言函数方法
Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。
Go 语言数组
var numbers [5]int
var numbers = [5]int{1, 2, 3, 4, 5}
numbers := [5]int{1, 2, 3, 4, 5}
注意:在 Go 语言中,数组的大小是类型的一部分,因此不同大小的数组是不兼容的,也就是说 [5]int 和 [10]int 是不同的类型。
以下定义了数组 balance 长度为 5 类型为 float32,并初始化数组的元素:
如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度:
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
或
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
1.初始化二维数组
a := [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11}, /* 第三行索引为 2 */
}
以上代码中倒数第二行的 } 必须要有逗号,因为最后一行的 } 不能单独一行,也可以写成这样:
a := [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11}} /* 第三行索引为 2 */
2.Go 语言向函数传递数组
方式一 形参设定数组大小 传递相同的
--
方式二 形参未设定数组大小:
指针使用流程:
定义指针变量。 为指针变量赋值。 访问指针变量中指向地址的值。
在指针类型前面加上 * 号(前缀)来获取指针所指向的内容
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
1.Go 空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
一个指针变量通常缩写为 ptr
> 首字母大写相当于 public。
> 首字母小写相当于 private。
> 注意: 这个 public 和 private 是相对于包(go 文件首行的 package 后面跟的包名)来说的。
>
> 敲黑板,划重点
>
> 当要将结构体对象转换为 JSON 时,对象中的属性首字母必须是大写,才能正常转换为 JSON。
```golang
type Person struct {
Name string //Name字段首字母大写
age int //age字段首字母小写
}
func main() {
person:=Person{"小明",18}
if result,err:=json.Marshal(&person);err==nil{ //json.Marshal 将对象转换为json字符串
fmt.Println(string(result))
}
}
Go 语言切片(Slice)
1定义切片
var identifier []type
切片不需要说明长度。
或使用 make() 函数来创建切片:
var slice1 []type = make([]type, len)
也可以简写为
slice1 := make([]type, len)
也可以指定容量,其中 capacity 为可选参数
make([]T, length, capacity)
2.切片初始化
直接初始化切片,[] 表示是切片类型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3。
s :=[] int {1,2,3 }
初始化切片 s,是数组 arr 的引用
s := arr[:]
3.len() 和 cap() 函数
切片是可索引的,并且可以由 len() 方法获取长度。
切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少
4.空(nil)切片
一个切片在未初始化之前默认为 nil,长度为 0
append() 和 copy() 函数
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。
下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法。
/* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
printSlice(numbers1)
}
Go 语言Map(集合)
Map 是一种无序的键值对的集合。
Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,遍历 Map 时返回的键值对的顺序是不确定的。
1.定义 Map
/* 使用 make 函数 */
map_variable := make(map[KeyType]ValueType, initialCapacity)
其中 KeyType 是键的类型,ValueType 是值的类型,initialCapacity 是可选的参数,用于指定 Map 的初始容量。Map 的容量是指 Map 中可以保存的键值对的数量,当 Map 中的键值对数量达到容量时,Map 会自动扩容。如果不指定 initialCapacity,Go 语言会根据实际情况选择一个合适的值。
// 创建一个空的 Map
m := make(map[string]int)
// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)
可以使用字面量创建 Map:
获取元素:
// 获取键值对
v1 := m["apple"]
v2, ok := m["pear"] // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值
修改元素
// 修改键值对
m["apple"] = 5
// 删除键值对
delete(m, "banana")
Go 语言类型转换
将整型转换为浮点型
var a int = 10
var b float64 = float64(a)
将一个字符串转换成另一个类型,可以使用以下语法
var str string = "10"
var num int
num, _ = strconv.Atoi(str)
以上代码将字符串变量 str 转换为整型变量 num。
注意,strconv.Atoi 函数返回两个值,第一个是转换后的整型值,第二个是可能发生的错误,我们可以使用空白标识符 _ 来忽略这个错误
Go 语言接口
package main
import (
"fmt"
)
type Phone interface {
call()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
Go 错误处理
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
error 类型是一个接口类型,这是它的定义:
type error interface {
Error() string
}
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用 errors.New 可返回一个错误信息:
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
// 实现
}
Go 并发
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")
}
执行以上代码,你会看到输出的 hello 和 world 是没有固定先后顺序。因为它们是两个 goroutine 在执行
通道(channel)
通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据
// 并把值赋给 v
声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建
ch := make(chan int)
通道缓冲区
通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小
ch := make(chan int, 100)
带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。
不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。
Go 遍历通道与关闭通道
Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片
v, ok := <-ch
如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭