包的概念,导入,可见性规则

包的概念

这是一个最经典的go程序

package main//main包表示一个可以执行的程序

import "fmt"

func main() {
fmt.Println("Hello, World!")
}

第一行指明这个文件属于哪个包,一个包可以由许多go文件组成,每个go文件都属于且仅属于一个包。一个应用程序可以包含不同的包,可以写很多个go文件,每一个的第一行都标为同一个包,但是不能声明多个main包

导入

导入包的时候可以分开导入

import "fmt"
improt "os"

或者一起导入

import(
"fmt"
"os"
)

可见性规则

当标识符(常量,变量,函数名,结构字段……)以大写字母开头,如(Group),那么就可以被外部包代码使用,称作导出,如果以小写开头,则无法被外部使用,只能在该包下使用,相当于oop的private。

例如:在pack1包中有一个变量Str1,那么在被导入后,在别的包可以直接使用pack1.Str1来使用该变量,注意pack1是不可省略的。

可以通过更改包名来解决名称冲突,例如:

package main//main包表示一个可以执行的程序

import fm "fmt"

func main() {
fm.Println("Hello, World!")
}

语法

main函数

  • main包必须包含main函数,main函数没有参数也没有返回类型,函数的左大括号不能换行,否则报错

定义变量

//var name type=value
var a int=3

常量

定义格式:

const identifier [type] = value
const Pi=3.14159

type可以省略,这样就交给编译器自行判断,数字型的常量是没有大小和符号的,并且可以使用任何精度而不会导致溢出(好耶)

:=和=

这两个都可以用来初始化变量,前者让编译器自动通过变量数值来判断变量类型,后者直接让变量值赋值给左侧变量,要注意的是前者不能作用于已经初始化后的变量,后者可以。go语言中可以并行或同时赋值,

a,b,c:=3,5,'abc'

如果想交换变量的值,可以像python一样:

a,b=b,a

变量

声明的格式是:

var identifier type
var a int

可以让声明和赋值一起来:

var identifier [type] = value
var a int = 15
var i = 5
var b bool = false
var str string = "Go says hello to the world!"

或者让编译器自己判断类型

var a = 15
var b = false
var str = "Go says hello to the world!"

字符串

前缀和后缀

strings包中包含一些常见操作:

strings.HasPrefix(s, prefix string) bool//查看是否以该前缀开头,返回布尔值
strings.HasSuffix(s, suffix string) bool//是否具有该后缀

例子:

package main

import (
"fmt"
"strings"
)

func main() {
var str string = "This is an example of a string"
fmt.Printf("T/F? Does the string \"%s\" have prefix %s? ", str, "Th")
fmt.Printf("%t\n", strings.HasPrefix(str, "Th"))
}

包含

strings.Contains(s, substr string) bool//判断s是否包含字串substr

索引

返回字符串str在父串s中的索引

strings.Index(s, str string) int
strings.LastIndex(s, str string) int//返回最后一次出现位置的索引

替换

Replace() 用于将字符串 str 中的前 n 个字符串 old 替换为字符串 new,并返回一个新的字符串,如果 n = -1 或n大于字串的个数则替换所有字符串 old 为字符串 new

strings.Replace(str, old, new string, n int) string

统计次数

统计字符串str在字符串s中出现的非重叠次数

strings.Count(s, str string) int

重复字符串

将字符串s重复count次

strings.Repeat(s,count int) string

大小写

strings.ToLower(s) string//变小写
strings.ToUpper(s) string//将字符串变大写

修剪

strings.TrimSpace(s)//剔除字符串开头和结尾的空白符号
strings.Trim(s,"cut")//剔除指定字符,剔除的位置是开头和结尾

如果要指定左边或者右边,用TrimLeft()和TrimRight()

分割

这里注意返回的是slice

strings.Split(s, sep)//自定义分割符号分割字符串

拼接

strings.Join(sl []string, sep string) string

时间和日期

示例:

package main
import (
"fmt"
"time"
)

var week time.Duration
func main() {
t := time.Now()
fmt.Println(t) // e.g. Wed Dec 21 09:52:14 +0100 RST 2011
fmt.Printf("%02d.%02d.%4d\n", t.Day(), t.Month(), t.Year())
// 21.12.2011
t = time.Now().UTC()
fmt.Println(t) // Wed Dec 21 08:52:14 +0000 UTC 2011
fmt.Println(time.Now()) // Wed Dec 21 09:52:14 +0100 RST 2011
// calculating times:
week = 60 * 60 * 24 * 7 * 1e9 // must be in nanosec
week_from_now := t.Add(time.Duration(week))
fmt.Println(week_from_now) // Wed Dec 28 08:52:14 +0000 UTC 2011
// formatting times:
fmt.Println(t.Format(time.RFC822)) // 21 Dec 11 0852 UTC
fmt.Println(t.Format(time.ANSIC)) // Wed Dec 21 08:56:34 2011
// The time must be 2006-01-02 15:04:05
fmt.Println(t.Format("02 Jan 2006 15:04")) // 21 Dec 2011 08:52
s := t.Format("20060102")
fmt.Println(t, "=>", s)
// Wed Dec 21 08:52:14 +0000 UTC 2011 => 20111221
}

带多个返回值的函数错误测试

value, err := pack1.Function1(param1)
if err != nil {
fmt.Printf("An error occured in pack1.Function1 with parameter %v", param1)
return err
}
// 未发生错误,继续执行:

switch结构

switch i {
case 0: // 空分支,只有当 i == 0 时才会进入分支
case 1:
f() // 当 i == 0 时函数不会被调用
}
switch i {
case 0: fallthrough
case 1:
f() // 当 i == 0 时函数也会被调用
}

fallthrough允许在执行case后继续往后执行,此时无论是否符合匹配条件都会执行

for range结构

用来遍历,对象可以是对象或者数组

ages := map[string]int{
"John": 25,
"Alice": 30,
"Bob": 35,
}

for key, value := range ages {
fmt.Println("Name:", key, "Age:", value)
}

标签和goto

package main

func main() {
i:=0
HERE://全部用大写
print(i)
i++
if i==5 {
return
}
goto HERE
}

函数

传递变长参数

使用方法:

func myFunc(a, b, arg ...int) {}

实例:

func Greeting(prefix string, who ...string)
Greeting("hello:", "Joe", "Anna", "Eileen")

defer关键字

defer 是 Go 语言中的关键字,用于延迟(defer)执行一个函数调用。当使用 defer 语句时,它会将函数调用推迟到所在函数返回之前执行。

defer 的主要用途包括:

  1. 延迟关闭资源:当你需要在函数执行完成后关闭打开的文件、数据库连接或网络连接等资源时,可以使用 defer 来确保在函数返回之前进行关闭操作,避免资源泄漏。
  2. 错误处理:当你需要在函数返回前进行一些清理操作,或者记录函数的结束状态时,可以使用 defer 来执行这些操作。例如,在函数中发生错误时,你可以在 defer 中记录错误日志。
  3. 代码简化:在某些情况下,使用 defer 可以简化代码逻辑。例如,在函数中进行加锁和解锁操作时,可以使用 defer 来确保在函数退出时正确释放锁,避免忘记解锁的问题。

defer 语句的执行顺序是“后进先出”(Last In, First Out)的顺序。也就是说,最后一个使用 defer 的函数调用会最先执行,而第一个使用 defer 的函数调用会最后执行。

例子:

package main

import "fmt"

func main() {
function1()
}

func function1() {
fmt.Printf("In function1 at the top\n")
function2()
fmt.Printf("In function1 at the bottom!\n")
}

func function2() {
fmt.Printf("Function2: Deferred until the end of the calling function!")
}

输出:

In function1 at the top
In function1 at the bottom!
Function2: Deferred until the end of the calling function!

如果没有defer那么就没有第二行

内置函数

名称 说明
close() 用于管道通信
len()cap() len() 用于返回某个类型的长度或数量(字符串、数组、切片、map 和管道);cap() 是容量的意思,用于返回某个类型的最大容量(只能用于数组、切片和管道,不能用于 map
new()make() new()make() 均是用于分配内存:new() 用于值类型和用户定义的类型,如自定义结构,make 用于内置引用类型(切片、map 和管道)。它们的用法就像是函数,但是将类型作为参数:new(type)make(type)new(T) 分配类型 T 的零值并返回其地址,也就是指向类型 T 的指针。它也可以被用于基本类型:v := new(int)make(T) 返回类型 T 的初始化之后的值,因此它比 new() 进行更多的工作。new() 是一个函数,不要忘记它的括号
copy()append() 用于复制和连接切片
panic()recover() 两者均用于错误处理机制
print()println() 底层打印函数,在部署环境中建议使用 fmt
complex()real ()imag() 用于创建和操作复数

回调

go的回调例子:

package main

import (
"fmt"
)

func main() {
callback(1, Add)
}

func Add(a, b int) {
fmt.Printf("The sum of %d and %d is: %d\n", a, b, a+b)
}

func callback(y int, f func(int, int)) {
f(y, 2) // this becomes Add(1, 2)
}

输出是:

The sum of 1 and 2 is: 3

匿名函数

package main

import "fmt"

func main() {
f()
}
func f() {
for i := 0; i < 4; i++ {
g := func(i int) { fmt.Printf("%d ", i) }
g(i)
fmt.Printf(" - g is of type %T and has value %v\n", g, g)
}
}