Golang特点总结
Posted 计算机程序
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang特点总结相关的知识,希望对你有一定的参考价值。
什么是Go
Go是一门 并发支持 、垃圾回收 的 编译型 系统编程语言,旨在创
造一门具有在静态编译语言的 高性能 和动态语言的 高效开发 之间拥有
良好平衡点的一门编程语言。
Go 的特点
类型安全 和 内存安全
以非常直观和极低代价的方案实现 高并发
高效的垃圾回收机制
快速编译(同时解决C语言中头文件太多的问题)
为多核计算机提供性能提升的方案
UTF-8编码支持
GOPATH
根据约定,GOPATH下需要建立3个目录:
bin(存放编译后生成的可执行文件)
pkg(存放编译后生成的包文件)
src(存放项目源码)
Go 常用的命令
go get:获取远程包(需 提前安装 git或hg)
go run:直接运行程序
go build:测试编译,检查是否有编译错误
go fmt:格式化源码(部分IDE在保存时自动调用)
go install:编译包文件并编译整个程序
go test:运行测试文件
go doc:查看文档(CHM手册)
本地官网搭建
godoc -http=:8080
Go 内置关键字
(25个均为小写)
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 注释
// :单行注释
/* */:多行注释
Go 程序的一般结构
basic_structure.go
Go程序是通过 package 来组织的(与python类似)
只有 package 名称为 main 的包可以包含 main 函数
一个可执行程序 有且仅有 一个 main 包
通过 import 关键字来导入其它非 main 包
通过 const 关键字来进行常量的定义
通过在函数体外部使用 var 关键字来进行全局变量的声明与赋值
通过 type 关键字来进行结构(struct)或接口(interface)的声明
通过 func 关键字来进行函数的声明
Go的package
package的导入
可以多次导入
import "os"
import "fmt"
一次导入
import (
"os"
"fmt"
)
注意
导入package未使用,编译会出错
package 别名
用字符替换
import std "fmt"
std.Println("Hello world")
主要用来区分导入的相似包,或者为一些包指定功能名方便使用
省略调用
import . "fmt"
Println("Hello world")
使用.作为别名,不推荐使用
可见性规则
Go语言中,使用 大小写 来决定该 常量、变量、类型、接口、结构
或函数 是否可以被外部包所调用。
根据约定:
函数名首字母 小写 即为private
函数名首字母 大写 即为public
Go 基本类型
布尔型:bool
长度:1字节
取值范围:true, false
注意事项:不可以用数字代表true或false
整型:int/uint
根据运行平台可能为32或64位
8位整型:int8/uint8
长度:1字节
取值范围:-128~127/0~255
字节型:byte(uint8别名)
16位整型:int16/uint16
长度:2字节
取值范围:-32768~32767/0~65535
32位整型:int32(rune)/uint32
长度:4字节
取值范围:-2^32/2~2^32/2-1/0~2^32-1
64位整型:int64/uint64
长度:8字节
取值范围:-2^64/2~2^64/2-1/0~2^64-1
浮点型:float32/float64
长度:4/8字节
小数位:精确到7/15小数位
复数:complex64/complex128
长度:8/16字节
足够保存指针的 32 位或 64 位整数型:uintptr
其它值类型:
array、struct、string
引用类型:
slice、map、chan
接口类型:inteface
函数类型:func
类型零值
零值并不等于空值,而是当变量被声明为某种类型后的默认值,
通常情况下值类型的默认值为0,bool为false,string为空字符串
类型别名
使用type
type (
KB int8
)
单个变量的声明与赋值
变量的声明格式:var <变量名称> <变量类型>
变量的赋值格式:<变量名称> = <表达式>
声明的同时赋值:var <变量名称> [变量类型] = <表达式>
多个变量的声明与赋值
全局变量的声明可使用 var() 的方式进行简写
全局变量的声明不可以省略 var,但可使用并行方式
所有变量都可以使用类型推断
局部变量不可以使用 var() 的方式简写,只能使用并行方式
变量的类型转换
Go中不存在隐式转换,所有类型转换必须显式声明
转换只能发生在两种相互兼容的类型之间
类型转换的格式:
<ValueA> [:]= <TypeOfValueA>(<ValueB>)
一个问题:数字转字符串
package main
import "fmt"
func main() {
var a = 65
b := string(a)
fmt.Println(b)
}
Consle
A
string() 表示将数据转换成文本格式,因为计算机中存储的任何东西
本质上都是数字,因此此函数自然地认为我们需要的是用数字65表示
的文本 A。
解决办法
引入strconv包
package main
import (
"fmt"
"reflect"
"strconv"
)
func main() {
var a = 65
b := strconv.Itoa(a)
fmt.Println(b)
_type := reflect.TypeOf(b)
fmt.Println(_type)
}
Console
$ go run main.go
65
string
常量的定义
- 常量的值在编译时就已经确定
- 常量的定义格式与变量基本相同
- 等号右侧必须是==常量==或者==常量表达式==
- ==常量表达式中的函数必须是内置函数==
常量的初始化规则与枚举
在定义常量组时,如果不提供初始值,则表示将使用上行的表达式
使用相同的表达式不代表具有相同的值
iota是常量的计数器,从0开始,组中每定义1个常量自动递增1
通过初始化规则与iota可以达到枚举的效果
每遇到一个const关键字,iota就会重置为0
运算符
Go中的运算符均是从左至右结合
优先级(从高到低):
`^` `!` (一元运算符)
`*` `/` `%` `<<` `>>` `&` `&^`
`+` `-` `|` `^` (二元运算符)
`==` `!=` `<` `<=` `>=` `>`
`<-` (专门用于channel)
`&&`
`||`
问题:
请尝试结合常量的iota与<<运算符实现计算机储存单位的枚举
const (
_ = iota
KB float64 = 1 << (iota * 10)
MB
GB
TB
PB
EB
ZB
YB
)
指针
Go虽然保留了指针,但与其它编程语言不同的是,在Go当中不支持指针运算以及`->`运算符,而直接采用`.`选择符来操作指针目标对象的成员
默认值为`nil`而非`NULL`
递增递减语句
在Go当中,++ 与 -- 是作为语句而并不是作为表达式
判断语句if
条件表达式没有括号
支持一个初始化表达式(可以是并行方式)
左大括号必须和条件语句或else在同一行
支持单行模式
初始化语句中的变量为block级别,同时隐藏外部同名变量
1.0.3版本中的编译器BUG
循环语句for
Go只有for一个循环语句关键字,但支持3种形式
初始化和步进表达式可以是多个值
条件语句每次循环都会被重新检查,因此不建议在条件语句中
使用函数,尽量提前计算好条件并以变量或常量代替
左大括号必须和条件语句在同一行
选择语句switch
可以使用任何类型或表达式作为条件语句
不需要写break,一旦条件符合自动终止
如希望继续执行下一个case,需使用fallthrough语句
支持一个初始化表达式(可以是并行方式),右侧需跟分号
左大括号必须和条件语句在同一行
跳转语句goto, break, continue
三个语法都可以配合标签使用
标签名区分大小写,若不使用会造成编译错误
Break与continue配合标签可用于多层循环的跳出
Goto是调整执行位置,与其它2个语句配合标签的结果并不相同
数组Array
定义数组的格式:var <varName> [n]<type>,n>=0
数组长度也是类型的一部分,因此具有不同长度的数组为不同类型==
注意区分指向数组的指针和指针数组
数组在Go中为值类型
相同类型数组之间可以使用或!=进行比较,但不可以使用<或>
可以使用new来创建数组,此方法返回一个指向数组的指针
Go支持多维数组
切片
其本身并不是数组,它指向底层的数组
作为变长数组的替代方案,可以关联底层数组的局部或全部
为引用类型
可以直接创建或从底层数组获取生成
使用len()获取元素个数,cap()获取容量
一般使用make()创建
如果多个slice指向相同底层数组,其中一个的值改变会影响全部
make([]T, len, cap)
其中cap可以省略,则和len的值相同
len表示存数的元素个数,cap表示容量
Reslice
Reslice时索引以被slice的切片为准
索引不可以超过被slice的切片的容量cap()值
索引越界不会导致底层数组的重新分配而是引发错误
Append
可以在slice尾部追加元素
可以将一个slice追加在另一个slice尾部
如果最终长度未超过追加到slice的容量则返回原始slice
如果超过追加到的slice的容量则将重新分配数组并拷贝原始数据
Copy
如果长度不同,copy只会去最少长度copy,因为会受到容量限制。
注意:copy后的两个slice还是指向原来的底层数组。
map
类似其它语言中的哈希表或者字典,以key-value形式存储数据
Key必须是支持==或!=比较运算的类型,不可以是函数、map或slice
Map查找比线性搜索快很多,但比使用索引访问数据的类型慢100倍
Map使用make()创建,支持 := 这种简写方式
make([keyType]valueType, cap),cap表示容量,可省略
超出容量时会自动扩容,但尽量提供一个合理的初始值
使用len()获取元素个数
键值对不存在时自动添加,使用delete()删除某键值对
使用 for range 对map和slice进行迭代操作
注意:for range 取出的值是值copy
线性搜索与索引访问数据
线性搜索
从一个元素序列中从头到尾检索查找目标数据,主要是用值比较
索引访问数据
比如数组,可以通过索引值直接获取数据。例如 a[0]为目标数据
函数function
Go 函数 不支持 嵌套、重载和默认参数
但支持以下特性:
无需声明原型、不定长度变参、多返回值、命名返回值参数
匿名函数、闭包
定义函数使用关键字 func,且左大括号不能另起一行
函数也可以作为一种类型使用
defer
执行方式类似其它语言中的析构函数,在函数体执行结束后
按照调用顺序的相反顺序逐个执行
即使函数发生严重错误也会执行
支持匿名函数的调用
常用于资源清理、文件关闭、解锁以及记录时间等操作
通过与匿名函数配合可在return之后修改函数计算结果
Go 没有异常机制,但有 panic/recover 模式来处理错误
Panic 可以在任何地方引发,但recover只有在defer调用的函数中有效
结构struct
- Go 中的struct与C中的struct非常相似,并且Go没有class
- 使用 type <Name> struct{} 定义结构,名称遵循可见性规则
- 支持指向自身的指针类型成员
- 支持匿名结构,可用作成员或定义成员变量
- 匿名结构也可以用于map的值
- 可以使用字面值对结构进行初始化
- 允许直接通过指针来读写结构成员
- 相同类型的成员可进行直接拷贝赋值
- 支持 == 与 !=比较运算符,但不支持 > 或 <
- 支持匿名字段,本质上是定义了以某个类型名为名称的字段
- 嵌入结构作为匿名字段看起来像继承,但不是继承
- 可以使用匿名字段指针
方法method
Go中虽没有class,但依旧有method
通过显示说明receiver来实现与某个类型的组合
只能为同一个包中的类型定义方法
Receiver 可以是类型的值或者指针
不存在方法重载
可以使用值或指针来调用方法,编译器会自动完成转换
从某种意义上来说,方法是函数的语法糖,因为receiver其实就是方法所接收的第1个参数(Method Value vs. Method Expression)
如果外部结构和嵌入结构存在同名方法,则优先调用外部结构的方法
类型别名不会拥有底层类型所附带的方法
方法可以调用结构中的非公开字段
Method value VS. Method Expression
package main
import (
"fmt"
)
type _A struct {
Name string
}
func main() {
// Method Value
a := _A{Name: "Alice"}
fmt.Println(a.Name)
a.Rename()
fmt.Println(a.Name)
// Method EAxpression
b := _A{Name: "Alice"}
fmt.Println(b.Name)
(*_A).Rename(&b)
fmt.Println(a.Name)
}
func (c *_A) Rename() {
c.Name = "Bob"
}
output
Alice
Bob
Alice
Bob
接口interface
接口是一个或多个方法签名的集合
只要某个类型拥有该接口的所有方法签名,即算实现该接口,无需显示
声明实现了哪个接口,这称为 Structural Typing
接口只有方法声明,没有实现,没有数据字段
接口可以匿名嵌入其它接口,或嵌入到结构中
将对象赋值给接口时,会发生拷贝,而接口内部存储的是指向这个
复制品的指针,既无法修改复制品的状态,也无法获取指针
只有当接口存储的类型和对象都为nil时,接口才等于nil
接口调用不会做receiver的自动转换
接口同样支持匿名字段方法
接口也可实现类似OOP中的多态
空接口可以作为任何类型数据的容器
类型断言
通过类型断言的ok pattern可以判断接口中的数据类型
使用type switch则可针对空接口进行比较全面的类型判断
接口转换
可以将拥有超集的接口转换为子集的接口
延伸阅读
评:为什么我不喜欢Go语言式的接口(www.ituring.com.cn/article/37642)
反射reflection
反射可大大提高程序的灵活性,使得 interface{} 有更大的发挥余地
反射使用 TypeOf 和 ValueOf 函数从接口中获取目标对象信息
反射会将匿名字段作为独立字段(匿名字段本质)
想要利用反射修改对象状态,前提是 interface.data 是 settable,即 pointer-interface
通过反射可以“动态”调用方法
并发concurrency
很多人都是冲着 Go 大肆宣扬的高并发而忍不住跃跃欲试,但其实从
源码的解析来看,goroutine 只是由官方实现的超级“线程池”而已。
不过话说回来,每个实例 4-5KB 的栈内存占用和由于实现机制而大幅
减少的创建和销毁开销,是制造 Go 号称的高并发的根本原因。另外,
goroutine 的简单易用,也在语言层面上给予了开发者巨大的便利。
并发不是并行:Concurrency Is Not Parallelism 并发主要由切换时间片来实现“同时”运行,在并行则是直接利用多核实现多线程的运行,但 Go 可以设置使用核数,以发挥多核计算机的能力。
Goroutine 奉行通过通信来共享内存,而不是共享内存来通信。
Channel
Channel是 goroutine 沟通的桥梁,大都是阻塞同步的
通过 make 创建,close 关闭
Channel 是引用类型
可以使用 for range 来迭代不断操作 channel
可以设置单向或双向通道
可以设置缓存大小,在未被填满前不会发生阻塞
Select
可处理一个或多个 channel 的发送与接收
同时有多个可用的 channel时按随机顺序处理
可用空的 select 来阻塞 main 函数
可设置超时
以上是关于Golang特点总结的主要内容,如果未能解决你的问题,请参考以下文章