贺胖娇的编程之旅......

学习go_20220806_01

2022.08.06

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

发表评论