go语言入门学习
文章目录
- 简介
- go 数据类型
- 数据类型基本介绍
- 类型强制转换
- 类型断言
- go 数组和切片数组
- go 指针
- go 结构体和接口
- Map 类型
- error 错误类型
- chan 通道
- go 运算符
- 算术运算符
- 关系远算符号
- 逻辑运算符
- 位运算符
- 赋值运算符
- go 变量和常量
- go 变量
- go 常量
- go 函数
- go 选择语句
- go 循环语句
- go 并发
- go 关键字
- channel 通道
- go 注释
- go 语言练习
简介
GO 一门开源的编程语言,07 年开发的,09 年开源,12 年出了 go 1 稳定版
go 专门被设计成应用于(搭载了 Web 服务器/存储集群/类似用户的巨型中央服务器)的系统编程语言,在高性能的分布式系统领域来说,go 比其他语言有着更高的开发效率,提供了海量并行支持,对于游戏服务端而言是非常合适的
这里有几个很好的 golang 的学习网址:
- https://tour.go-zh.org/welcome/1 (中文版)教你编写代码的形式来一步步带你入门 go
- https://github.com/Unknwon/the-way-to-go_ZH_CN github中电子书《go语言入门指南》
- https://books.studygolang.com/gopl-zh/ 在线阅读《go语言圣经》
- https://gobyexample.com 英文版本go的样例
go 数据类型
数据类型基本介绍
布尔型 bool
// 取值 true 或者 false
bool
字符型 byte/rune
go 不像 java 中特别指定字符类型// byte 代表 ascll 中一个字符,等价于 uint8
byte
// rune 识别 unicode 编码的字符,中文一个字也是一个字符,等价于 int32
rune
整型 int
// 一般情况 int 优先,8 字节
int
// 无符号
uint8 uint16 uint32 uint64
// 有符号
int8 int16 int32 int64
// 其他
uintptr byte
浮点型 float
// 浮点数
float32 float64
复数类型
// 实数和虚数
complex64 complex128
字符串型
// 字符串
string
错误型
// 错误
error
复合类型
指针类型
*int
**int
数组类型
[3][5]int
切片类型
[]int
Map 类型
map[string]string
通道类型
// 协程之间传输数据
chan int
结构化类型
type Person struct {
x,y float32
}
接口类型
type IPerson interface {
Born(buf []byte)(n int, err error)
}
类型强制转换
go 语言不像 java 可以做到不同类型数据运算时候做到自动数据转换,go 是没法做到的,它必须手动强制数据类型转换后再去做运算,但是为什么是不支持自动类型转换呢?
// 类型转换
var a int = 2
var b float32 = float32(a)
类型断言
go 数组和切片数组
数组声明和初始化举例
// var 指定数组并初始化
var arr = [3]int{
1, 2, 3}
// 指定数组并初始化
arr := [3]int{
1, 2, 3}
// 不确定数组长度,用...来代替
var arr2 = [...]float32{
1.0, 2.0, 3.0}
arr3 := [...]float32{
1.0, 2.0, 3.0}
// 声明数组
var arr [10]int
// 二维数组
var arr1 [3][4]int
var arr2 = [1][2]int{
{
3, 3}
{
4, 4}
}
// 创建空数组
arr := [][]string{
}
函数中的数组
// 指明数组的大小
func fun1(arr []int) {
}
// 没有指明数组大小
func fun2(arr [2]int) {
}
切片数组(动态数组),可以追加元素,追加时容量可以变大。切片无需说明长度
// 声明切片数组:声明一个未指定大小的数组定义切片,并且 a 切片数组在没有初始化之前 a 是 nil,a 的长度和容量都是 0
var a []int
// 创建切片:make 函数创建切片,下面 make 函数第二个参数代表长度
var a = make([]int, 5)
b := make([]int, 5)
// 创建切片:下面 make 第二个参数是数组长度,第三个参数是数组的容量
c := make([]int, 5, 6)
// 数组的地址值赋值,有多种形式 s := arr[startIndex:endIndex],s := arr[startIndex:],s := arr[:endIndex]
var arr []int = [3]int {
1, 2, 3}
s := arr[:]
// 创建空切片,并进行 append 追加元素
var arr1 []int
arr1 = append(arr1, 2, 3, 6)
// 将 arr1 copy 到 arr2(内容的拷贝)
arr2 := make([]int, len(arr1), cap(arr1)*2)
copy(arr2, arr1)
fmt.Println(arr2)
make,len,cap 函数的使用
var arr = make([]int, 5, 6)
fmt.Printf("%d, %d, %v", len(arr), cap(arr), arr);
go 指针
// 变量的地址值
var a int = 10
fmt.Println(&a)
// 指针使用
var a int = 10
var b *int
b = &a
fmt.Println(a)
fmt.Println(*b)
// 空指针 a 就是 nil,而 *a 的值是 0
var a *int
// 数组指针
var arr [3]*int
// 指向指针的指针
var a **int
// 函数中指针类型传参
func fun(x *int, y *int) {
}
go 结构体和接口
结构体
// 结构体
type Person struct {
name string
age int
address string
}
func main() {
// 初始化接口体的几种方式
fmt.Println(Person("liming", 12, "北京市"))
fmt.Println(Person(name:"liming", age:12, address:"北京市"))
fmt.Println(Person(name:"liming", age:12))
// 另一种初始化方式
var person Person
person.name = "liming"
person.age = 12
person.address = "北京市"
fmt.Println(person.name)
}
func fun1(person1 Person) {
}
func fun2(person2 *Person) {
}
接口
// Person 接口
type Person interface {
call()
}
// 结构体
type Student struct {
}
// 实现接口的方法
func(student Student) call() {
fmt.Println("我是学生")
}
Map 类型
无序的键值对集合,Map 是无序的,无法决定它的返回顺序,使用哈希表来实现
// 声明 map,并给 map 赋值,然后遍历 map,查看元素在集合中是否存在,map1 一开始是 nil
var map1 map[string]string
map2 := make(map[string]string)
map3 := map[string]string{
"c":"ccc", "d":"ddd"}
map1["a"] = "aaa"
map1["b"] = "bbb"
for ch := range map1 {
fmt.Println(map1[ch])
}
value, ok := map1["a"]
if (ok) {
} else {
}
// delete 函数
delete(map3, "c")
error 错误类型
go 语言通过内置的错误接口提供了非常简单的错误处理机制,error 类型是一个接口类型
type error interface {
Error() string
}
我们可以在最后的返回值中返回 error 信息
func function() (int, error) {
return 5, errors.New("错误信息")
}
func main() {
result, err := function()
}
chan 通道
具体讲解可以看下方的 channel 通道
go 运算符
运算优先级类似 java
算术运算符
加减乘除,求余,自增,自减和 java 一样
关系远算符号
== != > < >= <=
这些和 java 都是一样的
逻辑运算符
&& || !
这些和 java 都是一样的
位运算符
& | ^ << >>
赋值运算符
= += -= *= /= %= <<= >>= &= ^= |=
和 java 一样
go 变量和常量
go 变量
声明变量用 var,示例如下
// 声明一个变量
var param string
// 声明多个变量(非全局变量)
var param1, param2 int
param1, param2 = 1, 2
var param1, param2 = 1, 2
// 声明多个变量(全局变量)
var(
param1 string
param2 int
)
// 自行判断变量
var param = true
// var 和 := 等同
var param int
param1 := 1
方法中指定变量没有初始值则有默认值
- 数值对应 0
- string 对应 “”
- bool 对应 false
- 数组,指针,map,通道,error,方法等类型则是对应 nil
值类型和引用类型
- 值类型:变量直接指向内存中的值(int,float,bool,string),也就是说等号将值给一个变量时候是将内存中的值进行了拷贝,¶m 就行获取内存地址,值类型变量存储在栈内存中,
- 引用类型:变量存储值的地址值
go 常量
常量示例
// 局部
const LENGTH = 1
const WIDTH, CIRCLE = 1, 2
// 全局
const(
a = "aaa"
b = 1
)
// 特殊常量 iota,const 语句块中表示行索引,每当遇到新的 iota 时候重新计数行索引
// 下面 a 结果是 0,b 结果是 1,c 结果是 2
const(
a = iota
b
c
)
go 函数
go 语言最少有一个 main
// 返回一个值
func max(num1, num2 int) int {
}
// 返回多个值
func swap(nums1, nums2 int) (int, int) {
return nums2, nums1
}
func main() {
a, b := swap(1, 2)
}
函数闭包,go 支持匿名函数,可作为闭包,匿名函数优越在于可以使用函数内的变量,不必申明
func getSequence() func() int {
return func() int {
return 1
}
}
go 选择语句
if else 和 java 一样,但是小括号是不用加的,并且 else 不可单独另作一行
switch 语句如下:
var a int = 1
switch a {
case 1: xxx
case 2: xxx
default: xxx
}
switch {
case true: xxx
default: xxx
}
select 语句类似一个通信的 switch 语句,每个 case 都必须是一个通信操作,要么接收要么发送;select 随机执行一个可运行的 case,如果没有 case 运行,它将被阻塞
格式如下:
select {
case xxx : yyy
default : yyy
}
go 循环语句
for 循环如下
// for 循环一种形式
for i := 0; i < 10; i++ {
}
// for 类似 while
for i < 10 {
}
// for 无限循环
for {
}
// foreach range 用来遍历字符串数组等进行遍历,还可以用在键值对上,
strings := []string{
"aaa", "bbb"}
for i,s := range strings {
fmt.println(i, s);
}
kvs := map[string]int{
"a":1, "b":2}
for k,v := range kvs {
fmt.Printf("%s, %d", k, v)
}
另外 break,continue,goto 和 java 一样
go 并发
go 关键字
go 语言支持并发,通过 go 关键字来开启 goroutine 即可,goroutine 是轻量级线程,其是由 golang 运行时进行管理的,下面举例说明
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() {
// 通过 g 关键字开启一个 goroutine 轻量级线程
go say("aaa")
say("bbb")
}
channel 通道
通道关键字是 chan,可以在两个 goroutine 之间传递一个指定类型的值,<-
可以用来指定通道方向,如果没有指定方向,则为双向通道,下方举例使用通道
package main
import "fmt"
func main() {
var a int = 5
// 创建一个通道
b := make(chan int)
// 把 a 给通道 b
go function(a, b)
a2 := <- b
fmt.Println(a2)
}
func function(a int, b chan int) {
b <- a
}
chan 通道可以设置缓冲区大小,发送端发送的数据可以放在缓冲区中
如果通道不带缓冲区,发送方会阻塞,直到接收方从通道中接收了值
如果通道带有缓冲区,发送方会阻塞,直到发送的数据被拷贝到了缓冲区,如果缓冲区已经满了,则又会被阻塞,直到接收方开始消费缓冲区
package main
import "fmt"
func main() {
// 设置缓冲区大小为 2
ch := make(chan int, 2)
ch <- 3
ch <- 5
// 获取通道中的两个数据,这里打印出 3
fmt.Println(<-ch)
// 这里打印出来 4
fmt.Println(<-ch)
}
通道关闭为 close(通道),通道的遍历依然是通过 range
func main() {
// 声明一个缓冲区大小为 3 的通道
ch := make(chan int, 3)
ch <- 3
ch <- 1
ch <- 9
close(ch)
for i := range ch {
fmt.Println(i)
}
}
go 注释
// 单行注视
/*
多行注释
*/
go 语言练习
递归实现求斐波那契数列
package main
import "fmt"
func main() {
// n = 1 时候值为 1,n = 2 时值为 1,求 n = 10 时候值为多少?使用递归
fmt.Println(function(10))
}
/*
斐波那契函数
*/
func function(n int) int {
if n <= 0 {
return 0
} else if n == 1 {
return 1
} else if n == 2 {
return 1
} else {
return function(n-1) + function(n-2)
}
}
还没有评论,来说两句吧...