go语言学习

321 阅读20分钟

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。

总结:

  1. 高性能
  2. 语法简单、学习曲线平缓
  3. 丰富的标准库
  4. 完整的工具链
  5. 静态连接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收

tips:快速上手、应用广、便携方便

1.2 应用的公司

  • 字节跳动
  • 腾讯
  • 美团
  • 滴滴
  • 百度
  • Google
  • face book
  • 七牛云
  • 哔哩哔哩
  • Ping CAP
  • ......

tips:用的很广

1.3 字节为什么用go

  1. 性能问题(业务体量的增长)
  2. c++不太适合在线web业务
  3. 早期团队非java背景
  4. 部署简单、学习成本低
  5. 内部RPC和HTTP框架的推广

go语言的入门

2.1 开发环境

2.1.1 安装Golang

访问go.dev-->Download-->下载适合自己电脑操作系统的安装包-->傻瓜式安装

中文镜像网:

2.1.2 配置集成开发环境

2.2 基础语法

Go Hello World 实例

Go 语言的基础组成有以下几个部分:

  • 包声明
  • 引入包
  • 函数
  • 变量
  • 语句 & 表达式
  • 注释

接下来让我们来看下简单的代码,该代码输出了"Hello World!":

实例

package main

import "fmt"

func main() {
/* 这是我的第一个简单的程序 */
fmt.Println("Hello, World!")
}

让我们来看下以上程序的各个部分:

  1. 第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
  2. 下一行 import "fmt"  告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。
  3. 下一行 func main()  是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。
  4. 下一行 /.../ 是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
  5. 下一行 fmt.Println(...)  可以将字符串输出到控制台,并在最后自动增加换行字符 \n。
    使用 fmt.Print("hello, world\n") 可以得到相同的结果。
    Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。
  6. 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。

执行 Go 程序

让我们来看下如何编写 Go 代码并执行它。步骤如下:

  1. 打开编辑器如Sublime2,将以上代码添加到编辑器中。

  2. 将以上代码保存为 hello.go

  3. 打开命令行,并进入程序文件保存的目录中。

  4. 输入命令 go run hello.go 并按回车执行代码。

  5. 如果操作正确你将在屏幕上看到  "Hello World!"  字样的输出。

    $ go run hello.go
    Hello, World!
    
  6. 我们还可以使用 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。

序号类型和描述
1uint8 无符号 8 位整型 (0 到 255)
2uint16 无符号 16 位整型 (0 到 65535)
3uint32 无符号 32 位整型 (0 到 4294967295)
4uint64 无符号 64 位整型 (0 到 18446744073709551615)
5int8 有符号 8 位整型 (-128 到 127)
6int16 有符号 16 位整型 (-32768 到 32767)
7int32 有符号 32 位整型 (-2147483648 到 2147483647)
8int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

浮点型

序号类型和描述
1float32 IEEE-754 32位浮点型数
2float64 IEEE-754 64位浮点型数
3complex64 32 位实数和虚数
4complex128 64 位实数和虚数

其他数字类型

以下列出了其他更多的数字类型:

序号类型和描述
1byte 类似 uint8
2rune 类似 int32
3uint 32 或 64 位
4int 与 uint 一样大小
5uintptr 无符号整型,用于存放一个指针

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

图解:

图解.jpg

brr[0] = 4 //此时底层数组变为4,9,7,所以arr[0]的值也为4

arr与brr共享同一个底层数组,且(arr、brr)与底层数组由指针关联

append(arr,8) //向arr尾部追加一个8

图解:

图解2.jpg

brr := append(arr,8) //相当于为brr开辟了一个新的内存空间,且原空间被回收

图解:

图解3.jpg

arr = append(arr,8) //相当于为arr开辟了一个新的内存空间,且原空间被回收

图解:

图解4.jpg

ips3:

自扩容

当超了原有cap时会发生自扩容

不同的定义有不同的自扩容,但总体基本上就一个规律

自扩容的扩容大小为cap决定

  1. arr := make([]int, a),此种默认的cap为len,即为
  2. 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  
}

图解:

图解5.jpg

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  
}