go语言介绍
1.1 什么是go语言
1.1.1、Go 语言的发展史
Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型语言。Go 语言语法与 C 相近,但功能上有:内存安全,GC(垃圾回收),结构形态及 CSP-style 并发计算。Go语言由于其简洁、安全、内存管理和编译迅速等特点,现已在各个互联网公司中广泛应用。
1.1.2、Go语言的特点
Go的语法接近C语言,但对于变量的声明有所不同。Go支持垃圾回收功能。Go的并行模型是以东尼·霍尔的通信顺序进程(CSP)为基础,采取类似模型的其他语言包括Occam和Limbo,但它也具有Pi运算的特征,比如通道传输。在1.8版本中开放插件(Plugin)的支持,这意味着现在能从Go中动态加载部分函数。 与C++相比,Go并不包括如枚举、异常处理、继承、泛型、断言、虚函数等功能,但增加了 切片(Slice) 型、并发、管道、垃圾回收、接口(Interface)等特性的语言级支持。Go 2.0版本将支持泛型,对于断言的存在,则持负面态度,同时也为自己不提供类型继承来辩护。 不同于Java,Go内嵌了关联数组(也称为哈希表(Hashes)或字典(Dictionaries) ),就像字符串类型一样。
Go中有几项规定,当不匹配以下规定时编译将会产生错误。 每行程序结束后不需要撰写分号。 左大括号不能够换行放置。 if判断式和for循环不需要以小括号包覆起来。 Go亦有内置gofmt工具,能够自动整理代码多余的空白、变量名称对齐、并将对齐空格转换成Tab。
总结:
- 高性能
- 语法简单、学习曲线平缓
- 丰富的标准库
- 完整的工具链
- 静态连接
- 快速编译
- 跨平台
- 垃圾回收
tips:快速上手、应用广、便携方便
1.2 应用的公司
- 字节跳动
- 腾讯
- 美团
- 滴滴
- 百度
- face book
- 七牛云
- 哔哩哔哩
- Ping CAP
- ......
tips:用的很广
1.3 字节为什么用go
- 性能问题(业务体量的增长)
- c++不太适合在线web业务
- 早期团队非java背景
- 部署简单、学习成本低
- 内部RPC和HTTP框架的推广
go语言的入门
2.1 开发环境
2.1.1 安装Golang
访问go.dev-->Download-->下载适合自己电脑操作系统的安装包-->傻瓜式安装
中文镜像网:
- studygolang.com
- goproxy.cn (tips:个人感觉这个访问更快)
2.1.2 配置集成开发环境
2.2 基础语法
Go Hello World 实例
Go 语言的基础组成有以下几个部分:
- 包声明
- 引入包
- 函数
- 变量
- 语句 & 表达式
- 注释
接下来让我们来看下简单的代码,该代码输出了"Hello World!":
实例
package main
import "fmt"
func main() {
/* 这是我的第一个简单的程序 */
fmt.Println("Hello, World!")
}
让我们来看下以上程序的各个部分:
- 第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
- 下一行 import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。
- 下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。
- 下一行 /.../ 是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
- 下一行 fmt.Println(...) 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。
使用 fmt.Print("hello, world\n") 可以得到相同的结果。
Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。 - 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。
执行 Go 程序
让我们来看下如何编写 Go 代码并执行它。步骤如下:
-
打开编辑器如Sublime2,将以上代码添加到编辑器中。
-
将以上代码保存为 hello.go
-
打开命令行,并进入程序文件保存的目录中。
-
输入命令 go run hello.go 并按回车执行代码。
-
如果操作正确你将在屏幕上看到 "Hello World!" 字样的输出。
$ go run hello.go Hello, World! -
我们还可以使用 go build 命令来生成二进制文件:
$ go build hello.go $ ls hello hello.go $ ./hello Hello, World!
go run main.go----直接运行
go build main.go----编译为二进制 ---> ./main运行
fmt.print() //初始版
fmt.printf() //格式化 format(可以用%d等)
fmt.println() //每行末尾追加换行符
2.2.1 hello world
2.1 Go 语言数据类型
Go 语言按类别有以下几种数据类型:
| 序号 | 类型和描述 |
|---|---|
| 1 | 布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
| 2 | 数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
| 3 | 字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
| 4 | 派生类型: 包括:- (a) 指针类型(Pointer) |
- (b) 数组类型
- (c) 结构化类型(struct)
- (d) Channel 类型
- (e) 函数类型
- (f) 切片类型
- (g) 接口类型(interface)
- (h) Map 类型 |
数字类型
Go 也有基于架构的类型,例如:int、uint 和 uintptr。
| 序号 | 类型和描述 |
|---|---|
| 1 | uint8 无符号 8 位整型 (0 到 255) |
| 2 | uint16 无符号 16 位整型 (0 到 65535) |
| 3 | uint32 无符号 32 位整型 (0 到 4294967295) |
| 4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
| 5 | int8 有符号 8 位整型 (-128 到 127) |
| 6 | int16 有符号 16 位整型 (-32768 到 32767) |
| 7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
| 8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
浮点型
| 序号 | 类型和描述 |
|---|---|
| 1 | float32 IEEE-754 32位浮点型数 |
| 2 | float64 IEEE-754 64位浮点型数 |
| 3 | complex64 32 位实数和虚数 |
| 4 | complex128 64 位实数和虚数 |
其他数字类型
以下列出了其他更多的数字类型:
| 序号 | 类型和描述 |
|---|---|
| 1 | byte 类似 uint8 |
| 2 | rune 类似 int32 |
| 3 | uint 32 或 64 位 |
| 4 | int 与 uint 一样大小 |
| 5 | uintptr 无符号整型,用于存放一个指针 |
2.2.2 变量
常见变量类型:
string(字符串 可用加号直接拼接) int(整型) byte(一个字节) bool(布尔,用%t)float(浮点型,用%f)
fmt.printf("%T"a) //输出变量的类型
folat64 ≈ double
double用于c语言
tips:优先级类似于c++
变量的申明
变量类型
- var 变量名 (类型)=值
- 变量名 :=值(申明和赋值一起搞定,自动推断数据类型)
示例代码:
var a = "initial"
var b, c int = 1, 2
var d = true
var e float64
f := float32(e)
g := a + "foo"
fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0
fmt.Println(g) //initialapple
常量的申明
- const 常量名(类型)
示例代码:
const s string = "constant"
const h = 500000000
const i = 3e20 / h
fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
2.3 循环(只有for)
格式:
for i:=0;i<3;i++{
}
等价于:
i:=0
for i<3{
i++
}
示例代码:
package main
import "fmt"
func main() {
i := 1
for { //死循环
fmt.Println("loop")
break
}
for j := 7; j < 9; j++ {
fmt.Println(j)
}
for n := 0; n < 5; n++ { //嵌套
if n%2 == 0 {
continue
}
fmt.Println(n)
}
for i <= 3 {
fmt.Println(i)
i = i + 1
}
}
tips:
- break:直接跳出循环
- continue:只跳过此次循环后面的部分
eg:123break456---->123;123(continue4)56--->12456;12(3continue)456---->123456
2.4 if else
格式:
if (变量定义加初始化;)条件1{ //定义的变量以及初始化的值在if外无法使用,条件不用括号
}else if 条件2{
}else{
}
示例代码:
package main
import "fmt"
func main() {
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
if 8%4 == 0 {
fmt.Println("8 is divisible by 4")
}
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
}
2.5 switch
示例代码:
package main
import (
"fmt"
"time"
)
func main() {
a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
case 4, 5:
fmt.Println("four or five")
default:
fmt.Println("other")
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
}
tips:与其他语言的switch不同,go不用写break
2.6 数组
示例代码:
package main
import "fmt"
func main() {
var a [5]int //定义一个长度为5的数组
a[4] = 100 //写入值
fmt.Println("get:", a[4])
fmt.Println("len:", len(a))
b := [5]int{1, 2, 3, 4, 5}
fmt.Println(b)
//二维数组
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
}
2.7 切片(可变长度的数组)
示例代码:
package main
import "fmt"
func main() {
s := make([]string, 3) //创建切片
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get:", s[2]) // c
fmt.Println("len:", len(s)) // 3
s = append(s, "d") //追加值
s = append(s, "e", "f")
fmt.Println(s) // [a b c d e f]
c := make([]string, len(s))
copy(c, s) //拷贝数据
fmt.Println(c) // [a b c d e f]
fmt.Println(s[2:5]) // [c d e]
fmt.Println(s[:5]) // [a b c d e]
fmt.Println(s[2:]) // [c d e f]
good := []string{"g", "o", "o", "d"}
fmt.Println(good) // [g o o d]
}
tips1:
输出时可以用%v代表(eg:fmt.Println("%v",arr))
tips2:
len:长度(底层数组);cap:内存的申请(len+预分配的内存)
cap ≥ len
arr := make([]int,3,5) //其中len=3,cap=5
arr[0],arr[1],arr[2]=2,9,7 //此时无法执行arr[3]=()来赋值,因为len只有3
brr := arr //相当于拷贝arr的len和cap
图解:
![]()
brr[0] = 4 //此时底层数组变为4,9,7,所以arr[0]的值也为4
arr与brr共享同一个底层数组,且(arr、brr)与底层数组由指针关联
append(arr,8) //向arr尾部追加一个8
图解:
![]()
brr := append(arr,8) //相当于为brr开辟了一个新的内存空间,且原空间被回收
图解:
![]()
arr = append(arr,8) //相当于为arr开辟了一个新的内存空间,且原空间被回收
图解:
![]()
ips3:
自扩容
当超了原有cap时会发生自扩容
不同的定义有不同的自扩容,但总体基本上就一个规律
自扩容的扩容大小为cap决定
- arr := make([]int, a),此种默认的cap为len,即为
- arr := make([]int, a, b),此种的cap为b
注:cap会随长度不断增加变化
tips4:
自扩容之后,指针会指向新的切片
代码例子:
package main import "fmt" func main() { arr := make([]int, 3, 5) arr[0], arr[1], arr[2] = 1, 9, 7 brr := append(arr, 8) brr = append(brr, 8) fmt.Println(len(arr), cap(arr)) //3 5 fmt.Println(len(brr), cap(brr)) //5 5 fmt.Println(brr) //[1 9 7 8 8] arr[0] = 2 fmt.Println(brr) //[2 9 7 8 8] brr = append(brr, 8) arr[0] = 3 fmt.Println(brr) //[2 9 7 8 8 8] fmt.Println(len(brr), cap(brr)) //6 10 }图解:
tips4:
遍历切片
演示代码:
package main import "fmt" func main() { arr := make([]int, 3, 5) arr[0], arr[1], arr[2] = 2, 7, 9 for i, ele := range arr { fmt.Println(i, ele) } }输出结果:
0 2
1 7
2 9
深究一下原理
演示代码:
package main import "fmt" func main() { arr := make([]int, 3, 5) arr[0], arr[1], arr[2] = 2, 7, 9 for i, ele := range arr { fmt.Printf("%p %p %d %d\n", &ele, &arr[i], ele, arr[i]) } }输出结果:
0xc00001e098 0xc00000e360 2 2
0xc00001e098 0xc00000e368 7 7
0xc00001e098 0xc00000e370 9 9
注:%p为地址,此结果在不同电脑执行都不同,但是有一点相同,ele的地址永远都是一样的,arr的地址以8递增(地址为16进制,所以8~0是8)
2.8 map
格式:
var m map[string]int
m=make(map[string]int,100)//100表示容量,避免扩容,提高性能,减少成本
m=map[string]int{"a":3,"b":6}
m["c"]=9//修改添加都是这样,重复的重写,不重复的添加
fmt.Println(m["a"])
delete(m,"a")//删除,输出结果的话只输出类型的默认值(eg:int就输出0)
//可以加一个判断
if v,exists:=m["a"];exists{ //map的固定格式,将a的key赋值给v,exists不唯一,是一个布尔类型的,判断是否存在
fmt.Println(v)
}
遍历:
for k,v:=range m{
fmt.Println(k,v)
}
演示代码:
package main
import "fmt"
func main() {
m := make(map[string]int)
m["one"] = 1
m["two"] = 2
fmt.Println(m) // map[one:1 two:2]
fmt.Println(len(m)) // 2
fmt.Println(m["one"]) // 1
fmt.Println(m["unknow"]) // 0
r, ok := m["unknow"]
fmt.Println(r, ok) // 0 false
delete(m, "one")
m2 := map[string]int{"one": 1, "two": 2}
var m3 = map[string]int{"one": 1, "two": 2}
fmt.Println(m2, m3)
}
2.9 range
主要用于遍历,可以理解为按照顺序,一步一步的
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
sum := 0
for i, num := range nums {
sum += num
if num == 2 {
fmt.Println("index:", i, "num:", num) // index: 0 num: 2
}
}
fmt.Println(sum) // 9
m := map[string]string{"a": "A", "b": "B"}
for k, v := range m {
fmt.Println(k, v) // b 8; a A
}
for k := range m {
fmt.Println("key", k) // key a; key b
}
}
2.10 函数
关键词func
格式:
func 函数名(参数 类型,参数 类型)返回数值的类型{
c:=a+b
return c //相当于把c的值存到返回值的类型
}
//main主函数调用
func main(){ //程序入口
m,n:=4,7
q:=函数名(m,n)
}
//没有返回值可以直接函数名(参数)
//返回值可以有多个
演示代码:
package main
import "fmt"
func add(a int, b int) int {
return a + b
}
func add2(a, b int) int {
return a + b
}
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return v, ok
}
func main() {
res := add(1, 2)
fmt.Println(res) // 3
v, ok := exists(map[string]string{"a": "A"}, "a")
fmt.Println(v, ok) // A True
}
2.11 指针
package main
import "fmt"
func add2(n int) {
n += 2
}
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 5
add2(n)
fmt.Println(n) // 5
add2ptr(&n)
fmt.Println(n) // 7
}
2.12 结构体
格式:
type 一个命名 struct{ //固定格式
定义一些成员变量
eg:Age int
name string
}
func main() { //主函数中格式
var 变量 上面的命名
变量=命名{成员变量赋值 eg: Age:18,name:"abcd" = 18,"abcd"(按照顺序写)}
fmt.Printf("%d",a.Age) //18 abcd
fmt.Printf("%v",a) //{18 abcd}
fmt.Printf("%+v",a) //{Age:18 name:abcd}
fmt.Printf("%#v",a) //main.hee{Age:18, name:"abcd"}
}
演示代码:
package main
import "fmt"
type user struct {
name string
password string
}
func main() {
a := user{name: "wang", password: "1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}
c.password = "1024"
var d user
d.name = "wang"
d.password = "1024"
fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
fmt.Println(checkPassword(a, "haha")) // false
fmt.Println(checkPassword2(&a, "haha")) // false
}
func checkPassword(u user, password string) bool {
return u.password == password
}
func checkPassword2(u *user, password string) bool {
return u.password == password
}
2.13 结构体方法
演示代码:
package main
import "fmt"
type user struct {
name string
password string
}
func (u user) checkPassword(password string) bool {
return u.password == password
}
func (u *user) resetPassword(password string) {
u.password = password
}
func main() {
a := user{name: "wang", password: "1024"}
a.resetPassword("2048")
fmt.Println(a.checkPassword("2048")) // true
}
2.14 错误处理
根据函数讲解部分进行改写
格式:
func f(a,b int)(int,error){
if b==0{
return 0,errors.New("除数为0")
}
b:=a/b
return d,nil //nil表示无错误
}
func main(){
p,err:=f(4,7)
if err==nil{
fmt.Println(p)
}else{
fmt.Println(err)
}
}
演示代码:
package main
import (
"errors"
"fmt"
)
type user struct {
name string
password string
}
func findUser(users []user, name string) (v *user, err error) {
for _, u := range users {
if u.name == name {
return &u, nil
}
}
return nil, errors.New("not found")
}
func main() {
u, err := findUser([]user{{"wang", "1024"}}, "wang")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(u.name) // wang
if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
fmt.Println(err) // not found
return
} else {
fmt.Println(u.name)
}
}
golang异常用err如果正常的话就返回结构和nil,异常就返回nil和异常的信息,然后主函数判断异常,golang的异常感觉比java要简单很多。
2.15 字符串操作
字符串的本质是一个不可修改的byte数组(同理可以像数组一样用for range遍历)
个人认为在go中类似于切片(暂时保留意见)
不可修改(eg:s[0]="s"这句是无法执行的)
遍历:
for i, ele := range s {
fmt.Println(i, ele) //遍历的结果不是字符,而是Unicode码
}
字母和数字占1个byte,汉字占3个byte
a := "1"
fmt.Println(len(a)) //1
a = "a"
fmt.Println(len(a)) //1
a = "你"
fmt.Println(len(a)) //3
特殊情况:
arr:=[]rune(a)
fmt.Println(len(arr)) //1
字符串可以不用双引号,可以用反引号“·”来,但是换行不能用\n,直接回车就好,反引号基本上是直接输原文
字符串的拼接直接用“+”
演示代码:
package main
import (
"fmt"
"strings"
)
func main() {
a := "hello"
fmt.Println(strings.Contains(a, "ll")) // true
fmt.Println(strings.Count(a, "l")) // 2
fmt.Println(strings.HasPrefix(a, "he")) // true
fmt.Println(strings.HasSuffix(a, "llo")) // true
fmt.Println(strings.Index(a, "ll")) // 2
fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
fmt.Println(strings.Repeat(a, 2)) // hellohello
fmt.Println(strings.Replace(a, "e", "E", -1)) // hEllo
fmt.Println(strings.Split("a-b-c", "-")) // [a b c]
fmt.Println(strings.ToLower(a)) // hello
fmt.Println(strings.ToUpper(a)) // HELLO
fmt.Println(len(a)) // 5
b := "你好"
fmt.Println(len(b)) // 6
}
注:
fmt.Printf("%c\n", 65) //A fmt.Printf("%s\n", "你好") //你好 fmt.Printf("%q\n", 65) //'A' fmt.Printf("%q\n", "65你好") //"65你好"在Go语言中,
%c和%s是用于格式化输出的格式化占位符。 -%c用于格式化一个字符(rune),它将一个整数值(Unicode码)转换为相应的字符,并在输出中显示。例如:
go fmt.Printf("%c", 65) // 输出:A在上面的示例中,整数值65对应于字母'A'的Unicode码,
%c将其转换为字符输出。 -%s用于格式化一个字符串,它将一个字符串值直接显示在输出中。 例如:go fmt.Printf("%s", "Hello, World!") // 输出:Hello, World!使用
%s,字符串值会直接原样输出。 需要注意的是,%c用于字符的格式化,而%s用于字符串的格式化。它们之间的区别在于,%c需要一个整数值(Unicode码),而%s需要一个字符串值。另外,%s也可以用于格式化其他类型的值,如整数和浮点数,它会将其转换为字符串后输出。go fmt.Printf("%s", 65) // 输出:65需要注意的是,考虑到Unicode字符可能占用多个字节,为了正确处理使用
%s格式化输出的Unicode字符串,建议使用%q占位符,它会在输出中使用单引号包裹字符串,以区分空字符串和空格字符。go fmt.Printf("%q", "Hello, 世界!") // 输出:"Hello, 世界!"总结:(暂时本人总结)
%c需要填写unicode码,而且只能输出一个字母、数字或者汉字,%s是全字符串,%p是在原本字母上加单引号输出或者全字符串
2.16 字符串格式化
package main
import "fmt"
type point struct {
x, y int
}
func main() {
s := "hello"
n := 123
p := point{1, 2}
fmt.Println(s, n) // hello 123
fmt.Println(p) // {1 2}
fmt.Printf("s=%v\n", s) // s=hello
fmt.Printf("n=%v\n", n) // n=123
fmt.Printf("p=%v\n", p) // p={1 2}
fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
f := 3.141592653
fmt.Println(f) // 3.141592653
fmt.Printf("%.2f\n", f) // 3.14
}
2.17 JSON操作
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {
Name string
Age int `json:"age"`
Hobby []string
}
func main() {
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(buf) // [123 34 78 97...]
fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
buf, err = json.MarshalIndent(a, "", "\t")
if err != nil {
panic(err)
}
fmt.Println(string(buf))
var b userInfo
err = json.Unmarshal(buf, &b)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
}
2.19 时间处理
时间精度:
Year(年) Month(月) Day(日) Hour(小时) Minute(分钟) Second(秒) Milliseconds(毫秒)
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now() //获得当前时间
fmt.Println(now) // 2023-0-27 18:04:59.433297 +0800 CST m=+0.000087933
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
fmt.Println(t) // 2022-03-27 01:25:36 +0000 UTC
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
fmt.Println(t.Format("2006-01-02 15:04:05")) // 2022-03-27 01:25:36
diff := t2.Sub(t)
fmt.Println(diff) // 1h5m0s
fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
if err != nil {
panic(err)
}
fmt.Println(t3 == t) // true
fmt.Println(now.Unix()) // 1648738080
}
2.19 数字解析(字符串和数字的转化)
package main
import (
"fmt"
"strconv"
)
func main() {
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) // 1.234
n, _ := strconv.ParseInt("111", 10, 64)
fmt.Println(n) // 111
n, _ = strconv.ParseInt("0x1000", 0, 64)
fmt.Println(n) // 4096
n2, _ := strconv.Atoi("123")
fmt.Println(n2) // 123
n2, err := strconv.Atoi("AAA")
fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
}
2.20 进程信息
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
// go run example/20-env/main.go a b c d
fmt.Println(os.Args) // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
fmt.Println(os.Setenv("AA", "BB"))
buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
if err != nil {
panic(err)
}
fmt.Println(string(buf)) // 127.0.0.1 localhost
}
2.21 defer
defer在最后执行,多个defer共同存在时,后注册的先执行
defer后跟随一个go的语句,有变量的注册时就顺便已经赋值变量(只看前面的定义)
defer后跟随一个匿名函数,有变量的defer执行的时候才赋值(看到最后的定义)
演示代码:
package main
import "fmt"
func f() int {
a, b := 3, 5
c := a + b
defer fmt.Println("111", c)
fmt.Println(c)
defer fmt.Println("222", c)
defer func() { //匿名函数
fmt.Println("333", c)
}()
c = 100
return c
}
func main() {
f()
}
输出结果:
8
333 100
222 8
111 8
2.22 接口
本质是一组行为规范的集合
格式:
type 名称 interface{
函数名(参数)返回值 //可以有多个
}
type 名称 interface{ //空接口
}
//空接口相当于主函数写var a interface{}
接口在定义后需要再定义结构体,并且需要实现接口的方法
方法格式:
func (结构体名称)方法名(参数)返回值{
}
演示代码:
package main
import "fmt"
type Human interface {
Say(int, int) int
}
func foo(h Human) {
c := h.Say(3, 6)
fmt.Println("c=", c)
}
func main() {
var a Human
t := Tom{}
a = t
foo(a) //c= -3
j := Jim{}
a = j
foo(a) //c= 9
}
type Tom struct {
}
func (Tom) Say(a int, b int) int {
return a - b
}
type Jim struct {
}
func (Jim) Say(a int, b int) int {
return a + b
}