Go
Go 是一款编译型,强类型语言。
基础
变量
GO变量这一区域,借助于JsluaC系列的合体。
- 所有变量都是强类型限定。
- 值类型变量皆存在非
nil默认值,string是值类型。
- 值类型
| 类型 | 解释 | 备注 |
|---|---|---|
uint8 |
无符号整型 | 位数 8,16,32,64 |
int8 |
有符号整型 | 位数 8,16,32,64 |
float32 |
浮点数 | 位数 32,64 |
complex64 |
复数 | 位数 64,128 |
| 常用 | 标识 | 大小 |
|---|---|---|
| 字节 | byte |
1 字节 |
| 布尔 | bool |
1 字节 |
| 整形 |
int, uint
|
8 字节, 64位 |
| 指针 | uintptr |
8 字节, 64位,(uint) |
| 字符串 | string |
16 字节 |
- 特性
-
Js var const在此处作用相同。 -
lua _以及栈式赋值,以及栈式返回,在此处亦有相同的作用。 -
Go变量类型可强限定,亦可动态限定,但只要被确定一次即不可更改(本质上还是强限定)。 -
Go变量动态限定类型仅可在局部函数内部使用,且所有Go变量必须使用才可编译通过。
// 常量强限定
const num int = 100;
// 常量动态限定,仅限于第一次
const num = 100;
// 变量强限定
var num int = 120;
// 变量动态限定,仅限于第一次
var cnt = 100;
// 强限定,栈式赋值
var num, cnt int = 100, 120;
// 动态限定,栈式赋值
var num, str = 120, "stack";
// 虚变量
_, b int = demo();
// 语法糖
var num = 120; num := 120;
// iota 特殊编译器行数变量
流程控制
- 运算符
-
Go语言的运算符继承了C,且保留了指针。
| 运算符 | 常用 | 备注 |
|---|---|---|
| 算术 |
+ - * / % ++ --
|
/ 整除运算 |
| 比较 |
> >= < <= == !=
|
|
| 逻辑 |
&& || !
|
默认短路,复合!要加()
|
| 位 |
& | ^ << >>
|
某些算法中,位运算很重要 |
| 赋值 |
= := ...
|
赋值运算也是大同小异 |
| 指针 |
&a 获取a的地址 *a定义指针 |
& * 操作指针 |
- 选择控制
// if-then
if (true) {
//TODO
}
// if-else
if (true) {
//TODO
} else {
//TODO
}
// switch val语句
switch (val) {
case 0: // case 执行完即会退出
default:
}
// switch 语句
switch {
case true: // case 执行完成即退出
case true:
default:
}
// go 中 switch属于选择语句,即默认只会选中一项
switch {
case true:
fallthrough // 此处 case 执行完后,直接执行下一条case(跳过判断)
default:
}
// for 语句实现循环
for {
//TODO
}
- 函数
- 函数支持多值返回。
- 所有参数都执行值传递过程。
- 函数支持返回函数,也支持参数为函数。
// 默认无返回值函数
func demo() {
//TODO
}
// 单值返回
func demo() int{
//TODO
return 0
}
// 栈式返回
func demo() (int, int){
//TODO
return 0,1
}
// 函数传参类型
func demo(num int, cnt int){
//TODO
}
// defer 语句块,就相当于 finally 语句块,函数最终必须要执行此方法
func demo() {
defer //TODO
//TODO
}
数据结构
- 集合
// 声明集合,此时为 nil 值,即不可使用状态
var m = map[string] string
// 创建集合,此出过后才可使用
m = make(map[string] string)
// 此处是将前面两步合在一起执行
var mk = make(map[int] int)
// 常用方法
delete(mk, "str");
// 迭代集合
for k, v := range mk {
// k, v
}
- 数组
- 强内存、强类型限定的数组。
- 数组类型是兼容于
C系列的数组。 - 数组的切片类型执行引用复制,不会重新复制内存。
// 新建数组变量
var cnt[10] int32;
// 默认初始化
var cnt = [2] int32{1,2};
// 自动确定长度的数组
var cnt = [...] int32 {1,2,3,4,5,6,7,};
// 函数传递数组
func demo(arr int[], size int){
// TODO
}
// 基于 arr生成切片,内存共享
var tmp = arr[1:];
len(tmp); // 数组长度,一般指切片数组或原始数组的大小,操作 idx 区间最大值
cap(tmp); // 数组底层大小,一般指原始数组长度
// 常用的一些方法
append(); copy();
// 迭代数组
for v := range arr{
// v
}
- 字符串
-
Gostring 是一个基本类型,而非引用类型。 -
Gostring 默认为UTF-8编码,unicode需要额外转码。 - 一般用,
"str"表示,内部可转义,不可多行。 - 特殊时,
`str`表示,内部不可转义,可以多行。
// 定义并初始化字符串
var str string = "123456";
// 常见字符串拼接
var add = str + "789";
// 字符串,切片
var cut = str[2:];
len(str); // 获取string,字节占用大小
str[0]; // 获取字节索引的 ASCII 数值
特性
模块化
- package
- 一个文件夹就是一个包,
package。 - 一个文件
.go就是一个模块,module。 - 程序主入口是个特殊的类型
main/main.go => main()。
package main // 任何go程序,必须有 main 包,内部必须存在 main方法
import (
"fmt" // 标准库 IO
)
// 引入多个包
import (
. "fmt" // 调用此模块下方法,无需前置 fmt
tm "time" // 修改模块别名 tm
_ "sync" // 执行一次 sync.init() 内部方法不可调用
)
// 任意模块文件被引用或加载时会执行一次
func init() {
fmt.Println("当前文件加载时,会执行一次!")
}
// 程序主入口
func main() {
fmt.Println("程序启动入口,必须存在!")
}
// go 的语法风格,会在行尾自动补齐 ;
- module
待补充。
- execute
# 编译 demo 并执行
go run ./demo.go
# 编译 demo 为可执行文件 run
go build -o run ./demo.go
抽象化
- 结构体
- 结构体部分主要继承自
C系列。
// 这里一般都需要注释,不然会提示
type Atk struct{
idx int
val int32
nam string
}
// 新建结构体,固定位置填充
var atk = Atk{1, 2, "hello"}
// 新建结构体,索引位置填充,不索引时,以默认值填充
var atk = Atk{idx:1, val:2}
// 新建结构体,初始化默认值,并返回一个指针
var ptr = new(Atk)
ptr.idx, ptr.val, ptr.nam // 标准指针访问过程
- 接口
-
Go的接口就是Java中接口以及实现类的一个衍生方式。 - 其本质上就是根据的结构体(实体),绑定不同的处理方法。
// 定义接口
type Abs interface{
// Obj 访问
Name() string
// *Obj 访问
name() string
}
// 定义结构体
type Obj struct{
idx int
nam string
}
// 绑定 Obj 结构体,Name方法
func (e Obj) Name() string{
return e.nam
}
// 绑定 *Obj 结构体,Name方法
func (e *Obj) name() string{
e.nam = e.name + "tl"
return e.nam
}
var obj = Object{1, "zsh"}
var ptr = &obj
// 指针以及非指针,传参不同,故用法不同
obj.Name()
ptr.name()
- error
-
Go的异常处理机制基本相似于Java C等语言。
// 内置异常接口
type error interface{
Error() string
}
// 测试结构体
type Obj struct{
idx int
val string
}
// 预定义返回错误的方法
func logError(e Obj) error{
if(e.idx == 0){
errors.New("idx == 0")
}
errors.New("ok")
}
func main(){
var obj = Obj{0, 1}
// 输出预定义异常
fmt.Println(logError(obj))
}
// golang - 通用性异常处理方式
func demo(){
defer func(){
var err = recover() // 捕获异常
}() // 匿名函数
panic("throws-errors!") // 抛出异常
}
并发控制
- 携程
-
Java中的线程使用的是OS级别的资源,而此处携程使用的是虚拟机资源。 -
OS线程,并行处理,但切换上下文消耗资源,且本身的异步控制也较为复杂。 -
虚拟机线程,伪并行处理,不存在上下文切换,本身不会产生过多的异步控制逻辑。
// 开启携程,返回值会被抛弃,伪并行
func demo() {
var cnt = 0
for i := 0; i < 1000; i++ {
// go 启动携程
go func(){
cnt++
}()
}
fmt.Println("cnt", cnt)
}
// 并发的锁控制,代码控制并发,简单高效
var cnt = 0
func demo(lock *sync.Mutex) {
lock.Lock()
cnt++
fmt.Println("cnt", cnt)
lock.Unlock()
}
func main() {
var lock = &sync.Mutex{}
for i:=0; i < 50; i++ {
go demo(lock)
}
for {
lock.Lock()
tmp := cnt
lock.Unlock()
// 让出时间片
runtime.Gosched()
// 退出循环
if (tmp == 50) {
break
}
}
}
- 通道
- 通道是一种在不同线程之间传递数据的机制,核心机制类似
MQ。 - 通道仅支持不同线程之间的数据交互,单线程会发生死锁,不可使用。
- 无缓冲通道,即数据必须即刻送达,其中一方未准备好,即阻塞另一方。
- 缓冲通道,即数据无需即可送达,仅当超过一定阈值时,阻塞其中一方。
// 无缓冲通道
var ch = make(chan int)
ch <- 123 // 向 ch 通道发送数据,无返回值
var dat, ok = <-ch // 从 ch 通道取出数据,双返回值
// 缓冲通道,数据量超过阈值则发生阻塞
var ch = make(chan int, 10)
close(ch) // 关闭通道,通道被关闭后,即不可使用
// demo =====>
var ch = make(chan int, 2)
go func(){
ch <- 1
time.Sleep(time.Second)
ch <- 2
time.Sleep(time.Second)
close(ch)
fmt.Println("ch - closed")
}()
for i := 0; i < 4; i++ {
var dat, ok = <-ch
fmt.Println(ok, dat)
}
/*
true 1
true 1
ch - closed
false 0
false 0
*/
- chan - 用法
- chan 结构的设计非常相似于
node的异步设计。 - 可通过内置的异步方式,实现诸多的设计。
// 迭代型 for-range
var ch = make(chan int)
go func(){
for i := 0; i < 4; i++ {
ch <- i
fmt.Println("sending", i)
time.Sleep(time.Second)
}
close(ch)
}()
for i := range ch {
fmt.Println("receive", i)
}
fmt.Println("ending")
// 选择型 select
var ch1 = make(chan int)
var ch2 = make(chan int)
go func(){
ch1 <- 123
close(ch1)
}()
go func(){
ch2 <- 456
close(ch2)
}()
// 单选退出
select {
case i := <-ch1:
fmt.Println("ch1", i)
case i := <-ch2:
fmt.Println("ch2", i)
default:
fmt.Println("default")
}
// time 内置的时间触发器,内部就是个 chan
var ch = time.NewTicker(time.Second)
for t := range ch.C {
fmt.Println(t)
}