GO学习笔记
- GO
- 变量
- 常量
- 基本数据类型
- 整形
- 浮点数
- 复数
- 布尔型
- 字符串
- 流程控制
- 条件判断
- 循环语句
- 运算符
- 复杂数据类型
- 数组
- 切片
- 指针
- map
- 函数
- defer
- 函数类型和变量
- 匿名函数
- 闭包
- 结构体
- 自定义类型和类型别名
- 结构体
- 结构体指针和结构体初始化
- 方法和接受者
- 接口
- 值接收者和指针接收者
- 接口和类型关系
- 空接口
- 包
- 文件操作
- 打开和关闭文件
- 读文件
- 文件写入
- 标准输入和输出
- 内置包
- time
- 时间戳
GO
基本程序框架
package main // 表明这是一个可执行文件
import "fmt" // 引入的库
func main() { // 主函数入口
fmt.Print("Hello World")
}
变量
函数外只能放置变量的声明,不可放定义
// fm.Println("Hello World")非法
func main() {
// do something
}
函数外声明变量,函数内定义变量(非全局变量声明了必须使用)
var s1 string
// 批量声明
var {
s1 string
s2 int
s3 bool
}
func main() {
s1 = "lh"
s2 = 2
s3 = true
}
变量赋值(声明变量同时进行赋值)
// 1)
var s1 string = "lh"
// 2) 类型推导
var s2 = "lh"
// 3) 简短变量声明,只能在函数中使用
var s3 := "lh"
匿名变量:在使用多重赋值时,如果想要忽略某个值,可以使用匿名变量
func foo() {
return 10, "hl"
}
func main() {
_, b := foo()
}
常量
常量在定义的时候必须赋值
const s1 = "lh"
// 批量赋值
const {
s1 = "s2"
s2 = "s1"
}
// 如果批量赋值没有赋值,则按照第一个进行自动赋值
const {
s1 = "s2"
s2
s3
}
如果不是指定确定的值,可以使用iota代替,const中每新增一行常量声明将iota计数一次(有时也可用自定义值代替),可以理解为const的行索引
const {
a1 = iota // 默认为0
a2 // 默认为1
a3 // 默认为2
a4 // 默认为3
}
const {
a1 = iota + 1, a2 = iota + 2 // a1 = 0 + 1,a2 = 0 + 2
a3 = iota + 1, a4 = iota + 2 // a3 = 1 + 1,a4 = 1 + 2
}
基本数据类型
整形
int8 // 对应无符号uint8
int16 // 对应无符号uint16
int32 // 对应无符号uint32
int64 // 对应无符号uint64
uintptr // 无符号整形,用于存放一个指针
fmt.Printf("%d") // 十进制
fmt.Printf("%b") // 二进制
fmt.Printf("%o") // 八进制
fmt.Printf("%x") // 十六进制
浮点数
float32
float64
复数
complex64
complex128
布尔型
true
false
字符串
方法 | 介绍 |
len(str) | 求长度 |
+或fmt.Sprintf | 拼接字符串 |
strings.Split | 分割 |
strings.contains | 判断是否包含 |
strings.HasPrefix,strings.HasSuffxc | 前缀/后缀判断 |
strings.IndexO),strings.LastIndex0 | 子串出现的位置 |
流程控制
条件判断
单分支
package main
import "fmt"
func main() {
var s1 = 2
if s1 > 1 {
fmt.Print("Hello World!")
}
}
双分支
func main() {
var s1 = 2
if s1 > 1 {
fmt.Print("Hello World!")
}
else {
fmt.Print("Hello Linux!")
}
}
多分支
func main() {
var s1 = 2
if s1 > 1 {
fmt.Print("Hello World!")
}
else if s1 > 2 {
fmt.Print("Hello Linux!")
}
else {
fmt.Print("Hello Windows!")
}
}
循环语句
条件循环
for i := 0; i < 10; i++ {
fmt.Print("Hello World")
}
var i := 0
for ; i < 10; i++ {
fmt.Print("Hello World")
}
无限循环
for {
fmt.Print("Hello World")
}
for range:遍历数组、切片、字符串
s1 = "Hello World"
/*
i:索引
v:索引对应的值
*/
for i, v := range s1 {
fmt.Printf("%d $c", i, v)
}
switch和goto
s1 := 2
switch s1 {
case 1:
// do something
case 2:
// do something
case 3:
// do something
}
s := 1
if s == 1{
goto xx
}
xx: // xx标签
fmt.Print("Hello World")
运算符
略
复杂数据类型
数组
数组从声明时就必须确定大小,并且长度不可变
// var 数组名 [元素数量]元素类型
var arr [4]int
数组的初始化(如果不初始化,默认为0)
// 根据初始值自动推断数组的长度是多少
var arr [...]int{1, 2, 3, 4, 5}
// 未赋值的自动为0
var arr[4]int{2, 3}
数组的遍历
for i, v := range arr {
fmt.Printf("%d %c", i, v)
}
多维数组
var arr [3][2]int
切片
slice:数组的长度是固定的,并且数组c长度属于类型的一部分,切片是一个拥有相同长度的可变的序列,是对数组的封装
// 切片的定义
var arr []int
// nil表示为空
fmt.Print(arr == nil)
切片的长度和容量
var arr []int
// 求长度,类似于C++中STL中的size
fmt.Print(len(arr))
// 求容量,类似于C++中STL的cap
fmt.Print(cap(arr))
由数组得到切片
var ar[...]{2,3,4,5,6,7,8}
s3 = ar[0: 4]
为切片添加元素
// append
var arr[]int{2,1,1,1}
arr := append(arr, 3)
复制切片
var arr[]int{2,3,4}
var ar[]int
copy(ar, arr)
指针
go不存在指针操作,只有两个符号
- &:地址
- *:取值
获取指针类型
n := 1
p := &n
fmt.Printf("%T\n", p) // *int
new:初始化一个指针
var s2 := new(int)
make:也是用于内存分配,不过是用于slice、map和chan的分配,返回后的可直接看作一个变量名
make(s1[]int, 10)
map
映射数据结构,基于哈希表
// map名称[key类型]value类型
var mm[int]int
mm[1] := 2
mm[2] := 3
用make动态分配内存
func main() {
var m = make(map[string]int, 10)
for i := 0; i < 10; i++ {
key := "ppa"
value := rand.Intn(100) // 生成0~99的随机数
m[key] := value
}
// 取出所有的key存入切片
var pp = make(arr[]string, 0, 10)
for i, v := range pp {
pp = append(pp, v)
}
// 对切片进行排序
sort.Strings(pp)
// 按照排序后的key遍历map
for _, key := range pp {
fmt.Println(key, map[key])
}
}
函数
/*
func 函数名(参数)(返回值){
函数体
}
*/
func sum(x int, y int)(ret int) {
return x + y
}
func main() {
s := sum(1, 2)
fmt.Printf("%d\n", s)
}
特殊写法
// 返回值只写类型
func sum(x int, y int) int {
return x + y
}
// 返回值写了名称但是return自动返回
func sum(x int, y int)(ret int) {
ret := x + y
return // 此时默认返回ret
}
// 类型简写
func sum(x, y int) int {
return x + y
}
// 多个返回值
func sum()(string, int) {
return "Hl", 2
}
// 可变长参数
func sum(x string, y ...int) {
// do something
}
defer
defer语句会将其后面跟随的语句进行延迟处理,在defer归属的函数即将返回时,将延迟处理的y语句按照defer定义的逆序进行执行(多用于资源释放)
func sum() {
fmt.Print("Hello World") // 0
defer fmt.Print("Hello UNIX") // 3
defer fmt.Print("Hello Linux") // 2
fmt.Print("Hello Windows") // 1
}
函数类型和变量
函数也是可以作为一种类型存在,
func sum() {
fmt.Print("Hello World!")
}
func main() {
a := sum // 将函数赋值给变量a,此时a应该是什么类型?
}
函数也可以作为参数的类型
func x(f func()int) {
ret = f
ret()
}
函数也可以作为返回值
func x() func(x, y int)int {
ret := func(a, b int) int {
return x + y
}
return ret
}
匿名函数
var f1 = func(x, y int) {
return x + y
}
闭包
定义一个新的函数对原函数进行封装,一个函数包含了一个外部的变量的引用
func f1(x, y int) {
fmt.Print("Hello World")
}
func fm(x func(x, y int), x, y int) func() {
return func() {
x(x, y) // 去外部找到x和y
}
}
func main() {
ret := fm(1, 2)
ret()
}
当闭包的内部函数没有形参的时候,在外部去找
func f1(x, y int) {
fmt.Print("Hello World")
}
func fm(x func(x, y int), x, y int) func(int) {
return func(p int) {
x(x, y)
}
}
func main() {
ret := fm(1, 2)
ret(100)
}
结构体
自定义类型和类型别名
// 自定义类型
type myInt int
// 类型别名
type myInt = int
结构体
/*
type 类型名 struct {
字段名 字段类型
字段名 字段类型
字段名 字段类型
......
}
*/
type person struct {
name string
age int
hobby []string
}
func main() {
var p person
p.name = "lh"
p.age = 12
p.hobby = []string{"Hello World", "Hello Linux", "Hello UNIX"}
}
匿名结构体,不做概述
结构体指针和结构体初始化
type person struct {
name string
age int
}
func p(x person) {
p.name = "hl" // 修改的是副本,不是内存
}
func p1(x *person) {
p.name = "ha" // 修改的是内存
}
func main() {
var p person
p.name = "lh"
p.age = 12
f(p)
f(&p)
var d = new(person)
}
结构体初始化
// key-value
var ps = person {
name: "asd"
age: 12
}
// 列表
p4 = person{
"asd"
12
}
// 构造函数
func newPerson(name string, age int) person {
return person {
name: name
age: age
}
}
在GO中指针和普通值变量区别仅仅在于初始化和含义的不同,其他都是一样的(比如操作)
方法和接受者
go语言中的方法是作用于特定类型变量的函数,这种特殊变量叫做接受者(receiver)
/*
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
*/
type person struct {
name string
age int
}
func (p person)wang() {
fmt.Printf("%s", p.name)
}
值接收者和指针接收者
// 值类型
func (p person)wang() {
fmt.Printf("%s", p.name)
}
// 指针类型,改内存
func (p *person)wang() {
fmt.Printf("%s", p.name)
}
假继承:不做概述
接口
接口是一种类型
编程会会遇到一种情况,我不管她是什么类型,我只关心能调用她的方法
type dog struct {}
type cat struct {}
type sp interface {
speak() // 有这个方法就是这个类型
}
func (d dog) speak() { // dog有speak方法
fmt.Print("Hello A")
}
func (c cat) speak() { // cat有speak方法
fmt.Print("Hello B")
}
func ap(d sp) {
d.speak()
}
func main() {
var d dog
var c cat
ap(d)
ap(c)
}
值接收者和指针接收者
接口都能存入 ,但是使用方法的时候必须使用特定的方法
type animal interface {
move()
eat(string)
}
type cat struct {
name string
feet int8
}
func (c cat)move() {
fmt.Print("Hello Mike")
}
func (c cat)eat(food string) {
fmt.Print("Hello Amy")
}
func main() {
var a1 animal
c1 := cat {"Tom", 12}
c2 := &cat {"dd", 13}
a1 = c1 // 将c1存入到接口中
a1 = c2 // 将c2存入到接口中
}
接口和类型关系
- 多个类型实现一个接口
- 一个类型实现多个接口
- 接口嵌套
type person interface {
A
B
}
type A interface {
A()
}
type B interface {
B()
}
空接口
任何类型都可用
interface {}
空接口作为函数的参数,表示可以接收任何类型的参数
func a(p interface{})
空接口作为map的值
ma map[string]interface {}
包
包命名
package app
// 在GO语言导入包的时候会自动触发init函数的调用,其my参数也没有返回值
func init() {
fmt.Print("Hello World!")
}
// 包内的函数如果小写则表示只能在本包内使用
func Add(a, b int) int {
return a + b
}
包的导入
// 单包
import "app"
// 多包
import (
"app"
"bpp"
)
文件操作
打开和关闭文件
os .open()函数能够打开一个文件,返回士个File 和一个err 。对得到的文件实例调用close() 方法能够关闭文件。.
import "os"
// 打开文件
file, err := os.Open("path")
if err != nil {
fmt.Print(err)
return
}
// 关闭文件
file.Close()
读文件
var tmp = [128]byte
// 循环读取
for {
n, err := file.Read(tmp[:])
if err == io.EOF {
fmt.Print("读完了")
return
}
if err != nil {
fmt.Print(err)
return
}
// 读取了多少字节
fmt.Print(n)
// 打印内容
fmt.Print(tmp[: n])
if n < 128 {
return
}
}
文件写入
os .OpenFile() 函数能够以指定模式打开文件,从而实现文件写入相关功能。
模式 | 含义 |
os.O_WRONLY | 只写 |
os.O_CREATE | 创建文件 |
os.O_RDONLY | 只读 |
os.O_RDWR | 读写 |
os.O_TRUNC | 清空 |
os.O_APPEND | 追加 |
r:04,w:02,x:01
fileObj, err := os.OpenFile("./xx.txt", os.O_WRONLY|os.RDONLY, 0644)
if err != nil {
fmt.Print(err)
return
}
fileObj.Write([]byte("Hello World!\n"))
fileObj.WriteString("Hello Linux!")
fileObj.Close()
标准输入和输出
func io() {
var s string
fmt.Print("please input content")
fmt.Scanln(&s)
fmt.Printf("%s\n", s)
}
使用bufio:可以读入空格
import "io/bufio"
func use() {
var s string
// os.Stdin相当于是一个终端
reader := bufio.NewReader(os.Stdin)
s, _ = reader.ReadString('\n')
fmt.Printf("%s", s)
}
相同的也可以放在文件流中
file, err := os.Open("path")
if err != nil {
fmt.Print(err)
return
}
reader := bufio.NewReader(file)
for {
line, err = reader.ReadString('\n')
if err == EOF {
return
}
if err != nil {
fmt.Print(err)
return
}
fmt.Printf("%s", line)
}
内置包
time
time.Time类型表示时间。我们可以通过time . Now()|函数获取当前的时间对象,然后获取时间对象的年月日时分秒等信息。示例代码如下:
now := time.Now() // 获取当前时间
fmt.Printf("%v\n", now)
year := now.Year()
month := now.Month()
day := now.Day()
hour := now.Hour()
minute := now.Minute()
second := now.Second()
fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second)
时间戳
now := time.Now() // 获取当前时间
t1 := now.Unix()
t2 := now.UnixNano() // 纳秒数
// 使用time.Unix(a, 0)将时间戳转换成时间格式
timeObj = time.Unix(t1, 0)