go语言基础
介绍
专为并发而生,编译型语言,强类型语言。
语法简洁:学习曲线平缓。
代码风格统一,开发效率高。
特点
25个关键字,支持复合不支持继承。
基本程序结构
应用程序入口,必须是main包的main方法,文件名不一定是main
与其他编程语言的差异
GO中main函数不支持任何返回值
通过os.Exit来返回状态
示例:test.go
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println(os.Args)
if len(os.Args) > 1 {
fmt.Println("os args", os.Args[1])
}
os.Exit(1)
}
执行golang程序:
go run test.go // 直接运行
go build test.go // 编译
go run test.go 123 456 789 //带参数:os.Args
echo $? //输出上次运行结果,例如以上示例,exit(0),输出0,其他值输出1
编写测试程序
_test.go结尾的文件,方法Test开头,使用testing包的T注入参数,例如:
package main
import (
"testing"
)
func TestEcho(t *testing.T) {
var a int
t.Logf("the value of a is: %d", a)
}
快速设置连续值
package main
import (
"testing"
)
const (
Monday = iota + 1
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
const (
Open = 1 << iota
Close
Pending
)
func TestEcho(t *testing.T) {
var a int
t.Logf("the value of a is: %d\n", a) // 0
t.Logf("the value of Monday is: %d\n", Monday) //1
t.Logf("the value of Tuesday is: %d\n", Tuesday) //2
t.Logf("the value of Wednesday is: %d\n", Wednesday) //3
t.Logf("the value of Thursday is: %d\n", Thursday) //4
t.Logf("the value of Friday is: %d\n", Friday) //5
t.Logf("the value of Saturday is: %d\n", Saturday) //6
t.Logf("the value of Sunday is: %d\n", Sunday) //7
t.Logf("the value of Open is: %d\n", Open) // 1
t.Logf("the value of Close is: %d\n", Close) //2
t.Logf("the value of Pending is: %d\n", Pending) //4
}
iota是以0初始值0开始的自增值,左移代表 y=x^2 y=x « 1
基本数据类型
数据类型 |
---|
bool |
string |
int int8 int16 int32 int64 |
uint uint8 uint16 uint32 uint64 uintptr |
byte // alias for uint8 |
rune // alias for int32,represents a Unicode code point |
float32 float64 |
complex64 complex128 |
int和uint,以及int和int8等等的区别
int和机器本身有关,例如32位系统,那么int占4个字节,64位系统,int占8字节
以64位系统为例,各种int所占字节为:
int // 8
int8 // 1
int16 //2
int32 //4
int64 //8
一个字节是8位,可以理解为int后的数字除以8,得到对应的字节数。
扩展知识:表示范围
int8:-128~127
分析:int8代表8位int,也就是一个字节。其中一位符号位,7位二进制位,那么范围是:-2^7~2^7-1
uint8:2^8=256
byte和rune
byte (实际上是 uint8 的别名),代表 UTF-8 字符串 的 单个 字节的值,用来储存ASCII码,表示一个ASCII码字符
une(实际上是int32),代表单个 Unicode字符,常用来处理unicode或utf-8字符(一切字符),就是rune的使用范围更大
package main
import (
"testing"
"unsafe"
)
func TestEcho(t *testing.T) {
//testString := "testStrings"
sliceByteStr1 := []uint8("测试abc123") // 或者[]byte("测试abc123")
sliceRuneStr1 := []int32("测试abc123") // 或者[]rune("测试abc123")
t.Logf("the size of sliceByteStr1 is: %d\n", unsafe.Sizeof(sliceByteStr1)) //24
t.Logf("the len of sliceByteStr1 is: %d\n", len(sliceByteStr1)) // 12
t.Logf("the size of sliceRuneStr1 is: %d\n", unsafe.Sizeof(sliceRuneStr1)) //24
t.Logf("the len of sliceRuneStr1 is: %d\n", len(sliceRuneStr1)) //8
}
类型转换
1 不允许隐式类型转换
2 别名之间不能直接转换
类型别名
type MyInt int64 //也可以是结构体等
算术运算符
不支持前置的++和–
比较运算符
相同维数和相同个数的数组才可以比较。
每个值都相等,数组才相等
数组
例如
a := [...]string{"a", "b", "c"}
b := [3]int{1, 2, 3}
var c [10][2]int
位运算符
与其他语言位运算符一致,有一个特殊点:
&^按位置零
只要右边为1,无论左边是什么都清零
右边为0,左边是什么就是什么
1 &^ 0 – 1
1 &^ 1 – 0
0 &^ 0 – 0
0 &^ 1 – 0
例如清空读、写、可执行权限:
package main
import (
"testing"
"unsafe"
)
/**
7: 0111
读:100
写: 010
执行:001
*/
const (
Execute = 1 << iota
Writable
Readable
)
func TestClear(t *testing.T) {
origin := 7
t.Logf("Execute: %v\n", origin&^Execute) //6
t.Logf("Writable: %v\n", origin&^Writable) //5
t.Logf("Readable: %v\n", origin&^Readable) //3
}
循环
只有for循环
普通完整for循环
for (i := 0; i <= 10; i++) {
}
类似其他编程语言while循环
// 相当于while(i<10)
for i < 10 { i++
}
无限循环
一般内部需要有退出机制,或者就是常驻进程
//相当于while(true)
for {
}
if
1 condition表达式结果必须为布尔值
2 支持变量赋值
//写法一
if condition {
}
//写法二
if var declaration; condition {
// code
}
数组和切片
常用集合:数组、切片、map
定义
数组
在go语言中,代表有固定长度的连续空间
a := [3]int{1, 2, 3}
b := [...]int{1, 2, 3} // ...不指定个数
截取数组:
例如a为:a:=[...]int{1,2,3,4,5}
a[开始索引(包含):结束索引(不包含)]
a[1:len(a)] //2 3 4 5
a[1:] // 2 3 4 5
a[:3] // 1 2 3
切片
一种结构,包含三种属性:
ptr指针:*Elem
len长度:len(a):int
cap数组容量:cap(a):int,递增顺序为2^n,例如1,2,4,8
底层用数组实现
示例:
var a []int
a = append(a, 12) // append相当于拷贝一个副本进行操作,因此必须使用原参数接收值
s1 := []int{}
s1 := []int{1, 2, 3}
s3 := make([]int, 3, 4) //[]type, len, cap
特别地,截取切片的,计算cap:初始空间到最终空间的长度
func TestSlice(t *testing.T) {
a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
t.Logf("cap(a): %v\n", cap(a)) // 12
t.Logf("cap a[1:3]: %v\n", cap(a[1:3])) // 11
t.Logf("cap a[3:9]: %v\n", cap(a[3:9])) // 9
}
引用了切片或数组,底层数据修改了,那么截取的引用也会变化
数组和切片的比较
容量是否可伸缩
是否可以进行比较
map
初始化或声明:
m0 := map[string]int{"one": 1, "two": 2}
m1 := map[string]int
m2 := make(map[string]int, 10) //第二个参数是容量,不是长度
类似于make(map[string]int, 10)
,第二个参数是指定容量,但无法使用cap方法获取容量。
map初始化无法指定长度,因为无法填充入默认值。
与其他编程语言的差异
无法通过值是否是nil来判断是否有值,因为默认都会是空字符串,判断key是否存在的方法:
// 必须这样判断是否存在
if _, ok := m0["four"]; ok {
t.Log("exixts")
} else {
t.Log("not exixts")
}
遍历
遍历时一个参数是key,两个参数是key, value
func TestSlice(t *testing.T) {
m2 := make(map[string]int, 10)
m2 = map[string]int{"one": 1, "two": 2, "three": 3}
for v := range m2 {
t.Logf("range one item res is: %v", v) // one two three
}
for k, v := range m2 {
t.Logf("range one key item res is: %v", k) //one two three
t.Logf("range one value item res is: %v", v) // 1 2 3
}
t.Logf("m2 len: %v\n", len(m2))
}
map与工厂模式
在go语言中,函数可以出现在任何地方。
map的value也可以是函数。
与go的Dock type接口方式一起,可以方便地实现单一方法对象的工厂模式。
示例代码:
func TestMapWithValueFunc(t *testing.T) {
m := map[int]func(op int) int{}
m[1] = func(op int) int {
return op
}
m[2] = func(op int) int {
return op * op
}
m[3] = func(op int) int {
return op * op * op
}
t.Log(m[1](2), m[2](2), m[3](2))
}
实现Set
1 元素的唯一性(map本身已实现)
2 基本操作:
a. 添加元素:直接设置,例如m1[1]=“1”
b. 判断元素是否存在:通过上文例如:if _, ok := m["one"]; ok {}
c. 删除元素:delete(map, key)
,例如delete(m1, "one")
d. 元素个数:len