1、包的声明与导入
go语言中包的本质:创建不同的文件夹,以功能模块来存放程序文件
go语言的源码复用建立在包基础之上
-
main包
Go语言的入口main函数所在的包必须是main包,其他包不能使用
-
package
src目录是以代码包的形式组织并保存go源码文件的,每个代码包和src目录下的文件夹一一对应,每个子目录都是一个代码包。
Go语言中代码包与文件目录,不要求一致,比如文件目录叫hello,但是代码包的包名可以声明为main
同一目录下的所有go文件的第一行添加包定义,以标记该文件归属的包
package 包名
关于包的使用
- package 声明的包和对应的目录名可以不一致,但习惯上还是写成一致的
- 一个目录下的所有go文件的package必须同名
- 同包下的函数不要导入包,可以直接使用,函数名要大写
- main包,main函数所在的包必须是main包,其他包不能使用
- 对于包外而言,导入包的时候可以使用”包名.函数名”的方式使用包内函数,如fmt.Println,函数名必须要大写首字母
main函数外面Run,编译执行单个go文件
main函数里面Run,编译执行整个项目,一个项目下只能有一个main函数入口
1、导入其他包的标准写法,需要goworks下面的src目录开始导
import "studygo/service/user"
2、批量导入包
import {
// 系统包
"fmt"
// 自定义的包
"studygo/service"
"studygo/service/user"
// 从网上下载的包
}
3、相对路径导入包
import "../pojo"
4、包名冲突的解决方式,起别名
/* 包的重命名可以用于解决包名冲突,包名过长,避免与变量或常量名称冲突等情况*/
import "crypto/rand" // 默认模式,访问 rand.Function ,跟fmt一样
import R "crypto/rand" // 包的别名,通过别名访问该包下的函数等,访问 R.function
import . "crypto/rand" // 简便模式,可以直接调用该包下的函数等 (不建议,怕方法重名),直接访问函数
import _ "crypto/rand" // 匿名导入,仅让该包执行init初始化函数
Go 语言使用名称首字母大小写来判断一个对象(函数、全局变量、类型、结构字段、函数)的访问权限,对于包而言同样如此,首字母大写,可被包外访问,即public,首字母小写,仅包内成员可以访问,即internal
2、init函数初始化
Go语言有一个特殊的函数init,它先于main函数执行,实现包级别的一些初始化操作,如建立数据库连接、初始化全局变量、注册、检查修复程序状态等。
init用在设置包、初始化变量或者其他要在程序运行前优先完成的引导工作。
即使包被导入多次,初始化只执行一次
// 匿名导入,仅执行该包下的init初始化函数,先main函数执行
// 注意必须是Goworks/src目录下的包路径
import (
_ "awesomeProject/lesson10/test"
"fmt"
)
func main() {
fmt.Println("main---")
}
test包:
多个init函数
a.go 下多个init函数
import "fmt"
func init(){
fmt.Println("test a init1")
}
func init() {
fmt.Println("test a init2")
}
func init() {
fmt.Println("test a init3")
}
main包下也增加一个init函数
package main
// 匿名导入,执行该包下的init初始化函数,先与main函数执行
// 注意必须是Goworks/src目录下的包路径
import (
_ "awesomeProject/lesson10/test"
"fmt"
)
// init函数不需要传入参数,也不会返回任何值,与main相比,init没有被声明,因此也不能被引用
func init() {
fmt.Println("main init")
}
func main() {
fmt.Println("main---")
}
执行程序入口main函数,结果:
test a init1
test a init2
test a init3
main init
main---
init执行顺序
为了使用导入的包,首先必须将其初始化,初始化总是以单线程执行的,并且按照包的依赖关系顺序执行,这通过golang的运行时系统控制,如下图所示:
- 初始化导入的包(递归导入)
- 对包块中声明的变量进行计算和分配初始值
- 执行包中的init函数
例子:
上面的例子代码增加包test2与b.go
package test2
import "fmt"
func init() {
fmt.Println("test2-b-init-1")
}
a.go匿名引入包test2
import "fmt"
import _ "awesomeProject/lesson10/test2"
func init(){
fmt.Println("test a init1")
}
func init() {
fmt.Println("test a init2")
}
func init() {
fmt.Println("test a init3")
}
执行demo02.go的main函数
执行结果:
test2-b-init-1
test a init1
test a init2
test a init3
main init
main---
包test2下再增加多一个c.go,包含init函数
package test2
import "fmt"
func init() {
fmt.Println("test2-c-init-1")
}
执行结果:
test2-b-init-1
test2-c-init-1
test a init1
test a init2
test a init3
main init
main---
把b.go改名称为d.go,重新执行,结果如下:
test2-c-init-1
test2-b-init-1
test a init1
test a init2
test a init3
main init
main---
所以同一个包下的多个init函数,根据go文件名称的首字母排序执行init函数
总结,init函数总是自上而下执行的
3、strings包常用函数
字符串常用操作
package main
import (
"fmt"
"strings"
)
func main() {
// 定义一个字符串
str := "xuexiangban,kuangshen"
// Contains 是否包含指定的内容,返回布尔值,精确查询
fmt.Println(strings.Contains(str, "k")) // true
fmt.Println(strings.Contains(str, "z")) // false
fmt.Println(strings.Contains(str, "ka")) //false
// ContainsAny 是否包含指定的任意一个内容,有一个即可,返回布尔值
fmt.Println(strings.ContainsAny(str, "ka")) // true
// Count统计指定内容,在字符串中出现的次数
fmt.Println(strings.Count(str, "n")) // 4
// HasPrefix 以xx开头,返回布尔值
// 场景,返回2023开头的文件
filename := "20231203.mp4"
if strings.HasPrefix(filename, "2023") {
fmt.Println("找到2023开头的文件")
}
// HasSuffix,以xx结尾,返回布尔值
if strings.HasSuffix(filename, "mp4") {
fmt.Println("找到mp4结尾的文件")
}
// Index 寻找指定字符串第一次出现的位置,找到返回下标,找不到返回-1
fmt.Println(strings.Index(str, "n")) // 6
// IndexAny 寻找指定字符串任意字符第一次出现的位置,找到返回下标,找不到返回-1
fmt.Println(strings.IndexAny(str, "xz")) // 0
// LastIndex 寻找指定字符串最后一次出现的位置,找到返回下标,找不断返回-1
fmt.Println(strings.LastIndex(str, "n")) // 20
// Join 用指定符号进行字符串拼接
str2 := []string{"a", "b", "c", "d"}
str3 := strings.Join(str2, "-")
fmt.Println(str3) // a-b-c-d
// Split 分隔字符串返回切片,用的最多
str4 := strings.Split(str3, "-")
fmt.Println("Split 返回切片:", str4, "切片长度", len(str4)) // 切片长度4
// Repeat 重复拼接自己,返回字符串
str5 := strings.Repeat("a", 5)
fmt.Println(str5) // aaaaa
// Replace 替换指定字符,n代表替换个数,-1替换全部
str6 := strings.Replace(str, "u", "*", -1)
fmt.Println(str6) // x*exiangban,k*angshen
// ReplaceAll
// ToUpper 转大写 ToLower 转小写
fmt.Println(strings.ToUpper(str))
// 截取字符串 str[start,end],跟我们的数组转切片是非常像的
str7 := str[0:5]
fmt.Println(str7) // xuexi
// index=5到结尾
fmt.Println(str[5:]) // angban,kuangshen
}
4、strconv包常用函数
string convert ,字符串与基本类型之间的转换
前端:网页、小程序、app
后端:go代码
package main
import (
"fmt"
"strconv"
)
func main() {
s1 := "true"
// 1、字符串转换为bool类型
b1, _ := strconv.ParseBool(s1)
fmt.Printf("%T\n", b1)
fmt.Println(b1)
// 2、bool类型转换字符串
s2 := strconv.FormatBool(b1)
fmt.Printf("%T\n", s2)
fmt.Println(s2)
// 3、字符串与整型的互转, 转换为对应的进制数
// 第一个参数,字符串
// 第二个参数进制,如10进制,2进制
// 第三个参数大小
s3 := "100"
i1, _ := strconv.ParseInt(s3, 10, 64)
fmt.Printf("%T\n", i1) // 打印类型 int64
fmt.Println(i1) // 打印100
// 整型转字符串,
// 第一个参数,整数
// 第二个参数进制,如10进制,2进制
s4 := strconv.FormatInt(100, 2)
fmt.Printf("%T\n", s4)
fmt.Println(s4)
// 4、整型与字符串快速互转的方法 Ascii to Integer
// atoi 字符串转整型
i2, _ := strconv.Atoi("-20")
fmt.Printf("%T\n", i2)
fmt.Println(i2 + 10) // -10
// itoa 整型转字符串
s5 := strconv.Itoa(30)
fmt.Printf("%T\n", i2) // int
fmt.Println(s5) // 30
}
执行结果:
bool
true
string
true
int64
100
string
1100100
int
-10
int
30
5、时间与时间戳
time包
- 获取当前时间
- 时间格式化
- 定时
时间和日期是我们编程中经常会用到的,我们讲一下go内置的time包的基本用法
获取当前时间
func main() {
time1()
}
func time1() {
now := time.Now()
fmt.Println(now)
year := now.Year() // 年
month := now.Month() // 月
day := now.Day() // 日
hour := now.Hour()
minute := now.Minute()
second := now.Second()
// 格式化打印,%02d 整数不足两位,左边用0补齐
fmt.Printf("%d-%2d-%2d %2d:%2d:%2d", year, month, day, hour, minute, second)
}
执行结果:
2023-01-17 19:35:49.605219 +0800 CST m=+0.000101799
2023-01-17 19:35:49
时间格式化
时间类型有一个自带的方法Format
进行格式化,需要注意的是Go语言中格式化时间模版不常见的YY-MM-DD Hh:M:S
,而是go的诞生时间2006年1月2号15点04分,记忆口径 2006 1 2 3 4
func main() {
// time1()
now := time.Now()
fmt.Println(now.Format("2006-01-02 15:04:05")) // 24小时制
fmt.Println(now.Format("2006-01-02 03:04:05 PM")) // 12小时制
fmt.Println(now.Format("2006/01/02 15:04"))
fmt.Println(now.Format("15:04 2006/01/02"))
fmt.Println(now.Format("2006-01-02"))
}
执行结果:
2023-01-17 19:45:20
2023-01-17 07:45:20 PM
2023/01/17 19:45
19:45 2023/01/17
2023-01-17
解析字符串格式的时间
返回time对象
func main() {
// time1()
now := time.Now()
fmt.Println(now)
// 时区
loc, _ := time.LoadLocation("Asia/Shanghai") // 点进去看源码,UTC 0时区,Local 本地时区
// 字符串转时间对象,需要指定时区
timeObj, _ := time.ParseInLocation("2006-01-02 15:04:05", "2023-01-20 19:10:20", loc)
fmt.Printf("%T\n", timeObj) // 打印时间对象类型
fmt.Println(timeObj)
fmt.Println(timeObj.Format("2006/01/02"))
}
执行结果:
2023-01-17 20:31:31.525175 +0800 CST m=+0.000107246
2023-01-20 19:10:20 +0800 CST
2023/01/20
时间戳
时间戳是自1970年1月1日(08:00:00GMT),到当前时间的总毫秒数,它也被称为Unix时间戳UnixTimestamp
func main() {
// time1()
now := time.Now()
// 时间戳,获取当前时间戳
timestamp := now.Unix() // 秒
fmt.Println(timestamp)
timestamp2 := now.UnixMilli() // 毫秒
fmt.Println(timestamp2)
timestamp3 := now.UnixMicro() // 微秒
fmt.Println(timestamp3)
timestamp4 := now.UnixNano() // 纳秒
fmt.Println(timestamp4)
// 时间戳转换时间对象, 获取 年月日时分秒
time1 := time.Unix(timestamp, 0)
fmt.Println(time1)
}
执行结果:
1673959436
2023-01-17 20:43:56 +0800 CST
6、生成随机数
随机数需要调用math/rand
包,注意要设置种子数,这样每次生成的结果会不断变化
func main() {
rand.Seed(13) // 种子数不一样,生成的随机数才会不一样
number := rand.Int() // 5920220759044228662,不建议使用
fmt.Println(number)
// 指定随机数的范围
number2 := rand.Intn(100) // 0-9的随机数
fmt.Println(number2)
// 发现种子数不变,生成的随机数就不会变化
// 把当前时间戳做为种子数,那么每次生成的随机数就会不一样了
rand.Seed(time.Now().Unix())
for i := 0; i < 10; i++ {
num := rand.Intn(1000) // 0-999的随机数
fmt.Printf("%03d\n", num)
}
// 20-100的随机数
num3 := rand.Intn(20) + 80
fmt.Println(num3)
}
7、定时器与时间判断
间隔常量Duration
time.Duration是time包定义的一个类型,它代表两个时间点之间经过的时间
以纳秒为单位
time包中定义的时间间隔类型的常量如下:
const{
Nanosecond Duration =1 // 纳秒
// 微妙
Microsecond = 1000 * Nanosecond
// 毫秒
Millisecond = 1000 * Microsecond
// 秒
Second = 1000 * Millisecond
// 分
Minute = 60 * Second
// 时
Hour = 60 * Minute
}
time.Duration 表示1纳秒,time.Second表示1秒
定时器
定时器time.tick
import (
"fmt"
"time"
)
func main() {
// Tick 心跳
ticker := time.Tick(time.Second) // 间隔1秒
// ticker := time.Tick(3 * time.Second) // 间隔3秒
for t := range ticker { // 每隔1秒执行一次
fmt.Println(t)
}
for i := 0; i < 10; i++ {
fmt.Println(time.Now())
// 让程序休眠1秒
time.Sleep(time.Second)
}
}
执行结果:
2023-01-17 22:27:23.797529 +0800 CST m=+1.002337217
2023-01-17 22:27:24.799321 +0800 CST m=+2.004135374
2023-01-17 22:27:25.799947 +0800 CST m=+3.004767502
2023-01-17 22:27:26.799172 +0800 CST m=+4.003999735
...
8、时间常用操作
Add
func main() {
// Add
now := time.Now()
later := now.Add(time.Hour) // 1个小时后的时间
fmt.Println(later)
// Sub
subtime := later.Sub(now)
fmt.Println(subtime)
// Equal
fmt.Println(later.Equal(now))
// Before
fmt.Println(now.Before(later))
// After
fmt.Println(now.After(later))
}
执行结果:
2023-01-17 23:45:35.840123 +0800 CST m=+3600.000090272
1h0m0s
false
true
false
Sub
求两个时间之间的差值
Equal
判断两个时间是否相同,这种方法还会比较地点和时区信息,因此不同时区标准的时间也可以正确比较
Before
是否在某个时间点之前,如果t代表的时间点在u之前,返回true,否则返回false
After
是否在某个时间点之后